Annotation of embedaddon/rsync/patches/fileflags.diff, revision 1.1.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>