The patches for 3.0.9.
[rsync.git/patches.git] / fileflags.diff
1 This patch provides --fileflags, which preserves the st_flags stat() field.
2 Modified from a patch that was written by Rolf Grossmann.
3
4 To use this patch, run these commands for a successful build:
5
6     patch -p1 <patches/fileflags.diff
7     ./prepare-source
8     ./configure
9     make
10
11 based-on: 40afd365cc8ca968fd16e161d24df5b8a8a520cc
12 diff --git a/Makefile.in b/Makefile.in
13 --- a/Makefile.in
14 +++ b/Makefile.in
15 @@ -42,7 +42,7 @@ popt_OBJS=popt/findme.o  popt/popt.o  popt/poptconfig.o \
16         popt/popthelp.o popt/poptparse.o
17  OBJS=$(OBJS1) $(OBJS2) $(OBJS3) $(DAEMON_OBJ) $(LIBOBJ) $(ZLIBOBJ) @BUILD_POPT@
18  
19 -TLS_OBJ = tls.o syscall.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
20 +TLS_OBJ = tls.o syscall.o t_stub.o lib/compat.o lib/snprintf.o lib/permstring.o lib/sysxattrs.o @BUILD_POPT@
21  
22  # Programs we must have to run the test cases
23  CHECK_PROGS = rsync$(EXEEXT) tls$(EXEEXT) getgroups$(EXEEXT) getfsdev$(EXEEXT) \
24 @@ -107,7 +107,7 @@ getgroups$(EXEEXT): getgroups.o
25  getfsdev$(EXEEXT): getfsdev.o
26         $(CC) $(CFLAGS) $(LDFLAGS) -o $@ getfsdev.o $(LIBS)
27  
28 -TRIMSLASH_OBJ = trimslash.o syscall.o lib/compat.o lib/snprintf.o
29 +TRIMSLASH_OBJ = trimslash.o syscall.o t_stub.o lib/compat.o lib/snprintf.o
30  trimslash$(EXEEXT): $(TRIMSLASH_OBJ)
31         $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(TRIMSLASH_OBJ) $(LIBS)
32  
33 diff --git a/compat.c b/compat.c
34 --- a/compat.c
35 +++ b/compat.c
36 @@ -43,9 +43,11 @@ extern int checksum_seed;
37  extern int basis_dir_cnt;
38  extern int prune_empty_dirs;
39  extern int protocol_version;
40 +extern int force_change;
41  extern int protect_args;
42  extern int preserve_uid;
43  extern int preserve_gid;
44 +extern int preserve_fileflags;
45  extern int preserve_acls;
46  extern int preserve_xattrs;
47  extern int need_messages_from_generator;
48 @@ -63,7 +65,7 @@ extern char *iconv_opt;
49  #endif
50  
51  /* These index values are for the file-list's extra-attribute array. */
52 -int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
53 +int uid_ndx, gid_ndx, fileflags_ndx, acls_ndx, xattrs_ndx, unsort_ndx;
54  
55  int receiver_symlink_times = 0; /* receiver can set the time on a symlink */
56  int sender_symlink_iconv = 0;  /* sender should convert symlink content */
57 @@ -140,6 +142,8 @@ void setup_protocol(int f_out,int f_in)
58                 uid_ndx = ++file_extra_cnt;
59         if (preserve_gid)
60                 gid_ndx = ++file_extra_cnt;
61 +       if (preserve_fileflags || (force_change && !am_sender))
62 +               fileflags_ndx = ++file_extra_cnt;
63         if (preserve_acls && !am_sender)
64                 acls_ndx = ++file_extra_cnt;
65         if (preserve_xattrs)
66 diff --git a/configure.ac b/configure.ac
67 --- a/configure.ac
68 +++ b/configure.ac
69 @@ -569,6 +569,7 @@ AC_FUNC_UTIME_NULL
70  AC_FUNC_ALLOCA
71  AC_CHECK_FUNCS(waitpid wait4 getcwd strdup chown chmod lchmod mknod mkfifo \
72      fchmod fstat ftruncate strchr readlink link utime utimes lutimes strftime \
73 +    chflags \
74      memmove lchown vsnprintf snprintf vasprintf asprintf setsid strpbrk \
75      strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
76      setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
77 diff --git a/flist.c b/flist.c
78 --- a/flist.c
79 +++ b/flist.c
80 @@ -51,6 +51,7 @@ extern int preserve_links;
81  extern int preserve_hard_links;
82  extern int preserve_devices;
83  extern int preserve_specials;
84 +extern int preserve_fileflags;
85  extern int delete_during;
86  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,
89  {
90         static time_t modtime;
91         static mode_t mode;
92 +#ifdef SUPPORT_FILEFLAGS
93 +       static uint32 fileflags;
94 +#endif
95  #ifdef SUPPORT_HARD_LINKS
96         static int64 dev;
97  #endif
98 @@ -423,6 +427,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
99                 xflags |= XMIT_SAME_MODE;
100         else
101                 mode = file->mode;
102 +#ifdef SUPPORT_FILEFLAGS
103 +       if (preserve_fileflags) {
104 +               if (F_FFLAGS(file) == fileflags)
105 +                       xflags |= XMIT_SAME_FLAGS;
106 +               else
107 +                       fileflags = F_FFLAGS(file);
108 +       }
109 +#endif
110  
111         if (preserve_devices && IS_DEVICE(mode)) {
112                 if (protocol_version < 28) {
113 @@ -547,6 +559,10 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
114         }
115         if (!(xflags & XMIT_SAME_MODE))
116                 write_int(f, to_wire_mode(mode));
117 +#ifdef SUPPORT_FILEFLAGS
118 +       if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
119 +               write_int(f, (int)fileflags);
120 +#endif
121         if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
122                 if (protocol_version < 30)
123                         write_int(f, uid);
124 @@ -634,6 +650,9 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
125  {
126         static int64 modtime;
127         static mode_t mode;
128 +#ifdef SUPPORT_FILEFLAGS
129 +       static uint32 fileflags;
130 +#endif
131  #ifdef SUPPORT_HARD_LINKS
132         static int64 dev;
133  #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);
136                         modtime = first->modtime;
137                         mode = first->mode;
138 +#ifdef SUPPORT_FILEFLAGS
139 +                       if (preserve_fileflags)
140 +                               fileflags = F_FFLAGS(first);
141 +#endif
142                         if (preserve_uid)
143                                 uid = F_OWNER(first);
144                         if (preserve_gid)
145 @@ -768,6 +791,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
146  
147         if (chmod_modes && !S_ISLNK(mode))
148                 mode = tweak_mode(mode, chmod_modes);
149 +#ifdef SUPPORT_FILEFLAGS
150 +       if (preserve_fileflags && !(xflags & XMIT_SAME_FLAGS))
151 +               fileflags = (uint32)read_int(f);
152 +#endif
153  
154         if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
155                 if (protocol_version < 30)
156 @@ -909,6 +936,10 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
157         }
158  #endif
159         file->mode = mode;
160 +#ifdef SUPPORT_FILEFLAGS
161 +       if (fileflags_ndx) /* check the ndx for force_change w/o preserve_fileflags */
162 +               F_FFLAGS(file) = fileflags;
163 +#endif
164         if (preserve_uid)
165                 F_OWNER(file) = uid;
166         if (preserve_gid) {
167 @@ -1283,6 +1314,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
168         }
169  #endif
170         file->mode = st.st_mode;
171 +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
172 +       if (fileflags_ndx)
173 +               F_FFLAGS(file) = st.st_flags;
174 +#endif
175         if (preserve_uid)
176                 F_OWNER(file) = st.st_uid;
177         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;
188 diff --git a/generator.c b/generator.c
189 --- a/generator.c
190 +++ 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;
200  extern int preserve_specials;
201  extern int preserve_hard_links;
202  extern int preserve_executability;
203 +extern int preserve_fileflags;
204  extern int preserve_perms;
205  extern int preserve_times;
206  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)
241                         return 0;
242                 if (perms_differ(file, sxp))
243                         return 0;
244 +#ifdef SUPPORT_FILEFLAGS
245 +               if (preserve_fileflags && sxp->st.st_flags != F_FFLAGS(file))
246 +                       return 0;
247 +#endif
248                 if (ownership_differs(file, sxp))
249                         return 0;
250  #ifdef SUPPORT_ACLS
251 @@ -698,6 +714,11 @@ void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statre
252                 if (gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
253                     && sxp->st.st_gid != (gid_t)F_GROUP(file))
254                         iflags |= ITEM_REPORT_GROUP;
255 +#ifdef SUPPORT_FILEFLAGS
256 +               if (preserve_fileflags && !S_ISLNK(file->mode)
257 +                && sxp->st.st_flags != F_FFLAGS(file))
258 +                       iflags |= ITEM_REPORT_FFLAGS;
259 +#endif
260  #ifdef SUPPORT_ACLS
261                 if (preserve_acls && !S_ISLNK(file->mode)) {
262                         if (!ACL_READY(*sxp))
263 @@ -1491,6 +1512,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
264                         file->mode = dest_mode(file->mode, sx.st.st_mode,
265                                                dflt_perms, statret == 0);
266                 }
267 +#ifdef SUPPORT_FORCE_CHANGE
268 +               if (force_change && !preserve_fileflags)
269 +                       F_FFLAGS(file) = sx.st.st_flags;
270 +#endif
271                 if (statret != 0 && basis_dir[0] != NULL) {
272                         int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
273                                               itemizing, code);
274 @@ -1535,10 +1560,17 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
275                  * readable and writable permissions during the time we are
276                  * putting files within them.  This is then restored to the
277                  * former permissions after the transfer is done. */
278 +#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 +               }
284 +#endif
285  #ifdef HAVE_CHMOD
286                 if (!am_root && (file->mode & S_IRWXU) != S_IRWXU && dir_tweaking) {
287                         mode_t mode = file->mode | S_IRWXU;
288 -                       if (do_chmod(fname, mode) < 0) {
289 +                       if (do_chmod(fname, mode, 0) < 0) {
290                                 rsyserr(FERROR_XFER, errno,
291                                         "failed to modify permissions on %s",
292                                         full_fname(fname));
293 @@ -1573,6 +1605,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
294                 file->mode = dest_mode(file->mode, sx.st.st_mode, dflt_perms,
295                                        exists);
296         }
297 +#ifdef SUPPORT_FORCE_CHANGE
298 +       if (force_change && !preserve_fileflags)
299 +               F_FFLAGS(file) = sx.st.st_flags;
300 +#endif
301  
302  #ifdef SUPPORT_HARD_LINKS
303         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)
305                         continue;
306                 fname = f_name(file, NULL);
307                 if (fix_dir_perms)
308 -                       do_chmod(fname, file->mode);
309 +                       do_chmod(fname, file->mode, 0);
310                 if (need_retouch_dir_times) {
311                         STRUCT_STAT st;
312                         if (link_stat(fname, &st, 0) == 0
313                          && 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);
316                 }
317 +#ifdef SUPPORT_FORCE_CHANGE
318 +               if (force_change && F_FFLAGS(file) & force_change)
319 +                       undo_make_mutable(fname, F_FFLAGS(file));
320 +#endif
321                 if (counter >= loopchk_limit) {
322                         if (allowed_lull)
323                                 maybe_send_keepalive();
324 diff --git a/log.c b/log.c
325 --- a/log.c
326 +++ b/log.c
327 @@ -658,7 +658,7 @@ static void log_formatted(enum logcode code, const char *format, const char *op,
328                         c[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
329                         c[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
330                         c[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
331 -                       c[8] = !(iflags & ITEM_REPORT_ATIME) ? '.' : 'u';
332 +                       c[8] = !(iflags & ITEM_REPORT_FFLAGS) ? '.' : 'f';
333                         c[9] = !(iflags & ITEM_REPORT_ACL) ? '.' : 'a';
334                         c[10] = !(iflags & ITEM_REPORT_XATTR) ? '.' : 'x';
335                         c[11] = '\0';
336 diff --git a/main.c b/main.c
337 --- a/main.c
338 +++ b/main.c
339 @@ -26,6 +26,9 @@
340  #if defined CONFIG_LOCALE && defined HAVE_LOCALE_H
341  #include <locale.h>
342  #endif
343 +#ifdef SUPPORT_FORCE_CHANGE
344 +#include <sys/sysctl.h>
345 +#endif
346  
347  extern int verbose;
348  extern int dry_run;
349 @@ -51,6 +54,7 @@ extern int protocol_version;
350  extern int file_total;
351  extern int recurse;
352  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)
358          * points to an identical file won't be replaced by the referent. */
359         copy_links = copy_dirlinks = copy_unsafe_links = 0;
360  
361 +#ifdef SUPPORT_FORCE_CHANGE
362 +       if (force_change & SYS_IMMUTABLE) {
363 +               /* Determine whether we'll be able to unlock a system immutable item. */
364 +               int mib[2];
365 +               int securityLevel = 0;
366 +               size_t len = sizeof securityLevel;
367 +
368 +               mib[0] = CTL_KERN;
369 +               mib[1] = KERN_SECURELVL;
370 +               if (sysctl(mib, 2, &securityLevel, &len, NULL, 0) == 0 && securityLevel > 0) {
371 +                       rprintf(FERROR, "System security level is too high to force mutability on system immutable files and directories.\n");
372 +                       exit_cleanup(RERR_UNSUPPORTED);
373 +               }
374 +       }
375 +#endif
376 +
377  #ifdef SUPPORT_HARD_LINKS
378         if (preserve_hard_links && !inc_recurse)
379                 match_hard_links(first_flist);
380 diff --git a/options.c b/options.c
381 --- a/options.c
382 +++ b/options.c
383 @@ -53,6 +53,7 @@ int preserve_hard_links = 0;
384  int preserve_acls = 0;
385  int preserve_xattrs = 0;
386  int preserve_perms = 0;
387 +int preserve_fileflags = 0;
388  int preserve_executability = 0;
389  int preserve_devices = 0;
390  int preserve_specials = 0;
391 @@ -84,6 +85,7 @@ int implied_dirs = 1;
392  int numeric_ids = 0;
393  int allow_8bit_chars = 0;
394  int force_delete = 0;
395 +int force_change = 0;
396  int io_timeout = 0;
397  int prune_empty_dirs = 0;
398  int use_qsort = 0;
399 @@ -223,6 +225,7 @@ static void print_rsync_version(enum logcode f)
400         char const *links = "no ";
401         char const *iconv = "no ";
402         char const *ipv6 = "no ";
403 +       char const *fileflags = "no ";
404         STRUCT_STAT *dumstat;
405  
406  #if SUBPROTOCOL_VERSION != 0
407 @@ -256,6 +259,9 @@ static void print_rsync_version(enum logcode f)
408  #ifdef CAN_SET_SYMLINK_TIMES
409         symtimes = "";
410  #endif
411 +#ifdef SUPPORT_FILEFLAGS
412 +       fileflags = "";
413 +#endif
414  
415         rprintf(f, "%s  version %s  protocol version %d%s\n",
416                 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
417 @@ -269,8 +275,8 @@ static void print_rsync_version(enum logcode f)
418                 (int)(sizeof (int64) * 8));
419         rprintf(f, "    %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
420                 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);
425  
426  #ifdef MAINTAINER_MODE
427         rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
428 @@ -337,6 +343,9 @@ void usage(enum logcode F)
429    rprintf(F," -K, --keep-dirlinks         treat symlinked dir on receiver as dir\n");
430    rprintf(F," -H, --hard-links            preserve hard links\n");
431    rprintf(F," -p, --perms                 preserve permissions\n");
432 +#ifdef SUPPORT_FILEFLAGS
433 +  rprintf(F,"     --fileflags             preserve file-flags (aka chflags)\n");
434 +#endif
435    rprintf(F," -E, --executability         preserve the file's executability\n");
436    rprintf(F,"     --chmod=CHMOD           affect file and/or directory permissions\n");
437  #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");
441    rprintf(F,"     --ignore-errors         delete even if there are I/O errors\n");
442 -  rprintf(F,"     --force                 force deletion of directories even if not empty\n");
443 +  rprintf(F,"     --force-delete          force deletion of directories even if not empty\n");
444 +#ifdef SUPPORT_FORCE_CHANGE
445 +  rprintf(F,"     --force-change          affect user-/system-immutable files/dirs\n");
446 +  rprintf(F,"     --force-uchange         affect user-immutable files/dirs\n");
447 +  rprintf(F,"     --force-schange         affect system-immutable files/dirs\n");
448 +#endif
449    rprintf(F,"     --max-delete=NUM        don't delete more than NUM files\n");
450    rprintf(F,"     --max-size=SIZE         don't transfer any file larger than SIZE\n");
451    rprintf(F,"     --min-size=SIZE         don't transfer any file smaller than SIZE\n");
452 @@ -479,6 +493,10 @@ static struct poptOption long_options[] = {
453    {"perms",           'p', POPT_ARG_VAL,    &preserve_perms, 1, 0, 0 },
454    {"no-perms",         0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
455    {"no-p",             0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
456 +#ifdef SUPPORT_FILEFLAGS
457 +  {"fileflags",        0,  POPT_ARG_VAL,    &preserve_fileflags, 1, 0, 0 },
458 +  {"no-fileflags",     0,  POPT_ARG_VAL,    &preserve_fileflags, 0, 0, 0 },
459 +#endif
460    {"executability",   'E', POPT_ARG_NONE,   &preserve_executability, 0, 0, 0 },
461    {"acls",            'A', POPT_ARG_NONE,   0, 'A', 0, 0 },
462    {"no-acls",          0,  POPT_ARG_VAL,    &preserve_acls, 0, 0, 0 },
463 @@ -557,6 +575,14 @@ static struct poptOption long_options[] = {
464    {"remove-source-files",0,POPT_ARG_VAL,    &remove_source_files, 1, 0, 0 },
465    {"force",            0,  POPT_ARG_VAL,    &force_delete, 1, 0, 0 },
466    {"no-force",         0,  POPT_ARG_VAL,    &force_delete, 0, 0, 0 },
467 +  {"force-delete",     0,  POPT_ARG_VAL,    &force_delete, 1, 0, 0 },
468 +  {"no-force-delete",  0,  POPT_ARG_VAL,    &force_delete, 0, 0, 0 },
469 +#ifdef SUPPORT_FORCE_CHANGE
470 +  {"force-change",     0,  POPT_ARG_VAL,    &force_change, ALL_IMMUTABLE, 0, 0 },
471 +  {"no-force-change",  0,  POPT_ARG_VAL,    &force_change, 0, 0, 0 },
472 +  {"force-uchange",    0,  POPT_ARG_VAL,    &force_change, USR_IMMUTABLE, 0, 0 },
473 +  {"force-schange",    0,  POPT_ARG_VAL,    &force_change, SYS_IMMUTABLE, 0, 0 },
474 +#endif
475    {"ignore-errors",    0,  POPT_ARG_VAL,    &ignore_errors, 1, 0, 0 },
476    {"no-ignore-errors", 0,  POPT_ARG_VAL,    &ignore_errors, 0, 0, 0 },
477    {"max-delete",       0,  POPT_ARG_INT,    &max_delete, 0, 0, 0 },
478 @@ -1879,6 +1905,9 @@ void server_options(char **args, int *argc_p)
479         if (xfer_dirs && !recurse && delete_mode && am_sender)
480                 args[ac++] = "--no-r";
481  
482 +       if (preserve_fileflags)
483 +               args[ac++] = "--fileflags";
484 +
485         if (do_compression && def_compress_level != Z_DEFAULT_COMPRESSION) {
486                 if (asprintf(&arg, "--compress-level=%d", def_compress_level) < 0)
487                         goto oom;
488 @@ -1966,6 +1995,16 @@ void server_options(char **args, int *argc_p)
489                         args[ac++] = "--delete-excluded";
490                 if (force_delete)
491                         args[ac++] = "--force";
492 +#ifdef SUPPORT_FORCE_CHANGE
493 +               if (force_change) {
494 +                       if (force_change == ALL_IMMUTABLE)
495 +                               args[ac++] = "--force-change";
496 +                       else if (force_change == USR_IMMUTABLE)
497 +                               args[ac++] = "--force-uchange";
498 +                       else if (force_change == SYS_IMMUTABLE)
499 +                               args[ac++] = "--force-schange";
500 +               }
501 +#endif
502                 if (write_batch < 0)
503                         args[ac++] = "--only-write-batch=X";
504                 if (am_root > 1)
505 diff --git a/rsync.c b/rsync.c
506 --- a/rsync.c
507 +++ b/rsync.c
508 @@ -29,9 +29,11 @@
509  
510  extern int verbose;
511  extern int dry_run;
512 +extern int force_change;
513  extern int preserve_acls;
514  extern int preserve_xattrs;
515  extern int preserve_perms;
516 +extern int preserve_fileflags;
517  extern int preserve_executability;
518  extern int preserve_times;
519  extern int am_root;
520 @@ -374,6 +376,74 @@ mode_t dest_mode(mode_t flist_mode, mode_t stat_mode, int dflt_perms,
521         return new_mode;
522  }
523  
524 +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
525 +/* Set a file's st_flags. */
526 +static int set_fileflags(const char *fname, uint32 fileflags)
527 +{
528 +       if (do_chflags(fname, fileflags) != 0) {
529 +               rsyserr(FERROR_XFER, errno,
530 +                       "failed to set fileflags (%x) on %s",
531 +                       fileflags, full_fname(fname));
532 +               return 0;
533 +       }
534 +
535 +       return 1;
536 +}
537 +
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)
543 +{
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))
556 +               return 0;
557 +
558 +       if (!set_fileflags(fname, fileflags & ~iflags))
559 +               return 0;
560 +
561 +       return fileflags;
562 +}
563 +
564 +/* Undo a prior make_mutable() call that returned a 1. */
565 +int undo_make_mutable(const char *fname, uint32 fileflags)
566 +{
567 +       if (!set_fileflags(fname, fileflags)) {
568 +               rsyserr(FINFO, errno, "failed to relock %s", full_fname(fname));
569 +               return -1;
570 +       }
571 +       return 1;
572 +}
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 +}
590 +#endif
591 +
592  int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
593                    const char *fnamecmp, int flags)
594  {
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,
627                 if (am_root >= 0) {
628                         uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
629                         gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
630 -                       if (do_lchown(fname, uid, gid) != 0) {
631 +                       if (do_lchown(fname, uid, gid, sxp->st.st_mode, ST_FLAGS(sxp->st)) != 0) {
632                                 /* We shouldn't have attempted to change uid
633                                  * or gid unless have the privilege. */
634                                 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,
636  
637  #ifdef HAVE_CHMOD
638         if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
639 -               int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
640 +               int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode, ST_FLAGS(sxp->st));
641                 if (ret < 0) {
642                         rsyserr(FERROR_XFER, errno,
643                                 "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,
645         }
646  #endif
647  
648 +#ifdef SUPPORT_FORCE_CHANGE
649 +       if (became_mutable)
650 +               undo_make_mutable(fname, sxp->st.st_flags);
651 +#endif
652 +
653 +#ifdef SUPPORT_FILEFLAGS
654 +       if (preserve_fileflags && !S_ISLNK(sxp->st.st_mode)
655 +        && sxp->st.st_flags != F_FFLAGS(file)) {
656 +               uint32 fileflags = F_FFLAGS(file);
657 +               if (flags & ATTRS_DELAY_IMMUTABLE)
658 +                       fileflags &= ~ALL_IMMUTABLE;
659 +               if (sxp->st.st_flags != fileflags
660 +                && !set_fileflags(fname, fileflags))
661 +                       goto cleanup;
662 +               updated = 1;
663 +       }
664 +#endif
665 +
666         if (verbose > 1 && flags & ATTRS_REPORT) {
667                 if (updated)
668                         rprintf(FCLIENT, "%s\n", fname);
669 @@ -578,7 +674,8 @@ int finish_transfer(const char *fname, const char *fnametmp,
670  
671         /* Change permissions before putting the file into place. */
672         set_file_attrs(fnametmp, file, NULL, fnamecmp,
673 -                      ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
674 +                      ATTRS_DELAY_IMMUTABLE
675 +                      | (ok_to_set_time ? 0 : ATTRS_SKIP_MTIME));
676  
677         /* move tmp file over real file */
678         if (verbose > 2)
679 @@ -597,6 +694,10 @@ int finish_transfer(const char *fname, const char *fnametmp,
680         }
681         if (ret == 0) {
682                 /* The file was moved into place (not copied), so it's done. */
683 +#ifdef SUPPORT_FILEFLAGS
684 +               if (preserve_fileflags && F_FFLAGS(file) & ALL_IMMUTABLE)
685 +                       set_fileflags(fname, F_FFLAGS(file));
686 +#endif
687                 return 1;
688         }
689         /* The file was copied, so tweak the perms of the copied file.  If it
690 diff --git a/rsync.h b/rsync.h
691 --- a/rsync.h
692 +++ b/rsync.h
693 @@ -61,6 +61,7 @@
694  #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
695  #define XMIT_HLINK_FIRST (1<<12)       /* protocols 30 - now (HLINKED files only) */
696  #define XMIT_IO_ERROR_ENDLIST (1<<12)  /* protocols 31*- now (w/XMIT_EXTENDED_FLAGS) (also protocol 30 w/'f' compat flag) */
697 +#define XMIT_SAME_FLAGS (1<<14)                /* protocols ?? - now */
698  
699  /* These flags are used in the live flist data. */
700  
701 @@ -160,6 +161,7 @@
702  
703  #define ATTRS_REPORT           (1<<0)
704  #define ATTRS_SKIP_MTIME       (1<<1)
705 +#define ATTRS_DELAY_IMMUTABLE  (1<<2)
706  
707  #define FULL_FLUSH     1
708  #define NORMAL_FLUSH   0
709 @@ -186,6 +188,7 @@
710  #define ITEM_REPORT_GROUP (1<<6)
711  #define ITEM_REPORT_ACL (1<<7)
712  #define ITEM_REPORT_XATTR (1<<8)
713 +#define ITEM_REPORT_FFLAGS (1<<9)
714  #define ITEM_BASIS_TYPE_FOLLOWS (1<<11)
715  #define ITEM_XNAME_FOLLOWS (1<<12)
716  #define ITEM_IS_NEW (1<<13)
717 @@ -482,6 +485,28 @@ typedef unsigned int size_t;
718  #endif
719  #endif
720  
721 +#define NO_FFLAGS ((uint32)-1)
722 +
723 +#ifdef HAVE_CHFLAGS
724 +#define SUPPORT_FILEFLAGS 1
725 +#define SUPPORT_FORCE_CHANGE 1
726 +#endif
727 +
728 +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
729 +#ifndef UF_NOUNLINK
730 +#define UF_NOUNLINK 0
731 +#endif
732 +#ifndef SF_NOUNLINK
733 +#define SF_NOUNLINK 0
734 +#endif
735 +#define USR_IMMUTABLE (UF_IMMUTABLE|UF_NOUNLINK|UF_APPEND)
736 +#define SYS_IMMUTABLE (SF_IMMUTABLE|SF_NOUNLINK|SF_APPEND)
737 +#define ALL_IMMUTABLE (USR_IMMUTABLE|SYS_IMMUTABLE)
738 +#define ST_FLAGS(st) (st.st_flags)
739 +#else
740 +#define ST_FLAGS(st) NO_FFLAGS
741 +#endif
742 +
743  /* Find a variable that is either exactly 32-bits or longer.
744   * If some code depends on 32-bit truncation, it will need to
745   * take special action in a "#if SIZEOF_INT32 > 4" section. */
746 @@ -652,6 +677,7 @@ extern int file_extra_cnt;
747  extern int inc_recurse;
748  extern int uid_ndx;
749  extern int gid_ndx;
750 +extern int fileflags_ndx;
751  extern int acls_ndx;
752  extern int xattrs_ndx;
753  
754 @@ -689,6 +715,11 @@ extern int xattrs_ndx;
755  /* When the associated option is on, all entries will have these present: */
756  #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
757  #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
758 +#if defined SUPPORT_FILEFLAGS || defined SUPPORT_FORCE_CHANGE
759 +#define F_FFLAGS(f) REQ_EXTRA(f, fileflags_ndx)->unum
760 +#else
761 +#define F_FFLAGS(f) NO_FFLAGS
762 +#endif
763  #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
764  #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
765  #define F_NDX(f) REQ_EXTRA(f, unsort_ndx)->num
766 diff --git a/rsync.yo b/rsync.yo
767 --- a/rsync.yo
768 +++ b/rsync.yo
769 @@ -355,6 +355,7 @@ to the detailed description below for a complete description.  verb(
770   -K, --keep-dirlinks         treat symlinked dir on receiver as dir
771   -H, --hard-links            preserve hard links
772   -p, --perms                 preserve permissions
773 +     --fileflags             preserve file-flags (aka chflags)
774   -E, --executability         preserve executability
775       --chmod=CHMOD           affect file and/or directory permissions
776   -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
780       --ignore-errors         delete even if there are I/O errors
781 -     --force                 force deletion of dirs even if not empty
782 +     --force-delete          force deletion of dirs even if not empty
783 +     --force-change          affect user/system immutable files/dirs
784 +     --force-uchange         affect user-immutable files/dirs
785 +     --force-schange         affect system-immutable files/dirs
786       --max-delete=NUM        don't delete more than NUM files
787       --max-size=SIZE         don't transfer any file larger than SIZE
788       --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.
790  
791  Note that bf(-a) bf(does not preserve hardlinks), because
792  finding multiply-linked files is expensive.  You must separately
793 -specify bf(-H).
794 +specify bf(-H).  Note also that for backward compatibility, bf(-a)
795 +currently does bf(not) imply the bf(--fileflags) option.
796  
797  dit(--no-OPTION) You may turn off one or more implied options by prefixing
798  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).
800  Without this option, if the sending side has replaced a directory with a
801  symlink to a directory, the receiving side will delete anything that is in
802  the way of the new symlink, including a directory hierarchy (as long as
803 -bf(--force) or bf(--delete) is in effect).
804 +bf(--force-delete) or bf(--delete) is in effect).
805  
806  See also bf(--keep-dirlinks) for an analogous option for the receiving
807  side.
808 @@ -1009,6 +1014,29 @@ Note that this option does not copy rsyncs special xattr values (e.g. those
809  used by bf(--fake-super)) unless you repeat the option (e.g. -XX).  This
810  "copy all xattrs" mode cannot be used with bf(--fake-super).
811  
812 +dit(bf(--fileflags)) This option causes rsync to update the file-flags to be
813 +the same as the source files and directories (if your OS supports the
814 +bf(chflags)(2) system call).   Some flags can only be altered by the super-user
815 +and some might only be unset below a certain secure-level (usually single-user
816 +mode). It will not make files alterable that are set to immutable on the
817 +receiver.  To do that, see bf(--force-change), bf(--force-uchange), and
818 +bf(--force-schange).
819 +
820 +dit(bf(--force-change)) This option causes rsync to disable both user-immutable
821 +and system-immutable flags on files and directories that are being updated or
822 +deleted on the receiving side.  This option overrides bf(--force-uchange) and
823 +bf(--force-schange).
824 +
825 +dit(bf(--force-uchange)) This option causes rsync to disable user-immutable
826 +flags on files and directories that are being updated or deleted on the
827 +receiving side.  It does not try to affect system flags.  This option overrides
828 +bf(--force-change) and bf(--force-schange).
829 +
830 +dit(bf(--force-schange)) This option causes rsync to disable system-immutable
831 +flags on files and directories that are being updated or deleted on the
832 +receiving side.  It does not try to affect user flags.  This option overrides
833 +bf(--force-change) and bf(--force-schange).
834 +
835  dit(bf(--chmod)) This option tells rsync to apply one or more
836  comma-separated "chmod" modes to the permission of the files in the
837  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.
839  dit(bf(--ignore-errors)) Tells bf(--delete) to go ahead and delete files
840  even when there are I/O errors.
841  
842 -dit(bf(--force)) This option tells rsync to delete a non-empty directory
843 +dit(bf(--force-delete)) This option tells rsync to delete a non-empty directory
844  when it is to be replaced by a non-directory.  This is only relevant if
845  deletions are not active (see bf(--delete) for details).
846  
847 -Note for older rsync versions: bf(--force) used to still be required when
848 -using bf(--delete-after), and it used to be non-functional unless the
849 +This option can be abbreviated bf(--force) for backward compatibility.
850 +Note that some older rsync versions used to still require bf(--force)
851 +when using bf(--delete-after), and it used to be non-functional unless the
852  bf(--recursive) option was also enabled.
853  
854  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
856  verbose messages).
857  
858  The "%i" escape has a cryptic output that is 11 letters long.  The general
859 -format is like the string bf(YXcstpoguax), where bf(Y) is replaced by the
860 +format is like the string bf(YXcstpogfax), where bf(Y) is replaced by the
861  type of update being done, bf(X) is replaced by the file-type, and the
862  other letters represent attributes that may be output if they are being
863  modified.
864 @@ -1838,7 +1867,7 @@ quote(itemization(
865    sender's value (requires bf(--owner) and super-user privileges).
866    it() A bf(g) means the group is different and is being updated to the
867    sender's value (requires bf(--group) and the authority to set the group).
868 -  it() The bf(u) slot is reserved for future use.
869 +  it() The bf(f) means that the fileflags information changed.
870    it() The bf(a) means that the ACL information changed.
871    it() The bf(x) means that the extended attribute information changed.
872  ))
873 diff --git a/syscall.c b/syscall.c
874 --- a/syscall.c
875 +++ b/syscall.c
876 @@ -33,6 +33,7 @@ extern int dry_run;
877  extern int am_root;
878  extern int read_only;
879  extern int list_only;
880 +extern int force_change;
881  extern int preserve_perms;
882  extern int preserve_executability;
883  
884 @@ -50,14 +51,56 @@ int do_unlink(const char *fname)
885  {
886         if (dry_run) return 0;
887         RETURN_ERROR_IF_RO_OR_LO;
888 -       return unlink(fname);
889 +       if (unlink(fname) == 0)
890 +               return 0;
891 +
892 +#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;
923 +
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)
933 +                               return 0;
934 +               }
935 +               errno = saved_errno;
936 +       }
937 +#endif
938 +
939 +       return -1;
940  }
941  
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;
967  }
968  #endif
969  
970 -int do_lchown(const char *path, uid_t owner, gid_t group)
971 +int do_lchown(const char *path, uid_t owner, gid_t group, mode_t mode, uint32 fileflags)
972  {
973         if (dry_run) return 0;
974         RETURN_ERROR_IF_RO_OR_LO;
975  #ifndef HAVE_LCHOWN
976  #define lchown chown
977  #endif
978 -       return lchown(path, owner, group);
979 +       if (lchown(path, owner, group) == 0)
980 +               return 0;
981 +
982 +#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) {
987 +                       int ret = lchown(path, owner, group);
988 +                       undo_make_mutable(path, fileflags);
989 +                       if (ret == 0)
990 +                               return 0;
991 +               }
992 +               errno = saved_errno;
993 +       }
994 +#else
995 +       mode = fileflags = 0; /* avoid compiler warning */
996 +#endif
997 +
998 +       return -1;
999  }
1000  
1001  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)
1003                         return -1;
1004                 close(sock);
1005  #ifdef HAVE_CHMOD
1006 -               return do_chmod(pathname, mode);
1007 +               return do_chmod(pathname, mode, 0);
1008  #else
1009                 return 0;
1010  #endif
1011 @@ -133,21 +213,63 @@ int do_rmdir(const char *pathname)
1012  {
1013         if (dry_run) return 0;
1014         RETURN_ERROR_IF_RO_OR_LO;
1015 -       return rmdir(pathname);
1016 +       if (rmdir(pathname) == 0)
1017 +               return 0;
1018 +
1019 +#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)
1032 +                               return 0;
1033 +               }
1034 +               if (file_flags)
1035 +                       undo_make_mutable(pathname, file_flags);
1036 +               errno = saved_errno;
1037 +       }
1038 +#endif
1039 +
1040 +       return -1;
1041  }
1042  
1043  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;
1070  }
1071  
1072  #ifdef HAVE_CHMOD
1073 -int do_chmod(const char *path, mode_t mode)
1074 +int do_chmod(const char *path, mode_t mode, uint32 fileflags)
1075  {
1076         int code;
1077         if (dry_run) return 0;
1078 @@ -170,17 +292,93 @@ int do_chmod(const char *path, mode_t mode)
1079         } else
1080                 code = chmod(path, mode & CHMOD_BITS); /* DISCOURAGED FUNCTION */
1081  #endif /* !HAVE_LCHMOD */
1082 +#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
1090 +                       code = chmod(path, mode & CHMOD_BITS);
1091 +#endif
1092 +                       undo_make_mutable(path, fileflags);
1093 +                       if (code == 0)
1094 +                               return 0;
1095 +               }
1096 +               errno = saved_errno;
1097 +       }
1098 +#else
1099 +       fileflags = 0; /* avoid compiler warning */
1100 +#endif
1101         if (code != 0 && (preserve_perms || preserve_executability))
1102                 return code;
1103         return 0;
1104  }
1105  #endif
1106  
1107 +#ifdef HAVE_CHFLAGS
1108 +int do_chflags(const char *path, uint32 fileflags)
1109 +{
1110 +       if (dry_run) return 0;
1111 +       RETURN_ERROR_IF_RO_OR_LO;
1112 +       return chflags(path, fileflags);
1113 +}
1114 +#endif
1115 +
1116  int do_rename(const char *fname1, const char *fname2)
1117  {
1118         if (dry_run) return 0;
1119         RETURN_ERROR_IF_RO_OR_LO;
1120 -       return rename(fname1, fname2);
1121 +       if (rename(fname1, fname2) == 0)
1122 +               return 0;
1123 +
1124 +#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
1168 +
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)
1190 +                               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);
1213 +                       }
1214 +                       errno = saved_errno;
1215 +               }
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);
1302 +       }
1303 +#else
1304 +       mode = fileflags; /* avoid compiler warning */
1305 +#endif
1306 +
1307 +       return -1;
1308  }
1309  
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
1354 diff --git a/t_stub.c b/t_stub.c
1355 --- a/t_stub.c
1356 +++ b/t_stub.c
1357 @@ -26,6 +26,7 @@ int module_id = -1;
1358  int relative_paths = 0;
1359  int human_readable = 0;
1360  int module_dirlen = 0;
1361 +int force_change = 0;
1362  int preserve_times = 0;
1363  int preserve_xattrs = 0;
1364  mode_t orig_umask = 002;
1365 @@ -90,3 +91,27 @@ struct filter_list_struct daemon_filter_list;
1366  {
1367         return "tester";
1368  }
1369 +
1370 +#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))
1372 +{
1373 +       return 0;
1374 +}
1375 +
1376 + int undo_make_mutable(UNUSED(const char *fname), UNUSED(uint32 fileflags))
1377 +{
1378 +       return 0;
1379 +}
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 +}
1385 +#endif
1386 +
1387 +#ifdef SUPPORT_XATTRS
1388 + int x_lstat(UNUSED(const char *fname), UNUSED(STRUCT_STAT *fst), UNUSED(STRUCT_STAT *xst))
1389 +{
1390 +       return -1;
1391 +}
1392 +#endif
1393 diff --git a/util.c b/util.c
1394 --- a/util.c
1395 +++ b/util.c
1396 @@ -125,7 +125,7 @@ NORETURN void overflow_exit(const char *str)
1397  
1398  /* This returns 0 for success, 1 for a symlink if symlink time-setting
1399   * 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)
1402  {
1403         static int switch_step = 0;
1404  
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
1408  #include "case_N.h"
1409 -               if (do_utimensat(fname, modtime, 0) == 0)
1410 +               if (do_utimensat(fname, modtime, 0, mode, fileflags) == 0)
1411                         break;
1412                 if (errno != ENOSYS)
1413                         return -1;
1414 @@ -148,7 +148,7 @@ int set_modtime(const char *fname, time_t modtime, mode_t mode)
1415  
1416  #ifdef HAVE_LUTIMES
1417  #include "case_N.h"
1418 -               if (do_lutimes(fname, modtime, 0) == 0)
1419 +               if (do_lutimes(fname, modtime, 0, mode, fileflags) == 0)
1420                         break;
1421                 if (errno != ENOSYS)
1422                         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)
1433                         break;
1434  #endif
1435  
1436 diff --git a/xattrs.c b/xattrs.c
1437 --- a/xattrs.c
1438 +++ b/xattrs.c
1439 @@ -1042,7 +1042,7 @@ int set_stat_xattr(const char *fname, struct file_struct *file, mode_t new_mode)
1440         mode = (fst.st_mode & _S_IFMT) | (fmode & ACCESSPERMS)
1441              | (S_ISDIR(fst.st_mode) ? 0700 : 0600);
1442         if (fst.st_mode != mode)
1443 -               do_chmod(fname, mode);
1444 +               do_chmod(fname, mode, ST_FLAGS(fst));
1445         if (!IS_DEVICE(fst.st_mode))
1446                 fst.st_rdev = 0; /* just in case */
1447