Annotation of embedaddon/rsync/patches/fileflags.diff, revision 1.1
1.1 ! misho 1: This patch provides --fileflags, which preserves the st_flags stat() field.
! 2: Modified from a patch that was written by Rolf Grossmann.
! 3:
! 4: To use this patch, run these commands for a successful build:
! 5:
! 6: patch -p1 <patches/fileflags.diff
! 7: ./configure (optional if already run)
! 8: make
! 9:
! 10: based-on: e94bad1c156fc3910f24e2b3b71a81b0b0bdeb70
! 11: diff --git a/compat.c b/compat.c
! 12: --- a/compat.c
! 13: +++ b/compat.c
! 14: @@ -39,6 +39,7 @@ extern int checksum_seed;
! 15: extern int basis_dir_cnt;
! 16: extern int prune_empty_dirs;
! 17: extern int protocol_version;
! 18: +extern int force_change;
! 19: extern int protect_args;
! 20: extern int preserve_uid;
! 21: extern int preserve_gid;
! 22: @@ -46,6 +47,7 @@ extern int preserve_atimes;
! 23: extern int preserve_crtimes;
! 24: extern int preserve_acls;
! 25: extern int preserve_xattrs;
! 26: +extern int preserve_fileflags;
! 27: extern int xfer_flags_as_varint;
! 28: extern int need_messages_from_generator;
! 29: extern int delete_mode, delete_before, delete_during, delete_after;
! 30: @@ -77,7 +79,7 @@ int do_negotiated_strings = 0;
! 31: int xmit_id0_names = 0;
! 32:
! 33: /* These index values are for the file-list's extra-attribute array. */
! 34: -int pathname_ndx, depth_ndx, atimes_ndx, crtimes_ndx, uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
! 35: +int pathname_ndx, depth_ndx, atimes_ndx, crtimes_ndx, uid_ndx, gid_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
! 36:
! 37: int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
! 38: int sender_symlink_iconv = 0; /* sender should convert symlink content */
! 39: @@ -566,6 +568,8 @@ void setup_protocol(int f_out,int f_in)
! 40: uid_ndx = ++file_extra_cnt;
! 41: if (preserve_gid)
! 42: gid_ndx = ++file_extra_cnt;
! 43: + if (preserve_fileflags || (force_change && !am_sender))
! 44: + fileflags_ndx = ++file_extra_cnt;
! 45: if (preserve_acls && !am_sender)
! 46: acls_ndx = ++file_extra_cnt;
! 47: if (preserve_xattrs)
! 48: @@ -726,6 +730,10 @@ void setup_protocol(int f_out,int f_in)
! 49: fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --crtimes.\n");
! 50: exit_cleanup(RERR_PROTOCOL);
! 51: }
! 52: + if (!xfer_flags_as_varint && preserve_fileflags) {
! 53: + fprintf(stderr, "Both rsync versions must be at least 3.2.0 for --fileflags.\n");
! 54: + exit_cleanup(RERR_PROTOCOL);
! 55: + }
! 56: if (am_sender) {
! 57: receiver_symlink_times = am_server
! 58: ? strchr(client_info, 'L') != NULL
! 59: diff --git a/delete.c b/delete.c
! 60: --- a/delete.c
! 61: +++ b/delete.c
! 62: @@ -25,6 +25,7 @@
! 63: extern int am_root;
! 64: extern int make_backups;
! 65: extern int max_delete;
! 66: +extern int force_change;
! 67: extern char *backup_dir;
! 68: extern char *backup_suffix;
! 69: extern int backup_suffix_len;
! 70: @@ -97,8 +98,12 @@ static enum delret delete_dir_contents(char *fname, uint16 flags)
! 71: }
! 72:
! 73: strlcpy(p, fp->basename, remainder);
! 74: +#ifdef SUPPORT_FORCE_CHANGE
! 75: + if (force_change)
! 76: + make_mutable(fname, fp->mode, F_FFLAGS(fp), force_change);
! 77: +#endif
! 78: if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
! 79: - do_chmod(fname, fp->mode | S_IWUSR);
! 80: + do_chmod(fname, fp->mode | S_IWUSR, NO_FFLAGS);
! 81: /* Save stack by recursing to ourself directly. */
! 82: if (S_ISDIR(fp->mode)) {
! 83: if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
! 84: @@ -139,11 +144,18 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
! 85: }
! 86:
! 87: if (flags & DEL_NO_UID_WRITE)
! 88: - do_chmod(fbuf, mode | S_IWUSR);
! 89: + do_chmod(fbuf, mode | S_IWUSR, NO_FFLAGS);
! 90:
! 91: if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
! 92: /* This only happens on the first call to delete_item() since
! 93: * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
! 94: +#ifdef SUPPORT_FORCE_CHANGE
! 95: + if (force_change) {
! 96: + STRUCT_STAT st;
! 97: + if (x_lstat(fbuf, &st, NULL) == 0)
! 98: + make_mutable(fbuf, st.st_mode, st.st_flags, force_change);
! 99: + }
! 100: +#endif
! 101: ignore_perishable = 1;
! 102: /* If DEL_RECURSE is not set, this just reports emptiness. */
! 103: ret = delete_dir_contents(fbuf, flags);
! 104: diff --git a/flist.c b/flist.c
! 105: --- a/flist.c
! 106: +++ b/flist.c
! 107: @@ -52,6 +52,7 @@ extern int preserve_links;
! 108: extern int preserve_hard_links;
! 109: extern int preserve_devices;
! 110: extern int preserve_specials;
! 111: +extern int preserve_fileflags;
! 112: extern int delete_during;
! 113: extern int missing_args;
! 114: extern int eol_nulls;
! 115: @@ -383,6 +384,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
! 116: static time_t crtime;
! 117: #endif
! 118: static mode_t mode;
! 119: +#ifdef SUPPORT_FILEFLAGS
! 120: + static uint32 fileflags;
! 121: +#endif
! 122: #ifdef SUPPORT_HARD_LINKS
! 123: static int64 dev;
! 124: #endif
! 125: @@ -426,6 +430,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
! 126: xflags |= XMIT_SAME_MODE;
! 127: else
! 128: mode = file->mode;
! 129: +#ifdef SUPPORT_FILEFLAGS
! 130: + if (preserve_fileflags) {
! 131: + if (F_FFLAGS(file) == fileflags)
! 132: + xflags |= XMIT_SAME_FLAGS;
! 133: + else
! 134: + fileflags = F_FFLAGS(file);
! 135: + }
! 136: +#endif
! 137:
! 138: if (preserve_devices && IS_DEVICE(mode)) {
! 139: if (protocol_version < 28) {
! 140: @@ -587,6 +599,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
! 141: #endif
! 142: if (!(xflags & XMIT_SAME_MODE))
! 143: write_int(f, to_wire_mode(mode));
! 144: +#ifdef SUPPORT_FILEFLAGS
! 145: + if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
! 146: + write_int(f, (int)fileflags);
! 147: +#endif
! 148: if (atimes_ndx && !S_ISDIR(mode) && !(xflags & XMIT_SAME_ATIME))
! 149: write_varlong(f, atime, 4);
! 150: if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
! 151: @@ -681,6 +697,9 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
! 152: static time_t crtime;
! 153: #endif
! 154: static mode_t mode;
! 155: +#ifdef SUPPORT_FILEFLAGS
! 156: + static uint32 fileflags;
! 157: +#endif
! 158: #ifdef SUPPORT_HARD_LINKS
! 159: static int64 dev;
! 160: #endif
! 161: @@ -797,6 +816,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
! 162: #ifdef SUPPORT_CRTIMES
! 163: if (crtimes_ndx)
! 164: crtime = F_CRTIME(first);
! 165: +#endif
! 166: +#ifdef SUPPORT_FILEFLAGS
! 167: + if (preserve_fileflags)
! 168: + fileflags = F_FFLAGS(first);
! 169: #endif
! 170: if (preserve_uid)
! 171: uid = F_OWNER(first);
! 172: @@ -869,6 +892,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
! 173:
! 174: if (chmod_modes && !S_ISLNK(mode) && mode)
! 175: mode = tweak_mode(mode, chmod_modes);
! 176: +#ifdef SUPPORT_FILEFLAGS
! 177: + if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
! 178: + fileflags = (uint32)read_int(f);
! 179: +#endif
! 180:
! 181: if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
! 182: if (protocol_version < 30)
! 183: @@ -1027,6 +1054,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
! 184: }
! 185: #endif
! 186: file->mode = mode;
! 187: +#ifdef SUPPORT_FILEFLAGS
! 188: + if (preserve_fileflags)
! 189: + F_FFLAGS(file) = fileflags;
! 190: +#endif
! 191: if (preserve_uid)
! 192: F_OWNER(file) = uid;
! 193: if (preserve_gid) {
! 194: @@ -1428,6 +1459,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
! 195: }
! 196: #endif
! 197: file->mode = st.st_mode;
! 198: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
! 199: + if (fileflags_ndx)
! 200: + F_FFLAGS(file) = st.st_flags;
! 201: +#endif
! 202: if (preserve_uid)
! 203: F_OWNER(file) = st.st_uid;
! 204: if (preserve_gid)
! 205: diff --git a/generator.c b/generator.c
! 206: --- a/generator.c
! 207: +++ b/generator.c
! 208: @@ -43,8 +43,10 @@ extern int write_devices;
! 209: extern int preserve_specials;
! 210: extern int preserve_hard_links;
! 211: extern int preserve_executability;
! 212: +extern int preserve_fileflags;
! 213: extern int preserve_perms;
! 214: extern int preserve_times;
! 215: +extern int force_change;
! 216: extern int delete_mode;
! 217: extern int delete_before;
! 218: extern int delete_during;
! 219: @@ -487,6 +489,10 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
! 220: return 0;
! 221: if (perms_differ(file, sxp))
! 222: return 0;
! 223: +#ifdef SUPPORT_FILEFLAGS
! 224: + if (preserve_fileflags && sxp->st.st_flags != F_FFLAGS(file))
! 225: + return 0;
! 226: +#endif
! 227: if (ownership_differs(file, sxp))
! 228: return 0;
! 229: #ifdef SUPPORT_ACLS
! 230: @@ -548,6 +554,11 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
! 231: iflags |= ITEM_REPORT_OWNER;
! 232: if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP) && sxp->st.st_gid != (gid_t)F_GROUP(file))
! 233: iflags |= ITEM_REPORT_GROUP;
! 234: +#ifdef SUPPORT_FILEFLAGS
! 235: + if (preserve_fileflags && !S_ISLNK(file->mode)
! 236: + && sxp->st.st_flags != F_FFLAGS(file))
! 237: + iflags |= ITEM_REPORT_FFLAGS;
! 238: +#endif
! 239: #ifdef SUPPORT_ACLS
! 240: if (preserve_acls && !S_ISLNK(file->mode)) {
! 241: if (!ACL_READY(*sxp))
! 242: @@ -1442,6 +1453,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
! 243: if (!preserve_perms) { /* See comment in non-dir code below. */
! 244: file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, statret == 0);
! 245: }
! 246: +#ifdef SUPPORT_FORCE_CHANGE
! 247: + if (force_change && !preserve_fileflags)
! 248: + F_FFLAGS(file) = sx.st.st_flags;
! 249: +#endif
! 250: if (statret != 0 && basis_dir[0] != NULL) {
! 251: int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx, itemizing, code);
! 252: if (j == -2) {
! 253: @@ -1484,10 +1499,15 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
! 254: * readable and writable permissions during the time we are
! 255: * putting files within them. This is then restored to the
! 256: * former permissions after the transfer is done. */
! 257: +#ifdef SUPPORT_FORCE_CHANGE
! 258: + if (force_change && F_FFLAGS(file) & force_change
! 259: + && make_mutable(fname, file->mode, F_FFLAGS(file), force_change))
! 260: + need_retouch_dir_perms = 1;
! 261: +#endif
! 262: #ifdef HAVE_CHMOD
! 263: if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
! 264: mode_t mode = file->mode | S_IRWXU;
! 265: - if (do_chmod(fname, mode) < 0) {
! 266: + if (do_chmod(fname, mode, 0) < 0) {
! 267: rsyserr(FERROR_XFER, errno,
! 268: "failed to modify permissions on %s",
! 269: full_fname(fname));
! 270: @@ -1522,6 +1542,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
! 271: int exists = statret == 0 && !S_ISDIR(sx.st.st_mode);
! 272: file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms, exists);
! 273: }
! 274: +#ifdef SUPPORT_FORCE_CHANGE
! 275: + if (force_change && !preserve_fileflags)
! 276: + F_FFLAGS(file) = sx.st.st_flags;
! 277: +#endif
! 278:
! 279: #ifdef SUPPORT_HARD_LINKS
! 280: if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
! 281: @@ -2102,17 +2126,25 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
! 282: continue;
! 283: fname = f_name(file, NULL);
! 284: if (fix_dir_perms)
! 285: - do_chmod(fname, file->mode);
! 286: + do_chmod(fname, file->mode, 0);
! 287: if (need_retouch_dir_times) {
! 288: STRUCT_STAT st;
! 289: if (link_stat(fname, &st, 0) == 0 && mtime_differs(&st, file)) {
! 290: st.st_mtime = file->modtime;
! 291: #ifdef ST_MTIME_NSEC
! 292: st.ST_MTIME_NSEC = F_MOD_NSEC_or_0(file);
! 293: +#endif
! 294: +#ifdef SUPPORT_FORCE_CHANGE
! 295: + st.st_mode = file->mode;
! 296: + st.st_flags = 0;
! 297: #endif
! 298: set_times(fname, &st);
! 299: }
! 300: }
! 301: +#ifdef SUPPORT_FORCE_CHANGE
! 302: + if (force_change && F_FFLAGS(file) & force_change)
! 303: + undo_make_mutable(fname, F_FFLAGS(file));
! 304: +#endif
! 305: if (counter >= loopchk_limit) {
! 306: if (allowed_lull)
! 307: maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
! 308: diff --git a/log.c b/log.c
! 309: --- a/log.c
! 310: +++ b/log.c
! 311: @@ -725,7 +725,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
! 312: : iflags & ITEM_REPORT_ATIME ? 'u' : 'n';
! 313: c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
! 314: c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
! 315: - c[11] = '\0';
! 316: + c[11] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
! 317: + c[12] = '\0';
! 318:
! 319: if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
! 320: char ch = iflags & ITEM_IS_NEW ? '+' : '?';
! 321: diff --git a/main.c b/main.c
! 322: --- a/main.c
! 323: +++ b/main.c
! 324: @@ -31,6 +31,9 @@
! 325: #ifdef __TANDEM
! 326: #include <floss.h(floss_execlp)>
! 327: #endif
! 328: +#ifdef SUPPORT_FORCE_CHANGE
! 329: +#include <sys/sysctl.h>
! 330: +#endif
! 331:
! 332: extern int dry_run;
! 333: extern int list_only;
! 334: @@ -48,6 +51,7 @@ extern int called_from_signal_handler;
! 335: extern int need_messages_from_generator;
! 336: extern int kluge_around_eof;
! 337: extern int got_xfer_error;
! 338: +extern int force_change;
! 339: extern int msgs2stderr;
! 340: extern int module_id;
! 341: extern int read_only;
! 342: @@ -970,6 +974,22 @@ static int do_recv(int f_in, int f_out, char *local_name)
! 343: * points to an identical file won't be replaced by the referent. */
! 344: copy_links = copy_dirlinks = copy_unsafe_links = 0;
! 345:
! 346: +#ifdef SUPPORT_FORCE_CHANGE
! 347: + if (force_change & SYS_IMMUTABLE) {
! 348: + /* Determine whether we'll be able to unlock a system immutable item. */
! 349: + int mib[2];
! 350: + int securityLevel = 0;
! 351: + size_t len = sizeof securityLevel;
! 352: +
! 353: + mib[0] = CTL_KERN;
! 354: + mib[1] = KERN_SECURELVL;
! 355: + if (sysctl(mib, 2, &securityLevel, &len, NULL, 0) == 0 && securityLevel > 0) {
! 356: + rprintf(FERROR, "System security level is too high to force mutability on system immutable files and directories.\n");
! 357: + exit_cleanup(RERR_UNSUPPORTED);
! 358: + }
! 359: + }
! 360: +#endif
! 361: +
! 362: #ifdef SUPPORT_HARD_LINKS
! 363: if (preserve_hard_links && !inc_recurse)
! 364: match_hard_links(first_flist);
! 365: diff --git a/options.c b/options.c
! 366: --- a/options.c
! 367: +++ b/options.c
! 368: @@ -53,6 +53,7 @@ int preserve_hard_links = 0;
! 369: int preserve_acls = 0;
! 370: int preserve_xattrs = 0;
! 371: int preserve_perms = 0;
! 372: +int preserve_fileflags = 0;
! 373: int preserve_executability = 0;
! 374: int preserve_devices = 0;
! 375: int preserve_specials = 0;
! 376: @@ -90,6 +91,7 @@ int numeric_ids = 0;
! 377: int msgs2stderr = 2; /* Default: send errors to stderr for local & remote-shell transfers */
! 378: int allow_8bit_chars = 0;
! 379: int force_delete = 0;
! 380: +int force_change = 0;
! 381: int io_timeout = 0;
! 382: int prune_empty_dirs = 0;
! 383: int use_qsort = 0;
! 384: @@ -614,6 +616,8 @@ static struct poptOption long_options[] = {
! 385: {"perms", 'p', POPT_ARG_VAL, &preserve_perms, 1, 0, 0 },
! 386: {"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
! 387: {"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
! 388: + {"fileflags", 0, POPT_ARG_VAL, &preserve_fileflags, 1, 0, 0 },
! 389: + {"no-fileflags", 0, POPT_ARG_VAL, &preserve_fileflags, 0, 0, 0 },
! 390: {"executability", 'E', POPT_ARG_NONE, &preserve_executability, 0, 0, 0 },
! 391: {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
! 392: {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
! 393: @@ -711,6 +715,12 @@ static struct poptOption long_options[] = {
! 394: {"remove-source-files",0,POPT_ARG_VAL, &remove_source_files, 1, 0, 0 },
! 395: {"force", 0, POPT_ARG_VAL, &force_delete, 1, 0, 0 },
! 396: {"no-force", 0, POPT_ARG_VAL, &force_delete, 0, 0, 0 },
! 397: + {"force-delete", 0, POPT_ARG_VAL, &force_delete, 1, 0, 0 },
! 398: + {"no-force-delete", 0, POPT_ARG_VAL, &force_delete, 0, 0, 0 },
! 399: + {"force-change", 0, POPT_ARG_VAL, &force_change, ALL_IMMUTABLE, 0, 0 },
! 400: + {"no-force-change", 0, POPT_ARG_VAL, &force_change, 0, 0, 0 },
! 401: + {"force-uchange", 0, POPT_ARG_VAL, &force_change, USR_IMMUTABLE, 0, 0 },
! 402: + {"force-schange", 0, POPT_ARG_VAL, &force_change, SYS_IMMUTABLE, 0, 0 },
! 403: {"ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 1, 0, 0 },
! 404: {"no-ignore-errors", 0, POPT_ARG_VAL, &ignore_errors, 0, 0, 0 },
! 405: {"max-delete", 0, POPT_ARG_INT, &max_delete, 0, 0, 0 },
! 406: @@ -998,6 +1008,14 @@ static void set_refuse_options(void)
! 407: #ifndef SUPPORT_CRTIMES
! 408: parse_one_refuse_match(0, "crtimes", list_end);
! 409: #endif
! 410: +#ifndef SUPPORT_FILEFLAGS
! 411: + parse_one_refuse_match(0, "fileflags", list_end);
! 412: +#endif
! 413: +#ifndef SUPPORT_FORCE_CHANGE
! 414: + parse_one_refuse_match(0, "force-change", list_end);
! 415: + parse_one_refuse_match(0, "force-uchange", list_end);
! 416: + parse_one_refuse_match(0, "force-schange", list_end);
! 417: +#endif
! 418:
! 419: /* Now we use the descrip values to actually mark the options for refusal. */
! 420: for (op = long_options; op != list_end; op++) {
! 421: @@ -2646,6 +2664,9 @@ void server_options(char **args, int *argc_p)
! 422: if (xfer_dirs && !recurse && delete_mode && am_sender)
! 423: args[ac++] = "--no-r";
! 424:
! 425: + if (preserve_fileflags)
! 426: + args[ac++] = "--fileflags";
! 427: +
! 428: if (do_compression && do_compression_level != CLVL_NOT_SPECIFIED) {
! 429: if (asprintf(&arg, "--compress-level=%d", do_compression_level) < 0)
! 430: goto oom;
! 431: @@ -2754,6 +2775,16 @@ void server_options(char **args, int *argc_p)
! 432: args[ac++] = "--delete-excluded";
! 433: if (force_delete)
! 434: args[ac++] = "--force";
! 435: +#ifdef SUPPORT_FORCE_CHANGE
! 436: + if (force_change) {
! 437: + if (force_change == ALL_IMMUTABLE)
! 438: + args[ac++] = "--force-change";
! 439: + else if (force_change == USR_IMMUTABLE)
! 440: + args[ac++] = "--force-uchange";
! 441: + else if (force_change == SYS_IMMUTABLE)
! 442: + args[ac++] = "--force-schange";
! 443: + }
! 444: +#endif
! 445: if (write_batch < 0)
! 446: args[ac++] = "--only-write-batch=X";
! 447: if (am_root > 1)
! 448: diff --git a/rsync.1.md b/rsync.1.md
! 449: --- a/rsync.1.md
! 450: +++ b/rsync.1.md
! 451: @@ -361,6 +361,7 @@ detailed description below for a complete description.
! 452: --keep-dirlinks, -K treat symlinked dir on receiver as dir
! 453: --hard-links, -H preserve hard links
! 454: --perms, -p preserve permissions
! 455: +--fileflags preserve file-flags (aka chflags)
! 456: --executability, -E preserve executability
! 457: --chmod=CHMOD affect file and/or directory permissions
! 458: --acls, -A preserve ACLs (implies --perms)
! 459: @@ -401,7 +402,10 @@ detailed description below for a complete description.
! 460: --ignore-missing-args ignore missing source args without error
! 461: --delete-missing-args delete missing source args from destination
! 462: --ignore-errors delete even if there are I/O errors
! 463: ---force force deletion of dirs even if not empty
! 464: +--force-delete force deletion of directories even if not empty
! 465: +--force-change affect user-/system-immutable files/dirs
! 466: +--force-uchange affect user-immutable files/dirs
! 467: +--force-schange affect system-immutable files/dirs
! 468: --max-delete=NUM don't delete more than NUM files
! 469: --max-size=SIZE don't transfer any file larger than SIZE
! 470: --min-size=SIZE don't transfer any file smaller than SIZE
! 471: @@ -717,6 +721,8 @@ your home directory (remove the '=' for that).
! 472:
! 473: Note that `-a` **does not preserve hardlinks**, because finding
! 474: multiply-linked files is expensive. You must separately specify `-H`.
! 475: + Note also that for backward compatibility, `-a` currently does **not**
! 476: + imply the `--fileflags` option.
! 477:
! 478: 0. `--no-OPTION`
! 479:
! 480: @@ -1076,7 +1082,7 @@ your home directory (remove the '=' for that).
! 481: Without this option, if the sending side has replaced a directory with a
! 482: symlink to a directory, the receiving side will delete anything that is in
! 483: the way of the new symlink, including a directory hierarchy (as long as
! 484: - `--force` or `--delete` is in effect).
! 485: + `--force-delete` or `--delete` is in effect).
! 486:
! 487: See also `--keep-dirlinks` for an analogous option for the receiving side.
! 488:
! 489: @@ -1262,6 +1268,29 @@ your home directory (remove the '=' for that).
! 490: those used by `--fake-super`) unless you repeat the option (e.g. `-XX`).
! 491: This "copy all xattrs" mode cannot be used with `--fake-super`.
! 492:
! 493: +0. `--fileflags` This option causes rsync to update the file-flags to be the
! 494: + same as the source files and directories (if your OS supports the
! 495: + **chflags**(2) system call). Some flags can only be altered by the
! 496: + super-user and some might only be unset below a certain secure-level
! 497: + (usually single-user mode). It will not make files alterable that are set
! 498: + to immutable on the receiver. To do that, see `--force-change`,
! 499: + `--force-uchange`, and `--force-schange`.
! 500: +
! 501: +0. `--force-change` This option causes rsync to disable both user-immutable
! 502: + and system-immutable flags on files and directories that are being updated
! 503: + or deleted on the receiving side. This option overrides `--force-uchange`
! 504: + and `--force-schange`.
! 505: +
! 506: +0. `--force-uchange` This option causes rsync to disable user-immutable flags
! 507: + on files and directories that are being updated or deleted on the receiving
! 508: + side. It does not try to affect system flags. This option overrides
! 509: + `--force-change` and `--force-schange`.
! 510: +
! 511: +0. `--force-schange` This option causes rsync to disable system-immutable
! 512: + flags on files and directories that are being updated or deleted on the
! 513: + receiving side. It does not try to affect user flags. This option
! 514: + overrides `--force-change` and `--force-uchange`.
! 515: +
! 516: 0. `--chmod=CHMOD`
! 517:
! 518: This option tells rsync to apply one or more comma-separated "chmod" modes
! 519: @@ -1729,7 +1758,7 @@ your home directory (remove the '=' for that).
! 520: option a step farther: each missing arg will become a deletion request of
! 521: the corresponding destination file on the receiving side (should it exist).
! 522: If the destination file is a non-empty directory, it will only be
! 523: - successfully deleted if `--force` or `--delete` are in effect. Other than
! 524: + successfully deleted if `--force-delete` or `--delete` are in effect. Other than
! 525: that, this option is independent of any other type of delete processing.
! 526:
! 527: The missing source files are represented by special file-list entries which
! 528: @@ -1740,15 +1769,16 @@ your home directory (remove the '=' for that).
! 529: Tells `--delete` to go ahead and delete files even when there are I/O
! 530: errors.
! 531:
! 532: -0. `--force`
! 533: +0. `--force-delete`
! 534:
! 535: This option tells rsync to delete a non-empty directory when it is to be
! 536: replaced by a non-directory. This is only relevant if deletions are not
! 537: active (see `--delete` for details).
! 538:
! 539: - Note for older rsync versions: `--force` used to still be required when
! 540: - using `--delete-after`, and it used to be non-functional unless the
! 541: - `--recursive` option was also enabled.
! 542: + This option can be abbreviated `--force` for backward compatibility. Note
! 543: + that some older rsync versions used to still require `--force` when using
! 544: + `--delete-after`, and it used to be non-functional unless the `--recursive`
! 545: + option was also enabled.
! 546:
! 547: 0. `--max-delete=NUM`
! 548:
! 549: @@ -2700,7 +2730,7 @@ your home directory (remove the '=' for that).
! 550: output of other verbose messages).
! 551:
! 552: The "%i" escape has a cryptic output that is 11 letters long. The general
! 553: - format is like the string `YXcstpoguax`, where **Y** is replaced by the type
! 554: + format is like the string `YXcstpoguaxf`, where **Y** is replaced by the type
! 555: of update being done, **X** is replaced by the file-type, and the other
! 556: letters represent attributes that may be output if they are being modified.
! 557:
! 558: diff --git a/rsync.c b/rsync.c
! 559: --- a/rsync.c
! 560: +++ b/rsync.c
! 561: @@ -31,6 +31,7 @@ extern int dry_run;
! 562: extern int preserve_acls;
! 563: extern int preserve_xattrs;
! 564: extern int preserve_perms;
! 565: +extern int preserve_fileflags;
! 566: extern int preserve_executability;
! 567: extern int preserve_times;
! 568: extern int am_root;
! 569: @@ -467,6 +468,39 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
! 570: return new_mode;
! 571: }
! 572:
! 573: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
! 574: +/* Set a file's st_flags. */
! 575: +static int set_fileflags(const char *fname, uint32 fileflags)
! 576: +{
! 577: + if (do_chflags(fname, fileflags) != 0) {
! 578: + rsyserr(FERROR_XFER, errno,
! 579: + "failed to set file flags on %s",
! 580: + full_fname(fname));
! 581: + return 0;
! 582: + }
! 583: +
! 584: + return 1;
! 585: +}
! 586: +
! 587: +/* Remove immutable flags from an object, so it can be altered/removed. */
! 588: +int make_mutable(const char *fname, mode_t mode, uint32 fileflags, uint32 iflags)
! 589: +{
! 590: + if (S_ISLNK(mode) || !(fileflags & iflags))
! 591: + return 0;
! 592: + if (!set_fileflags(fname, fileflags & ~iflags))
! 593: + return -1;
! 594: + return 1;
! 595: +}
! 596: +
! 597: +/* Undo a prior make_mutable() call that returned a 1. */
! 598: +int undo_make_mutable(const char *fname, uint32 fileflags)
! 599: +{
! 600: + if (!set_fileflags(fname, fileflags))
! 601: + return -1;
! 602: + return 1;
! 603: +}
! 604: +#endif
! 605: +
! 606: static int same_mtime(struct file_struct *file, STRUCT_STAT *st, int extra_accuracy)
! 607: {
! 608: #ifdef ST_MTIME_NSEC
! 609: @@ -543,7 +577,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
! 610: if (am_root >= 0) {
! 611: uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
! 612: gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
! 613: - if (do_lchown(fname, uid, gid) != 0) {
! 614: + if (do_lchown(fname, uid, gid, sxp->st.st_mode, ST_FLAGS(sxp->st)) != 0) {
! 615: /* We shouldn't have attempted to change uid
! 616: * or gid unless have the privilege. */
! 617: rsyserr(FERROR_XFER, errno, "%s %s failed",
! 618: @@ -642,7 +676,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
! 619:
! 620: #ifdef HAVE_CHMOD
! 621: if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
! 622: - int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
! 623: + int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode, ST_FLAGS(sxp->st));
! 624: if (ret < 0) {
! 625: rsyserr(FERROR_XFER, errno,
! 626: "failed to set permissions on %s",
! 627: @@ -654,6 +688,19 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
! 628: }
! 629: #endif
! 630:
! 631: +#ifdef SUPPORT_FILEFLAGS
! 632: + if (preserve_fileflags && !S_ISLNK(sxp->st.st_mode)
! 633: + && sxp->st.st_flags != F_FFLAGS(file)) {
! 634: + uint32 fileflags = F_FFLAGS(file);
! 635: + if (flags & ATTRS_DELAY_IMMUTABLE)
! 636: + fileflags &= ~ALL_IMMUTABLE;
! 637: + if (sxp->st.st_flags != fileflags
! 638: + && !set_fileflags(fname, fileflags))
! 639: + goto cleanup;
! 640: + updated = 1;
! 641: + }
! 642: +#endif
! 643: +
! 644: if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) {
! 645: if (updated)
! 646: rprintf(FCLIENT, "%s\n", fname);
! 647: @@ -731,7 +778,8 @@ int finish_transfer(const char *fname, const char *fnametmp,
! 648:
! 649: /* Change permissions before putting the file into place. */
! 650: set_file_attrs(fnametmp, file, NULL, fnamecmp,
! 651: - ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME);
! 652: + ATTRS_DELAY_IMMUTABLE
! 653: + | (ok_to_set_time ? ATTRS_ACCURATE_TIME : ATTRS_SKIP_MTIME | ATTRS_SKIP_ATIME | ATTRS_SKIP_CRTIME));
! 654:
! 655: /* move tmp file over real file */
! 656: if (DEBUG_GTE(RECV, 1))
! 657: @@ -748,6 +796,10 @@ int finish_transfer(const char *fname, const char *fnametmp,
! 658: }
! 659: if (ret == 0) {
! 660: /* The file was moved into place (not copied), so it's done. */
! 661: +#ifdef SUPPORT_FILEFLAGS
! 662: + if (preserve_fileflags && F_FFLAGS(file) & ALL_IMMUTABLE)
! 663: + set_fileflags(fname, F_FFLAGS(file));
! 664: +#endif
! 665: return 1;
! 666: }
! 667: /* The file was copied, so tweak the perms of the copied file. If it
! 668: diff --git a/rsync.h b/rsync.h
! 669: --- a/rsync.h
! 670: +++ b/rsync.h
! 671: @@ -69,7 +69,7 @@
! 672:
! 673: /* The following XMIT flags require an rsync that uses a varint for the flag values */
! 674:
! 675: -#define XMIT_RESERVED_16 (1<<16) /* reserved for future fileflags use */
! 676: +#define XMIT_SAME_FLAGS (1<<16) /* any protocol - restricted by command-line option */
! 677: #define XMIT_CRTIME_EQ_MTIME (1<<17) /* any protocol - restricted by command-line option */
! 678:
! 679: /* These flags are used in the live flist data. */
! 680: @@ -182,6 +182,7 @@
! 681: #define ATTRS_SKIP_MTIME (1<<1)
! 682: #define ATTRS_ACCURATE_TIME (1<<2)
! 683: #define ATTRS_SKIP_ATIME (1<<3)
! 684: +#define ATTRS_DELAY_IMMUTABLE (1<<4)
! 685: #define ATTRS_SKIP_CRTIME (1<<5)
! 686:
! 687: #define MSG_FLUSH 2
! 688: @@ -210,6 +211,7 @@
! 689: #define ITEM_REPORT_GROUP (1<<6)
! 690: #define ITEM_REPORT_ACL (1<<7)
! 691: #define ITEM_REPORT_XATTR (1<<8)
! 692: +#define ITEM_REPORT_FFLAGS (1<<9)
! 693: #define ITEM_REPORT_CRTIME (1<<10)
! 694: #define ITEM_BASIS_TYPE_FOLLOWS (1<<11)
! 695: #define ITEM_XNAME_FOLLOWS (1<<12)
! 696: @@ -578,6 +580,31 @@ typedef unsigned int size_t;
! 697: #define SUPPORT_CRTIMES 1
! 698: #endif
! 699:
! 700: +#define NO_FFLAGS ((uint32)-1)
! 701: +
! 702: +#ifdef HAVE_CHFLAGS
! 703: +#define SUPPORT_FILEFLAGS 1
! 704: +#define SUPPORT_FORCE_CHANGE 1
! 705: +#endif
! 706: +
! 707: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
! 708: +#ifndef UF_NOUNLINK
! 709: +#define UF_NOUNLINK 0
! 710: +#endif
! 711: +#ifndef SF_NOUNLINK
! 712: +#define SF_NOUNLINK 0
! 713: +#endif
! 714: +#define USR_IMMUTABLE (UF_IMMUTABLE|UF_NOUNLINK|UF_APPEND)
! 715: +#define SYS_IMMUTABLE (SF_IMMUTABLE|SF_NOUNLINK|SF_APPEND)
! 716: +#define ALL_IMMUTABLE (USR_IMMUTABLE|SYS_IMMUTABLE)
! 717: +#define ST_FLAGS(st) ((st).st_flags)
! 718: +#else
! 719: +#define USR_IMMUTABLE 0
! 720: +#define SYS_IMMUTABLE 0
! 721: +#define ALL_IMMUTABLE 0
! 722: +#define ST_FLAGS(st) NO_FFLAGS
! 723: +#endif
! 724: +
! 725: /* Find a variable that is either exactly 32-bits or longer.
! 726: * If some code depends on 32-bit truncation, it will need to
! 727: * take special action in a "#if SIZEOF_INT32 > 4" section. */
! 728: @@ -804,6 +831,7 @@ extern int pathname_ndx;
! 729: extern int depth_ndx;
! 730: extern int uid_ndx;
! 731: extern int gid_ndx;
! 732: +extern int fileflags_ndx;
! 733: extern int acls_ndx;
! 734: extern int xattrs_ndx;
! 735:
! 736: @@ -858,6 +886,11 @@ extern int xattrs_ndx;
! 737: /* When the associated option is on, all entries will have these present: */
! 738: #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
! 739: #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
! 740: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
! 741: +#define F_FFLAGS(f) REQ_EXTRA(f, fileflags_ndx)->unum
! 742: +#else
! 743: +#define F_FFLAGS(f) NO_FFLAGS
! 744: +#endif
! 745: #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
! 746: #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
! 747: #define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num
! 748: diff --git a/syscall.c b/syscall.c
! 749: --- a/syscall.c
! 750: +++ b/syscall.c
! 751: @@ -38,6 +38,7 @@ extern int am_root;
! 752: extern int am_sender;
! 753: extern int read_only;
! 754: extern int list_only;
! 755: +extern int force_change;
! 756: extern int inplace;
! 757: extern int preallocate_files;
! 758: extern int preserve_perms;
! 759: @@ -77,7 +78,23 @@ int do_unlink(const char *fname)
! 760: {
! 761: if (dry_run) return 0;
! 762: RETURN_ERROR_IF_RO_OR_LO;
! 763: - return unlink(fname);
! 764: + if (unlink(fname) == 0)
! 765: + return 0;
! 766: +#ifdef SUPPORT_FORCE_CHANGE
! 767: + if (force_change && errno == EPERM) {
! 768: + STRUCT_STAT st;
! 769: +
! 770: + if (x_lstat(fname, &st, NULL) == 0
! 771: + && make_mutable(fname, st.st_mode, st.st_flags, force_change) > 0) {
! 772: + if (unlink(fname) == 0)
! 773: + return 0;
! 774: + undo_make_mutable(fname, st.st_flags);
! 775: + }
! 776: + /* TODO: handle immutable directories */
! 777: + errno = EPERM;
! 778: + }
! 779: +#endif
! 780: + return -1;
! 781: }
! 782:
! 783: #ifdef SUPPORT_LINKS
! 784: @@ -142,14 +159,35 @@ int do_link(const char *old_path, const char *new_path)
! 785: }
! 786: #endif
! 787:
! 788: -int do_lchown(const char *path, uid_t owner, gid_t group)
! 789: +int do_lchown(const char *path, uid_t owner, gid_t group, UNUSED(mode_t mode), UNUSED(uint32 fileflags))
! 790: {
! 791: if (dry_run) return 0;
! 792: RETURN_ERROR_IF_RO_OR_LO;
! 793: #ifndef HAVE_LCHOWN
! 794: #define lchown chown
! 795: #endif
! 796: - return lchown(path, owner, group);
! 797: + if (lchown(path, owner, group) == 0)
! 798: + return 0;
! 799: +#ifdef SUPPORT_FORCE_CHANGE
! 800: + if (force_change && errno == EPERM) {
! 801: + if (fileflags == NO_FFLAGS) {
! 802: + STRUCT_STAT st;
! 803: + if (x_lstat(path, &st, NULL) == 0) {
! 804: + mode = st.st_mode;
! 805: + fileflags = st.st_flags;
! 806: + }
! 807: + }
! 808: + if (fileflags != NO_FFLAGS
! 809: + && make_mutable(path, mode, fileflags, force_change) > 0) {
! 810: + int ret = lchown(path, owner, group);
! 811: + undo_make_mutable(path, fileflags);
! 812: + if (ret == 0)
! 813: + return 0;
! 814: + }
! 815: + errno = EPERM;
! 816: + }
! 817: +#endif
! 818: + return -1;
! 819: }
! 820:
! 821: int do_mknod(const char *pathname, mode_t mode, dev_t dev)
! 822: @@ -189,7 +227,7 @@ int do_mknod(const char *pathname, mode_t mode, dev_t dev)
! 823: return -1;
! 824: close(sock);
! 825: #ifdef HAVE_CHMOD
! 826: - return do_chmod(pathname, mode);
! 827: + return do_chmod(pathname, mode, 0);
! 828: #else
! 829: return 0;
! 830: #endif
! 831: @@ -206,7 +244,22 @@ int do_rmdir(const char *pathname)
! 832: {
! 833: if (dry_run) return 0;
! 834: RETURN_ERROR_IF_RO_OR_LO;
! 835: - return rmdir(pathname);
! 836: + if (rmdir(pathname) == 0)
! 837: + return 0;
! 838: +#ifdef SUPPORT_FORCE_CHANGE
! 839: + if (force_change && errno == EPERM) {
! 840: + STRUCT_STAT st;
! 841: +
! 842: + if (x_lstat(pathname, &st, NULL) == 0
! 843: + && make_mutable(pathname, st.st_mode, st.st_flags, force_change) > 0) {
! 844: + if (rmdir(pathname) == 0)
! 845: + return 0;
! 846: + undo_make_mutable(pathname, st.st_flags);
! 847: + }
! 848: + errno = EPERM;
! 849: + }
! 850: +#endif
! 851: + return -1;
! 852: }
! 853:
! 854: int do_open(const char *pathname, int flags, mode_t mode)
! 855: @@ -225,7 +278,7 @@ int do_open(const char *pathname, int flags, mode_t mode)
! 856: }
! 857:
! 858: #ifdef HAVE_CHMOD
! 859: -int do_chmod(const char *path, mode_t mode)
! 860: +int do_chmod(const char *path, mode_t mode, UNUSED(uint32 fileflags))
! 861: {
! 862: int code;
! 863: if (dry_run) return 0;
! 864: @@ -248,17 +301,72 @@ int do_chmod(const char *path, mode_t mode)
! 865: } else
! 866: code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */
! 867: #endif /* !HAVE_LCHMOD */
! 868: +#ifdef SUPPORT_FORCE_CHANGE
! 869: + if (code < 0 && force_change && errno == EPERM && !S_ISLNK(mode)) {
! 870: + if (fileflags == NO_FFLAGS) {
! 871: + STRUCT_STAT st;
! 872: + if (x_lstat(path, &st, NULL) == 0)
! 873: + fileflags = st.st_flags;
! 874: + }
! 875: + if (fileflags != NO_FFLAGS
! 876: + && make_mutable(path, mode, fileflags, force_change) > 0) {
! 877: + code = chmod(path, mode & CHMOD_BITS);
! 878: + undo_make_mutable(path, fileflags);
! 879: + if (code == 0)
! 880: + return 0;
! 881: + }
! 882: + errno = EPERM;
! 883: + }
! 884: +#endif
! 885: if (code != 0 && (preserve_perms || preserve_executability))
! 886: return code;
! 887: return 0;
! 888: }
! 889: #endif
! 890:
! 891: +#ifdef HAVE_CHFLAGS
! 892: +int do_chflags(const char *path, uint32 fileflags)
! 893: +{
! 894: + if (dry_run) return 0;
! 895: + RETURN_ERROR_IF_RO_OR_LO;
! 896: + return chflags(path, fileflags);
! 897: +}
! 898: +#endif
! 899: +
! 900: int do_rename(const char *old_path, const char *new_path)
! 901: {
! 902: if (dry_run) return 0;
! 903: RETURN_ERROR_IF_RO_OR_LO;
! 904: - return rename(old_path, new_path);
! 905: + if (rename(old_path, new_path) == 0)
! 906: + return 0;
! 907: +#ifdef SUPPORT_FORCE_CHANGE
! 908: + if (force_change && errno == EPERM) {
! 909: + STRUCT_STAT st1, st2;
! 910: + int became_mutable;
! 911: +
! 912: + if (x_lstat(old_path, &st1, NULL) != 0)
! 913: + goto failed;
! 914: + became_mutable = make_mutable(old_path, st1.st_mode, st1.st_flags, force_change) > 0;
! 915: + if (became_mutable && rename(old_path, new_path) == 0)
! 916: + goto success;
! 917: + if (x_lstat(new_path, &st2, NULL) == 0
! 918: + && make_mutable(new_path, st2.st_mode, st2.st_flags, force_change) > 0) {
! 919: + if (rename(old_path, new_path) == 0) {
! 920: + success:
! 921: + if (became_mutable) /* Yes, use new_path and st1! */
! 922: + undo_make_mutable(new_path, st1.st_flags);
! 923: + return 0;
! 924: + }
! 925: + undo_make_mutable(new_path, st2.st_flags);
! 926: + }
! 927: + /* TODO: handle immutable directories */
! 928: + if (became_mutable)
! 929: + undo_make_mutable(old_path, st1.st_flags);
! 930: + failed:
! 931: + errno = EPERM;
! 932: + }
! 933: +#endif
! 934: + return -1;
! 935: }
! 936:
! 937: #ifdef HAVE_FTRUNCATE
! 938: diff --git a/t_stub.c b/t_stub.c
! 939: --- a/t_stub.c
! 940: +++ b/t_stub.c
! 941: @@ -28,6 +28,8 @@ int protect_args = 0;
! 942: int module_id = -1;
! 943: int relative_paths = 0;
! 944: int module_dirlen = 0;
! 945: +int force_change = 0;
! 946: +int preserve_acls = 0;
! 947: int preserve_times = 0;
! 948: int preserve_xattrs = 0;
! 949: int preserve_perms = 0;
! 950: @@ -110,3 +112,23 @@ filter_rule_list daemon_filter_list;
! 951: {
! 952: return cst ? 0 : 0;
! 953: }
! 954: +
! 955: +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
! 956: + int make_mutable(UNUSED(const char *fname), UNUSED(mode_t mode), UNUSED(uint32 fileflags), UNUSED(uint32 iflags))
! 957: +{
! 958: + return 0;
! 959: +}
! 960: +
! 961: +/* Undo a prior make_mutable() call that returned a 1. */
! 962: + int undo_make_mutable(UNUSED(const char *fname), UNUSED(uint32 fileflags))
! 963: +{
! 964: + return 0;
! 965: +}
! 966: +#endif
! 967: +
! 968: +#ifdef SUPPORT_XATTRS
! 969: + int x_lstat(UNUSED(const char *fname), UNUSED(STRUCT_STAT *fst), UNUSED(STRUCT_STAT *xst))
! 970: +{
! 971: + return -1;
! 972: +}
! 973: +#endif
! 974: diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
! 975: --- a/testsuite/rsync.fns
! 976: +++ b/testsuite/rsync.fns
! 977: @@ -23,9 +23,9 @@ todir="$tmpdir/to"
! 978: chkdir="$tmpdir/chk"
! 979:
! 980: # For itemized output:
! 981: -all_plus='+++++++++'
! 982: -allspace=' '
! 983: -dots='.....' # trailing dots after changes
! 984: +all_plus='++++++++++'
! 985: +allspace=' '
! 986: +dots='......' # trailing dots after changes
! 987: tab_ch=' ' # a single tab character
! 988:
! 989: # Berkley's nice.
! 990: diff --git a/usage.c b/usage.c
! 991: --- a/usage.c
! 992: +++ b/usage.c
! 993: @@ -131,6 +131,11 @@ static void print_info_flags(enum logcode f)
! 994: #endif
! 995: "crtimes",
! 996:
! 997: +#ifndef SUPPORT_FILEFLAGS
! 998: + "no "
! 999: +#endif
! 1000: + "file-flags",
! 1001: +
! 1002: "*Optimizations",
! 1003:
! 1004: #ifndef HAVE_SIMD
! 1005: diff --git a/util.c b/util.c
! 1006: --- a/util.c
! 1007: +++ b/util.c
! 1008: @@ -33,6 +33,7 @@ extern int relative_paths;
! 1009: extern int preserve_times;
! 1010: extern int preserve_xattrs;
! 1011: extern int preallocate_files;
! 1012: +extern int force_change;
! 1013: extern char *module_dir;
! 1014: extern unsigned int module_dirlen;
! 1015: extern char *partial_dir;
! 1016: @@ -115,6 +116,33 @@ void print_child_argv(const char *prefix, char **cmd)
! 1017: rprintf(FCLIENT, " (%d args)\n", cnt);
! 1018: }
! 1019:
! 1020: +#ifdef SUPPORT_FORCE_CHANGE
! 1021: +static int try_a_force_change(const char *fname, STRUCT_STAT *stp)
! 1022: +{
! 1023: + uint32 fileflags = ST_FLAGS(*stp);
! 1024: + if (fileflags == NO_FFLAGS) {
! 1025: + STRUCT_STAT st;
! 1026: + if (x_lstat(fname, &st, NULL) == 0)
! 1027: + fileflags = st.st_flags;
! 1028: + }
! 1029: + if (fileflags != NO_FFLAGS && make_mutable(fname, stp->st_mode, fileflags, force_change) > 0) {
! 1030: + int ret, save_force_change = force_change;
! 1031: +
! 1032: + force_change = 0; /* Make certain we can't come back here. */
! 1033: + ret = set_times(fname, stp);
! 1034: + force_change = save_force_change;
! 1035: +
! 1036: + undo_make_mutable(fname, fileflags);
! 1037: +
! 1038: + return ret;
! 1039: + }
! 1040: +
! 1041: + errno = EPERM;
! 1042: +
! 1043: + return -1;
! 1044: +}
! 1045: +#endif
! 1046: +
! 1047: /* This returns 0 for success, 1 for a symlink if symlink time-setting
! 1048: * is not possible, or -1 for any other error. */
! 1049: int set_times(const char *fname, STRUCT_STAT *stp)
! 1050: @@ -142,6 +170,10 @@ int set_times(const char *fname, STRUCT_STAT *stp)
! 1051: #include "case_N.h"
! 1052: if (do_utimensat(fname, stp) == 0)
! 1053: break;
! 1054: +#ifdef SUPPORT_FORCE_CHANGE
! 1055: + if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0)
! 1056: + break;
! 1057: +#endif
! 1058: if (errno != ENOSYS)
! 1059: return -1;
! 1060: switch_step++;
! 1061: @@ -151,6 +183,10 @@ int set_times(const char *fname, STRUCT_STAT *stp)
! 1062: #include "case_N.h"
! 1063: if (do_lutimes(fname, stp) == 0)
! 1064: break;
! 1065: +#ifdef SUPPORT_FORCE_CHANGE
! 1066: + if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0)
! 1067: + break;
! 1068: +#endif
! 1069: if (errno != ENOSYS)
! 1070: return -1;
! 1071: switch_step++;
! 1072: @@ -172,6 +208,10 @@ int set_times(const char *fname, STRUCT_STAT *stp)
! 1073: if (do_utime(fname, stp) == 0)
! 1074: break;
! 1075: #endif
! 1076: +#ifdef SUPPORT_FORCE_CHANGE
! 1077: + if (force_change && errno == EPERM && try_a_force_change(fname, stp) == 0)
! 1078: + break;
! 1079: +#endif
! 1080:
! 1081: return -1;
! 1082: }
! 1083: diff --git a/xattrs.c b/xattrs.c
! 1084: --- a/xattrs.c
! 1085: +++ b/xattrs.c
! 1086: @@ -1202,7 +1202,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
! 1087: mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
! 1088: | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
! 1089: if (fst.st_mode != mode)
! 1090: - do_chmod(fname, mode);
! 1091: + do_chmod(fname, mode, ST_FLAGS(fst));
! 1092: if (!IS_DEVICE(fst.st_mode))
! 1093: fst.st_rdev = 0; /* just in case */
! 1094:
! 1095: diff -Nurp a/rsync.1 b/rsync.1
! 1096: --- a/rsync.1
! 1097: +++ b/rsync.1
! 1098: @@ -437,6 +437,7 @@ detailed description below for a complet
! 1099: --keep-dirlinks, -K treat symlinked dir on receiver as dir
! 1100: --hard-links, -H preserve hard links
! 1101: --perms, -p preserve permissions
! 1102: +--fileflags preserve file-flags (aka chflags)
! 1103: --executability, -E preserve executability
! 1104: --chmod=CHMOD affect file and/or directory permissions
! 1105: --acls, -A preserve ACLs (implies --perms)
! 1106: @@ -477,7 +478,10 @@ detailed description below for a complet
! 1107: --ignore-missing-args ignore missing source args without error
! 1108: --delete-missing-args delete missing source args from destination
! 1109: --ignore-errors delete even if there are I/O errors
! 1110: ---force force deletion of dirs even if not empty
! 1111: +--force-delete force deletion of directories even if not empty
! 1112: +--force-change affect user-/system-immutable files/dirs
! 1113: +--force-uchange affect user-immutable files/dirs
! 1114: +--force-schange affect system-immutable files/dirs
! 1115: --max-delete=NUM don't delete more than NUM files
! 1116: --max-size=SIZE don't transfer any file larger than SIZE
! 1117: --min-size=SIZE don't transfer any file smaller than SIZE
! 1118: @@ -783,6 +787,8 @@ omission). The only exception to the ab
! 1119: .IP
! 1120: Note that \fB\-a\fP \fBdoes not preserve hardlinks\fP, because finding
! 1121: multiply-linked files is expensive. You must separately specify \fB\-H\fP.
! 1122: +Note also that for backward compatibility, \fB\-a\fP currently does \fBnot\fP
! 1123: +imply the \fB\-\-fileflags\fP option.
! 1124: .IP "\fB\-\-no-OPTION\fP"
! 1125: You may turn off one or more implied options by prefixing the option name
! 1126: with "no-". Not all options may be prefixed with a "no-": only options that
! 1127: @@ -1139,7 +1145,7 @@ to non-directories to be affected, as th
! 1128: Without this option, if the sending side has replaced a directory with a
! 1129: symlink to a directory, the receiving side will delete anything that is in
! 1130: the way of the new symlink, including a directory hierarchy (as long as
! 1131: -\fB\-\-force\fP or \fB\-\-delete\fP is in effect).
! 1132: +\fB\-\-force-delete\fP or \fB\-\-delete\fP is in effect).
! 1133: .IP
! 1134: See also \fB\-\-keep-dirlinks\fP for an analogous option for the receiving side.
! 1135: .IP
! 1136: @@ -1348,6 +1354,25 @@ receiver-only rule that excludes all nam
! 1137: Note that the \fB\-X\fP option does not copy rsync's special xattr values (e.g.
! 1138: those used by \fB\-\-fake-super\fP) unless you repeat the option (e.g. \fB\-XX\fP).
! 1139: This "copy all xattrs" mode cannot be used with \fB\-\-fake-super\fP.
! 1140: +.IP "\fB\-\-fileflags\fP This option causes rsync to update the file-flags to be the
! 1141: +same as the source files and directories (if your OS supports the
! 1142: +\fBchflags\fP(2) system call). Some flags can only be altered by the
! 1143: +super-user and some might only be unset below a certain secure-level
! 1144: +(usually single-user mode). It will not make files alterable that are set
! 1145: +to immutable on the receiver. To do that, see \fB\-\-force-change\fP,
! 1146: +\fB\-\-force-uchange\fP, and \fB\-\-force-schange\fP."
! 1147: +.IP "\fB\-\-force-change\fP This option causes rsync to disable both user-immutable
! 1148: +and system-immutable flags on files and directories that are being updated
! 1149: +or deleted on the receiving side. This option overrides \fB\-\-force-uchange\fP
! 1150: +and \fB\-\-force-schange\fP."
! 1151: +.IP "\fB\-\-force-uchange\fP This option causes rsync to disable user-immutable flags
! 1152: +on files and directories that are being updated or deleted on the receiving
! 1153: +side. It does not try to affect system flags. This option overrides
! 1154: +\fB\-\-force-change\fP and \fB\-\-force-schange\fP."
! 1155: +.IP "\fB\-\-force-schange\fP This option causes rsync to disable system-immutable
! 1156: +flags on files and directories that are being updated or deleted on the
! 1157: +receiving side. It does not try to affect user flags. This option
! 1158: +overrides \fB\-\-force-change\fP and \fB\-\-force-uchange\fP."
! 1159: .IP "\fB\-\-chmod=CHMOD\fP"
! 1160: This option tells rsync to apply one or more comma-separated "chmod" modes
! 1161: to the permission of the files in the transfer. The resulting value is
! 1162: @@ -1773,7 +1798,7 @@ This option takes the behavior of (the i
! 1163: option a step farther: each missing arg will become a deletion request of
! 1164: the corresponding destination file on the receiving side (should it exist).
! 1165: If the destination file is a non-empty directory, it will only be
! 1166: -successfully deleted if \fB\-\-force\fP or \fB\-\-delete\fP are in effect. Other than
! 1167: +successfully deleted if \fB\-\-force-delete\fP or \fB\-\-delete\fP are in effect. Other than
! 1168: that, this option is independent of any other type of delete processing.
! 1169: .IP
! 1170: The missing source files are represented by special file-list entries which
! 1171: @@ -1781,14 +1806,15 @@ display as a "\fB*missing\fP" entry in t
! 1172: .IP "\fB\-\-ignore-errors\fP"
! 1173: Tells \fB\-\-delete\fP to go ahead and delete files even when there are I/O
! 1174: errors.
! 1175: -.IP "\fB\-\-force\fP"
! 1176: +.IP "\fB\-\-force-delete\fP"
! 1177: This option tells rsync to delete a non-empty directory when it is to be
! 1178: replaced by a non-directory. This is only relevant if deletions are not
! 1179: active (see \fB\-\-delete\fP for details).
! 1180: .IP
! 1181: -Note for older rsync versions: \fB\-\-force\fP used to still be required when
! 1182: -using \fB\-\-delete-after\fP, and it used to be non-functional unless the
! 1183: -\fB\-\-recursive\fP option was also enabled.
! 1184: +This option can be abbreviated \fB\-\-force\fP for backward compatibility. Note
! 1185: +that some older rsync versions used to still require \fB\-\-force\fP when using
! 1186: +\fB\-\-delete-after\fP, and it used to be non-functional unless the \fB\-\-recursive\fP
! 1187: +option was also enabled.
! 1188: .IP "\fB\-\-max-delete=NUM\fP"
! 1189: This tells rsync not to delete more than NUM files or directories. If that
! 1190: limit is exceeded, all further deletions are skipped through the end of the
! 1191: @@ -2735,7 +2761,7 @@ also be output, but only if the receivin
! 1192: output of other verbose messages).
! 1193: .IP
! 1194: The "%i" escape has a cryptic output that is 11 letters long. The general
! 1195: -format is like the string \fBYXcstpoguax\fP, where \fBY\fP is replaced by the type
! 1196: +format is like the string \fBYXcstpoguaxf\fP, where \fBY\fP is replaced by the type
! 1197: of update being done, \fBX\fP is replaced by the file-type, and the other
! 1198: letters represent attributes that may be output if they are being modified.
! 1199: .IP
! 1200: diff -Nurp a/rsync.1.html b/rsync.1.html
! 1201: --- a/rsync.1.html
! 1202: +++ b/rsync.1.html
! 1203: @@ -352,6 +352,7 @@ detailed description below for a complet
! 1204: --keep-dirlinks, -K treat symlinked dir on receiver as dir
! 1205: --hard-links, -H preserve hard links
! 1206: --perms, -p preserve permissions
! 1207: +--fileflags preserve file-flags (aka chflags)
! 1208: --executability, -E preserve executability
! 1209: --chmod=CHMOD affect file and/or directory permissions
! 1210: --acls, -A preserve ACLs (implies --perms)
! 1211: @@ -392,7 +393,10 @@ detailed description below for a complet
! 1212: --ignore-missing-args ignore missing source args without error
! 1213: --delete-missing-args delete missing source args from destination
! 1214: --ignore-errors delete even if there are I/O errors
! 1215: ---force force deletion of dirs even if not empty
! 1216: +--force-delete force deletion of directories even if not empty
! 1217: +--force-change affect user-/system-immutable files/dirs
! 1218: +--force-uchange affect user-immutable files/dirs
! 1219: +--force-schange affect system-immutable files/dirs
! 1220: --max-delete=NUM don't delete more than NUM files
! 1221: --max-size=SIZE don't transfer any file larger than SIZE
! 1222: --min-size=SIZE don't transfer any file smaller than SIZE
! 1223: @@ -697,7 +701,9 @@ recursion and want to preserve almost ev
! 1224: omission). The only exception to the above equivalence is when
! 1225: <code>--files-from</code> is specified, in which case <code>-r</code> is not implied.</p>
! 1226: <p>Note that <code>-a</code> <strong>does not preserve hardlinks</strong>, because finding
! 1227: -multiply-linked files is expensive. You must separately specify <code>-H</code>.</p>
! 1228: +multiply-linked files is expensive. You must separately specify <code>-H</code>.
! 1229: +Note also that for backward compatibility, <code>-a</code> currently does <strong>not</strong>
! 1230: +imply the <code>--fileflags</code> option.</p>
! 1231: </dd>
! 1232:
! 1233: <dt><code>--no-OPTION</code></dt><dd>
! 1234: @@ -1039,7 +1045,7 @@ to non-directories to be affected, as th
! 1235: <p>Without this option, if the sending side has replaced a directory with a
! 1236: symlink to a directory, the receiving side will delete anything that is in
! 1237: the way of the new symlink, including a directory hierarchy (as long as
! 1238: -<code>--force</code> or <code>--delete</code> is in effect).</p>
! 1239: +<code>--force-delete</code> or <code>--delete</code> is in effect).</p>
! 1240: <p>See also <code>--keep-dirlinks</code> for an analogous option for the receiving side.</p>
! 1241: <p><code>--copy-dirlinks</code> applies to all symlinks to directories in the source. If
! 1242: you want to follow only a few specified symlinks, a trick you can use is to
! 1243: @@ -1219,6 +1225,33 @@ those used by <code>--fake-super</code>)
! 1244: This "copy all xattrs" mode cannot be used with <code>--fake-super</code>.</p>
! 1245: </dd>
! 1246:
! 1247: +<dt><code>--fileflags</code> This option causes rsync to update the file-flags to be the
! 1248: +same as the source files and directories (if your OS supports the
! 1249: +<strong>chflags</strong>(2) system call). Some flags can only be altered by the
! 1250: +super-user and some might only be unset below a certain secure-level
! 1251: +(usually single-user mode). It will not make files alterable that are set
! 1252: +to immutable on the receiver. To do that, see <code>--force-change</code>,
! 1253: +<code>--force-uchange</code>, and <code>--force-schange</code>.</dt><dd>
! 1254: +</dd>
! 1255: +
! 1256: +<dt><code>--force-change</code> This option causes rsync to disable both user-immutable
! 1257: +and system-immutable flags on files and directories that are being updated
! 1258: +or deleted on the receiving side. This option overrides <code>--force-uchange</code>
! 1259: +and <code>--force-schange</code>.</dt><dd>
! 1260: +</dd>
! 1261: +
! 1262: +<dt><code>--force-uchange</code> This option causes rsync to disable user-immutable flags
! 1263: +on files and directories that are being updated or deleted on the receiving
! 1264: +side. It does not try to affect system flags. This option overrides
! 1265: +<code>--force-change</code> and <code>--force-schange</code>.</dt><dd>
! 1266: +</dd>
! 1267: +
! 1268: +<dt><code>--force-schange</code> This option causes rsync to disable system-immutable
! 1269: +flags on files and directories that are being updated or deleted on the
! 1270: +receiving side. It does not try to affect user flags. This option
! 1271: +overrides <code>--force-change</code> and <code>--force-uchange</code>.</dt><dd>
! 1272: +</dd>
! 1273: +
! 1274: <dt><code>--chmod=CHMOD</code></dt><dd>
! 1275: <p>This option tells rsync to apply one or more comma-separated "chmod" modes
! 1276: to the permission of the files in the transfer. The resulting value is
! 1277: @@ -1652,7 +1685,7 @@ is no longer there.</p>
! 1278: option a step farther: each missing arg will become a deletion request of
! 1279: the corresponding destination file on the receiving side (should it exist).
! 1280: If the destination file is a non-empty directory, it will only be
! 1281: -successfully deleted if <code>--force</code> or <code>--delete</code> are in effect. Other than
! 1282: +successfully deleted if <code>--force-delete</code> or <code>--delete</code> are in effect. Other than
! 1283: that, this option is independent of any other type of delete processing.</p>
! 1284: <p>The missing source files are represented by special file-list entries which
! 1285: display as a "<code>*missing</code>" entry in the <code>--list-only</code> output.</p>
! 1286: @@ -1663,13 +1696,14 @@ display as a "<code>*missing</code>
! 1287: errors.</p>
! 1288: </dd>
! 1289:
! 1290: -<dt><code>--force</code></dt><dd>
! 1291: +<dt><code>--force-delete</code></dt><dd>
! 1292: <p>This option tells rsync to delete a non-empty directory when it is to be
! 1293: replaced by a non-directory. This is only relevant if deletions are not
! 1294: active (see <code>--delete</code> for details).</p>
! 1295: -<p>Note for older rsync versions: <code>--force</code> used to still be required when
! 1296: -using <code>--delete-after</code>, and it used to be non-functional unless the
! 1297: -<code>--recursive</code> option was also enabled.</p>
! 1298: +<p>This option can be abbreviated <code>--force</code> for backward compatibility. Note
! 1299: +that some older rsync versions used to still require <code>--force</code> when using
! 1300: +<code>--delete-after</code>, and it used to be non-functional unless the <code>--recursive</code>
! 1301: +option was also enabled.</p>
! 1302: </dd>
! 1303:
! 1304: <dt><code>--max-delete=NUM</code></dt><dd>
! 1305: @@ -2552,7 +2586,7 @@ also be output, but only if the receivin
! 1306: (you can use <code>-vv</code> with older versions of rsync, but that also turns on the
! 1307: output of other verbose messages).</p>
! 1308: <p>The "%i" escape has a cryptic output that is 11 letters long. The general
! 1309: -format is like the string <code>YXcstpoguax</code>, where <strong>Y</strong> is replaced by the type
! 1310: +format is like the string <code>YXcstpoguaxf</code>, where <strong>Y</strong> is replaced by the type
! 1311: of update being done, <strong>X</strong> is replaced by the file-type, and the other
! 1312: letters represent attributes that may be output if they are being modified.</p>
! 1313: <p>The update types that replace the <strong>Y</strong> are as follows:</p>
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>