/* * Routines that are exclusive to the generator process. * * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2002 Martin Pool <mbp@samba.org> * Copyright (C) 2003-2023 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */ #include "rsync.h"#include "inums.h"#include "ifuncs.h" extern int dry_run;extern int do_xfers;extern int stdout_format_has_i;extern int logfile_format_has_i;extern int am_root;extern int operator_path_resolve;extern int am_server;extern int am_daemon;extern int inc_recurse;extern int relative_paths;extern int implied_dirs;extern int keep_dirlinks;extern int write_devices;extern int preserve_acls;extern int preserve_xattrs;extern int preserve_links;extern int preserve_devices;extern int preserve_specials;extern int drop_devices;extern int preserve_hard_links;extern int preserve_executability;extern int preserve_perms;extern int preserve_mtimes;extern int omit_dir_times;extern int omit_link_times;extern int delete_mode;extern int delete_before;extern int delete_during;extern int delete_after;extern int missing_args;extern int msgdone_cnt;extern int ignore_errors;extern int remove_source_files;extern int delay_updates;extern int update_only;extern int human_readable;extern int ignore_existing;extern int ignore_non_existing;extern int want_xattr_optim;extern int modify_window;extern int inplace;extern int append_mode;extern int make_backups;extern int csum_length;extern int xfer_sum_len;extern int ignore_times;extern int size_only;extern OFF_T max_size;extern OFF_T min_size;extern int io_error;extern int flist_eof;extern int allowed_lull;extern int sock_f_out;extern int protocol_version;extern int file_total;extern int fuzzy_basis;extern int always_checksum;extern int flist_csum_len;extern char *partial_dir;extern int alt_dest_type;extern int whole_file;extern int list_only;extern int read_batch;extern int write_batch;extern int safe_symlinks;extern int32 block_size;extern int unsort_ndx;extern int max_delete;extern int force_delete;extern int one_file_system;extern int skipped_deletes;extern dev_t filesystem_dev;extern mode_t orig_umask;extern uid_t our_uid;extern char *tmpdir;extern char *basis_dir[MAX_BASIS_DIRS+1];extern struct file_list *cur_flist, *first_flist, *dir_flist;extern filter_rule_list filter_list, daemon_filter_list; int maybe_ATTRS_REPORT = 0;int maybe_ATTRS_ACCURATE_TIME = 0; static dev_t dev_zero;static int deldelay_size = 0, deldelay_cnt = 0;static char *deldelay_buf = NULL;static int deldelay_fd = -1;static int loopchk_limit;static int dir_tweaking;static int symlink_timeset_failed_flags;static int need_retouch_dir_times;static int need_retouch_dir_perms;static const char *solo_file = NULL; /* Forward declarations. */#ifdef SUPPORT_HARD_LINKSstatic void handle_skipped_hlink(struct file_struct *file, int itemizing, enum logcode code, int f_out);#endif #define EARLY_DELAY_DONE_MSG() (!delay_updates)#define EARLY_DELETE_DONE_MSG() (!(delete_during == 2 || delete_after)) static int start_delete_delay_temp(void){ char fnametmp[MAXPATHLEN]; int save_dry_run = dry_run; dry_run = 0; if (!get_tmpname(fnametmp, "deldelay", False) || (deldelay_fd = do_mkstemp(fnametmp, 0600)) < 0) { rprintf(FINFO, "NOTE: Unable to create delete-delay temp file%s.\n", inc_recurse ? "" : " -- switching to --delete-after"); delete_during = 0; delete_after = !inc_recurse; dry_run = save_dry_run; return 0; } unlink(fnametmp); dry_run = save_dry_run; return 1;} static int flush_delete_delay(void){ if (deldelay_fd < 0 && !start_delete_delay_temp()) return 0; if (write(deldelay_fd, deldelay_buf, deldelay_cnt) != deldelay_cnt) { rsyserr(FERROR, errno, "flush of delete-delay buffer"); delete_during = 0; delete_after = !inc_recurse; close(deldelay_fd); return 0; } deldelay_cnt = 0; return 1;} static int remember_delete(struct file_struct *file, const char *fname, int flags){ int len; if (deldelay_cnt == deldelay_size && !flush_delete_delay()) return 0; if (flags & DEL_NO_UID_WRITE) deldelay_buf[deldelay_cnt++] = '!'; while (1) { len = snprintf(deldelay_buf + deldelay_cnt, deldelay_size - deldelay_cnt, "%x %s%c", (int)file->mode, fname, '\0'); if ((deldelay_cnt += len) <= deldelay_size) break; deldelay_cnt -= len; if (!flush_delete_delay()) return 0; } return 1;} static int read_delay_line(char *buf, int *flags_p){ static int read_pos = 0; unsigned int mode; int j, len; char *bp, *past_space; while (1) { for (j = read_pos; j < deldelay_cnt && deldelay_buf[j]; j++) {} if (j < deldelay_cnt) break; if (deldelay_fd < 0) { if (j > read_pos) goto invalid_data; return -1; } deldelay_cnt -= read_pos; if (deldelay_cnt == deldelay_size) goto invalid_data; if (deldelay_cnt && read_pos) { memmove(deldelay_buf, deldelay_buf + read_pos, deldelay_cnt); } len = read(deldelay_fd, deldelay_buf + deldelay_cnt, deldelay_size - deldelay_cnt); if (len == 0) { if (deldelay_cnt) { rprintf(FERROR, "ERROR: unexpected EOF in delete-delay file.\n"); } return -1; } if (len < 0) { rsyserr(FERROR, errno, "reading delete-delay file"); return -1; } deldelay_cnt += len; read_pos = 0; } bp = deldelay_buf + read_pos; if (*bp == '!') { bp++; *flags_p = DEL_NO_UID_WRITE; } else *flags_p = 0; if (sscanf(bp, "%x ", &mode) != 1) { goto invalid_data; } past_space = strchr(bp, ' '); if (!past_space) { goto invalid_data; } past_space++; /* Name length + NUL. Computed from past_space directly: the old * `j - read_pos - (past_space - bp)` form was off by +1 when a '!' * prefix had advanced bp past read_pos, over-reading deldelay_buf * by one byte on a buffer-filling final entry. */ len = (deldelay_buf + j) - past_space + 1; read_pos = j + 1; if (len > MAXPATHLEN) { rprintf(FERROR, "ERROR: filename too long in delete-delay file.\n"); return -1; } /* The caller needs the name in a MAXPATHLEN buffer, so we copy it * instead of returning a pointer to our buffer. */ memcpy(buf, past_space, len); return mode; invalid_data: rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n"); return -1;} static void do_delayed_deletions(char *delbuf){ int mode, flags; if (deldelay_fd >= 0) { if (deldelay_cnt && !flush_delete_delay()) return; lseek(deldelay_fd, 0, 0); } while ((mode = read_delay_line(delbuf, &flags)) >= 0) delete_item(delbuf, mode, flags | DEL_RECURSE); if (deldelay_fd >= 0) close(deldelay_fd);} /* This function is used to implement per-directory deletion, and is used by * all the --delete-WHEN options. Note that the fbuf pointer must point to a * MAXPATHLEN buffer with the name of the directory in it (the functions we * call will append names onto the end, but the old dir value will be restored * on exit). */static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t fs_dev){ static int already_warned = 0; static struct hashtable *dev_tbl; struct file_list *dirlist; char delbuf[MAXPATHLEN]; int dlen, i; if (!fbuf) { change_local_filter_dir(NULL, 0, 0); return; } if (DEBUG_GTE(DEL, 2)) rprintf(FINFO, "delete_in_dir(%s)\n", fbuf); if (allowed_lull) maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); if (io_error & IOERR_GENERAL && !ignore_errors) { if (already_warned) return; rprintf(FINFO, "IO error encountered -- skipping file deletion\n"); already_warned = 1; return; } dlen = strlen(fbuf); change_local_filter_dir(fbuf, dlen, F_DEPTH(file)); if (one_file_system) { if (!dev_tbl) dev_tbl = hashtable_create(16, HT_KEY64); if (file->flags & FLAG_TOP_DIR) { hashtable_find(dev_tbl, fs_dev+1, ""); filesystem_dev = fs_dev; } else if (filesystem_dev != fs_dev) { if (!hashtable_find(dev_tbl, fs_dev+1, NULL)) return; filesystem_dev = fs_dev; /* it's a prior top-dir dev */ } } dirlist = get_dirlist(fbuf, dlen, 0); /* If an item in dirlist is not found in flist, delete it * from the filesystem. */ for (i = dirlist->used; i--; ) { struct file_struct *fp = dirlist->files[i]; if (!F_IS_ACTIVE(fp)) continue; if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) { if (INFO_GTE(MOUNT, 1)) rprintf(FINFO, "cannot delete mount point: %s\n", f_name(fp, NULL)); continue; } /* Here we want to match regardless of file type. Replacement * of a file with one of another type is handled separately by * a delete_item call with a DEL_MAKE_ROOM flag. */ if (flist_find_ignore_dirness(cur_flist, fp) < 0) { int flags = DEL_RECURSE; if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US) flags |= DEL_NO_UID_WRITE; f_name(fp, delbuf); if (delete_during == 2) { if (!remember_delete(fp, delbuf, flags)) break; } else delete_item(delbuf, fp->mode, flags); } } flist_free(dirlist);} /* This deletes any files on the receiving side that are not present on the * sending side. This is used by --delete-before and --delete-after. */static void do_delete_pass(void){ char fbuf[MAXPATHLEN]; STRUCT_STAT st; int j; /* dry_run is incremented when the destination doesn't exist yet. */ if (dry_run > 1 || list_only) return; for (j = 0; j < cur_flist->used; j++) { struct file_struct *file = cur_flist->sorted[j]; if (!F_IS_ACTIVE(file)) continue; f_name(file, fbuf); if (!(file->flags & FLAG_CONTENT_DIR)) { change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(file)); continue; } if (DEBUG_GTE(DEL, 1) && file->flags & FLAG_TOP_DIR) rprintf(FINFO, "deleting in %s\n", fbuf); if (link_stat(fbuf, &st, keep_dirlinks) < 0 || !S_ISDIR(st.st_mode)) continue; delete_in_dir(fbuf, file, st.st_dev); } delete_in_dir(NULL, NULL, dev_zero); if (INFO_GTE(FLIST, 2) && !am_server) rprintf(FINFO, " \r");} static inline int mtime_differs(STRUCT_STAT *stp, struct file_struct *file){#ifdef ST_MTIME_NSEC return !same_time(stp->st_mtime, stp->ST_MTIME_NSEC, file->modtime, F_MOD_NSEC_or_0(file));#else return !same_time(stp->st_mtime, 0, file->modtime, 0);#endif} static inline int any_time_differs(stat_x *sxp, struct file_struct *file, UNUSED(const char *fname)){ int differs = mtime_differs(&sxp->st, file);#ifdef SUPPORT_CRTIMES if (!differs && crtimes_ndx) { if (sxp->crtime == 0) sxp->crtime = get_create_time(fname, &sxp->st); differs = !same_time(sxp->crtime, 0, F_CRTIME(file), 0); }#endif return differs;} static inline int perms_differ(struct file_struct *file, stat_x *sxp){ if (preserve_perms) return !BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS); if (preserve_executability) return (sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0); return 0;} static inline int ownership_differs(struct file_struct *file, stat_x *sxp){ if (am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file)) return 1; if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file)) return 1; return 0;} #ifdef SUPPORT_ACLSstatic inline int acls_differ(const char *fname, struct file_struct *file, stat_x *sxp){ if (preserve_acls) { if (!ACL_READY(*sxp)) get_acl(fname, sxp); if (set_acl(NULL, file, sxp, file->mode)) return 1; } return 0;}#endif #ifdef SUPPORT_XATTRSstatic inline int xattrs_differ(const char *fname, struct file_struct *file, stat_x *sxp){ if (preserve_xattrs) { if (!XATTR_READY(*sxp)) get_xattr(fname, -1, sxp); if (xattr_diff(file, sxp, 0)) return 1; } return 0;}#endif int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp){ if (S_ISLNK(file->mode)) {#ifdef CAN_SET_SYMLINK_TIMES if (preserve_mtimes && !omit_link_times && any_time_differs(sxp, file, fname)) return 0;#endif#ifdef CAN_CHMOD_SYMLINK if (perms_differ(file, sxp)) return 0;#endif#ifdef CAN_CHOWN_SYMLINK if (ownership_differs(file, sxp)) return 0;#endif#if defined SUPPORT_ACLS && 0 /* no current symlink-ACL support */ if (acls_differ(fname, file, sxp)) return 0;#endif#if defined SUPPORT_XATTRS && !defined NO_SYMLINK_XATTRS if (xattrs_differ(fname, file, sxp)) return 0;#endif } else { if (preserve_mtimes && any_time_differs(sxp, file, fname)) return 0; if (perms_differ(file, sxp))Lines 1–500 of 2929. Load more
/* * Routines that are exclusive to the generator process. * * Copyright (C) 1996-2000 Andrew Tridgell * Copyright (C) 1996 Paul Mackerras * Copyright (C) 2002 Martin Pool <mbp@samba.org> * Copyright (C) 2003-2023 Wayne Davison * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, visit the http://fsf.org website. */ #include "rsync.h" #include "inums.h" #include "ifuncs.h" extern int dry_run; extern int do_xfers; extern int stdout_format_has_i; extern int logfile_format_has_i; extern int am_root; extern int operator_path_resolve; extern int am_server; extern int am_daemon; extern int inc_recurse; extern int relative_paths; extern int implied_dirs; extern int keep_dirlinks; extern int write_devices; extern int preserve_acls; extern int preserve_xattrs; extern int preserve_links; extern int preserve_devices; extern int preserve_specials; extern int drop_devices; extern int preserve_hard_links; extern int preserve_executability; extern int preserve_perms; extern int preserve_mtimes; extern int omit_dir_times; extern int omit_link_times; extern int delete_mode; extern int delete_before; extern int delete_during; extern int delete_after; extern int missing_args; extern int msgdone_cnt; extern int ignore_errors; extern int remove_source_files; extern int delay_updates; extern int update_only; extern int human_readable; extern int ignore_existing; extern int ignore_non_existing; extern int want_xattr_optim; extern int modify_window; extern int inplace; extern int append_mode; extern int make_backups; extern int csum_length; extern int xfer_sum_len; extern int ignore_times; extern int size_only; extern OFF_T max_size; extern OFF_T min_size; extern int io_error; extern int flist_eof; extern int allowed_lull; extern int sock_f_out; extern int protocol_version; extern int file_total; extern int fuzzy_basis; extern int always_checksum; extern int flist_csum_len; extern char *partial_dir; extern int alt_dest_type; extern int whole_file; extern int list_only; extern int read_batch; extern int write_batch; extern int safe_symlinks; extern int32 block_size; extern int unsort_ndx; extern int max_delete; extern int force_delete; extern int one_file_system; extern int skipped_deletes; extern dev_t filesystem_dev; extern mode_t orig_umask; extern uid_t our_uid; extern char *tmpdir; extern char *basis_dir[MAX_BASIS_DIRS+1]; extern struct file_list *cur_flist, *first_flist, *dir_flist; extern filter_rule_list filter_list, daemon_filter_list; int maybe_ATTRS_REPORT = 0; int maybe_ATTRS_ACCURATE_TIME = 0; static dev_t dev_zero; static int deldelay_size = 0, deldelay_cnt = 0; static char *deldelay_buf = NULL; static int deldelay_fd = -1; static int loopchk_limit; static int dir_tweaking; static int symlink_timeset_failed_flags; static int need_retouch_dir_times; static int need_retouch_dir_perms; static const char *solo_file = NULL; /* Forward declarations. */ #ifdef SUPPORT_HARD_LINKS static void handle_skipped_hlink(struct file_struct *file, int itemizing, enum logcode code, int f_out); #endif #define EARLY_DELAY_DONE_MSG() (!delay_updates) #define EARLY_DELETE_DONE_MSG() (!(delete_during == 2 || delete_after)) static int start_delete_delay_temp(void) { char fnametmp[MAXPATHLEN]; int save_dry_run = dry_run; dry_run = 0; if (!get_tmpname(fnametmp, "deldelay", False) || (deldelay_fd = do_mkstemp(fnametmp, 0600)) < 0) { rprintf(FINFO, "NOTE: Unable to create delete-delay temp file%s.\n", inc_recurse ? "" : " -- switching to --delete-after"); delete_during = 0; delete_after = !inc_recurse; dry_run = save_dry_run; return 0; } unlink(fnametmp); dry_run = save_dry_run; return 1; } static int flush_delete_delay(void) { if (deldelay_fd < 0 && !start_delete_delay_temp()) return 0; if (write(deldelay_fd, deldelay_buf, deldelay_cnt) != deldelay_cnt) { rsyserr(FERROR, errno, "flush of delete-delay buffer"); delete_during = 0; delete_after = !inc_recurse; close(deldelay_fd); return 0; } deldelay_cnt = 0; return 1; } static int remember_delete(struct file_struct *file, const char *fname, int flags) { int len; if (deldelay_cnt == deldelay_size && !flush_delete_delay()) return 0; if (flags & DEL_NO_UID_WRITE) deldelay_buf[deldelay_cnt++] = '!'; while (1) { len = snprintf(deldelay_buf + deldelay_cnt, deldelay_size - deldelay_cnt, "%x %s%c", (int)file->mode, fname, '\0'); if ((deldelay_cnt += len) <= deldelay_size) break; deldelay_cnt -= len; if (!flush_delete_delay()) return 0; } return 1; } static int read_delay_line(char *buf, int *flags_p) { static int read_pos = 0; unsigned int mode; int j, len; char *bp, *past_space; while (1) { for (j = read_pos; j < deldelay_cnt && deldelay_buf[j]; j++) {} if (j < deldelay_cnt) break; if (deldelay_fd < 0) { if (j > read_pos) goto invalid_data; return -1; } deldelay_cnt -= read_pos; if (deldelay_cnt == deldelay_size) goto invalid_data; if (deldelay_cnt && read_pos) { memmove(deldelay_buf, deldelay_buf + read_pos, deldelay_cnt); } len = read(deldelay_fd, deldelay_buf + deldelay_cnt, deldelay_size - deldelay_cnt); if (len == 0) { if (deldelay_cnt) { rprintf(FERROR, "ERROR: unexpected EOF in delete-delay file.\n"); } return -1; } if (len < 0) { rsyserr(FERROR, errno, "reading delete-delay file"); return -1; } deldelay_cnt += len; read_pos = 0; } bp = deldelay_buf + read_pos; if (*bp == '!') { bp++; *flags_p = DEL_NO_UID_WRITE; } else *flags_p = 0; if (sscanf(bp, "%x ", &mode) != 1) { goto invalid_data; } past_space = strchr(bp, ' '); if (!past_space) { goto invalid_data; } past_space++; /* Name length + NUL. Computed from past_space directly: the old * `j - read_pos - (past_space - bp)` form was off by +1 when a '!' * prefix had advanced bp past read_pos, over-reading deldelay_buf * by one byte on a buffer-filling final entry. */ len = (deldelay_buf + j) - past_space + 1; read_pos = j + 1; if (len > MAXPATHLEN) { rprintf(FERROR, "ERROR: filename too long in delete-delay file.\n"); return -1; } /* The caller needs the name in a MAXPATHLEN buffer, so we copy it * instead of returning a pointer to our buffer. */ memcpy(buf, past_space, len); return mode; invalid_data: rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n"); return -1; } static void do_delayed_deletions(char *delbuf) { int mode, flags; if (deldelay_fd >= 0) { if (deldelay_cnt && !flush_delete_delay()) return; lseek(deldelay_fd, 0, 0); } while ((mode = read_delay_line(delbuf, &flags)) >= 0) delete_item(delbuf, mode, flags | DEL_RECURSE); if (deldelay_fd >= 0) close(deldelay_fd); } /* This function is used to implement per-directory deletion, and is used by * all the --delete-WHEN options. Note that the fbuf pointer must point to a * MAXPATHLEN buffer with the name of the directory in it (the functions we * call will append names onto the end, but the old dir value will be restored * on exit). */ static void delete_in_dir(char *fbuf, struct file_struct *file, dev_t fs_dev) { static int already_warned = 0; static struct hashtable *dev_tbl; struct file_list *dirlist; char delbuf[MAXPATHLEN]; int dlen, i; if (!fbuf) { change_local_filter_dir(NULL, 0, 0); return; } if (DEBUG_GTE(DEL, 2)) rprintf(FINFO, "delete_in_dir(%s)\n", fbuf); if (allowed_lull) maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH); if (io_error & IOERR_GENERAL && !ignore_errors) { if (already_warned) return; rprintf(FINFO, "IO error encountered -- skipping file deletion\n"); already_warned = 1; return; } dlen = strlen(fbuf); change_local_filter_dir(fbuf, dlen, F_DEPTH(file)); if (one_file_system) { if (!dev_tbl) dev_tbl = hashtable_create(16, HT_KEY64); if (file->flags & FLAG_TOP_DIR) { hashtable_find(dev_tbl, fs_dev+1, ""); filesystem_dev = fs_dev; } else if (filesystem_dev != fs_dev) { if (!hashtable_find(dev_tbl, fs_dev+1, NULL)) return; filesystem_dev = fs_dev; /* it's a prior top-dir dev */ } } dirlist = get_dirlist(fbuf, dlen, 0); /* If an item in dirlist is not found in flist, delete it * from the filesystem. */ for (i = dirlist->used; i--; ) { struct file_struct *fp = dirlist->files[i]; if (!F_IS_ACTIVE(fp)) continue; if (fp->flags & FLAG_MOUNT_DIR && S_ISDIR(fp->mode)) { if (INFO_GTE(MOUNT, 1)) rprintf(FINFO, "cannot delete mount point: %s\n", f_name(fp, NULL)); continue; } /* Here we want to match regardless of file type. Replacement * of a file with one of another type is handled separately by * a delete_item call with a DEL_MAKE_ROOM flag. */ if (flist_find_ignore_dirness(cur_flist, fp) < 0) { int flags = DEL_RECURSE; if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US) flags |= DEL_NO_UID_WRITE; f_name(fp, delbuf); if (delete_during == 2) { if (!remember_delete(fp, delbuf, flags)) break; } else delete_item(delbuf, fp->mode, flags); } } flist_free(dirlist); } /* This deletes any files on the receiving side that are not present on the * sending side. This is used by --delete-before and --delete-after. */ static void do_delete_pass(void) { char fbuf[MAXPATHLEN]; STRUCT_STAT st; int j; /* dry_run is incremented when the destination doesn't exist yet. */ if (dry_run > 1 || list_only) return; for (j = 0; j < cur_flist->used; j++) { struct file_struct *file = cur_flist->sorted[j]; if (!F_IS_ACTIVE(file)) continue; f_name(file, fbuf); if (!(file->flags & FLAG_CONTENT_DIR)) { change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(file)); continue; } if (DEBUG_GTE(DEL, 1) && file->flags & FLAG_TOP_DIR) rprintf(FINFO, "deleting in %s\n", fbuf); if (link_stat(fbuf, &st, keep_dirlinks) < 0 || !S_ISDIR(st.st_mode)) continue; delete_in_dir(fbuf, file, st.st_dev); } delete_in_dir(NULL, NULL, dev_zero); if (INFO_GTE(FLIST, 2) && !am_server) rprintf(FINFO, " \r"); } static inline int mtime_differs(STRUCT_STAT *stp, struct file_struct *file) { #ifdef ST_MTIME_NSEC return !same_time(stp->st_mtime, stp->ST_MTIME_NSEC, file->modtime, F_MOD_NSEC_or_0(file)); #else return !same_time(stp->st_mtime, 0, file->modtime, 0); #endif } static inline int any_time_differs(stat_x *sxp, struct file_struct *file, UNUSED(const char *fname)) { int differs = mtime_differs(&sxp->st, file); #ifdef SUPPORT_CRTIMES if (!differs && crtimes_ndx) { if (sxp->crtime == 0) sxp->crtime = get_create_time(fname, &sxp->st); differs = !same_time(sxp->crtime, 0, F_CRTIME(file), 0); } #endif return differs; } static inline int perms_differ(struct file_struct *file, stat_x *sxp) { if (preserve_perms) return !BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS); if (preserve_executability) return (sxp->st.st_mode & 0111 ? 1 : 0) ^ (file->mode & 0111 ? 1 : 0); return 0; } static inline int ownership_differs(struct file_struct *file, stat_x *sxp) { if (am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file)) return 1; if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file)) return 1; return 0; } #ifdef SUPPORT_ACLS static inline int acls_differ(const char *fname, struct file_struct *file, stat_x *sxp) { if (preserve_acls) { if (!ACL_READY(*sxp)) get_acl(fname, sxp); if (set_acl(NULL, file, sxp, file->mode)) return 1; } return 0; } #endif #ifdef SUPPORT_XATTRS static inline int xattrs_differ(const char *fname, struct file_struct *file, stat_x *sxp) { if (preserve_xattrs) { if (!XATTR_READY(*sxp)) get_xattr(fname, -1, sxp); if (xattr_diff(file, sxp, 0)) return 1; } return 0; } #endif int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp) { if (S_ISLNK(file->mode)) { #ifdef CAN_SET_SYMLINK_TIMES if (preserve_mtimes && !omit_link_times && any_time_differs(sxp, file, fname)) return 0; #endif #ifdef CAN_CHMOD_SYMLINK if (perms_differ(file, sxp)) return 0; #endif #ifdef CAN_CHOWN_SYMLINK if (ownership_differs(file, sxp)) return 0; #endif #if defined SUPPORT_ACLS && 0 /* no current symlink-ACL support */ if (acls_differ(fname, file, sxp)) return 0; #endif #if defined SUPPORT_XATTRS && !defined NO_SYMLINK_XATTRS if (xattrs_differ(fname, file, sxp)) return 0; #endif } else { if (preserve_mtimes && any_time_differs(sxp, file, fname)) return 0; if (perms_differ(file, sxp))