Ticket #44117: rsync-3.1.1.patch

File rsync-3.1.1.patch, 119.8 KB (added by jimjag (Jim Jagielski), 10 years ago)
  • rsync/Portfile

    diff --git a/rsync/Portfile b/rsync/Portfile
    index cc6131e..7182f7f 100644
    a b  
    44PortSystem          1.0
    55
    66name                rsync
    7 version             3.0.9
    8 revision            2
     7version             3.1.1
     8revision            0
    99categories          net
    1010license             GPL-3+
    1111installs_libs       no
    homepage http://samba.org/rsync/ 
    2121master_sites        http://rsync.samba.org/ftp/rsync/ \
    2222                    http://rsync.samba.org/ftp/rsync/src/
    2323
    24 checksums           md5     5ee72266fe2c1822333c407e1761b92b \
    25                     sha1    c64c8341984aea647506eb504496999fd968ddfc \
    26                     rmd160  e5ee8d786defb0d8f937c8d027466f418c63c97e \
    27                     sha256  30f10f8dd5490d28240d4271bb652b1da7a60b22ed2b9ae28090668de9247c05
     24checksums           md5     43bd6676f0b404326eee2d63be3cdcfe \
     25                    sha1    c84faba04f721d393feccfa0476bfeed9b5b5250 \
     26                    rmd160  de7ad955cb05d481a963aa30423790f3d82efe7b \
     27                    sha256  7de4364fcf5fe42f3bdb514417f1c40d10bbca896abe7e7f2c581c6ea08a2621
    2828
    2929depends_lib         port:popt port:libiconv
    3030
    31 # these come from http://rsync.samba.org/ftp/rsync/rsync-patches-3.0.9.tar.gz
     31# these come from http://rsync.samba.org/ftp/rsync/rsync-patches-3.1.1.tar.gz
    3232# and need to be updated with each release
    3333patchfiles          patch-fileflags.diff \
    3434                    patch-crtimes.diff \
    patch.pre_args -p1 
    3939
    4040configure.args      --with-rsyncd-conf=${prefix}/etc/rsyncd.conf
    4141configure.cflags   "-Os -I${prefix}/include"
     42configure.ldflags  "-L${prefix}/lib"
    4243
    4344pre-configure {
    4445    system "cd ${worksrcpath}; ./prepare-source"
  • rsync/files/patch-crtimes.diff

    diff --git a/rsync/files/patch-crtimes.diff b/rsync/files/patch-crtimes.diff
    index b0db0fd..33d0a44 100644
    a b To use this patch, run these commands for a successful build: 
    99    ./configure
    1010    make
    1111
    12 based-on: patch/b3.0.x/fileflags
     12based-on: patch/master/fileflags
    1313diff --git a/compat.c b/compat.c
    1414--- a/compat.c
    1515+++ b/compat.c
    16 @@ -47,6 +47,7 @@ extern int force_change;
     16@@ -48,6 +48,7 @@ extern int force_change;
    1717 extern int protect_args;
    1818 extern int preserve_uid;
    1919 extern int preserve_gid;
    diff --git a/compat.c b/compat.c 
    2121 extern int preserve_fileflags;
    2222 extern int preserve_acls;
    2323 extern int preserve_xattrs;
    24 @@ -65,7 +66,7 @@ extern char *iconv_opt;
     24@@ -66,7 +67,7 @@ extern char *iconv_opt;
    2525 #endif
    2626 
    2727 /* These index values are for the file-list's extra-attribute array. */
    diff --git a/compat.c b/compat.c 
    3030 
    3131 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
    3232 int sender_symlink_iconv = 0;  /* sender should convert symlink content */
    33 @@ -142,6 +143,8 @@ void setup_protocol(int f_out,int f_in)
     33@@ -144,6 +145,8 @@ void setup_protocol(int f_out,int f_in)
    3434                uid_ndx = ++file_extra_cnt;
    3535        if (preserve_gid)
    3636                gid_ndx = ++file_extra_cnt;
    diff --git a/compat.c b/compat.c 
    4242diff --git a/flist.c b/flist.c
    4343--- a/flist.c
    4444+++ b/flist.c
    45 @@ -54,6 +54,7 @@ extern int preserve_specials;
    46  extern int preserve_fileflags;
     45@@ -54,6 +54,7 @@ extern int preserve_fileflags;
    4746 extern int delete_during;
     47 extern int missing_args;
    4848 extern int eol_nulls;
    4949+extern int crtimes_ndx;
    5050 extern int relative_paths;
    5151 extern int implied_dirs;
    5252 extern int ignore_perishable;
    53 @@ -393,7 +394,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
     53@@ -398,7 +399,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    5454 #endif
    5555                            int ndx, int first_ndx)
    5656 {
    diff --git a/flist.c b/flist.c 
    5959        static mode_t mode;
    6060 #ifdef SUPPORT_FILEFLAGS
    6161        static uint32 fileflags;
    62 @@ -488,6 +489,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    63                 xflags |= XMIT_SAME_TIME;
    64         else
     62@@ -509,6 +510,13 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    6563                modtime = file->modtime;
     64        if (NSEC_BUMP(file) && protocol_version >= 31)
     65                xflags |= XMIT_MOD_NSEC;
    6666+       if (crtimes_ndx) {
    6767+               time_t file_crtime = f_crtime(file);
    6868+               if (file_crtime == modtime)
    diff --git a/flist.c b/flist.c 
    7373 
    7474 #ifdef SUPPORT_HARD_LINKS
    7575        if (tmp_dev != -1) {
    76 @@ -557,6 +565,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    77                 else
    78                         write_int(f, modtime);
     76@@ -593,6 +601,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    7977        }
     78        if (xflags & XMIT_MOD_NSEC)
     79                write_varint(f, F_MOD_NSEC(file));
    8080+       if (crtimes_ndx && !(xflags & XMIT_CRTIME_EQ_MTIME))
    8181+               write_varlong(f, crtime, 4);
    8282        if (!(xflags & XMIT_SAME_MODE))
    8383                write_int(f, to_wire_mode(mode));
    8484 #ifdef SUPPORT_FILEFLAGS
    85 @@ -648,7 +658,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
     85@@ -686,7 +696,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    8686 
    8787 static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
    8888 {
    diff --git a/flist.c b/flist.c 
    9191        static mode_t mode;
    9292 #ifdef SUPPORT_FILEFLAGS
    9393        static uint32 fileflags;
    94 @@ -758,6 +768,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
    95                                 uid = F_OWNER(first);
    96                         if (preserve_gid)
    97                                 gid = F_GROUP(first);
    98 +                       if (crtimes_ndx)
    99 +                               crtime = f_crtime(first);
    100                         if (preserve_devices && IS_DEVICE(mode)) {
    101                                 uint32 *devp = F_RDEV_P(first);
    102                                 rdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
    103 @@ -786,6 +798,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
    104                 } else
    105                         modtime = read_int(f);
    106         }
     94@@ -838,6 +848,19 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
     95                modtime_nsec = read_varint(f);
     96        else
     97                modtime_nsec = 0;
    10798+       if (crtimes_ndx) {
    10899+               if (!(xflags & XMIT_CRTIME_EQ_MTIME)) {
    109100+                       crtime = read_varlong(f, 4);
    diff --git a/flist.c b/flist.c 
    120111        if (!(xflags & XMIT_SAME_MODE))
    121112                mode = from_wire_mode(read_int(f));
    122113 
    123 @@ -946,6 +971,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
     114@@ -1015,6 +1038,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
    124115                F_GROUP(file) = gid;
    125116                file->flags |= gid_flags;
    126117        }
    diff --git a/flist.c b/flist.c 
    129120        if (unsort_ndx)
    130121                F_NDX(file) = flist->used + flist->ndx_start;
    131122 
    132 @@ -1324,6 +1351,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
     123@@ -1416,6 +1441,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
    133124                F_GROUP(file) = st.st_gid;
    134125        if (am_generator && st.st_uid == our_uid)
    135126                file->flags |= FLAG_OWNED_BY_US;
    diff --git a/flist.c b/flist.c 
    141132diff --git a/generator.c b/generator.c
    142133--- a/generator.c
    143134+++ b/generator.c
    144 @@ -21,6 +21,7 @@
    145   */
    146  
    147  #include "rsync.h"
    148 +#include "ifuncs.h"
    149  
    150  extern int verbose;
    151  extern int dry_run;
    152 @@ -41,6 +42,7 @@ extern int preserve_xattrs;
     135@@ -40,6 +40,7 @@ extern int preserve_xattrs;
    153136 extern int preserve_links;
    154137 extern int preserve_devices;
    155138 extern int preserve_specials;
    diff --git a/generator.c b/generator.c 
    157140 extern int preserve_hard_links;
    158141 extern int preserve_executability;
    159142 extern int preserve_fileflags;
    160 @@ -576,8 +578,15 @@ static void do_delete_pass(void)
     143@@ -384,8 +385,15 @@ static void do_delete_pass(void)
    161144                rprintf(FINFO, "                    \r");
    162145 }
    163146 
    diff --git a/generator.c b/generator.c 
    174157        return cmp_time(sxp->st.st_mtime, file->modtime);
    175158 }
    176159 
    177 @@ -635,7 +644,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
     160@@ -443,7 +451,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
    178161 {
    179162        if (S_ISLNK(file->mode)) {
    180163 #ifdef CAN_SET_SYMLINK_TIMES
    diff --git a/generator.c b/generator.c 
    183166                        return 0;
    184167 #endif
    185168 #ifdef CAN_CHMOD_SYMLINK
    186 @@ -655,7 +664,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
     169@@ -463,7 +471,7 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
    187170                        return 0;
    188171 #endif
    189172        } else {
    diff --git a/generator.c b/generator.c 
    192175                        return 0;
    193176                if (perms_differ(file, sxp))
    194177                        return 0;
    195 @@ -698,6 +707,12 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
     178@@ -506,6 +514,12 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
    196179                 : iflags & (ITEM_TRANSFER|ITEM_LOCAL_CHANGE) && !(iflags & ITEM_MATCHED)
    197180                  && (!(iflags & ITEM_XNAME_FOLLOWS) || *xname))
    198181                        iflags |= ITEM_REPORT_TIME;
    diff --git a/generator.c b/generator.c 
    205188 #if !defined HAVE_LCHMOD && !defined HAVE_SETATTRLIST
    206189                if (S_ISLNK(file->mode)) {
    207190                        ;
    208 @@ -1263,7 +1278,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
    209  
     191@@ -1130,6 +1144,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
    210192 static void list_file_entry(struct file_struct *f)
    211193 {
    212 -       char permbuf[PERMSTRING_SIZE];
    213 +       char permbuf[PERMSTRING_SIZE], crtime_buf[32];
    214         double len;
    215  
    216         if (!F_IS_ACTIVE(f)) {
    217 @@ -1274,19 +1289,24 @@ static void list_file_entry(struct file_struct *f)
    218         permstring(permbuf, f->mode);
    219         len = F_LENGTH(f);
     194        char permbuf[PERMSTRING_SIZE];
     195+       time_t crtime = crtimes_ndx ? f_crtime(f) : 0;
     196        int64 len;
     197        int colwidth = human_readable ? 14 : 11;
    220198 
    221 +       if (crtimes_ndx)
    222 +               snprintf(crtime_buf, sizeof crtime_buf, " %s", timestring(f_crtime(f)));
    223 +       else
    224 +               *crtime_buf = '\0';
    225 +
    226         /* TODO: indicate '+' if the entry has an ACL. */
     199@@ -1145,10 +1160,12 @@ static void list_file_entry(struct file_struct *f)
    227200 
    228201 #ifdef SUPPORT_LINKS
    229202        if (preserve_links && S_ISLNK(f->mode)) {
    230 -               rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
    231 +               rprintf(FINFO, "%s %11.0f %s%s %s -> %s\n",
    232                         permbuf, len, timestring(f->modtime),
    233 -                       f_name(f, NULL), F_SYMLINK(f));
    234 +                       crtime_buf, f_name(f, NULL), F_SYMLINK(f));
     203-               rprintf(FINFO, "%s %*s %s %s -> %s\n",
     204+               rprintf(FINFO, "%s %*s %s%s%s %s -> %s\n",
     205                        permbuf, colwidth, human_num(len),
     206-                       timestring(f->modtime), f_name(f, NULL),
     207-                       F_SYMLINK(f));
     208+                       timestring(f->modtime),
     209+                       crtimes_ndx ? " " : "",
     210+                       crtimes_ndx ? timestring(crtime) : "",
     211+                       f_name(f, NULL), F_SYMLINK(f));
    235212        } else
    236213 #endif
    237         {
    238 -               rprintf(FINFO, "%s %11.0f %s %s\n",
    239 +               rprintf(FINFO, "%s %11.0f %s%s %s\n",
    240                         permbuf, len, timestring(f->modtime),
    241 -                       f_name(f, NULL));
    242 +                       crtime_buf, f_name(f, NULL));
     214        if (missing_args == 2 && f->mode == 0) {
     215@@ -1156,9 +1173,12 @@ static void list_file_entry(struct file_struct *f)
     216                        colwidth + 31, "*missing",
     217                        f_name(f, NULL));
     218        } else {
     219-               rprintf(FINFO, "%s %*s %s %s\n",
     220+               rprintf(FINFO, "%s %*s %s%s%s %s\n",
     221                        permbuf, colwidth, human_num(len),
     222-                       timestring(f->modtime), f_name(f, NULL));
     223+                       timestring(f->modtime),
     224+                       crtimes_ndx ? " " : "",
     225+                       crtimes_ndx ? timestring(crtime) : "",
     226+                       f_name(f, NULL));
    243227        }
    244228 }
    245229 
    246 @@ -1383,6 +1403,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
     230@@ -1250,6 +1270,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
    247231                        return;
    248232                }
    249233        }
    250234+       sx.crtime = 0;
    251235 
    252236        if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
    253           parent_is_dry_missing:
    254 diff --git a/hlink.c b/hlink.c
    255 --- a/hlink.c
    256 +++ b/hlink.c
    257 @@ -371,6 +371,7 @@ int hard_link_check(struct file_struct *file, int ndx, const char *fname,
    258                 char cmpbuf[MAXPATHLEN];
    259                 stat_x alt_sx;
    260                 int j = 0;
    261 +               alt_sx.crtime = 0;
    262  #ifdef SUPPORT_ACLS
    263                 alt_sx.acc_acl = alt_sx.def_acl = NULL;
    264  #endif
    265 @@ -499,6 +500,7 @@ void finish_hard_link(struct file_struct *file, const char *fname, int fin_ndx,
    266         } else
    267                 our_name = fname;
    268  
    269 +       prev_sx.crtime = 0;
    270  #ifdef SUPPORT_ACLS
    271         prev_sx.acc_acl = prev_sx.def_acl = NULL;
    272  #endif
     237                int i;
    273238diff --git a/ifuncs.h b/ifuncs.h
    274239--- a/ifuncs.h
    275240+++ b/ifuncs.h
    276 @@ -67,6 +67,28 @@ d_name(struct dirent *di)
    277  #endif
     241@@ -43,6 +43,28 @@ free_xbuf(xbuf *xb)
     242        memset(xb, 0, sizeof (xbuf));
    278243 }
    279244 
    280245+static inline time_t
    diff --git a/ifuncs.h b/ifuncs.h 
    300265+}
    301266+
    302267 static inline int
    303  isDigit(const char *ptr)
     268 to_wire_mode(mode_t mode)
    304269 {
    305270diff --git a/log.c b/log.c
    306271--- a/log.c
    307272+++ b/log.c
    308 @@ -661,7 +661,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
     273@@ -723,7 +723,8 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
    309274                        c[8] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
    310275                        c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
    311276                        c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
    diff --git a/log.c b/log.c 
    318283diff --git a/options.c b/options.c
    319284--- a/options.c
    320285+++ b/options.c
    321 @@ -60,6 +60,7 @@ int preserve_specials = 0;
     286@@ -62,6 +62,7 @@ int preserve_specials = 0;
    322287 int preserve_uid = 0;
    323288 int preserve_gid = 0;
    324289 int preserve_times = 0;
    diff --git a/options.c b/options.c 
    326291 int update_only = 0;
    327292 int cvs_exclude = 0;
    328293 int dry_run = 0;
    329 @@ -361,6 +362,7 @@ void usage(enum logcode F)
     294@@ -718,6 +719,7 @@ void usage(enum logcode F)
     295   rprintf(F,"     --specials              preserve special files\n");
    330296   rprintf(F," -D                          same as --devices --specials\n");
    331297   rprintf(F," -t, --times                 preserve modification times\n");
    332    rprintf(F," -O, --omit-dir-times        omit directories from --times\n");
    333298+  rprintf(F," -N, --crtimes               preserve create times (newness)\n");
     299   rprintf(F," -O, --omit-dir-times        omit directories from --times\n");
     300   rprintf(F," -J, --omit-link-times       omit symlinks from --times\n");
    334301   rprintf(F,"     --super                 receiver attempts super-user activities\n");
    335  #ifdef SUPPORT_XATTRS
    336    rprintf(F,"     --fake-super            store/recover privileged attrs using xattrs\n");
    337 @@ -507,6 +509,9 @@ static struct poptOption long_options[] = {
     302@@ -885,6 +887,9 @@ static struct poptOption long_options[] = {
    338303   {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
    339304   {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
    340305   {"no-t",             0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
    diff --git a/options.c b/options.c 
    344309   {"omit-dir-times",  'O', POPT_ARG_VAL,    &omit_dir_times, 1, 0, 0 },
    345310   {"no-omit-dir-times",0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
    346311   {"no-O",             0,  POPT_ARG_VAL,    &omit_dir_times, 0, 0, 0 },
    347 @@ -1810,6 +1815,8 @@ void server_options(char **args, int *argc_p)
     312@@ -2465,6 +2470,8 @@ void server_options(char **args, int *argc_p)
    348313                argstr[x++] = 'D';
    349314        if (preserve_times)
    350315                argstr[x++] = 't';
    diff --git a/options.c b/options.c 
    356321diff --git a/rsync.c b/rsync.c
    357322--- a/rsync.c
    358323+++ b/rsync.c
    359 @@ -464,6 +464,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    360                                 full_fname(fname));
    361                         return 0;
    362                 }
    363 +               sx2.crtime = 0;
    364  #ifdef SUPPORT_ACLS
    365                 sx2.acc_acl = sx2.def_acl = NULL;
    366  #endif
    367 @@ -505,6 +506,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     324@@ -581,6 +581,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    368325         || (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
    369326         || (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
    370327                flags |= ATTRS_SKIP_MTIME;
    diff --git a/rsync.c b/rsync.c 
    373330+               flags |= ATTRS_SKIP_CRTIME;
    374331        if (!(flags & ATTRS_SKIP_MTIME)
    375332            && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
    376                 int ret = set_modtime(fname, file->modtime, sxp->st.st_mode, ST_FLAGS(sxp->st));
    377 @@ -518,6 +522,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     333                int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode, ST_FLAGS(sxp->st));
     334@@ -594,6 +597,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    378335                else
    379336                        file->flags |= FLAG_TIME_FAILED;
    380337        }
    diff --git a/rsync.c b/rsync.c 
    387344+                       updated = 1;
    388345+       }
    389346 
    390         change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
    391         change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
    392 @@ -675,7 +687,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
     347 #ifdef SUPPORT_ACLS
     348        /* It's OK to call set_acl() now, even for a dir, as the generator
     349@@ -710,7 +721,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
    393350        /* Change permissions before putting the file into place. */
    394351        set_file_attrs(fnametmp, file, NULL, fnamecmp,
    395352                       ATTRS_DELAY_IMMUTABLE
    diff --git a/rsync.c b/rsync.c 
    397354+                      | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME | ATTRS_SKIP_CRTIME));
    398355 
    399356        /* move tmp file over real file */
    400         if (verbose > 2)
    401 @@ -706,7 +718,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
     357        if (DEBUG_GTE(RECV, 1))
     358@@ -739,7 +750,7 @@ int finish_transfer(const char *fname, const char *fnametmp,
    402359 
    403360   do_set_file_attrs:
    404361        set_file_attrs(fnametmp, file, NULL, fnamecmp,
    diff --git a/rsync.c b/rsync.c 
    410367diff --git a/rsync.h b/rsync.h
    411368--- a/rsync.h
    412369+++ b/rsync.h
    413 @@ -61,6 +61,7 @@
    414  #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
     370@@ -62,7 +62,8 @@
    415371 #define XMIT_HLINK_FIRST (1<<12)       /* protocols 30 - now (HLINKED files only) */
    416372 #define XMIT_IO_ERROR_ENDLIST (1<<12)  /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
    417 +#define XMIT_CRTIME_EQ_MTIME (1<<13)   /* protocols ?? - now */
    418  #define XMIT_SAME_FLAGS (1<<14)                /* protocols ?? - now */
     373 #define XMIT_MOD_NSEC (1<<13)          /* protocols 31 - now */
     374-#define XMIT_SAME_FLAGS (1<<14)                /* protocols ?? - now */
     375+#define XMIT_CRTIME_EQ_MTIME (1<<14)   /* protocols ?? - now */
     376+#define XMIT_SAME_FLAGS (1<<15)                /* protocols ?? - now */
    419377 
    420378 /* These flags are used in the live flist data. */
    421 @@ -162,6 +163,7 @@
     379 
     380@@ -167,6 +168,7 @@
    422381 #define ATTRS_REPORT           (1<<0)
    423382 #define ATTRS_SKIP_MTIME       (1<<1)
    424383 #define ATTRS_DELAY_IMMUTABLE  (1<<2)
    diff --git a/rsync.h b/rsync.h 
    426385 
    427386 #define FULL_FLUSH     1
    428387 #define NORMAL_FLUSH   0
    429 @@ -178,7 +180,7 @@
     388@@ -183,7 +185,7 @@
    430389 #define FNAMECMP_FUZZY         0x83
    431390 
    432391 /* For use by the itemize_changes code */
    diff --git a/rsync.h b/rsync.h 
    435394 #define ITEM_REPORT_CHANGE (1<<1)
    436395 #define ITEM_REPORT_SIZE (1<<2)     /* regular files only */
    437396 #define ITEM_REPORT_TIMEFAIL (1<<2) /* symlinks only */
    438 @@ -677,6 +679,7 @@ extern int file_extra_cnt;
     397@@ -734,6 +736,7 @@ extern int file_extra_cnt;
    439398 extern int inc_recurse;
    440399 extern int uid_ndx;
    441400 extern int gid_ndx;
    diff --git a/rsync.h b/rsync.h 
    443402 extern int fileflags_ndx;
    444403 extern int acls_ndx;
    445404 extern int xattrs_ndx;
    446 @@ -684,6 +687,7 @@ extern int xattrs_ndx;
     405@@ -741,6 +744,7 @@ extern int xattrs_ndx;
    447406 #define FILE_STRUCT_LEN (offsetof(struct file_struct, basename))
    448407 #define EXTRA_LEN (sizeof (union file_extras))
    449408 #define PTR_EXTRA_CNT ((sizeof (char *) + EXTRA_LEN - 1) / EXTRA_LEN)
    diff --git a/rsync.h b/rsync.h 
    451410 #define DEV_EXTRA_CNT 2
    452411 #define DIRNODE_EXTRA_CNT 3
    453412 #define SUM_EXTRA_CNT ((MAX_DIGEST_LEN + EXTRA_LEN - 1) / EXTRA_LEN)
    454 @@ -951,6 +955,7 @@ typedef struct {
     413@@ -1022,6 +1026,7 @@ typedef struct {
    455414 
    456415 typedef struct {
    457416     STRUCT_STAT st;
    diff --git a/rsync.h b/rsync.h 
    462421diff --git a/rsync.yo b/rsync.yo
    463422--- a/rsync.yo
    464423+++ b/rsync.yo
    465 @@ -367,6 +367,7 @@ to the detailed description below for a complete description.  verb(
     424@@ -373,6 +373,7 @@ to the detailed description below for a complete description.  verb(
     425      --specials              preserve special files
    466426  -D                          same as --devices --specials
    467427  -t, --times                 preserve modification times
    468   -O, --omit-dir-times        omit directories from --times
    469428+ -N, --crtimes               preserve create times (newness)
     429  -O, --omit-dir-times        omit directories from --times
     430  -J, --omit-link-times       omit symlinks from --times
    470431      --super                 receiver attempts super-user activities
    471       --fake-super            store/recover privileged attrs using xattrs
    472   -S, --sparse                handle sparse files efficiently
    473 @@ -1105,6 +1106,9 @@ it is preserving modification times (see bf(--times)).  If NFS is sharing
    474  the directories on the receiving side, it is a good idea to use bf(-O).
    475  This option is inferred if you use bf(--backup) without bf(--backup-dir).
     432@@ -1201,6 +1202,9 @@ cause the next transfer to behave as if it used bf(-I), causing all files to be
     433 updated (though rsync's delta-transfer algorithm will make the update fairly efficient
     434 if the files haven't actually changed, you're much better off using bf(-t)).
    476435 
    477436+dit(bf(-N, --crtimes)) This tells rsync to set the create times (newness) of
    478437+the destination files to the same value as the source files.
    479438+
    480  dit(bf(--super)) This tells the receiving side to attempt super-user
    481  activities even if the receiving rsync wasn't run by the super-user.  These
    482  activities include: preserving users via the bf(--owner) option, preserving
    483 @@ -1811,7 +1815,7 @@ with older versions of rsync, but that also turns on the output of other
     439 dit(bf(-O, --omit-dir-times)) This tells rsync to omit directories when
     440 it is preserving modification times (see bf(--times)).  If NFS is sharing
     441 the directories on the receiving side, it is a good idea to use bf(-O).
     442@@ -2103,7 +2107,7 @@ with older versions of rsync, but that also turns on the output of other
    484443 verbose messages).
    485444 
    486445 The "%i" escape has a cryptic output that is 11 letters long.  The general
    diff --git a/rsync.yo b/rsync.yo 
    489448 type of update being done, bf(X) is replaced by the file-type, and the
    490449 other letters represent attributes that may be output if they are being
    491450 modified.
    492 @@ -1870,6 +1874,8 @@ quote(itemization(
     451@@ -2162,6 +2166,8 @@ quote(itemization(
    493452   it() The bf(f) means that the fileflags information changed.
    494453   it() The bf(a) means that the ACL information changed.
    495454   it() The bf(x) means that the extended attribute information changed.
    diff --git a/rsync.yo b/rsync.yo 
    501460diff --git a/syscall.c b/syscall.c
    502461--- a/syscall.c
    503462+++ b/syscall.c
    504 @@ -37,6 +37,13 @@ extern int force_change;
     463@@ -42,6 +42,13 @@ extern int force_change;
    505464 extern int preserve_perms;
    506465 extern int preserve_executability;
    507466 
    diff --git a/syscall.c b/syscall.c 
    515474 #define RETURN_ERROR_IF(x,e) \
    516475        do { \
    517476                if (x) { \
    518 @@ -529,6 +536,36 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence)
     477@@ -460,6 +467,36 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence)
    519478 #endif
    520479 }
    521480 
    diff --git a/syscall.c b/syscall.c 
    550509+}
    551510+
    552511 #ifdef HAVE_UTIMENSAT
    553  int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags)
     512 int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec)
    554513 {
    555514diff --git a/testsuite/crtimes.test b/testsuite/crtimes.test
    556515new file mode 100644
    new file mode 100644 
    584543diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns
    585544--- a/testsuite/rsync.fns
    586545+++ b/testsuite/rsync.fns
    587 @@ -24,9 +24,9 @@ todir="$tmpdir/to"
     546@@ -23,9 +23,9 @@ todir="$tmpdir/to"
    588547 chkdir="$tmpdir/chk"
    589548 
    590549 # For itemized output:
    diff --git a/testsuite/rsync.fns b/testsuite/rsync.fns 
    600559diff --git a/tls.c b/tls.c
    601560--- a/tls.c
    602561+++ b/tls.c
    603 @@ -107,6 +107,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
     562@@ -109,6 +109,8 @@ static int stat_xattr(const char *fname, STRUCT_STAT *fst)
    604563 
    605564 #endif
    606565 
    diff --git a/tls.c b/tls.c 
    609568 static void failed(char const *what, char const *where)
    610569 {
    611570        fprintf(stderr, PROGRAM ": %s %s: %s\n",
    612 @@ -114,16 +116,36 @@ static void failed(char const *what, char const *where)
     571@@ -116,16 +118,44 @@ static void failed(char const *what, char const *where)
    613572        exit(1);
    614573 }
    615574 
    616 +static void storetime(char *dest, time_t t, size_t destsize)
     575+static void storetime(char *dest, size_t destsize, time_t t, int nsecs)
    617576+{
    618577+       if (t) {
     578+               int len;
    619579+               struct tm *mt = gmtime(&t);
    620580+
    621 +               snprintf(dest, destsize,
    622 +                       "%04d-%02d-%02d %02d:%02d:%02d ",
     581+               len = snprintf(dest, destsize,
     582+                       "%04d-%02d-%02d %02d:%02d:%02d",
    623583+                       (int)mt->tm_year + 1900,
    624584+                       (int)mt->tm_mon + 1,
    625585+                       (int)mt->tm_mday,
    626586+                       (int)mt->tm_hour,
    627587+                       (int)mt->tm_min,
    628588+                       (int)mt->tm_sec);
    629 +       } else
    630 +               strlcpy(dest, "                    ", destsize);
     589+               if (nsecs >= 0 && len >= 0)
     590+                       snprintf(dest + len, destsize - len, ".%09d", nsecs);
     591+       } else {
     592+               int has_nsecs = nsecs >= 0 ? 1 : 0;
     593+               int len = MIN(19 + 9*has_nsecs, (int)destsize - 1);
     594+               memset(dest, ' ', len);
     595+               dest[len] = '\0';
     596+       }
    631597+}
    632598+
    633599 static void list_file(const char *fname)
    diff --git a/tls.c b/tls.c 
    640606+       char mtimebuf[50];
    641607+       char crtimebuf[50];
    642608        char linkbuf[4096];
     609+       int nsecs;
    643610 
    644611        if (do_lstat(fname, &buf) < 0)
    645612                failed("stat", fname);
    diff --git a/tls.c b/tls.c 
    648615 #ifdef SUPPORT_XATTRS
    649616        if (am_root < 0)
    650617                stat_xattr(fname, &buf);
    651 @@ -158,19 +180,11 @@ static void list_file(const char *fname)
     618@@ -159,30 +189,17 @@ static void list_file(const char *fname)
     619        }
    652620 
    653621        permstring(permbuf, buf.st_mode);
    654  
     622-
    655623-       if (buf.st_mtime) {
     624-               int len;
    656625-               mt = gmtime(&buf.st_mtime);
    657626-
    658 -               snprintf(datebuf, sizeof datebuf,
     627-               len = snprintf(datebuf, sizeof datebuf,
    659628-                       "%04d-%02d-%02d %02d:%02d:%02d",
    660629-                       (int)mt->tm_year + 1900,
    661630-                       (int)mt->tm_mon + 1,
    diff --git a/tls.c b/tls.c 
    663632-                       (int)mt->tm_hour,
    664633-                       (int)mt->tm_min,
    665634-                       (int)mt->tm_sec);
    666 -       } else
    667 -               strlcpy(datebuf, "                   ", sizeof datebuf);
    668 +       storetime(mtimebuf, buf.st_mtime, sizeof mtimebuf);
     635 #ifdef ST_MTIME_NSEC
     636-               if (nsec_times) {
     637-                       snprintf(datebuf + len, sizeof datebuf - len,
     638-                               ".%09d", (int)buf.ST_MTIME_NSEC);
     639-               }
     640+       if (nsec_times)
     641+               nsecs = (int)buf.ST_MTIME_NSEC;
     642+       else
     643 #endif
     644-       } else {
     645-               int len = MIN(19 + 9*nsec_times, (int)sizeof datebuf - 1);
     646-               memset(datebuf, ' ', len);
     647-               datebuf[len] = '\0';
     648-       }
     649+               nsecs = -1;
     650+       storetime(mtimebuf, sizeof mtimebuf, buf.st_mtime, nsecs);
    669651+       if (display_crtimes)
    670 +               storetime(crtimebuf, crtime, sizeof crtimebuf);
     652+               storetime(crtimebuf, sizeof crtimebuf, crtime, -1);
    671653+       else
    672654+               crtimebuf[0] = '\0';
    673655 
    674656        /* TODO: Perhaps escape special characters in fname? */
    675657 
    676 @@ -181,13 +195,14 @@ static void list_file(const char *fname)
     658@@ -193,13 +210,14 @@ static void list_file(const char *fname)
    677659                    (long)minor(buf.st_rdev));
    678         } else /* NB: use double for size since it might not fit in a long. */
    679                 printf("%12.0f", (double)buf.st_size);
     660        } else
     661                printf("%15s", do_big_num(buf.st_size, 1, NULL));
    680662-       printf(" %6ld.%-6ld %6ld %s %s%s\n",
    681663+       printf(" %6ld.%-6ld %6ld %s%s%s%s\n",
    682664               (long)buf.st_uid, (long)buf.st_gid, (long)buf.st_nlink,
    diff --git a/tls.c b/tls.c 
    690672   {"link-times",      'l', POPT_ARG_NONE,   &link_times, 0, 0, 0 },
    691673   {"link-owner",      'L', POPT_ARG_NONE,   &link_owner, 0, 0, 0 },
    692674 #ifdef SUPPORT_XATTRS
    693 @@ -203,6 +218,7 @@ static void tls_usage(int ret)
     675@@ -218,6 +236,7 @@ static void tls_usage(int ret)
    694676   fprintf(F,"usage: " PROGRAM " [OPTIONS] FILE ...\n");
    695677   fprintf(F,"Trivial file listing program for portably checking rsync\n");
    696678   fprintf(F,"\nOptions:\n");
    diff --git a/tls.c b/tls.c 
    698680   fprintf(F," -l, --link-times            display the time on a symlink\n");
    699681   fprintf(F," -L, --link-owner            display the owner+group on a symlink\n");
    700682 #ifdef SUPPORT_XATTRS
    701 diff -up a/proto.h b/proto.h
     683diff -Nurp a/proto.h b/proto.h
    702684--- a/proto.h
    703685+++ b/proto.h
    704 @@ -315,6 +315,8 @@ int do_stat(const char *fname, STRUCT_ST
     686@@ -327,6 +327,8 @@ int do_stat(const char *fname, STRUCT_ST
    705687 int do_lstat(const char *fname, STRUCT_STAT *st);
    706688 int do_fstat(int fd, STRUCT_STAT *st);
    707689 OFF_T do_lseek(int fd, OFF_T offset, int whence);
    708690+time_t get_create_time(const char *path);
    709691+int set_create_time(const char *path, time_t crtime);
    710  int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags);
    711  int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags);
    712  int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags);
    713 diff -up a/rsync.1 b/rsync.1
     692 int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec);
     693 int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec);
     694 int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec);
     695diff -Nurp a/rsync.1 b/rsync.1
    714696--- a/rsync.1
    715697+++ b/rsync.1
    716 @@ -443,6 +443,7 @@ to the detailed description below for a
     698@@ -449,6 +449,7 @@ to the detailed description below for a
     699      \-\-specials              preserve special files
    717700  \-D                          same as \-\-devices \-\-specials
    718701  \-t, \-\-times                 preserve modification times
    719   \-O, \-\-omit\-dir\-times        omit directories from \-\-times
    720702+ \-N, \-\-crtimes               preserve create times (newness)
     703  \-O, \-\-omit\-dir\-times        omit directories from \-\-times
     704  \-J, \-\-omit\-link\-times       omit symlinks from \-\-times
    721705      \-\-super                 receiver attempts super\-user activities
    722       \-\-fake\-super            store/recover privileged attrs using xattrs
    723   \-S, \-\-sparse                handle sparse files efficiently
    724 @@ -1273,6 +1274,10 @@ it is preserving modification times (see
    725  the directories on the receiving side, it is a good idea to use \fB\-O\fP.
    726  This option is inferred if you use \fB\-\-backup\fP without \fB\-\-backup\-dir\fP.
     706@@ -1379,6 +1380,10 @@ cause the next transfer to behave as if
     707 updated (though rsync\(cq\&s delta\-transfer algorithm will make the update fairly efficient
     708 if the files haven\(cq\&t actually changed, you\(cq\&re much better off using \fB\-t\fP).
    727709 .IP
    728710+.IP "\fB\-N, \-\-crtimes\fP"
    729711+This tells rsync to set the create times (newness) of
    730712+the destination files to the same value as the source files.
    731713+.IP
    732  .IP "\fB\-\-super\fP"
    733  This tells the receiving side to attempt super\-user
    734  activities even if the receiving rsync wasn\(cq\&t run by the super\-user.  These
    735 @@ -2067,7 +2072,7 @@ with older versions of rsync, but that a
     714 .IP "\fB\-O, \-\-omit\-dir\-times\fP"
     715 This tells rsync to omit directories when
     716 it is preserving modification times (see \fB\-\-times\fP).  If NFS is sharing
     717@@ -2390,7 +2395,7 @@ with older versions of rsync, but that a
    736718 verbose messages).
    737719 .IP
    738720 The \(dq\&%i\(dq\& escape has a cryptic output that is 11 letters long.  The general
    diff -up a/rsync.1 b/rsync.1 
    741723 type of update being done, \fBX\fP is replaced by the file\-type, and the
    742724 other letters represent attributes that may be output if they are being
    743725 modified.
    744 @@ -2142,6 +2147,9 @@ The \fBf\fP means that the fileflags inf
     726@@ -2465,6 +2470,9 @@ The \fBf\fP means that the fileflags inf
    745727 The \fBa\fP means that the ACL information changed.
    746728 .IP o
    747729 The \fBx\fP means that the extended attribute information changed.
  • rsync/files/patch-fileflags.diff

    diff --git a/rsync/files/patch-fileflags.diff b/rsync/files/patch-fileflags.diff
    index 2b307ff..2bf570c 100644
    a b To use this patch, run these commands for a successful build: 
    88    ./configure
    99    make
    1010
    11 based-on: 40afd365cc8ca968fd16e161d24df5b8a8a520cc
     11based-on: 7cb0de6326c915a72253fd103dae93308031ec3f
    1212diff --git a/Makefile.in b/Makefile.in
    1313--- a/Makefile.in
    1414+++ b/Makefile.in
    15 @@ -42,7 +42,7 @@ popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
     15@@ -45,7 +45,7 @@ popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
    1616        popt/popthelp.o popt/poptparse.o
    17  OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@
     17 OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) @BUILD_ZLIB@ @BUILD_POPT@
    1818 
    1919-TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
    2020+TLS_OBJ = tls.o syscall.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
    2121 
    2222 # Programs we must have to run the test cases
    2323 CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
    24 @@ -107,7 +107,7 @@ getgroups$(EXEEXT): getgroups.o
     24@@ -127,7 +127,7 @@ getgroups$(EXEEXT): getgroups.o
    2525 getfsdev$(EXEEXT): getfsdev.o
    2626        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
    2727 
    diff --git a/Makefile.in b/Makefile.in 
    3333diff --git a/compat.c b/compat.c
    3434--- a/compat.c
    3535+++ b/compat.c
    36 @@ -43,9 +43,11 @@ extern int checksum_seed;
     36@@ -44,9 +44,11 @@ extern int checksum_seed;
    3737 extern int basis_dir_cnt;
    3838 extern int prune_empty_dirs;
    3939 extern int protocol_version;
    diff --git a/compat.c b/compat.c 
    4545 extern int preserve_acls;
    4646 extern int preserve_xattrs;
    4747 extern int need_messages_from_generator;
    48 @@ -63,7 +65,7 @@ extern char *iconv_opt;
     48@@ -64,7 +66,7 @@ extern char *iconv_opt;
    4949 #endif
    5050 
    5151 /* These index values are for the file-list's extra-attribute array. */
    diff --git a/compat.c b/compat.c 
    5454 
    5555 int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
    5656 int sender_symlink_iconv = 0;  /* sender should convert symlink content */
    57 @@ -140,6 +142,8 @@ void setup_protocol(int f_out,int f_in)
     57@@ -142,6 +144,8 @@ void setup_protocol(int f_out,int f_in)
    5858                uid_ndx = ++file_extra_cnt;
    5959        if (preserve_gid)
    6060                gid_ndx = ++file_extra_cnt;
    diff --git a/compat.c b/compat.c 
    6666diff --git a/configure.ac b/configure.ac
    6767--- a/configure.ac
    6868+++ b/configure.ac
    69 @@ -569,6 +569,7 @@ AC_FUNC_UTIME_NULL
     69@@ -597,6 +597,7 @@ AC_FUNC_UTIME_NULL
    7070 AC_FUNC_ALLOCA
    7171 AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
    7272     fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
    diff --git a/configure.ac b/configure.ac 
    7474     memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
    7575     strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
    7676     setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
     77diff --git a/delete.c b/delete.c
     78--- a/delete.c
     79+++ b/delete.c
     80@@ -25,6 +25,7 @@
     81 extern int am_root;
     82 extern int make_backups;
     83 extern int max_delete;
     84+extern int force_change;
     85 extern char *backup_dir;
     86 extern char *backup_suffix;
     87 extern int backup_suffix_len;
     88@@ -97,8 +98,12 @@ static enum delret delete_dir_contents(char *fname, uint16 flags)
     89                }
     90 
     91                strlcpy(p, fp->basename, remainder);
     92+#ifdef SUPPORT_FORCE_CHANGE
     93+               if (force_change)
     94+                       make_mutable(fname, fp->mode, F_FFLAGS(fp), force_change);
     95+#endif
     96                if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
     97-                       do_chmod(fname, fp->mode | S_IWUSR);
     98+                       do_chmod(fname, fp->mode | S_IWUSR, NO_FFLAGS);
     99                /* Save stack by recursing to ourself directly. */
     100                if (S_ISDIR(fp->mode)) {
     101                        if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
     102@@ -139,11 +144,18 @@ enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
     103        }
     104 
     105        if (flags & DEL_NO_UID_WRITE)
     106-               do_chmod(fbuf, mode | S_IWUSR);
     107+               do_chmod(fbuf, mode | S_IWUSR, NO_FFLAGS);
     108 
     109        if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
     110                /* This only happens on the first call to delete_item() since
     111                 * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
     112+#ifdef SUPPORT_FORCE_CHANGE
     113+               if (force_change) {
     114+                       STRUCT_STAT st;
     115+                       if (x_lstat(fbuf, &st, NULL) == 0)
     116+                               make_mutable(fbuf, st.st_mode, st.st_flags, force_change);
     117+               }
     118+#endif
     119                ignore_perishable = 1;
     120                /* If DEL_RECURSE is not set, this just reports emptiness. */
     121                ret = delete_dir_contents(fbuf, flags);
    77122diff --git a/flist.c b/flist.c
    78123--- a/flist.c
    79124+++ b/flist.c
    80 @@ -51,6 +51,7 @@ extern int preserve_links;
     125@@ -50,6 +50,7 @@ extern int preserve_links;
    81126 extern int preserve_hard_links;
    82127 extern int preserve_devices;
    83128 extern int preserve_specials;
    84129+extern int preserve_fileflags;
    85130 extern int delete_during;
     131 extern int missing_args;
    86132 extern int eol_nulls;
    87  extern int relative_paths;
    88 @@ -394,6 +395,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
     133@@ -399,6 +400,9 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    89134 {
    90135        static time_t modtime;
    91136        static mode_t mode;
    diff --git a/flist.c b/flist.c 
    95140 #ifdef SUPPORT_HARD_LINKS
    96141        static int64 dev;
    97142 #endif
    98 @@ -423,6 +427,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
     143@@ -442,6 +446,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    99144                xflags |= XMIT_SAME_MODE;
    100145        else
    101146                mode = file->mode;
    diff --git a/flist.c b/flist.c 
    110155 
    111156        if (preserve_devices && IS_DEVICE(mode)) {
    112157                if (protocol_version < 28) {
    113 @@ -547,6 +559,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
    114         }
     158@@ -583,6 +595,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
     159                write_varint(f, F_MOD_NSEC(file));
    115160        if (!(xflags & XMIT_SAME_MODE))
    116161                write_int(f, to_wire_mode(mode));
    117162+#ifdef SUPPORT_FILEFLAGS
    diff --git a/flist.c b/flist.c 
    121166        if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
    122167                if (protocol_version < 30)
    123168                        write_int(f, uid);
    124 @@ -634,6 +650,9 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
     169@@ -672,6 +688,9 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
    125170 {
    126171        static int64 modtime;
    127172        static mode_t mode;
    diff --git a/flist.c b/flist.c 
    131176 #ifdef SUPPORT_HARD_LINKS
    132177        static int64 dev;
    133178 #endif
    134 @@ -731,6 +750,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
    135                         file_length = F_LENGTH(first);
     179@@ -779,6 +798,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
    136180                        modtime = first->modtime;
     181                        modtime_nsec = F_MOD_NSEC(first);
    137182                        mode = first->mode;
    138183+#ifdef SUPPORT_FILEFLAGS
    139184+                       if (preserve_fileflags)
    diff --git a/flist.c b/flist.c 
    142187                        if (preserve_uid)
    143188                                uid = F_OWNER(first);
    144189                        if (preserve_gid)
    145 @@ -768,6 +791,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
     190@@ -820,6 +843,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
    146191 
    147         if (chmod_modes && !S_ISLNK(mode))
     192        if (chmod_modes && !S_ISLNK(mode) && mode)
    148193                mode = tweak_mode(mode, chmod_modes);
    149194+#ifdef SUPPORT_FILEFLAGS
    150195+       if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
    diff --git a/flist.c b/flist.c 
    153198 
    154199        if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
    155200                if (protocol_version < 30)
    156 @@ -909,6 +936,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
     201@@ -978,6 +1005,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
    157202        }
    158203 #endif
    159204        file->mode = mode;
    160205+#ifdef SUPPORT_FILEFLAGS
    161 +       if (fileflags_ndx) /* check the ndx for force_change w/o preserve_fileflags */
     206+       if (preserve_fileflags)
    162207+               F_FFLAGS(file) = fileflags;
    163208+#endif
    164209        if (preserve_uid)
    165210                F_OWNER(file) = uid;
    166211        if (preserve_gid) {
    167 @@ -1283,6 +1314,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
     212@@ -1375,6 +1406,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
    168213        }
    169214 #endif
    170215        file->mode = st.st_mode;
    diff --git a/flist.c b/flist.c 
    175220        if (preserve_uid)
    176221                F_OWNER(file) = st.st_uid;
    177222        if (preserve_gid)
    178 @@ -1429,6 +1464,9 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
    179  #ifdef SUPPORT_XATTRS
    180                 if (preserve_xattrs) {
    181                         sx.st.st_mode = file->mode;
    182 +#ifdef SUPPORT_FILEFLAGS
    183 +                       sx.st.st_flags = preserve_fileflags ? F_FFLAGS(file) : 0;
    184 +#endif
    185                         sx.xattr = NULL;
    186                         if (get_xattr(fname, &sx) < 0) {
    187                                 io_error |= IOERR_GENERAL;
    188223diff --git a/generator.c b/generator.c
    189224--- a/generator.c
    190225+++ b/generator.c
    191 @@ -35,6 +35,7 @@ extern int do_progress;
    192  extern int relative_paths;
    193  extern int implied_dirs;
    194  extern int keep_dirlinks;
    195 +extern int force_change;
    196  extern int preserve_acls;
    197  extern int preserve_xattrs;
    198  extern int preserve_links;
    199 @@ -42,6 +43,7 @@ extern int preserve_devices;
     226@@ -42,8 +42,10 @@ extern int preserve_devices;
    200227 extern int preserve_specials;
    201228 extern int preserve_hard_links;
    202229 extern int preserve_executability;
    203230+extern int preserve_fileflags;
    204231 extern int preserve_perms;
    205232 extern int preserve_times;
     233+extern int force_change;
    206234 extern int delete_mode;
    207 @@ -164,11 +166,15 @@ static enum delret delete_item(char *fbuf, uint16 mode, uint16 flags)
    208         }
    209  
    210         if (flags & DEL_NO_UID_WRITE)
    211 -               do_chmod(fbuf, mode | S_IWUSR);
    212 +               do_chmod(fbuf, mode | S_IWUSR, NO_FFLAGS);
    213  
    214         if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
    215                 /* This only happens on the first call to delete_item() since
    216                  * delete_dir_contents() always calls us w/DEL_DIR_IS_EMPTY. */
    217 +#ifdef SUPPORT_FORCE_CHANGE
    218 +               if (force_change)
    219 +                       make_mutable(fbuf, NULL, NO_FFLAGS, force_change);
    220 +#endif
    221                 ignore_perishable = 1;
    222                 /* If DEL_RECURSE is not set, this just reports emptiness. */
    223                 ret = delete_dir_contents(fbuf, flags);
    224 @@ -285,8 +291,14 @@ static enum delret delete_dir_contents(char *fname, uint16 flags)
    225                 }
    226  
    227                 strlcpy(p, fp->basename, remainder);
    228 +#ifdef SUPPORT_FORCE_CHANGE
    229 +               if (force_change) {
    230 +                       mode_t mode = fp->mode;
    231 +                       make_mutable(fname, &mode, F_FFLAGS(fp), force_change);
    232 +               }
    233 +#endif
    234                 if (!(fp->mode & S_IWUSR) && !am_root && fp->flags & FLAG_OWNED_BY_US)
    235 -                       do_chmod(fname, fp->mode | S_IWUSR);
    236 +                       do_chmod(fname, fp->mode | S_IWUSR, NO_FFLAGS);
    237                 /* Save stack by recursing to ourself directly. */
    238                 if (S_ISDIR(fp->mode)) {
    239                         if (delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
    240 @@ -647,6 +659,10 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
     235 extern int delete_before;
     236 extern int delete_during;
     237@@ -465,6 +467,10 @@ int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
    241238                        return 0;
    242239                if (perms_differ(file, sxp))
    243240                        return 0;
    diff --git a/generator.c b/generator.c 
    248245                if (ownership_differs(file, sxp))
    249246                        return 0;
    250247 #ifdef SUPPORT_ACLS
    251 @@ -698,6 +714,11 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
     248@@ -516,6 +522,11 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
    252249                if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
    253250                    && sxp->st.st_gid != (gid_t)F_GROUP(file))
    254251                        iflags |= ITEM_REPORT_GROUP;
    diff --git a/generator.c b/generator.c 
    260257 #ifdef SUPPORT_ACLS
    261258                if (preserve_acls && !S_ISLNK(file->mode)) {
    262259                        if (!ACL_READY(*sxp))
    263 @@ -1491,6 +1512,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
     260@@ -1395,6 +1406,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
    264261                        file->mode = dest_mode(file->mode, sx.st.st_mode,
    265262                                               dflt_perms, statret == 0);
    266263                }
    diff --git a/generator.c b/generator.c 
    271268                if (statret != 0 && basis_dir[0] != NULL) {
    272269                        int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
    273270                                              itemizing, code);
    274 @@ -1535,10 +1560,17 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
     271@@ -1439,10 +1454,15 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
    275272                 * readable and writable permissions during the time we are
    276273                 * putting files within them.  This is then restored to the
    277274                 * former permissions after the transfer is done. */
    278275+#ifdef SUPPORT_FORCE_CHANGE
    279 +               if (force_change && F_FFLAGS(file) & force_change) {
    280 +                       mode_t mode = file->mode;
    281 +                       if (make_mutable(fname, &mode, F_FFLAGS(file), force_change))
    282 +                               need_retouch_dir_perms = 1;
    283 +               }
     276+               if (force_change && F_FFLAGS(file) & force_change
     277+                && make_mutable(fname, file->mode, F_FFLAGS(file), force_change))
     278+                       need_retouch_dir_perms = 1;
    284279+#endif
    285280 #ifdef HAVE_CHMOD
    286281                if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
    diff --git a/generator.c b/generator.c 
    290285                                rsyserr(FERROR_XFER, errno,
    291286                                        "failed to modify permissions on %s",
    292287                                        full_fname(fname));
    293 @@ -1573,6 +1605,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
     288@@ -1477,6 +1497,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
    294289                file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms,
    295290                                       exists);
    296291        }
    diff --git a/generator.c b/generator.c 
    301296 
    302297 #ifdef SUPPORT_HARD_LINKS
    303298        if (preserve_hard_links && F_HLINK_NOT_FIRST(file)
    304 @@ -2116,13 +2152,17 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
     299@@ -2045,13 +2069,17 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
    305300                        continue;
    306301                fname = f_name(file, NULL);
    307302                if (fix_dir_perms)
    diff --git a/generator.c b/generator.c 
    311306                        STRUCT_STAT st;
    312307                        if (link_stat(fname, &st, 0) == 0
    313308                         && cmp_time(st.st_mtime, file->modtime) != 0)
    314 -                               set_modtime(fname, file->modtime, file->mode);
    315 +                               set_modtime(fname, file->modtime, file->mode, 0);
     309-                               set_modtime(fname, file->modtime, F_MOD_NSEC(file), file->mode);
     310+                               set_modtime(fname, file->modtime, F_MOD_NSEC(file), file->mode, 0);
    316311                }
    317312+#ifdef SUPPORT_FORCE_CHANGE
    318313+               if (force_change && F_FFLAGS(file) & force_change)
    diff --git a/generator.c b/generator.c 
    320315+#endif
    321316                if (counter >= loopchk_limit) {
    322317                        if (allowed_lull)
    323                                 maybe_send_keepalive();
     318                                maybe_send_keepalive(time(NULL), MSK_ALLOW_FLUSH);
    324319diff --git a/log.c b/log.c
    325320--- a/log.c
    326321+++ b/log.c
    327 @@ -658,7 +658,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
     322@@ -720,7 +720,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
    328323                        c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
    329324                        c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
    330325                        c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
    diff --git a/main.c b/main.c 
    344339+#include <sys/sysctl.h>
    345340+#endif
    346341 
    347  extern int verbose;
    348342 extern int dry_run;
    349 @@ -51,6 +54,7 @@ extern int protocol_version;
     343 extern int list_only;
     344@@ -51,6 +54,7 @@ extern int copy_unsafe_links;
     345 extern int keep_dirlinks;
     346 extern int preserve_hard_links;
     347 extern int protocol_version;
     348+extern int force_change;
    350349 extern int file_total;
    351350 extern int recurse;
    352351 extern int xfer_dirs;
    353 +extern int force_change;
    354  extern int protect_args;
    355  extern int relative_paths;
    356  extern int sanitize_paths;
    357 @@ -753,6 +757,22 @@ static int do_recv(int f_in, int f_out, char *local_name)
     352@@ -839,6 +843,22 @@ static int do_recv(int f_in, int f_out, char *local_name)
    358353         * points to an identical file won't be replaced by the referent. */
    359354        copy_links = copy_dirlinks = copy_unsafe_links = 0;
    360355 
    diff --git a/main.c b/main.c 
    380375diff --git a/options.c b/options.c
    381376--- a/options.c
    382377+++ b/options.c
    383 @@ -53,6 +53,7 @@ int preserve_hard_links = 0;
     378@@ -55,6 +55,7 @@ int preserve_hard_links = 0;
    384379 int preserve_acls = 0;
    385380 int preserve_xattrs = 0;
    386381 int preserve_perms = 0;
    diff --git a/options.c b/options.c 
    388383 int preserve_executability = 0;
    389384 int preserve_devices = 0;
    390385 int preserve_specials = 0;
    391 @@ -84,6 +85,7 @@ int implied_dirs = 1;
    392  int numeric_ids = 0;
     386@@ -89,6 +90,7 @@ int numeric_ids = 0;
     387 int msgs2stderr = 0;
    393388 int allow_8bit_chars = 0;
    394389 int force_delete = 0;
    395390+int force_change = 0;
    396391 int io_timeout = 0;
    397392 int prune_empty_dirs = 0;
    398393 int use_qsort = 0;
    399 @@ -223,6 +225,7 @@ static void print_rsync_version(enum logcode f)
     394@@ -574,6 +576,7 @@ static void print_rsync_version(enum logcode f)
    400395        char const *links = "no ";
    401396        char const *iconv = "no ";
    402397        char const *ipv6 = "no ";
    diff --git a/options.c b/options.c 
    404399        STRUCT_STAT *dumstat;
    405400 
    406401 #if SUBPROTOCOL_VERSION != 0
    407 @@ -256,6 +259,9 @@ static void print_rsync_version(enum logcode f)
     402@@ -610,6 +613,9 @@ static void print_rsync_version(enum logcode f)
    408403 #ifdef CAN_SET_SYMLINK_TIMES
    409404        symtimes = "";
    410405 #endif
    diff --git a/options.c b/options.c 
    414409 
    415410        rprintf(f, "%s  version %s  protocol version %d%s\n",
    416411                RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
    417 @@ -269,8 +275,8 @@ static void print_rsync_version(enum logcode f)
     412@@ -623,8 +629,8 @@ static void print_rsync_version(enum logcode f)
    418413                (int)(sizeof (int64) * 8));
    419414        rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
    420415                got_socketpair, hardlinks, links, ipv6, have_inplace);
    421 -       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes\n",
    422 -               have_inplace, acls, xattrs, iconv, symtimes);
    423 +       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sfile-flags\n",
    424 +               have_inplace, acls, xattrs, iconv, symtimes, fileflags);
     416-       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc\n",
     417-               have_inplace, acls, xattrs, iconv, symtimes, prealloc);
     418+       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc, %sfile-flags\n",
     419+               have_inplace, acls, xattrs, iconv, symtimes, prealloc, fileflags);
    425420 
    426421 #ifdef MAINTAINER_MODE
    427422        rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
    428 @@ -337,6 +343,9 @@ void usage(enum logcode F)
     423@@ -695,6 +701,9 @@ void usage(enum logcode F)
    429424   rprintf(F," -K, --keep-dirlinks         treat symlinked dir on receiver as dir\n");
    430425   rprintf(F," -H, --hard-links            preserve hard links\n");
    431426   rprintf(F," -p, --perms                 preserve permissions\n");
    diff --git a/options.c b/options.c 
    435430   rprintf(F," -E, --executability         preserve the file's executability\n");
    436431   rprintf(F,"     --chmod=CHMOD           affect file and/or directory permissions\n");
    437432 #ifdef SUPPORT_ACLS
    438 @@ -374,7 +383,12 @@ void usage(enum logcode F)
    439    rprintf(F,"     --delete-after          receiver deletes after transfer, not during\n");
    440    rprintf(F,"     --delete-excluded       also delete excluded files from destination dirs\n");
     433@@ -740,7 +749,12 @@ void usage(enum logcode F)
     434   rprintf(F,"     --ignore-missing-args   ignore missing source args without error\n");
     435   rprintf(F,"     --delete-missing-args   delete missing source args from destination\n");
    441436   rprintf(F,"     --ignore-errors         delete even if there are I/O errors\n");
    442437-  rprintf(F,"     --force                 force deletion of directories even if not empty\n");
    443438+  rprintf(F,"     --force-delete          force deletion of directories even if not empty\n");
    diff --git a/options.c b/options.c 
    449444   rprintf(F,"     --max-delete=NUM        don't delete more than NUM files\n");
    450445   rprintf(F,"     --max-size=SIZE         don't transfer any file larger than SIZE\n");
    451446   rprintf(F,"     --min-size=SIZE         don't transfer any file smaller than SIZE\n");
    452 @@ -479,6 +493,10 @@ static struct poptOption long_options[] = {
     447@@ -857,6 +871,10 @@ static struct poptOption long_options[] = {
    453448   {"perms",           'p', POPT_ARG_VAL,    &preserve_perms, 1, 0, 0 },
    454449   {"no-perms",         0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
    455450   {"no-p",             0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
    diff --git a/options.c b/options.c 
    460455   {"executability",   'E', POPT_ARG_NONE,   &preserve_executability, 0, 0, 0 },
    461456   {"acls",            'A', POPT_ARG_NONE,   0, 'A', 0, 0 },
    462457   {"no-acls",          0,  POPT_ARG_VAL,    &preserve_acls, 0, 0, 0 },
    463 @@ -557,6 +575,14 @@ static struct poptOption long_options[] = {
     458@@ -943,6 +961,14 @@ static struct poptOption long_options[] = {
    464459   {"remove-source-files",0,POPT_ARG_VAL,    &remove_source_files, 1, 0, 0 },
    465460   {"force",            0,  POPT_ARG_VAL,    &force_delete, 1, 0, 0 },
    466461   {"no-force",         0,  POPT_ARG_VAL,    &force_delete, 0, 0, 0 },
    diff --git a/options.c b/options.c 
    475470   {"ignore-errors",    0,  POPT_ARG_VAL,    &ignore_errors, 1, 0, 0 },
    476471   {"no-ignore-errors", 0,  POPT_ARG_VAL,    &ignore_errors, 0, 0, 0 },
    477472   {"max-delete",       0,  POPT_ARG_INT,    &max_delete, 0, 0, 0 },
    478 @@ -1879,6 +1905,9 @@ void server_options(char **args, int *argc_p)
     473@@ -2537,6 +2563,9 @@ void server_options(char **args, int *argc_p)
    479474        if (xfer_dirs && !recurse && delete_mode && am_sender)
    480475                args[ac++] = "--no-r";
    481476 
    diff --git a/options.c b/options.c 
    485480        if (do_compression && def_compress_level != Z_DEFAULT_COMPRESSION) {
    486481                if (asprintf(&arg, "--compress-level=%d", def_compress_level) < 0)
    487482                        goto oom;
    488 @@ -1966,6 +1995,16 @@ void server_options(char **args, int *argc_p)
     483@@ -2624,6 +2653,16 @@ void server_options(char **args, int *argc_p)
    489484                        args[ac++] = "--delete-excluded";
    490485                if (force_delete)
    491486                        args[ac++] = "--force";
    diff --git a/options.c b/options.c 
    505500diff --git a/rsync.c b/rsync.c
    506501--- a/rsync.c
    507502+++ b/rsync.c
    508 @@ -29,9 +29,11 @@
    509  
    510  extern int verbose;
    511  extern int dry_run;
    512 +extern int force_change;
     503@@ -31,6 +31,7 @@ extern int dry_run;
    513504 extern int preserve_acls;
    514505 extern int preserve_xattrs;
    515506 extern int preserve_perms;
    diff --git a/rsync.c b/rsync.c 
    517508 extern int preserve_executability;
    518509 extern int preserve_times;
    519510 extern int am_root;
    520 @@ -374,6 +376,74 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
     511@@ -452,6 +453,39 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
    521512        return new_mode;
    522513 }
    523514 
    diff --git a/rsync.c b/rsync.c 
    527518+{
    528519+       if (do_chflags(fname, fileflags) != 0) {
    529520+               rsyserr(FERROR_XFER, errno,
    530 +                       "failed to set fileflags (%x) on %s",
    531 +                       fileflags, full_fname(fname));
     521+                       "failed to set file flags on %s",
     522+                       full_fname(fname));
    532523+               return 0;
    533524+       }
    534525+
    535526+       return 1;
    536527+}
    537528+
    538 +/* Remove immutable flags from an object, so it can be altered/removed.
    539 + * Returns the fileflags if flags were removed, otherwise 0.  If the
    540 + * fileflags value is NO_FFLAGS, we will stat the fname to figure out
    541 + * what the flags are, and return the mode via *mode_ptr (if non-NULL). */
    542 +uint32 make_mutable(const char *fname, mode_t *mode_ptr, uint32 fileflags, uint32 iflags)
     529+/* Remove immutable flags from an object, so it can be altered/removed. */
     530+int make_mutable(const char *fname, mode_t mode, uint32 fileflags, uint32 iflags)
    543531+{
    544 +       if (fileflags == NO_FFLAGS) {
    545 +               STRUCT_STAT st;
    546 +               if (x_lstat(fname, &st, NULL) < 0)
    547 +                       return 0;
    548 +               fileflags = st.st_flags;
    549 +               if (mode_ptr)
    550 +                       *mode_ptr = st.st_mode;
    551 +               else
    552 +                       mode_ptr = &st.st_mode;
    553 +       }
    554 +
    555 +       if ((mode_ptr && S_ISLNK(*mode_ptr)) || !(fileflags & iflags))
     532+       if (S_ISLNK(mode) || !(fileflags & iflags))
    556533+               return 0;
    557 +
    558534+       if (!set_fileflags(fname, fileflags & ~iflags))
    559 +               return 0;
    560 +
    561 +       return fileflags;
     535+               return -1;
     536+       return 1;
    562537+}
    563538+
    564539+/* Undo a prior make_mutable() call that returned a 1. */
    565540+int undo_make_mutable(const char *fname, uint32 fileflags)
    566541+{
    567 +       if (!set_fileflags(fname, fileflags)) {
    568 +               rsyserr(FINFO, errno, "failed to relock %s", full_fname(fname));
     542+       if (!set_fileflags(fname, fileflags))
    569543+               return -1;
    570 +       }
    571544+       return 1;
    572545+}
    573 +
    574 +/* This returns the st_flags value if the parent directory was made mutable, otherwise 0.
    575 + * It stores the parent directory path into parent_dirbuf. */
    576 +int make_parentdir_mutable(const char *fname, uint32 iflags, char *parent_dirbuf, int parent_dirbuf_size)
    577 +{
    578 +       char *slash = strrchr(fname, '/');
    579 +
    580 +       if (slash) {
    581 +               int len = slash - fname;
    582 +               if (len >= parent_dirbuf_size)
    583 +                       return 0;
    584 +               strlcpy(parent_dirbuf, fname, len+1);
    585 +       } else
    586 +               strlcpy(parent_dirbuf, ".", parent_dirbuf_size);
    587 +
    588 +       return make_mutable(parent_dirbuf, NULL, NO_FFLAGS, iflags);
    589 +}
    590546+#endif
    591547+
    592548 int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    593549                   const char *fnamecmp, int flags)
    594550 {
    595 @@ -382,6 +452,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    596         int change_uid, change_gid;
    597         mode_t new_mode = file->mode;
    598         int inherit;
    599 +#ifdef SUPPORT_FORCE_CHANGE
    600 +       int became_mutable = 0;
    601 +#endif
    602  
    603         if (!sxp) {
    604                 if (dry_run)
    605 @@ -411,6 +484,11 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    606         if (daemon_chmod_modes && !S_ISLNK(new_mode))
    607                 new_mode = tweak_mode(new_mode, daemon_chmod_modes);
    608  
    609 +#ifdef SUPPORT_FORCE_CHANGE
    610 +       if (force_change)
    611 +               became_mutable = make_mutable(fname, &sxp->st.st_mode, sxp->st.st_flags, force_change);
    612 +#endif
    613 +
    614  #ifdef SUPPORT_ACLS
    615         if (preserve_acls && !S_ISLNK(file->mode) && !ACL_READY(*sxp))
    616                 get_acl(fname, sxp);
    617 @@ -429,7 +507,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    618                 flags |= ATTRS_SKIP_MTIME;
    619         if (!(flags & ATTRS_SKIP_MTIME)
    620             && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
    621 -               int ret = set_modtime(fname, file->modtime, sxp->st.st_mode);
    622 +               int ret = set_modtime(fname, file->modtime, sxp->st.st_mode, ST_FLAGS(sxp->st));
    623                 if (ret < 0) {
    624                         rsyserr(FERROR_XFER, errno, "failed to set times on %s",
    625                                 full_fname(fname));
    626 @@ -465,7 +543,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     551@@ -513,7 +547,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    627552                if (am_root >= 0) {
    628553                        uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
    629554                        gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
    diff --git a/rsync.c b/rsync.c 
    632557                                /* We shouldn't have attempted to change uid
    633558                                 * or gid unless have the privilege. */
    634559                                rsyserr(FERROR_XFER, errno, "%s %s failed",
    635 @@ -503,7 +581,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     560@@ -549,7 +583,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     561                flags |= ATTRS_SKIP_MTIME;
     562        if (!(flags & ATTRS_SKIP_MTIME)
     563            && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
     564-               int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode);
     565+               int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode, ST_FLAGS(sxp->st));
     566                if (ret < 0) {
     567                        rsyserr(FERROR_XFER, errno, "failed to set times on %s",
     568                                full_fname(fname));
     569@@ -576,7 +610,7 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    636570 
    637571 #ifdef HAVE_CHMOD
    638572        if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
    diff --git a/rsync.c b/rsync.c 
    641575                if (ret < 0) {
    642576                        rsyserr(FERROR_XFER, errno,
    643577                                "failed to set permissions on %s",
    644 @@ -515,6 +593,24 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     578@@ -588,6 +622,19 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    645579        }
    646580 #endif
    647581 
    648 +#ifdef SUPPORT_FORCE_CHANGE
    649 +       if (became_mutable)
    650 +               undo_make_mutable(fname, sxp->st.st_flags);
    651 +#endif
    652 +
    653582+#ifdef SUPPORT_FILEFLAGS
    654583+       if (preserve_fileflags && !S_ISLNK(sxp->st.st_mode)
    655584+        && sxp->st.st_flags != F_FFLAGS(file)) {
    diff --git a/rsync.c b/rsync.c 
    663592+       }
    664593+#endif
    665594+
    666         if (verbose > 1 && flags & ATTRS_REPORT) {
     595        if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) {
    667596                if (updated)
    668597                        rprintf(FCLIENT, "%s\n", fname);
    669 @@ -578,7 +674,8 @@ int finish_transfer(const char *fname, const char *fnametmp,
     598@@ -662,7 +709,8 @@ int finish_transfer(const char *fname, const char *fnametmp,
    670599 
    671600        /* Change permissions before putting the file into place. */
    672601        set_file_attrs(fnametmp, file, NULL, fnamecmp,
    diff --git a/rsync.c b/rsync.c 
    675604+                      | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME));
    676605 
    677606        /* move tmp file over real file */
    678         if (verbose > 2)
    679 @@ -597,6 +694,10 @@ int finish_transfer(const char *fname, const char *fnametmp,
     607        if (DEBUG_GTE(RECV, 1))
     608@@ -679,6 +727,10 @@ int finish_transfer(const char *fname, const char *fnametmp,
    680609        }
    681610        if (ret == 0) {
    682611                /* The file was moved into place (not copied), so it's done. */
    diff --git a/rsync.c b/rsync.c 
    690619diff --git a/rsync.h b/rsync.h
    691620--- a/rsync.h
    692621+++ b/rsync.h
    693 @@ -61,6 +61,7 @@
    694  #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
     622@@ -62,6 +62,7 @@
    695623 #define XMIT_HLINK_FIRST (1<<12)       /* protocols 30 - now (HLINKED files only) */
    696624 #define XMIT_IO_ERROR_ENDLIST (1<<12)  /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
     625 #define XMIT_MOD_NSEC (1<<13)          /* protocols 31 - now */
    697626+#define XMIT_SAME_FLAGS (1<<14)                /* protocols ?? - now */
    698627 
    699628 /* These flags are used in the live flist data. */
    700629 
    701 @@ -160,6 +161,7 @@
     630@@ -165,6 +166,7 @@
    702631 
    703632 #define ATTRS_REPORT           (1<<0)
    704633 #define ATTRS_SKIP_MTIME       (1<<1)
    diff --git a/rsync.h b/rsync.h 
    706635 
    707636 #define FULL_FLUSH     1
    708637 #define NORMAL_FLUSH   0
    709 @@ -186,6 +188,7 @@
     638@@ -191,6 +193,7 @@
    710639 #define ITEM_REPORT_GROUP (1<<6)
    711640 #define ITEM_REPORT_ACL (1<<7)
    712641 #define ITEM_REPORT_XATTR (1<<8)
    diff --git a/rsync.h b/rsync.h 
    714643 #define ITEM_BASIS_TYPE_FOLLOWS (1<<11)
    715644 #define ITEM_XNAME_FOLLOWS (1<<12)
    716645 #define ITEM_IS_NEW (1<<13)
    717 @@ -482,6 +485,28 @@ typedef unsigned int size_t;
     646@@ -522,6 +525,28 @@ typedef unsigned int size_t;
    718647 #endif
    719648 #endif
    720649 
    diff --git a/rsync.h b/rsync.h 
    743672 /* Find a variable that is either exactly 32-bits or longer.
    744673  * If some code depends on 32-bit truncation, it will need to
    745674  * take special action in a "#if SIZEOF_INT32 > 4" section. */
    746 @@ -652,6 +677,7 @@ extern int file_extra_cnt;
     675@@ -709,6 +734,7 @@ extern int file_extra_cnt;
    747676 extern int inc_recurse;
    748677 extern int uid_ndx;
    749678 extern int gid_ndx;
    diff --git a/rsync.h b/rsync.h 
    751680 extern int acls_ndx;
    752681 extern int xattrs_ndx;
    753682 
    754 @@ -689,6 +715,11 @@ extern int xattrs_ndx;
     683@@ -750,6 +776,11 @@ extern int xattrs_ndx;
    755684 /* When the associated option is on, all entries will have these present: */
    756685 #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
    757686 #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
    diff --git a/rsync.h b/rsync.h 
    766695diff --git a/rsync.yo b/rsync.yo
    767696--- a/rsync.yo
    768697+++ b/rsync.yo
    769 @@ -355,6 +355,7 @@ to the detailed description below for a complete description.  verb(
     698@@ -362,6 +362,7 @@ to the detailed description below for a complete description.  verb(
    770699  -K, --keep-dirlinks         treat symlinked dir on receiver as dir
    771700  -H, --hard-links            preserve hard links
    772701  -p, --perms                 preserve permissions
    diff --git a/rsync.yo b/rsync.yo 
    774703  -E, --executability         preserve executability
    775704      --chmod=CHMOD           affect file and/or directory permissions
    776705  -A, --acls                  preserve ACLs (implies -p)
    777 @@ -386,7 +387,10 @@ to the detailed description below for a complete description.  verb(
    778       --delete-after          receiver deletes after transfer, not during
    779       --delete-excluded       also delete excluded files from dest dirs
     706@@ -397,7 +398,10 @@ to the detailed description below for a complete description.  verb(
     707      --ignore-missing-args   ignore missing source args without error
     708      --delete-missing-args   delete missing source args from destination
    780709      --ignore-errors         delete even if there are I/O errors
    781710-     --force                 force deletion of dirs even if not empty
    782711+     --force-delete          force deletion of dirs even if not empty
    diff --git a/rsync.yo b/rsync.yo 
    786715      --max-delete=NUM        don't delete more than NUM files
    787716      --max-size=SIZE         don't transfer any file larger than SIZE
    788717      --min-size=SIZE         don't transfer any file smaller than SIZE
    789 @@ -566,7 +570,8 @@ specified, in which case bf(-r) is not implied.
     718@@ -644,7 +648,8 @@ specified, in which case bf(-r) is not implied.
    790719 
    791720 Note that bf(-a) bf(does not preserve hardlinks), because
    792721 finding multiply-linked files is expensive.  You must separately
    diff --git a/rsync.yo b/rsync.yo 
    796725 
    797726 dit(--no-OPTION) You may turn off one or more implied options by prefixing
    798727 the option name with "no-".  Not all options may be prefixed with a "no-":
    799 @@ -846,7 +851,7 @@ they would be using bf(--copy-links).
     728@@ -943,7 +948,7 @@ they would be using bf(--copy-links).
    800729 Without this option, if the sending side has replaced a directory with a
    801730 symlink to a directory, the receiving side will delete anything that is in
    802731 the way of the new symlink, including a directory hierarchy (as long as
    diff --git a/rsync.yo b/rsync.yo 
    805734 
    806735 See also bf(--keep-dirlinks) for an analogous option for the receiving
    807736 side.
    808 @@ -1009,6 +1014,29 @@ Note that this option does not copy rsyncs special xattr values (e.g. those
     737@@ -1106,6 +1111,29 @@ Note that this option does not copy rsyncs special xattr values (e.g. those
    809738 used by bf(--fake-super)) unless you repeat the option (e.g. -XX).  This
    810739 "copy all xattrs" mode cannot be used with bf(--fake-super).
    811740 
    diff --git a/rsync.yo b/rsync.yo 
    830759+dit(bf(--force-schange)) This option causes rsync to disable system-immutable
    831760+flags on files and directories that are being updated or deleted on the
    832761+receiving side.  It does not try to affect user flags.  This option overrides
    833 +bf(--force-change) and bf(--force-schange).
     762+bf(--force-change) and bf(--force-uchange).
    834763+
    835764 dit(bf(--chmod)) This option tells rsync to apply one or more
    836765 comma-separated "chmod" modes to the permission of the files in the
    837766 transfer.  The resulting value is treated as though it were the permissions
    838 @@ -1289,12 +1317,13 @@ See bf(--delete) (which is implied) for more details on file-deletion.
     767@@ -1435,12 +1463,13 @@ display as a "*missing" entry in the bf(--list-only) output.
    839768 dit(bf(--ignore-errors)) Tells bf(--delete) to go ahead and delete files
    840769 even when there are I/O errors.
    841770 
    diff --git a/rsync.yo b/rsync.yo 
    852781 bf(--recursive) option was also enabled.
    853782 
    854783 dit(bf(--max-delete=NUM)) This tells rsync not to delete more than NUM
    855 @@ -1782,7 +1811,7 @@ with older versions of rsync, but that also turns on the output of other
     784@@ -2074,7 +2103,7 @@ with older versions of rsync, but that also turns on the output of other
    856785 verbose messages).
    857786 
    858787 The "%i" escape has a cryptic output that is 11 letters long.  The general
    diff --git a/rsync.yo b/rsync.yo 
    861790 type of update being done, bf(X) is replaced by the file-type, and the
    862791 other letters represent attributes that may be output if they are being
    863792 modified.
    864 @@ -1838,7 +1867,7 @@ quote(itemization(
     793@@ -2130,7 +2159,7 @@ quote(itemization(
    865794   sender's value (requires bf(--owner) and super-user privileges).
    866795   it() A bf(g) means the group is different and is being updated to the
    867796   sender's value (requires bf(--group) and the authority to set the group).
    diff --git a/rsync.yo b/rsync.yo 
    873802diff --git a/syscall.c b/syscall.c
    874803--- a/syscall.c
    875804+++ b/syscall.c
    876 @@ -33,6 +33,7 @@ extern int dry_run;
    877  extern int am_root;
     805@@ -38,6 +38,7 @@ extern int am_root;
     806 extern int am_sender;
    878807 extern int read_only;
    879808 extern int list_only;
    880809+extern int force_change;
    881810 extern int preserve_perms;
    882811 extern int preserve_executability;
    883812 
    884 @@ -50,14 +51,56 @@ int do_unlink(const char *fname)
     813@@ -55,7 +56,23 @@ int do_unlink(const char *fname)
    885814 {
    886815        if (dry_run) return 0;
    887816        RETURN_ERROR_IF_RO_OR_LO;
    888817-       return unlink(fname);
    889818+       if (unlink(fname) == 0)
    890819+               return 0;
    891 +
    892820+#ifdef SUPPORT_FORCE_CHANGE
    893 +       if (force_change && (errno == EPERM || errno == EACCES)) {
    894 +               char parent[MAXPATHLEN];
    895 +               int parent_flags;
    896 +               int saved_errno = errno;
    897 +               int file_flags = make_mutable(fname, NULL, NO_FFLAGS, force_change);
    898 +               if (file_flags && unlink(fname) == 0)
    899 +                       return 0;
    900 +               parent_flags = make_parentdir_mutable(fname, force_change, parent, sizeof parent);
    901 +               if (parent_flags) {
    902 +                       int ret = unlink(fname);
    903 +                       undo_make_mutable(parent, parent_flags);
    904 +                       if (ret == 0)
    905 +                               return 0;
    906 +               }
    907 +               if (file_flags)
    908 +                       undo_make_mutable(fname, file_flags);
    909 +               errno = saved_errno;
    910 +       }
    911 +#endif
    912 +
    913 +       return -1;
    914  }
    915  
    916  int do_symlink(const char *fname1, const char *fname2)
    917  {
    918         if (dry_run) return 0;
    919         RETURN_ERROR_IF_RO_OR_LO;
    920 -       return symlink(fname1, fname2);
    921 +       if (symlink(fname1, fname2) == 0)
    922 +               return 0;
     821+       if (force_change && errno == EPERM) {
     822+               STRUCT_STAT st;
    923823+
    924 +#ifdef SUPPORT_FORCE_CHANGE
    925 +       if (force_change && (errno == EPERM || errno == EACCES)) {
    926 +               char parent[MAXPATHLEN];
    927 +               int saved_errno = errno;
    928 +               int parent_flags = make_parentdir_mutable(fname2, force_change, parent, sizeof parent);
    929 +               if (parent_flags) {
    930 +                       int ret = symlink(fname1, fname2);
    931 +                       undo_make_mutable(parent, parent_flags);
    932 +                       if (ret == 0)
     824+               if (x_lstat(fname, &st, NULL) == 0
     825+                && make_mutable(fname, st.st_mode, st.st_flags, force_change) > 0) {
     826+                       if (unlink(fname) == 0)
    933827+                               return 0;
     828+                       undo_make_mutable(fname, st.st_flags);
    934829+               }
    935 +               errno = saved_errno;
     830+               /* TODO: handle immutable directories */
     831+               errno = EPERM;
    936832+       }
    937833+#endif
    938 +
    939834+       return -1;
    940835 }
    941836 
    942  #ifdef HAVE_LINK
    943 @@ -65,18 +108,55 @@ int do_link(const char *fname1, const char *fname2)
    944  {
    945         if (dry_run) return 0;
    946         RETURN_ERROR_IF_RO_OR_LO;
    947 -       return link(fname1, fname2);
    948 +       if (link(fname1, fname2) == 0)
    949 +               return 0;
    950 +
    951 +#ifdef SUPPORT_FORCE_CHANGE
    952 +       if (force_change && (errno == EPERM || errno == EACCES)) {
    953 +               char parent[MAXPATHLEN];
    954 +               int saved_errno = errno;
    955 +               int parent_flags = make_parentdir_mutable(fname2, force_change, parent, sizeof parent);
    956 +               if (parent_flags) {
    957 +                       int ret = link(fname1, fname2);
    958 +                       undo_make_mutable(parent, parent_flags);
    959 +                       if (ret == 0)
    960 +                               return 0;
    961 +               }
    962 +               errno = saved_errno;
    963 +       }
    964 +#endif
    965 +
    966 +       return -1;
     837 #ifdef SUPPORT_LINKS
     838@@ -116,14 +133,37 @@ int do_link(const char *fname1, const char *fname2)
    967839 }
    968840 #endif
    969841 
    diff --git a/syscall.c b/syscall.c 
    978850-       return lchown(path, owner, group);
    979851+       if (lchown(path, owner, group) == 0)
    980852+               return 0;
    981 +
    982853+#ifdef SUPPORT_FORCE_CHANGE
    983 +       if (force_change && (errno == EPERM || errno == EACCES)) {
    984 +               int saved_errno = errno;
    985 +               fileflags = make_mutable(path, &mode, fileflags, force_change);
    986 +               if (fileflags) {
     854+       if (force_change && errno == EPERM) {
     855+               if (fileflags == NO_FFLAGS) {
     856+                       STRUCT_STAT st;
     857+                       if (x_lstat(path, &st, NULL) == 0) {
     858+                               mode = st.st_mode;
     859+                               fileflags = st.st_flags;
     860+                       }
     861+               }
     862+               if (fileflags != NO_FFLAGS
     863+                && make_mutable(path, mode, fileflags, force_change) > 0) {
    987864+                       int ret = lchown(path, owner, group);
    988865+                       undo_make_mutable(path, fileflags);
    989866+                       if (ret == 0)
    990867+                               return 0;
    991868+               }
    992 +               errno = saved_errno;
     869+               errno = EPERM;
    993870+       }
    994871+#else
    995872+       mode = fileflags = 0; /* avoid compiler warning */
    996873+#endif
    997 +
    998874+       return -1;
    999875 }
    1000876 
    1001877 int do_mknod(const char *pathname, mode_t mode, dev_t dev)
    1002 @@ -116,7 +196,7 @@ int do_mknod(const char *pathname, mode_t mode, dev_t dev)
     878@@ -163,7 +203,7 @@ int do_mknod(const char *pathname, mode_t mode, dev_t dev)
    1003879                        return -1;
    1004880                close(sock);
    1005881 #ifdef HAVE_CHMOD
    diff --git a/syscall.c b/syscall.c 
    1008884 #else
    1009885                return 0;
    1010886 #endif
    1011 @@ -133,21 +213,63 @@ int do_rmdir(const char *pathname)
     887@@ -180,7 +220,22 @@ int do_rmdir(const char *pathname)
    1012888 {
    1013889        if (dry_run) return 0;
    1014890        RETURN_ERROR_IF_RO_OR_LO;
    1015891-       return rmdir(pathname);
    1016892+       if (rmdir(pathname) == 0)
    1017893+               return 0;
    1018 +
    1019894+#ifdef SUPPORT_FORCE_CHANGE
    1020 +       if (force_change && (errno == EPERM || errno == EACCES)) {
    1021 +               char parent[MAXPATHLEN];
    1022 +               int parent_flags;
    1023 +               int saved_errno = errno;
    1024 +               int file_flags = make_mutable(pathname, NULL, NO_FFLAGS, force_change);
    1025 +               if (file_flags && rmdir(pathname) == 0)
    1026 +                       return 0;
    1027 +               parent_flags = make_parentdir_mutable(pathname, force_change, parent, sizeof parent);
    1028 +               if (parent_flags) {
    1029 +                       int ret = rmdir(pathname);
    1030 +                       undo_make_mutable(parent, parent_flags);
    1031 +                       if (ret == 0)
     895+       if (force_change && errno == EPERM) {
     896+               STRUCT_STAT st;
     897+
     898+               if (x_lstat(pathname, &st, NULL) == 0
     899+                && make_mutable(pathname, st.st_mode, st.st_flags, force_change) > 0) {
     900+                       if (rmdir(pathname) == 0)
    1032901+                               return 0;
     902+                       undo_make_mutable(pathname, st.st_flags);
    1033903+               }
    1034 +               if (file_flags)
    1035 +                       undo_make_mutable(pathname, file_flags);
    1036 +               errno = saved_errno;
     904+               errno = EPERM;
    1037905+       }
    1038906+#endif
    1039 +
    1040907+       return -1;
    1041908 }
    1042909 
    1043910 int do_open(const char *pathname, int flags, mode_t mode)
    1044  {
    1045 +       int fd;
    1046         if (flags != O_RDONLY) {
    1047                 RETURN_ERROR_IF(dry_run, 0);
    1048                 RETURN_ERROR_IF_RO_OR_LO;
    1049         }
    1050 +       if ((fd = open(pathname, flags | O_BINARY, mode)) >= 0)
    1051 +               return fd;
    1052 +
    1053 +#ifdef SUPPORT_FORCE_CHANGE
    1054 +       if (force_change && (errno == EPERM || errno == EACCES)) {
    1055 +               char parent[MAXPATHLEN];
    1056 +               int saved_errno = errno;
    1057 +               int parent_flags = make_parentdir_mutable(pathname, force_change, parent, sizeof parent);
    1058 +               if (parent_flags) {
    1059 +                       fd = open(pathname, flags | O_BINARY, mode);
    1060 +                       undo_make_mutable(parent, parent_flags);
    1061 +                       if (fd >= 0)
    1062 +                               return fd;
    1063 +               }
    1064 +               errno = saved_errno;
    1065 +       }
    1066 +#endif
    1067  
    1068 -       return open(pathname, flags | O_BINARY, mode);
    1069 +       return -1;
     911@@ -194,7 +249,7 @@ int do_open(const char *pathname, int flags, mode_t mode)
    1070912 }
    1071913 
    1072914 #ifdef HAVE_CHMOD
    diff --git a/syscall.c b/syscall.c 
    1075917 {
    1076918        int code;
    1077919        if (dry_run) return 0;
    1078 @@ -170,17 +292,93 @@ int do_chmod(const char *path, mode_t mode)
     920@@ -217,17 +272,74 @@ int do_chmod(const char *path, mode_t mode)
    1079921        } else
    1080922                code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */
    1081923 #endif /* !HAVE_LCHMOD */
    1082924+#ifdef SUPPORT_FORCE_CHANGE
    1083 +       if (code < 0 && force_change && (errno == EPERM || errno == EACCES) && !S_ISLNK(mode)) {
    1084 +               int saved_errno = errno;
    1085 +               fileflags = make_mutable(path, &mode, fileflags, force_change);
    1086 +               if (fileflags) {
    1087 +#ifdef HAVE_LCHMOD
    1088 +                       code = lchmod(path, mode & CHMOD_BITS);
    1089 +#else
     925+       if (code < 0 && force_change && errno == EPERM && !S_ISLNK(mode)) {
     926+               if (fileflags == NO_FFLAGS) {
     927+                       STRUCT_STAT st;
     928+                       if (x_lstat(path, &st, NULL) == 0)
     929+                               fileflags = st.st_flags;
     930+               }
     931+               if (fileflags != NO_FFLAGS
     932+                && make_mutable(path, mode, fileflags, force_change) > 0) {
    1090933+                       code = chmod(path, mode & CHMOD_BITS);
    1091 +#endif
    1092934+                       undo_make_mutable(path, fileflags);
    1093935+                       if (code == 0)
    1094936+                               return 0;
    1095937+               }
    1096 +               errno = saved_errno;
     938+               errno = EPERM;
    1097939+       }
    1098940+#else
    1099941+       fileflags = 0; /* avoid compiler warning */
    diff --git a/syscall.c b/syscall.c 
    1120962-       return rename(fname1, fname2);
    1121963+       if (rename(fname1, fname2) == 0)
    1122964+               return 0;
    1123 +
    1124965+#ifdef SUPPORT_FORCE_CHANGE
    1125 +       if (force_change && (errno == EPERM || errno == EACCES)) {
    1126 +               int saved_errno = errno;
    1127 +               int ret = -1, file2_flags = 0;
    1128 +               int file1_flags = make_mutable(fname1, NULL, NO_FFLAGS, force_change);
    1129 +               if (file1_flags && rename(fname1, fname2) == 0)
    1130 +                       ret = 0;
    1131 +               else {
    1132 +                       file2_flags = make_mutable(fname2, NULL, NO_FFLAGS, force_change);
    1133 +                       if (file2_flags && rename(fname1, fname2) == 0)
    1134 +                               ret = 0;
    1135 +                       else {
    1136 +                               char parent1[MAXPATHLEN];
    1137 +                               int parent1_flags = make_parentdir_mutable(fname1, force_change,
    1138 +                                                       parent1, sizeof parent1);
    1139 +                               if (parent1_flags && rename(fname1, fname2) == 0)
    1140 +                                       ret = 0;
    1141 +                               else {
    1142 +                                       char parent2[MAXPATHLEN];
    1143 +                                       int parent2_flags = make_parentdir_mutable(fname2, force_change,
    1144 +                                                               parent2, sizeof parent2);
    1145 +                                       if (parent2_flags) {
    1146 +                                               if (rename(fname1, fname2) == 0)
    1147 +                                                       ret = 0;
    1148 +                                               undo_make_mutable(parent2, parent2_flags);
    1149 +                                       }
    1150 +                               }
    1151 +                               if (parent1_flags)
    1152 +                                       undo_make_mutable(parent1, parent1_flags);
    1153 +                       }
    1154 +               }
    1155 +
    1156 +               if (ret == 0)
    1157 +                       file2_flags = file1_flags; /* file1 is now file2 */
    1158 +               else if (file1_flags)
    1159 +                       undo_make_mutable(fname1, file1_flags);
    1160 +               if (file2_flags)
    1161 +                       undo_make_mutable(fname2, file2_flags);
    1162 +               if (ret == 0)
    1163 +                       return 0;
    1164 +
    1165 +               errno = saved_errno;
    1166 +       }
    1167 +#endif
     966+       if (force_change && errno == EPERM) {
     967+               STRUCT_STAT st1, st2;
     968+               int became_mutable;
    1168969+
    1169 +       return -1;
    1170  }
    1171  
    1172  #ifdef HAVE_FTRUNCATE
    1173 @@ -222,7 +420,25 @@ int do_mkdir(char *fname, mode_t mode)
    1174         if (dry_run) return 0;
    1175         RETURN_ERROR_IF_RO_OR_LO;
    1176         trim_trailing_slashes(fname);
    1177 -       return mkdir(fname, mode);
    1178 +       if (mkdir(fname, mode) == 0)
    1179 +               return 0;
    1180 +
    1181 +#ifdef SUPPORT_FORCE_CHANGE
    1182 +       if (force_change && (errno == EPERM || errno == EACCES)) {
    1183 +               char parent[MAXPATHLEN];
    1184 +               int saved_errno = errno;
    1185 +               int parent_flags = make_parentdir_mutable(fname, force_change, parent, sizeof parent);
    1186 +               if (parent_flags) {
    1187 +                       int ret = mkdir(fname, mode);
    1188 +                       undo_make_mutable(parent, parent_flags);
    1189 +                       if (ret == 0)
     970+               if (x_lstat(fname1, &st1, NULL) != 0)
     971+                       goto failed;
     972+               became_mutable = make_mutable(fname1, st1.st_mode, st1.st_flags, force_change) > 0;
     973+               if (became_mutable && rename(fname1, fname2) == 0)
     974+                       goto success;
     975+               if (x_lstat(fname2, &st2, NULL) == 0
     976+                && make_mutable(fname2, st2.st_mode, st2.st_flags, force_change) > 0) {
     977+                       if (rename(fname1, fname2) == 0) {
     978+                         success:
     979+                               if (became_mutable) /* Yes, use fname2 and st1! */
     980+                                       undo_make_mutable(fname2, st1.st_flags);
    1190981+                               return 0;
    1191 +               }
    1192 +               errno = saved_errno;
    1193 +       }
    1194 +#endif
    1195 +
    1196 +       return -1;
    1197  }
    1198  
    1199  /* like mkstemp but forces permissions */
    1200 @@ -235,7 +451,19 @@ int do_mkstemp(char *template, mode_t perms)
    1201  #if defined HAVE_SECURE_MKSTEMP && defined HAVE_FCHMOD && (!defined HAVE_OPEN64 || defined HAVE_MKSTEMP64)
    1202         {
    1203                 int fd = mkstemp(template);
    1204 -               if (fd == -1)
    1205 +#ifdef SUPPORT_FORCE_CHANGE
    1206 +               if (fd < 0 && force_change) {
    1207 +                       char parent[MAXPATHLEN];
    1208 +                       int saved_errno = errno;
    1209 +                       int parent_flags = make_parentdir_mutable(template, force_change, parent, sizeof parent);
    1210 +                       if (parent_flags) {
    1211 +                               fd = mkstemp(template);
    1212 +                               undo_make_mutable(parent, parent_flags);
    1213982+                       }
    1214 +                       errno = saved_errno;
     983+                       undo_make_mutable(fname2, st2.st_flags);
    1215984+               }
    1216 +#endif
    1217 +               if (fd < 0)
    1218                         return -1;
    1219                 if (fchmod(fd, perms) != 0 && preserve_perms) {
    1220                         int errno_save = errno;
    1221 @@ -302,7 +530,7 @@ OFF_T do_lseek(int fd, OFF_T offset, int whence)
    1222  }
    1223  
    1224  #ifdef HAVE_UTIMENSAT
    1225 -int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec)
    1226 +int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags)
    1227  {
    1228         struct timespec t[2];
    1229  
    1230 @@ -313,12 +541,26 @@ int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec)
    1231         t[0].tv_nsec = UTIME_NOW;
    1232         t[1].tv_sec = modtime;
    1233         t[1].tv_nsec = mod_nsec;
    1234 -       return utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW);
    1235 +       if (utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW) == 0)
    1236 +               return 0;
    1237 +
    1238 +#ifdef SUPPORT_FORCE_CHANGE
    1239 +       fileflags = make_mutable(fname, &mode, fileflags, force_change);
    1240 +       if (fileflags) {
    1241 +               if (utimensat(AT_FDCWD, fname, t, AT_SYMLINK_NOFOLLOW) == 0)
    1242 +                       return 0;
    1243 +               undo_make_mutable(fname, fileflags);
    1244 +       }
    1245 +#else
    1246 +       mode = fileflags; /* avoid compiler warning */
    1247 +#endif
    1248 +
    1249 +       return -1;
    1250  }
    1251  #endif
    1252  
    1253  #ifdef HAVE_LUTIMES
    1254 -int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec)
    1255 +int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags)
    1256  {
    1257         struct timeval t[2];
    1258  
    1259 @@ -329,12 +571,26 @@ int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec)
    1260         t[0].tv_usec = 0;
    1261         t[1].tv_sec = modtime;
    1262         t[1].tv_usec = mod_nsec / 1000;
    1263 -       return lutimes(fname, t);
    1264 +       if (lutimes(fname, t) == 0)
    1265 +               return 0;
    1266 +
    1267 +#ifdef SUPPORT_FORCE_CHANGE
    1268 +       fileflags = make_mutable(fname, &mode, fileflags, force_change);
    1269 +       if (fileflags) {
    1270 +               if (lutimes(fname, t) == 0)
    1271 +                       return 0;
    1272 +               undo_make_mutable(fname, fileflags);
    1273 +       }
    1274 +#else
    1275 +       mode = fileflags; /* avoid compiler warning */
    1276 +#endif
    1277 +
    1278 +       return -1;
    1279  }
    1280  #endif
    1281  
    1282  #ifdef HAVE_UTIMES
    1283 -int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec)
    1284 +int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags)
    1285  {
    1286         struct timeval t[2];
    1287  
    1288 @@ -345,14 +601,28 @@ int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec)
    1289         t[0].tv_usec = 0;
    1290         t[1].tv_sec = modtime;
    1291         t[1].tv_usec = mod_nsec / 1000;
    1292 -       return utimes(fname, t);
    1293 +       if (utimes(fname, t) == 0)
    1294 +               return 0;
    1295 +
    1296 +#ifdef SUPPORT_FORCE_CHANGE
    1297 +       fileflags = make_mutable(fname, &mode, fileflags, force_change);
    1298 +       if (fileflags) {
    1299 +               if (utimes(fname, t) == 0)
    1300 +                       return 0;
    1301 +               undo_make_mutable(fname, fileflags);
     985+               /* TODO: handle immutable directories */
     986+               if (became_mutable)
     987+                       undo_make_mutable(fname1, st1.st_flags);
     988+         failed:
     989+               errno = EPERM;
    1302990+       }
    1303 +#else
    1304 +       mode = fileflags; /* avoid compiler warning */
    1305991+#endif
    1306 +
    1307992+       return -1;
    1308993 }
    1309994 
    1310  #elif defined HAVE_UTIME
    1311 -int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec))
    1312 +int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec), mode_t mode, uint32 fileflags)
    1313  {
    1314  #ifdef HAVE_STRUCT_UTIMBUF
    1315 -       struct utimbuf tbuf;
    1316 +       struct utimbuf tbuf, *t = &tbuf;
    1317  #else
    1318         time_t t[2];
    1319  #endif
    1320 @@ -360,15 +630,28 @@ int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec))
    1321         if (dry_run) return 0;
    1322         RETURN_ERROR_IF_RO_OR_LO;
    1323  
    1324 -# ifdef HAVE_STRUCT_UTIMBUF
    1325 +#ifdef HAVE_STRUCT_UTIMBUF
    1326         tbuf.actime = time(NULL);
    1327         tbuf.modtime = modtime;
    1328 -       return utime(fname, &tbuf);
    1329 -# else
    1330 +#else
    1331         t[0] = time(NULL);
    1332         t[1] = modtime;
    1333 -       return utime(fname, t);
    1334 -# endif
    1335 +#endif
    1336 +       if (utime(fname, t) == 0)
    1337 +               return 0;
    1338 +
    1339 +#ifdef SUPPORT_FORCE_CHANGE
    1340 +       fileflags = make_mutable(fname, &mode, fileflags, force_change);
    1341 +       if (fileflags) {
    1342 +               if (utime(fname, t) == 0)
    1343 +                       return 0;
    1344 +               undo_make_mutable(fname, fileflags);
    1345 +       }
    1346 +#else
    1347 +       mode = fileflags; /* avoid compiler warning */
    1348 +#endif
    1349 +
    1350 +       return -1;
    1351  }
    1352  
    1353  #else
     995 #ifdef HAVE_FTRUNCATE
    1354996diff --git a/t_stub.c b/t_stub.c
    1355997--- a/t_stub.c
    1356998+++ b/t_stub.c
    1357 @@ -26,6 +26,7 @@ int module_id = -1;
     999@@ -28,6 +28,7 @@ int module_id = -1;
     1000 int checksum_len = 0;
    13581001 int relative_paths = 0;
    1359  int human_readable = 0;
    13601002 int module_dirlen = 0;
    13611003+int force_change = 0;
     1004 int preserve_acls = 0;
    13621005 int preserve_times = 0;
    13631006 int preserve_xattrs = 0;
    1364  mode_t orig_umask = 002;
    1365 @@ -90,3 +91,27 @@ struct filter_list_struct daemon_filter_list;
     1007@@ -97,3 +98,23 @@ filter_rule_list daemon_filter_list;
    13661008 {
    13671009        return "tester";
    13681010 }
    13691011+
    13701012+#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
    1371 + uint32 make_mutable(UNUSED(const char *fname), UNUSED(mode_t *mode), UNUSED(uint32 fileflags), UNUSED(uint32 iflags))
     1013+ int make_mutable(UNUSED(const char *fname), UNUSED(mode_t mode), UNUSED(uint32 fileflags), UNUSED(uint32 iflags))
    13721014+{
    13731015+       return 0;
    13741016+}
    13751017+
     1018+/* Undo a prior make_mutable() call that returned a 1. */
    13761019+ int undo_make_mutable(UNUSED(const char *fname), UNUSED(uint32 fileflags))
    13771020+{
    13781021+       return 0;
    13791022+}
    1380 +
    1381 + int make_parentdir_mutable(UNUSED(const char *fname), UNUSED(uint32 iflags), UNUSED(char *parent_dirbuf), UNUSED(int parent_dirbuf_size))
    1382 +{
    1383 +       return 0;
    1384 +}
    13851023+#endif
    13861024+
    13871025+#ifdef SUPPORT_XATTRS
    diff --git a/t_stub.c b/t_stub.c 
    13931031diff --git a/util.c b/util.c
    13941032--- a/util.c
    13951033+++ b/util.c
    1396 @@ -125,7 +125,7 @@ NORETURN void overflow_exit(const char *str)
     1034@@ -33,6 +33,7 @@ extern int relative_paths;
     1035 extern int preserve_times;
     1036 extern int preserve_xattrs;
     1037 extern int preallocate_files;
     1038+extern int force_change;
     1039 extern char *module_dir;
     1040 extern unsigned int module_dirlen;
     1041 extern char *partial_dir;
     1042@@ -115,9 +116,36 @@ void print_child_argv(const char *prefix, char **cmd)
     1043        rprintf(FCLIENT, " (%d args)\n", cnt);
     1044 }
    13971045 
     1046+#ifdef SUPPORT_FORCE_CHANGE
     1047+static int try_a_force_change(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags)
     1048+{
     1049+       if (fileflags == NO_FFLAGS) {
     1050+               STRUCT_STAT st;
     1051+               if (x_lstat(fname, &st, NULL) == 0)
     1052+                       fileflags = st.st_flags;
     1053+       }
     1054+
     1055+       if (fileflags != NO_FFLAGS && make_mutable(fname, mode, fileflags, force_change) > 0) {
     1056+               int ret, save_force_change = force_change;
     1057+
     1058+               force_change = 0; /* Make certain we can't come back here. */
     1059+               ret = set_modtime(fname, modtime, mod_nsec, mode, fileflags);
     1060+               force_change = save_force_change;
     1061+
     1062+               undo_make_mutable(fname, fileflags);
     1063+
     1064+               return ret;
     1065+       }
     1066+
     1067+       errno = EPERM;
     1068+
     1069+       return -1;
     1070+}
     1071+#endif
     1072+
    13981073 /* This returns 0 for success, 1 for a symlink if symlink time-setting
    13991074  * is not possible, or -1 for any other error. */
    1400 -int set_modtime(const char *fname, time_t modtime, mode_t mode)
    1401 +int set_modtime(const char *fname, time_t modtime, mode_t mode, uint32 fileflags)
     1075-int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
     1076+int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags)
    14021077 {
    14031078        static int switch_step = 0;
    14041079 
    1405 @@ -138,7 +138,7 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
    1406         switch (switch_step) {
    1407  #ifdef HAVE_UTIMENSAT
     1080@@ -132,6 +160,11 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
    14081081 #include "case_N.h"
    1409 -               if (do_utimensat(fname, modtime, 0) == 0)
    1410 +               if (do_utimensat(fname, modtime, 0, mode, fileflags) == 0)
     1082                if (do_utimensat(fname, modtime, mod_nsec) == 0)
    14111083                        break;
     1084+#ifdef SUPPORT_FORCE_CHANGE
     1085+               if (force_change && errno == EPERM
     1086+                && try_a_force_change(fname, modtime, mod_nsec, mode, fileflags) == 0)
     1087+                       break;
     1088+#endif
    14121089                if (errno != ENOSYS)
    14131090                        return -1;
    1414 @@ -148,7 +148,7 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
    1415  
    1416  #ifdef HAVE_LUTIMES
     1091                switch_step++;
     1092@@ -142,6 +175,11 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
    14171093 #include "case_N.h"
    1418 -               if (do_lutimes(fname, modtime, 0) == 0)
    1419 +               if (do_lutimes(fname, modtime, 0, mode, fileflags) == 0)
     1094                if (do_lutimes(fname, modtime, mod_nsec) == 0)
    14201095                        break;
     1096+#ifdef SUPPORT_FORCE_CHANGE
     1097+               if (force_change && errno == EPERM
     1098+                && try_a_force_change(fname, modtime, mod_nsec, mode, fileflags) == 0)
     1099+                       break;
     1100+#endif
    14211101                if (errno != ENOSYS)
    14221102                        return -1;
    1423 @@ -167,10 +167,10 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
    1424  
    1425  #include "case_N.h"
    1426  #ifdef HAVE_UTIMES
    1427 -               if (do_utimes(fname, modtime, 0) == 0)
    1428 +               if (do_utimes(fname, modtime, 0, mode, fileflags) == 0)
    1429                         break;
    1430  #else
    1431 -               if (do_utime(fname, modtime, 0) == 0)
    1432 +               if (do_utime(fname, modtime, 0, mode, fileflags) == 0)
     1103                switch_step++;
     1104@@ -165,6 +203,13 @@ int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode)
     1105                if (do_utime(fname, modtime, mod_nsec) == 0)
    14331106                        break;
    14341107 #endif
     1108+#ifdef SUPPORT_FORCE_CHANGE
     1109+               if (force_change && errno == EPERM
     1110+                && try_a_force_change(fname, modtime, mod_nsec, mode, fileflags) == 0)
     1111+                       break;
     1112+#else
     1113+               fileflags = 0; /* avoid compiler warning */
     1114+#endif
    14351115 
     1116                return -1;
     1117        }
    14361118diff --git a/xattrs.c b/xattrs.c
    14371119--- a/xattrs.c
    14381120+++ b/xattrs.c
    1439 @@ -1042,7 +1042,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
     1121@@ -1045,7 +1045,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
    14401122        mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
    14411123             | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
    14421124        if (fst.st_mode != mode)
    diff --git a/xattrs.c b/xattrs.c 
    14451127        if (!IS_DEVICE(fst.st_mode))
    14461128                fst.st_rdev = 0; /* just in case */
    14471129 
    1448 diff -up a/config.h.in b/config.h.in
     1130diff -Nurp a/config.h.in b/config.h.in
    14491131--- a/config.h.in
    14501132+++ b/config.h.in
    1451 @@ -70,6 +70,9 @@
     1133@@ -76,6 +76,9 @@
    14521134 /* Define to 1 if vsprintf has a C99-compatible return value */
    14531135 #undef HAVE_C99_VSNPRINTF
    14541136 
    diff -up a/config.h.in b/config.h.in 
    14581140 /* Define to 1 if you have the `chmod' function. */
    14591141 #undef HAVE_CHMOD
    14601142 
    1461 diff -up a/configure.sh b/configure.sh
     1143diff -Nurp a/configure.sh b/configure.sh
    14621144--- a/configure.sh
    14631145+++ b/configure.sh
    1464 @@ -7444,6 +7444,7 @@ fi
     1146@@ -7652,6 +7652,7 @@ fi
    14651147 
    14661148 for ac_func in waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
    14671149     fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
    diff -up a/configure.sh b/configure.sh 
    14691151     memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
    14701152     strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
    14711153     setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
    1472 diff -up a/proto.h b/proto.h
     1154diff -Nurp a/proto.h b/proto.h
    14731155--- a/proto.h
    14741156+++ b/proto.h
    1475 @@ -274,6 +274,9 @@ int read_ndx_and_attrs(int f_in, int *if
     1157@@ -286,6 +286,8 @@ int read_ndx_and_attrs(int f_in, int f_o
    14761158 void free_sums(struct sum_struct *s);
    14771159 mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
    14781160                 int exists);
    1479 +uint32 make_mutable(const char *fname, mode_t *mode_ptr, uint32 fileflags, uint32 iflags);
     1161+int make_mutable(const char *fname, mode_t mode, uint32 fileflags, uint32 iflags);
    14801162+int undo_make_mutable(const char *fname, uint32 fileflags);
    1481 +int make_parentdir_mutable(const char *fname, uint32 iflags, char *parent_dirbuf, int parent_dirbuf_size);
    14821163 int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    14831164                   const char *fnamecmp, int flags);
    1484  RETSIGTYPE sig_int(UNUSED(int val));
    1485 @@ -297,11 +300,12 @@ void set_socket_options(int fd, char *op
    1486  int do_unlink(const char *fname);
    1487  int do_symlink(const char *fname1, const char *fname2);
     1165 RETSIGTYPE sig_int(int sig_num);
     1166@@ -310,11 +312,12 @@ int do_unlink(const char *fname);
     1167 int do_symlink(const char *lnk, const char *fname);
     1168 ssize_t do_readlink(const char *path, char *buf, size_t bufsiz);
    14881169 int do_link(const char *fname1, const char *fname2);
    14891170-int do_lchown(const char *path, uid_t owner, gid_t group);
    14901171+int do_lchown(const char *path, uid_t owner, gid_t group, mode_t mode, uint32 fileflags);
    diff -up a/proto.h b/proto.h 
    14971178 int do_rename(const char *fname1, const char *fname2);
    14981179 int do_ftruncate(int fd, OFF_T size);
    14991180 void trim_trailing_slashes(char *name);
    1500 @@ -311,10 +315,10 @@ int do_stat(const char *fname, STRUCT_ST
    1501  int do_lstat(const char *fname, STRUCT_STAT *st);
    1502  int do_fstat(int fd, STRUCT_STAT *st);
    1503  OFF_T do_lseek(int fd, OFF_T offset, int whence);
    1504 -int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec);
    1505 -int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec);
    1506 -int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec);
    1507 -int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec));
    1508 +int do_utimensat(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags);
    1509 +int do_lutimes(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags);
    1510 +int do_utimes(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags);
    1511 +int do_utime(const char *fname, time_t modtime, UNUSED(uint32 mod_nsec), mode_t mode, uint32 fileflags);
    1512  void set_compression(const char *fname);
    1513  void send_token(int f, int32 token, struct map_struct *buf, OFF_T offset,
    1514                 int32 n, int32 toklen);
    1515 @@ -334,7 +338,7 @@ int fd_pair(int fd[2]);
     1181@@ -353,7 +356,7 @@ void set_nonblocking(int fd);
     1182 void set_blocking(int fd);
     1183 int fd_pair(int fd[2]);
    15161184 void print_child_argv(const char *prefix, char **cmd);
    1517  NORETURN void out_of_memory(const char *str);
    1518  NORETURN void overflow_exit(const char *str);
    1519 -int set_modtime(const char *fname, time_t modtime, mode_t mode);
    1520 +int set_modtime(const char *fname, time_t modtime, mode_t mode, uint32 fileflags);
    1521  int mkdir_defmode(char *fname);
    1522  int create_directory_path(char *fname);
     1185-int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode);
     1186+int set_modtime(const char *fname, time_t modtime, uint32 mod_nsec, mode_t mode, uint32 fileflags);
     1187 int make_path(char *fname, int flags);
    15231188 int full_write(int desc, const char *ptr, size_t len);
    1524 diff -up a/rsync.1 b/rsync.1
     1189 int copy_file(const char *source, const char *dest, int ofd, mode_t mode);
     1190diff -Nurp a/rsync.1 b/rsync.1
    15251191--- a/rsync.1
    15261192+++ b/rsync.1
    1527 @@ -431,6 +431,7 @@ to the detailed description below for a
     1193@@ -438,6 +438,7 @@ to the detailed description below for a
    15281194  \-K, \-\-keep\-dirlinks         treat symlinked dir on receiver as dir
    15291195  \-H, \-\-hard\-links            preserve hard links
    15301196  \-p, \-\-perms                 preserve permissions
    diff -up a/rsync.1 b/rsync.1 
    15321198  \-E, \-\-executability         preserve executability
    15331199      \-\-chmod=CHMOD           affect file and/or directory permissions
    15341200  \-A, \-\-acls                  preserve ACLs (implies \-p)
    1535 @@ -462,7 +463,10 @@ to the detailed description below for a
    1536       \-\-delete\-after          receiver deletes after transfer, not during
    1537       \-\-delete\-excluded       also delete excluded files from dest dirs
     1201@@ -473,7 +474,10 @@ to the detailed description below for a
     1202      \-\-ignore\-missing\-args   ignore missing source args without error
     1203      \-\-delete\-missing\-args   delete missing source args from destination
    15381204      \-\-ignore\-errors         delete even if there are I/O errors
    15391205-     \-\-force                 force deletion of dirs even if not empty
    15401206+     \-\-force\-delete          force deletion of dirs even if not empty
    diff -up a/rsync.1 b/rsync.1 
    15441210      \-\-max\-delete=NUM        don'\&t delete more than NUM files
    15451211      \-\-max\-size=SIZE         don'\&t transfer any file larger than SIZE
    15461212      \-\-min\-size=SIZE         don'\&t transfer any file smaller than SIZE
    1547 @@ -658,7 +662,8 @@ specified, in which case \fB\-r\fP is no
     1213@@ -743,7 +747,8 @@ specified, in which case \fB\-r\fP is no
    15481214 .IP
    15491215 Note that \fB\-a\fP \fBdoes not preserve hardlinks\fP, because
    15501216 finding multiply\-linked files is expensive.  You must separately
    diff -up a/rsync.1 b/rsync.1 
    15541220 .IP
    15551221 .IP "\-\-no\-OPTION"
    15561222 You may turn off one or more implied options by prefixing
    1557 @@ -975,7 +980,7 @@ they would be using \fB\-\-copy\-links\f
     1223@@ -1080,7 +1085,7 @@ they would be using \fB\-\-copy\-links\f
    15581224 Without this option, if the sending side has replaced a directory with a
    15591225 symlink to a directory, the receiving side will delete anything that is in
    15601226 the way of the new symlink, including a directory hierarchy (as long as
    diff -up a/rsync.1 b/rsync.1 
    15631229 .IP
    15641230 See also \fB\-\-keep\-dirlinks\fP for an analogous option for the receiving
    15651231 side.
    1566 @@ -1162,6 +1167,33 @@ Note that this option does not copy rsyn
     1232@@ -1267,6 +1272,33 @@ Note that this option does not copy rsyn
    15671233 used by \fB\-\-fake\-super\fP) unless you repeat the option (e.g. \-XX).  This
    15681234 \(dq\&copy all xattrs\(dq\& mode cannot be used with \fB\-\-fake\-super\fP.
    15691235 .IP
    diff -up a/rsync.1 b/rsync.1 
    15921258+This option causes rsync to disable system\-immutable
    15931259+flags on files and directories that are being updated or deleted on the
    15941260+receiving side.  It does not try to affect user flags.  This option overrides
    1595 +\fB\-\-force\-change\fP and \fB\-\-force\-schange\fP.
     1261+\fB\-\-force\-change\fP and \fB\-\-force\-uchange\fP.
    15961262+.IP
    15971263 .IP "\fB\-\-chmod\fP"
    15981264 This option tells rsync to apply one or more
    15991265 comma\-separated \(dq\&chmod\(dq\& modes to the permission of the files in the
    1600 @@ -1472,13 +1504,14 @@ See \fB\-\-delete\fP (which is implied)
     1266@@ -1633,13 +1665,14 @@ display as a \(dq\&*missing\(dq\& entry
    16011267 Tells \fB\-\-delete\fP to go ahead and delete files
    16021268 even when there are I/O errors.
    16031269 .IP
    diff -up a/rsync.1 b/rsync.1 
    16151281 \fB\-\-recursive\fP option was also enabled.
    16161282 .IP
    16171283 .IP "\fB\-\-max\-delete=NUM\fP"
    1618 @@ -2034,7 +2067,7 @@ with older versions of rsync, but that a
     1284@@ -2357,7 +2390,7 @@ with older versions of rsync, but that a
    16191285 verbose messages).
    16201286 .IP
    16211287 The \(dq\&%i\(dq\& escape has a cryptic output that is 11 letters long.  The general
    diff -up a/rsync.1 b/rsync.1 
    16241290 type of update being done, \fBX\fP is replaced by the file\-type, and the
    16251291 other letters represent attributes that may be output if they are being
    16261292 modified.
    1627 @@ -2104,7 +2137,7 @@ sender\(cq\&s value (requires \fB\-\-own
     1293@@ -2427,7 +2460,7 @@ sender\(cq\&s value (requires \fB\-\-own
    16281294 A \fBg\fP means the group is different and is being updated to the
    16291295 sender\(cq\&s value (requires \fB\-\-group\fP and the authority to set the group).
    16301296 .IP o
  • rsync/files/patch-hfs-compression-options.diff

    diff --git a/rsync/files/patch-hfs-compression-options.diff b/rsync/files/patch-hfs-compression-options.diff
    index 55198b4..89b535d 100644
    a b  
    1 This patch adds support for HFS+ compression status to be
    2 shown during options display.
    3 
    4 To use this patch, run these commands for a successful build:
    5 
    6     patch -p1 <patches/fileflags.diff
    7     patch -p1 <patches/crtimes.diff
    8     patch -p1 <patches/hfs-compression.diff
    9     patch -p1 <patches/hfs-compression-options.diff
    10     ./prepare-source
    11     ./configure
    12     make
    13 
    14 TODO:
    15  - Should rsync try to treat the compressed data as file data and use the
    16    rsync algorithm on the data transfer?
    17 
    181diff --git a/options.c b/options.c
    192--- a/options.c
    203+++ b/options.c
    21 @@ -228,6 +228,7 @@
     4@@ -579,6 +579,7 @@
    225        char const *iconv = "no ";
    236        char const *ipv6 = "no ";
    247        char const *fileflags = "no ";
    diff --git a/options.c b/options.c 
    269        STRUCT_STAT *dumstat;
    2710 
    2811 #if SUBPROTOCOL_VERSION != 0
    29 @@ -264,6 +265,9 @@
     12@@ -618,6 +619,9 @@
    3013 #ifdef SUPPORT_FILEFLAGS
    3114        fileflags = "";
    3215 #endif
    diff --git a/options.c b/options.c 
    3619 
    3720        rprintf(f, "%s  version %s  protocol version %d%s\n",
    3821                RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
    39 @@ -277,8 +281,10 @@
     22@@ -631,8 +635,10 @@
    4023                (int)(sizeof (int64) * 8));
    4124        rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
    4225                got_socketpair, hardlinks, links, ipv6, have_inplace);
    43 -       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sfile-flags\n",
    44 +       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sfile-flags,\n",
    45                 have_inplace, acls, xattrs, iconv, symtimes, fileflags);
    46 +       rprintf(f, "    %sHFS-compression\n",
    47 +               hfscomp);
     26-       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc, %sfile-flags\n",
     27+       rprintf(f, "    %sappend, %sACLs, %sxattrs, %siconv, %ssymtimes, %sprealloc, %sfile-flags,\n",
     28                have_inplace, acls, xattrs, iconv, symtimes, prealloc, fileflags);
     29+       rprintf(f, "    %sHFS-compression\n",
     30+               hfscomp);
    4831 
    4932 #ifdef MAINTAINER_MODE
    5033        rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
  • rsync/files/patch-hfs-compression.diff

    diff --git a/rsync/files/patch-hfs-compression.diff b/rsync/files/patch-hfs-compression.diff
    index 1275a92..77a04ea 100644
    a b TODO: 
    1818 - Should rsync try to treat the compressed data as file data and use the
    1919   rsync algorithm on the data transfer?
    2020
    21 based-on: patch/b3.0.x/crtimes
     21based-on: patch/master/crtimes
    2222diff --git a/flist.c b/flist.c
    2323--- a/flist.c
    2424+++ b/flist.c
    25 @@ -1496,6 +1496,7 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
    26  #ifdef SUPPORT_FILEFLAGS
    27                         sx.st.st_flags = preserve_fileflags ? F_FFLAGS(file) : 0;
    28  #endif
     25@@ -1583,6 +1583,9 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
     26 #ifdef SUPPORT_XATTRS
     27                if (preserve_xattrs) {
     28                        sx.st.st_mode = file->mode;
     29+                       if (preserve_fileflags)
     30+                               sx.st.st_flags = F_FFLAGS(file);
    2931+                       sx.st.st_mtime = file->modtime; /* get_xattr needs mtime for decmpfs xattrs */
    30                         sx.xattr = NULL;
    3132                        if (get_xattr(fname, &sx) < 0) {
    3233                                io_error |= IOERR_GENERAL;
     34                                return NULL;
    3335diff --git a/generator.c b/generator.c
    3436--- a/generator.c
    3537+++ b/generator.c
    36 @@ -39,6 +39,7 @@ extern int keep_dirlinks;
    37  extern int force_change;
     38@@ -37,6 +37,7 @@ extern int implied_dirs;
     39 extern int keep_dirlinks;
    3840 extern int preserve_acls;
    3941 extern int preserve_xattrs;
    4042+extern int preserve_hfs_compression;
    4143 extern int preserve_links;
    4244 extern int preserve_devices;
    4345 extern int preserve_specials;
    44 @@ -1888,6 +1889,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
     46@@ -1762,6 +1763,14 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
    4547                                        fname, fnamecmpbuf);
    4648                        }
    4749                        sx.st.st_size = F_LENGTH(fuzzy_file);
    diff --git a/generator.c b/generator.c 
    5557+#endif
    5658                        statret = 0;
    5759                        fnamecmp = fnamecmpbuf;
    58                         fnamecmp_type = FNAMECMP_FUZZY;
    59 @@ -2072,6 +2081,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
     60                }
     61@@ -1929,6 +1938,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
    6062        if (read_batch)
    6163                goto cleanup;
    6264 
    diff --git a/generator.c b/generator.c 
    7880diff --git a/lib/sysxattrs.c b/lib/sysxattrs.c
    7981--- a/lib/sysxattrs.c
    8082+++ b/lib/sysxattrs.c
    81 @@ -22,6 +22,17 @@
     83@@ -22,10 +22,17 @@
    8284 #include "rsync.h"
    8385 #include "sysxattrs.h"
    8486 
    8587+extern int preserve_hfs_compression;
    8688+
    87 +#ifdef HAVE_OSX_XATTRS
     89 #ifdef SUPPORT_XATTRS
     90 
     91 #ifdef HAVE_OSX_XATTRS
    8892+#ifndef XATTR_SHOWCOMPRESSION
    8993+#define XATTR_SHOWCOMPRESSION 0x0020
    9094+#endif
    91 +#define GETXATTR_FETCH_LIMIT (64*1024*1024)
     95 #define GETXATTR_FETCH_LIMIT (64*1024*1024)
    9296+
    9397+int xattr_options = XATTR_NOFOLLOW;
    94 +#endif
    95 +
    96  #ifdef SUPPORT_XATTRS
     98 #endif
    9799 
    98100 #if defined HAVE_LINUX_XATTRS
    99 @@ -55,7 +66,27 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
     101@@ -59,7 +66,12 @@ ssize_t sys_llistxattr(const char *path, char *list, size_t size)
    100102 
    101103 ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t size)
    102104 {
    103 -       return getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
     105-       ssize_t len = getxattr(path, name, value, size, 0, XATTR_NOFOLLOW);
    104106+       ssize_t len;
    105107+
    106108+       if (preserve_hfs_compression)
    107109+               xattr_options |= XATTR_SHOWCOMPRESSION;
    108110+
    109111+       len = getxattr(path, name, value, size, 0, xattr_options);
    110 +
    111 +       /* If we're retrieving data, handle resource forks > 64MB specially */
    112 +       if (value != NULL && strcmp(name, XATTR_RESOURCEFORK_NAME) == 0 && len == GETXATTR_FETCH_LIMIT) {
    113 +               /* getxattr will only return 64MB of data at a time, need to call again with a new offset */
    114 +               u_int32_t offset = GETXATTR_FETCH_LIMIT;
    115 +               ssize_t data_retrieved = len;
    116 +               while (data_retrieved < (ssize_t)size) {
    117 +                       len = getxattr(path, name, value + offset, size - data_retrieved, offset, xattr_options);
    118 +                       data_retrieved += len;
    119 +                       offset += (u_int32_t)len;
    120 +               }
    121 +               len = data_retrieved;
    122 +       }
    123 +
    124 +       return len;
    125  }
    126112 
    127  ssize_t sys_fgetxattr(int filedes, const char *name, void *value, size_t size)
    128 @@ -70,12 +101,16 @@ int sys_lsetxattr(const char *path, const char *name, const void *value, size_t
     113        /* If we're retrieving data, handle resource forks > 64MB specially */
     114        if (value != NULL && len == GETXATTR_FETCH_LIMIT && (size_t)len < size) {
     115@@ -67,7 +79,7 @@ ssize_t sys_lgetxattr(const char *path, const char *name, void *value, size_t si
     116                u_int32_t offset = len;
     117                size_t data_retrieved = len;
     118                while (data_retrieved < size) {
     119-                       len = getxattr(path, name, value + offset, size - data_retrieved, offset, XATTR_NOFOLLOW);
     120+                       len = getxattr(path, name, value + offset, size - data_retrieved, offset, xattr_options);
     121                        if (len <= 0)
     122                                break;
     123                        data_retrieved += len;
     124@@ -91,12 +103,16 @@ int sys_lsetxattr(const char *path, const char *name, const void *value, size_t
    129125 
    130126 int sys_lremovexattr(const char *path, const char *name)
    131127 {
    diff --git a/main.c b/main.c 
    156152+#include <sys/mount.h> /* For statfs() */
    157153+#endif
    158154 
    159  extern int verbose;
    160155 extern int dry_run;
    161 @@ -50,7 +54,9 @@ extern int copy_dirlinks;
     156 extern int list_only;
     157@@ -53,6 +57,7 @@ extern int copy_dirlinks;
    162158 extern int copy_unsafe_links;
    163159 extern int keep_dirlinks;
    164160 extern int preserve_hard_links;
    165161+extern int preserve_hfs_compression;
    166162 extern int protocol_version;
    167 +extern int force_change;
     163 extern int force_change;
    168164 extern int file_total;
    169  extern int recurse;
    170  extern int xfer_dirs;
    171 @@ -490,6 +496,43 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
     165@@ -107,6 +112,7 @@ int daemon_over_rsh = 0;
     166 mode_t orig_umask = 0;
     167 int batch_gen_fd = -1;
     168 int sender_keeps_checksum = 0;
     169+int fs_supports_hfs_compression = 0;
     170 
     171 /* There's probably never more than at most 2 outstanding child processes,
     172  * but set it higher, just in case. */
     173@@ -559,6 +565,43 @@ static pid_t do_cmd(char *cmd, char *machine, char *user, char **remote_argv, in
    172174        return 0; /* not reached */
    173175 }
    174176 
    diff --git a/main.c b/main.c 
    212214 /* The receiving side operates in one of two modes:
    213215  *
    214216  * 1. it receives any number of files into a destination directory,
    215 @@ -548,6 +591,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
     217@@ -617,6 +660,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
    216218                                exit_cleanup(RERR_FILESELECT);
    217219                        }
    218220                        filesystem_dev = st.st_dev; /* ensures --force works right w/-x */
    diff --git a/main.c b/main.c 
    222224                        return NULL;
    223225                }
    224226                if (file_total > 1) {
    225 @@ -608,7 +654,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
     227@@ -677,7 +723,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
    226228                                full_fname(dest_path));
    227229                        exit_cleanup(RERR_FILESELECT);
    228230                }
    diff --git a/main.c b/main.c 
    233235                return NULL;
    234236        }
    235237 
    236 @@ -628,6 +676,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
     238@@ -697,6 +745,9 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
    237239                        full_fname(dest_path));
    238240                exit_cleanup(RERR_FILESELECT);
    239241        }
    diff --git a/main.c b/main.c 
    243245        *cp = '/';
    244246 
    245247        return cp + 1;
    246 @@ -981,7 +1032,6 @@ int child_main(int argc, char *argv[])
    247         return 0;
    248  }
    249  
    250 -
    251  void start_server(int f_in, int f_out, int argc, char *argv[])
    252  {
    253         set_nonblocking(f_in);
    254248diff --git a/options.c b/options.c
    255249--- a/options.c
    256250+++ b/options.c
    257 @@ -52,6 +52,7 @@ int preserve_links = 0;
     251@@ -54,6 +54,7 @@ int preserve_links = 0;
    258252 int preserve_hard_links = 0;
    259253 int preserve_acls = 0;
    260254 int preserve_xattrs = 0;
    diff --git a/options.c b/options.c 
    262256 int preserve_perms = 0;
    263257 int preserve_fileflags = 0;
    264258 int preserve_executability = 0;
    265 @@ -355,6 +356,10 @@ void usage(enum logcode F)
     259@@ -713,6 +714,10 @@ void usage(enum logcode F)
    266260 #ifdef SUPPORT_XATTRS
    267261   rprintf(F," -X, --xattrs                preserve extended attributes\n");
    268262 #endif
    diff --git a/options.c b/options.c 
    273267   rprintf(F," -o, --owner                 preserve owner (super-user only)\n");
    274268   rprintf(F," -g, --group                 preserve group\n");
    275269   rprintf(F,"     --devices               preserve device files (super-user only)\n");
    276 @@ -588,6 +593,12 @@ static struct poptOption long_options[] = {
     270@@ -974,6 +979,12 @@ static struct poptOption long_options[] = {
    277271   {"force-uchange",    0,  POPT_ARG_VAL,    &force_change, USR_IMMUTABLE, 0, 0 },
    278272   {"force-schange",    0,  POPT_ARG_VAL,    &force_change, SYS_IMMUTABLE, 0, 0 },
    279273 #endif
    diff --git a/options.c b/options.c 
    286280   {"ignore-errors",    0,  POPT_ARG_VAL,    &ignore_errors, 1, 0, 0 },
    287281   {"no-ignore-errors", 0,  POPT_ARG_VAL,    &ignore_errors, 0, 0, 0 },
    288282   {"max-delete",       0,  POPT_ARG_INT,    &max_delete, 0, 0, 0 },
    289 @@ -1362,6 +1373,15 @@ int parse_arguments(int *argc_p, const char ***argv_p)
     283@@ -1974,6 +1985,15 @@ int parse_arguments(int *argc_p, const char ***argv_p)
    290284        }
    291285 #endif
    292286 
    diff --git a/options.c b/options.c 
    299293+       }
    300294+#endif
    301295+
    302         if (write_batch && read_batch) {
     296        if (block_size > MAX_BLOCK_SIZE) {
    303297                snprintf(err_buf, sizeof err_buf,
    304                         "--write-batch and --read-batch can not be used together\n");
    305 @@ -1915,6 +1935,11 @@ void server_options(char **args, int *argc_p)
     298                         "--block-size=%lu is too large (max: %u)\n", block_size, MAX_BLOCK_SIZE);
     299@@ -2573,6 +2593,11 @@ void server_options(char **args, int *argc_p)
    306300        if (preserve_fileflags)
    307301                args[ac++] = "--fileflags";
    308302 
    diff --git a/options.c b/options.c 
    317311diff --git a/rsync.c b/rsync.c
    318312--- a/rsync.c
    319313+++ b/rsync.c
    320 @@ -498,8 +498,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     314@@ -573,8 +573,14 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    321315 #ifdef SUPPORT_XATTRS
    322316        if (am_root < 0)
    323317                set_stat_xattr(fname, file, new_mode);
    diff --git a/rsync.c b/rsync.c 
    333327 #endif
    334328 
    335329        if (!preserve_times
    336 @@ -510,6 +516,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     330@@ -585,6 +591,9 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    337331        if (sxp->st.st_ino == 2 && S_ISDIR(sxp->st.st_mode))
    338332                flags |= ATTRS_SKIP_CRTIME;
    339333        if (!(flags & ATTRS_SKIP_MTIME)
    diff --git a/rsync.c b/rsync.c 
    341335+           && !(sxp->st.st_flags & UF_COMPRESSED) /* setting this alters mtime, so defer to after set_fileflags */
    342336+#endif
    343337            && cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
    344                 int ret = set_modtime(fname, file->modtime, sxp->st.st_mode, ST_FLAGS(sxp->st));
     338                int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode, ST_FLAGS(sxp->st));
    345339                if (ret < 0) {
    346 @@ -620,6 +629,16 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
     340@@ -643,6 +652,16 @@ int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
    347341                 && !set_fileflags(fname, fileflags))
    348342                        goto cleanup;
    349343                updated = 1;
    350344+#ifdef SUPPORT_HFS_COMPRESSION
    351 +               int ret = set_modtime(fname, file->modtime, new_mode, fileflags);
     345+               int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), new_mode, fileflags);
    352346+               if (ret < 0) {
    353347+                       rsyserr(FERROR_XFER, errno, "failed to set times on %s",
    354348+                               full_fname(fname));
    diff --git a/rsync.c b/rsync.c 
    363357diff --git a/rsync.h b/rsync.h
    364358--- a/rsync.h
    365359+++ b/rsync.h
    366 @@ -509,6 +509,17 @@ typedef unsigned int size_t;
     360@@ -549,6 +549,17 @@ typedef unsigned int size_t;
    367361 #define ST_FLAGS(st) NO_FFLAGS
    368362 #endif
    369363 
    diff --git a/rsync.h b/rsync.h 
    384378diff --git a/rsync.yo b/rsync.yo
    385379--- a/rsync.yo
    386380+++ b/rsync.yo
    387 @@ -360,6 +360,8 @@ to the detailed description below for a complete description.  verb(
     381@@ -367,6 +367,8 @@ to the detailed description below for a complete description.  verb(
    388382      --chmod=CHMOD           affect file and/or directory permissions
    389383  -A, --acls                  preserve ACLs (implies -p)
    390384  -X, --xattrs                preserve extended attributes
    diff --git a/rsync.yo b/rsync.yo 
    393387  -o, --owner                 preserve owner (super-user only)
    394388  -g, --group                 preserve group
    395389      --devices               preserve device files (super-user only)
    396 @@ -1038,6 +1040,42 @@ flags on files and directories that are being updated or deleted on the
     390@@ -1135,6 +1137,42 @@ flags on files and directories that are being updated or deleted on the
    397391 receiving side.  It does not try to affect user flags.  This option overrides
    398  bf(--force-change) and bf(--force-schange).
     392 bf(--force-change) and bf(--force-uchange).
    399393 
    400394+dit(bf(--hfs-compression)) This option causes rsync to preserve HFS+
    401395+compression if the destination filesystem supports it.  If the destination
    diff --git a/rsync.yo b/rsync.yo 
    439433diff --git a/t_stub.c b/t_stub.c
    440434--- a/t_stub.c
    441435+++ b/t_stub.c
    442 @@ -29,6 +29,7 @@ int module_dirlen = 0;
    443  int force_change = 0;
     436@@ -32,6 +32,7 @@ int force_change = 0;
     437 int preserve_acls = 0;
    444438 int preserve_times = 0;
    445439 int preserve_xattrs = 0;
    446440+int preserve_hfs_compression = 0;
    447  mode_t orig_umask = 002;
    448441 char *partial_dir;
    449442 char *module_dir;
     443 filter_rule_list daemon_filter_list;
    450444diff --git a/xattrs.c b/xattrs.c
    451445--- a/xattrs.c
    452446+++ b/xattrs.c
    453 @@ -32,6 +32,7 @@ extern int am_generator;
     447@@ -33,6 +33,7 @@ extern int am_generator;
    454448 extern int read_only;
    455449 extern int list_only;
    456450 extern int preserve_xattrs;
    diff --git a/xattrs.c b/xattrs.c 
    458452 extern int preserve_links;
    459453 extern int preserve_devices;
    460454 extern int preserve_specials;
    461 @@ -40,6 +41,10 @@ extern int checksum_seed;
     455@@ -41,6 +42,10 @@ extern int checksum_seed;
    462456 #define RSYNC_XAL_INITIAL 5
    463457 #define RSYNC_XAL_LIST_INITIAL 100
    464458 
    diff --git a/xattrs.c b/xattrs.c 
    469463 #define MAX_FULL_DATUM 32
    470464 
    471465 #define HAS_PREFIX(str, prfx) (*(str) == *(prfx) \
    472 @@ -72,6 +77,17 @@ extern int checksum_seed;
     466@@ -73,6 +78,17 @@ extern int checksum_seed;
    473467 #define XDEF_ACL_SUFFIX "dacl"
    474468 #define XDEF_ACL_ATTR RSYNC_PREFIX "%" XDEF_ACL_SUFFIX
    475469 
    diff --git a/xattrs.c b/xattrs.c 
    487481 typedef struct {
    488482        char *datum, *name;
    489483        size_t datum_len, name_len;
    490 @@ -166,8 +182,7 @@ static ssize_t get_xattr_names(const char *fname)
     484@@ -167,8 +183,7 @@ static ssize_t get_xattr_names(const char *fname)
    491485 /* On entry, the *len_ptr parameter contains the size of the extra space we
    492486  * should allocate when we create a buffer for the data.  On exit, it contains
    493487  * the length of the datum. */
    diff --git a/xattrs.c b/xattrs.c 
    497491 {
    498492        size_t datum_len = sys_lgetxattr(fname, name, NULL, 0);
    499493        size_t extra_len = *len_ptr;
    500 @@ -176,7 +191,7 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
     494@@ -177,7 +192,7 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
    501495        *len_ptr = datum_len;
    502496 
    503497        if (datum_len == (size_t)-1) {
    diff --git a/xattrs.c b/xattrs.c 
    506500                        return NULL;
    507501                rsyserr(FERROR_XFER, errno,
    508502                        "get_xattr_data: lgetxattr(\"%s\",\"%s\",0) failed",
    509 @@ -184,6 +199,15 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
     503@@ -185,6 +200,15 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
    510504                return NULL;
    511505        }
    512506 
    diff --git a/xattrs.c b/xattrs.c 
    522516        if (!datum_len && !extra_len)
    523517                extra_len = 1; /* request non-zero amount of memory */
    524518        if (datum_len + extra_len < datum_len)
    525 @@ -212,7 +236,29 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
     519@@ -213,7 +237,29 @@ static char *get_xattr_data(const char *fname, const char *name, size_t *len_ptr
    526520        return ptr;
    527521 }
    528522 
    diff --git a/xattrs.c b/xattrs.c 
    553547 {
    554548        ssize_t list_len, name_len;
    555549        size_t datum_len, name_offset;
    556 @@ -221,7 +267,8 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
    557         int user_only = am_sender ? 0 : am_root <= 0;
     550@@ -222,7 +268,8 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
     551        int user_only = am_sender ? 0 : !am_root;
    558552 #endif
    559553        rsync_xa *rxa;
    560554-       int count;
    diff --git a/xattrs.c b/xattrs.c 
    563557 
    564558        /* This puts the name list into the "namebuf" buffer. */
    565559        if ((list_len = get_xattr_names(fname)) < 0)
    566 @@ -251,20 +298,22 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
     560@@ -252,20 +299,22 @@ static int rsync_xal_get(const char *fname, item_list *xalp)
    567561                }
    568562 
    569563                datum_len = name_len; /* Pass extra size to get_xattr_data() */
    diff --git a/xattrs.c b/xattrs.c 
    592586                } else
    593587                        name_offset = datum_len;
    594588 
    595 @@ -309,7 +358,7 @@ int get_xattr(const char *fname, stat_x *sxp)
    596                         return 0;
    597         }
     589@@ -311,7 +360,7 @@ int get_xattr(const char *fname, stat_x *sxp)
     590        } else if (IS_MISSING_FILE(sxp->st))
     591                return 0;
    598592 
    599593-       if (rsync_xal_get(fname, sxp->xattr) < 0) {
    600594+       if (rsync_xal_get(fname, sxp) < 0) {
    601595                free_xattr(sxp);
    602596                return -1;
    603597        }
    604 @@ -344,6 +393,8 @@ int copy_xattrs(const char *source, const char *dest)
     598@@ -346,6 +395,8 @@ int copy_xattrs(const char *source, const char *dest)
    605599                datum_len = 0;
    606600                if (!(ptr = get_xattr_data(source, name, &datum_len, 0)))
    607601                        return -1;
    diff --git a/xattrs.c b/xattrs.c 
    610604                if (sys_lsetxattr(dest, name, ptr, datum_len) < 0) {
    611605                        int save_errno = errno ? errno : EINVAL;
    612606                        rsyserr(FERROR_XFER, errno,
    613 @@ -360,6 +411,10 @@ int copy_xattrs(const char *source, const char *dest)
     607@@ -362,6 +413,10 @@ int copy_xattrs(const char *source, const char *dest)
    614608 
    615609 static int find_matching_xattr(item_list *xalp)
    616610 {
    diff --git a/xattrs.c b/xattrs.c 
    621615        size_t i, j;
    622616        item_list *lst = rsync_xal_l.items;
    623617 
    624 @@ -393,6 +448,7 @@ static int find_matching_xattr(item_list *xalp)
     618@@ -395,6 +450,7 @@ static int find_matching_xattr(item_list *xalp)
    625619        }
    626620 
    627621        return -1;
    diff --git a/xattrs.c b/xattrs.c 
    629623 }
    630624 
    631625 /* Store *xalp on the end of rsync_xal_l */
    632 @@ -572,11 +628,13 @@ void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
     626@@ -574,11 +630,13 @@ void send_xattr_request(const char *fname, struct file_struct *file, int f_out)
    633627 
    634628                        /* Re-read the long datum. */
    635629                        if (!(ptr = get_xattr_data(fname, rxa->name, &len, 0))) {
    diff --git a/xattrs.c b/xattrs.c 
    642636 
    643637+                       assert(ptr != UNREAD_DATA);
    644638                        write_varint(f_out, len); /* length might have changed! */
    645                         write_buf(f_out, ptr, len);
     639                        write_bigbuf(f_out, ptr, len);
    646640                        free(ptr);
    647 @@ -792,7 +850,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
     641@@ -795,7 +853,7 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
    648642        int user_only = am_root <= 0;
    649643 #endif
    650644        size_t name_len;
    diff --git a/xattrs.c b/xattrs.c 
    653647 
    654648        /* This puts the current name list into the "namebuf" buffer. */
    655649        if ((list_len = get_xattr_names(fname)) < 0)
    656 @@ -804,7 +862,10 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
     650@@ -807,7 +865,10 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
    657651                if (XATTR_ABBREV(rxas[i])) {
    658652                        /* See if the fnamecmp version is identical. */
    659653                        len = name_len = rxas[i].name_len;
    diff --git a/xattrs.c b/xattrs.c 
    665659                          still_abbrev:
    666660                                if (am_generator)
    667661                                        continue;
    668 @@ -813,14 +874,14 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
     662@@ -816,14 +877,14 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
    669663                                ret = -1;
    670664                                continue;
    671665                        }
    diff --git a/xattrs.c b/xattrs.c 
    683677                        if (memcmp(sum, rxas[i].datum + 1, MAX_DIGEST_LEN) != 0) {
    684678                                free(ptr);
    685679                                goto still_abbrev;
    686 @@ -889,6 +950,10 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
     680@@ -892,6 +953,10 @@ static int rsync_xal_set(const char *fname, item_list *xalp,
    687681                }
    688682        }
    689683 
    diff --git a/xattrs.c b/xattrs.c 
    694688        return ret;
    695689 }
    696690 
    697 @@ -935,7 +1000,7 @@ char *get_xattr_acl(const char *fname, int is_access_acl, size_t *len_p)
     691@@ -938,7 +1003,7 @@ char *get_xattr_acl(const char *fname, int is_access_acl, size_t *len_p)
    698692 {
    699693        const char *name = is_access_acl ? XACC_ACL_ATTR : XDEF_ACL_ATTR;
    700694        *len_p = 0; /* no extra data alloc needed from get_xattr_data() */
    diff --git a/xattrs.c b/xattrs.c 
    703697 }
    704698 
    705699 int set_xattr_acl(const char *fname, int is_access_acl, const char *buf, size_t buf_len)
    706 @@ -1078,11 +1143,33 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
     700@@ -1081,11 +1146,33 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
    707701        return 0;
    708702 }
    709703 
    diff --git a/xattrs.c b/xattrs.c 
    737731        return ret;
    738732 }
    739733 
    740 @@ -1091,6 +1178,9 @@ int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
     734@@ -1094,6 +1181,9 @@ int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst)
    741735        int ret = do_lstat(fname, fst);
    742736        if ((ret < 0 || get_stat_xattr(fname, -1, fst, xst) < 0) && xst)
    743737                xst->st_mode = 0;
    diff --git a/xattrs.c b/xattrs.c 
    747741        return ret;
    748742 }
    749743 
    750 @@ -1099,6 +1189,9 @@ int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
     744@@ -1102,6 +1192,9 @@ int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst)
    751745        int ret = do_fstat(fd, fst);
    752746        if ((ret < 0 || get_stat_xattr(NULL, fd, fst, xst) < 0) && xst)
    753747                xst->st_mode = 0;
    diff --git a/xattrs.c b/xattrs.c 
    757751        return ret;
    758752 }
    759753 
    760 diff -up a/rsync.1 b/rsync.1
     754diff -Nurp a/rsync.1 b/rsync.1
    761755--- a/rsync.1
    762756+++ b/rsync.1
    763 @@ -436,6 +436,8 @@ to the detailed description below for a
     757@@ -443,6 +443,8 @@ to the detailed description below for a
    764758      \-\-chmod=CHMOD           affect file and/or directory permissions
    765759  \-A, \-\-acls                  preserve ACLs (implies \-p)
    766760  \-X, \-\-xattrs                preserve extended attributes
    diff -up a/rsync.1 b/rsync.1 
    769763  \-o, \-\-owner                 preserve owner (super\-user only)
    770764  \-g, \-\-group                 preserve group
    771765      \-\-devices               preserve device files (super\-user only)
    772 @@ -1195,6 +1197,44 @@ flags on files and directories that are
     766@@ -1300,6 +1302,44 @@ flags on files and directories that are
    773767 receiving side.  It does not try to affect user flags.  This option overrides
    774  \fB\-\-force\-change\fP and \fB\-\-force\-schange\fP.
     768 \fB\-\-force\-change\fP and \fB\-\-force\-uchange\fP.
    775769 .IP
    776770+.IP "\fB\-\-hfs\-compression\fP"
    777771+This option causes rsync to preserve HFS+