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 &quot;copy all xattrs&quot; 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 &quot;chmod&quot; 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 &quot;<code>*missing</code>&quot; entry in the <code>--list-only</code> output.</p>
        !          1286: @@ -1663,13 +1696,14 @@ display as a &quot;<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 &quot;%i&quot; 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>