Fixed failing hunks.
[rsync/rsync-patches.git] / flags.diff
CommitLineData
31611b92 1This patch provides --flags, which preserves the st_flags field.
9f62ddf8 2Modified from a patch that was written by Rolf Grossmann.
31611b92 3
03019e41 4To use this patch, run these commands for a successful build:
9f62ddf8 5
03019e41 6 patch -p1 <patches/flags.diff
9f62ddf8
WD
7 ./prepare-source
8 ./configure
9 make
31611b92 10
fdf967c7
WD
11--- old/compat.c
12+++ new/compat.c
ccdb48f6
WD
13@@ -44,6 +44,7 @@ extern int protocol_version;
14 extern int protect_args;
898a2112
WD
15 extern int preserve_uid;
16 extern int preserve_gid;
17+extern int preserve_fileflags;
18 extern int preserve_acls;
19 extern int preserve_xattrs;
9c25eef5 20 extern int need_messages_from_generator;
52e09c4e 21@@ -60,7 +61,7 @@ extern iconv_t ic_send, ic_recv;
ccdb48f6 22 #endif
898a2112
WD
23
24 /* These index values are for the file-list's extra-attribute array. */
25-int uid_ndx, gid_ndx, acls_ndx, xattrs_ndx;
26+int uid_ndx, gid_ndx, fileflags_ndx, acls_ndx, xattrs_ndx;
ccdb48f6
WD
27 #ifdef ICONV_OPTION
28 int ic_ndx;
898a2112 29
6240d1e4 30@@ -125,6 +126,8 @@ void setup_protocol(int f_out,int f_in)
898a2112 31 uid_ndx = ++file_extra_cnt;
fdf967c7 32 if (preserve_gid)
898a2112 33 gid_ndx = ++file_extra_cnt;
fdf967c7 34+ if (preserve_fileflags)
898a2112 35+ fileflags_ndx = ++file_extra_cnt;
ffc18846 36 if (preserve_acls && !am_sender)
898a2112 37 acls_ndx = ++file_extra_cnt;
5795bf59 38 if (preserve_xattrs)
31611b92
WD
39--- old/configure.in
40+++ new/configure.in
6240d1e4 41@@ -557,7 +557,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strd
31611b92
WD
42 memmove lchown vsnprintf snprintf vasprintf asprintf setsid glob strpbrk \
43 strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
44 setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
80c89075
WD
45- strerror putenv iconv_open locale_charset nl_langinfo getxattr \
46+ chflags strerror putenv iconv_open locale_charset nl_langinfo getxattr \
7c4c2959 47 extattr_get_link sigaction sigprocmask setattrlist)
31611b92
WD
48
49 AC_CHECK_FUNCS(getpgrp tcgetpgrp)
50--- old/flist.c
51+++ new/flist.c
790ba11a
WD
52@@ -50,6 +50,7 @@ extern int preserve_links;
53 extern int preserve_hard_links;
54 extern int preserve_devices;
31611b92 55 extern int preserve_specials;
790ba11a 56+extern int fileflags_ndx;
898a2112
WD
57 extern int uid_ndx;
58 extern int gid_ndx;
790ba11a 59 extern int eol_nulls;
52e09c4e 60@@ -335,6 +336,9 @@ static void send_file_entry(int f, struc
7e27b6c0 61 {
31611b92
WD
62 static time_t modtime;
63 static mode_t mode;
64+#ifdef SUPPORT_FLAGS
65+ static uint32 fileflags;
66+#endif
6240d1e4 67 #ifdef SUPPORT_HARD_LINKS
31611b92 68 static int64 dev;
6240d1e4
WD
69 #endif
70@@ -393,6 +397,12 @@ static void send_file_entry(int f, struc
99650e0d 71 xflags |= XMIT_SAME_MODE;
31611b92
WD
72 else
73 mode = file->mode;
74+#ifdef SUPPORT_FLAGS
8871c12a 75+ if (F_FFLAGS(file) == fileflags)
99650e0d 76+ xflags |= XMIT_SAME_FLAGS;
31611b92 77+ else
7d006b5b 78+ fileflags = F_FFLAGS(file);
31611b92 79+#endif
99650e0d 80
2ad6fab3
WD
81 if ((preserve_devices && IS_DEVICE(mode))
82 || (preserve_specials && IS_SPECIAL(mode))) {
6240d1e4 83@@ -513,6 +523,10 @@ static void send_file_entry(int f, struc
82c2230a 84 }
99650e0d 85 if (!(xflags & XMIT_SAME_MODE))
31611b92
WD
86 write_int(f, to_wire_mode(mode));
87+#ifdef SUPPORT_FLAGS
8871c12a 88+ if (fileflags_ndx && !(xflags & XMIT_SAME_FLAGS))
31611b92
WD
89+ write_int(f, (int)fileflags);
90+#endif
99650e0d 91 if (uid_ndx && !(xflags & XMIT_SAME_UID)) {
caf38d8d
WD
92 if (protocol_version < 30)
93 write_int(f, uid);
6240d1e4 94@@ -601,6 +615,9 @@ static struct file_struct *recv_file_ent
31611b92 95 {
82c2230a 96 static int64 modtime;
31611b92
WD
97 static mode_t mode;
98+#ifdef SUPPORT_FLAGS
8871c12a 99+ static uint32 fileflags;
31611b92 100+#endif
6240d1e4 101 #ifdef SUPPORT_HARD_LINKS
31611b92 102 static int64 dev;
6240d1e4
WD
103 #endif
104@@ -732,9 +749,12 @@ static struct file_struct *recv_file_ent
82c2230a 105 }
898a2112 106 if (!(xflags & XMIT_SAME_MODE))
31611b92
WD
107 mode = from_wire_mode(read_int(f));
108-
109 if (chmod_modes && !S_ISLNK(mode))
110 mode = tweak_mode(mode, chmod_modes);
111+#ifdef SUPPORT_FLAGS
8871c12a 112+ if (fileflags_ndx && !(xflags & XMIT_SAME_FLAGS))
31611b92
WD
113+ fileflags = (uint32)read_int(f);
114+#endif
115
898a2112 116 if (uid_ndx && !(xflags & XMIT_SAME_UID)) {
caf38d8d 117 if (protocol_version < 30)
6240d1e4 118@@ -854,6 +874,10 @@ static struct file_struct *recv_file_ent
1aa236e1
WD
119 OPT_EXTRA(file, 0)->unum = (uint32)(file_length >> 32);
120 }
31611b92
WD
121 file->mode = mode;
122+#ifdef SUPPORT_FLAGS
898a2112 123+ if (fileflags_ndx)
70891d26 124+ F_FFLAGS(file) = fileflags;
31611b92 125+#endif
898a2112 126 if (uid_ndx)
fc068916 127 F_OWNER(file) = uid;
898a2112 128 if (gid_ndx) {
6240d1e4 129@@ -1182,6 +1206,10 @@ struct file_struct *make_file(const char
1aa236e1
WD
130 OPT_EXTRA(file, 0)->unum = (uint32)(st.st_size >> 32);
131 }
31611b92
WD
132 file->mode = st.st_mode;
133+#ifdef SUPPORT_FLAGS
898a2112 134+ if (fileflags_ndx)
70891d26 135+ F_FFLAGS(file) = st.st_flags;
31611b92 136+#endif
898a2112 137 if (uid_ndx)
fc068916 138 F_OWNER(file) = st.st_uid;
898a2112 139 if (gid_ndx)
31611b92
WD
140--- old/generator.c
141+++ new/generator.c
ccdb48f6 142@@ -114,6 +114,14 @@ static int need_retouch_dir_times;
99650e0d 143 static int need_retouch_dir_perms;
ffc18846 144 static const char *solo_file = NULL;
f90a882e
WD
145
146+#ifdef SUPPORT_FLAGS
7d006b5b
WD
147+#define FF_PTR(p) F_FFLAGS(p)
148+#define FF_STAT(s) s.st_flags
f90a882e 149+#else
7d006b5b
WD
150+#define FF_PTR(p) 0
151+#define FF_STAT(s) 0
f90a882e
WD
152+#endif
153+
d16b5fd6 154 /* For calling delete_item() and delete_dir_contents(). */
87d0091c 155 #define DEL_RECURSE (1<<1) /* recurse */
d16b5fd6 156 #define DEL_DIR_IS_EMPTY (1<<2) /* internal delete_FUNCTIONS use only */
ccdb48f6 157@@ -129,7 +137,6 @@ enum delret {
87d0091c
WD
158 /* Forward declaration for delete_item(). */
159 static enum delret delete_dir_contents(char *fname, int flags);
f90a882e 160
87d0091c
WD
161-
162 static int is_backup_file(char *fn)
163 {
164 int k = strlen(fn) - backup_suffix_len;
ccdb48f6 165@@ -142,17 +149,20 @@ static int is_backup_file(char *fn)
f813befd 166 * Note that fbuf must point to a MAXPATHLEN buffer if the mode indicates it's
f90a882e
WD
167 * a directory! (The buffer is used for recursion, but returned unchanged.)
168 */
f813befd
WD
169-static enum delret delete_item(char *fbuf, int mode, char *replace, int flags)
170+static enum delret delete_item(char *fbuf, int mode, uint32 fileflags, char *replace, int flags)
f90a882e 171 {
87d0091c
WD
172 enum delret ret;
173 char *what;
174 int ok;
175
176 if (verbose > 2) {
177- rprintf(FINFO, "delete_item(%s) mode=%o flags=%d\n",
f813befd 178- fbuf, mode, flags);
87d0091c 179+ rprintf(FINFO, "delete_item(%s) mode=%o fileflags=%o flags=%d\n",
f813befd 180+ fbuf, mode, fileflags, flags);
87d0091c
WD
181 }
182
31611b92 183+#ifdef SUPPORT_FLAGS
f813befd 184+ make_mutable(fbuf, mode, fileflags);
31611b92 185+#endif
d16b5fd6
WD
186 if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
187 ignore_perishable = 1;
188 /* If DEL_RECURSE is not set, this just reports emptiness. */
ccdb48f6 189@@ -264,7 +274,7 @@ static enum delret delete_dir_contents(c
d16b5fd6
WD
190 if (S_ISDIR(fp->mode)
191 && delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
192 ret = DR_NOT_EMPTY;
193- if (delete_item(fname, fp->mode, NULL, flags) != DR_SUCCESS)
7d006b5b 194+ if (delete_item(fname, fp->mode, FF_PTR(fp), NULL, flags) != DR_SUCCESS)
d16b5fd6 195 ret = DR_NOT_EMPTY;
f90a882e 196 }
d16b5fd6 197
ccdb48f6 198@@ -320,8 +330,9 @@ static int remember_delete(struct file_s
ee76479d
WD
199
200 while (1) {
201 len = snprintf(deldelay_buf + deldelay_cnt,
202- deldelay_size - deldelay_cnt,
203- "%x %s%c", (int)file->mode, fname, '\0');
204+ deldelay_size - deldelay_cnt, "%x %x %s%c",
7d006b5b 205+ (int)file->mode, (int)FF_PTR(file),
ee76479d
WD
206+ fname, '\0');
207 if ((deldelay_cnt += len) <= deldelay_size)
208 break;
209 if (deldelay_fd < 0 && !start_delete_delay_temp())
ccdb48f6 210@@ -334,7 +345,7 @@ static int remember_delete(struct file_s
ee76479d
WD
211 return 1;
212 }
213
214-static int read_delay_line(char *buf)
215+static int read_delay_line(char *buf, int *fileflags_p)
216 {
217 static int read_pos = 0;
218 int j, len, mode;
2ad6fab3 219@@ -376,12 +387,12 @@ static int read_delay_line(char *buf)
ee76479d
WD
220
221 bp = deldelay_buf + read_pos;
222
223- if (sscanf(bp, "%x ", &mode) != 1) {
224+ if (sscanf(bp, "%x %x ", &mode, fileflags_p) != 2) {
225 invalid_data:
226 rprintf(FERROR, "ERROR: invalid data in delete-delay file.\n");
227 return -1;
2ad6fab3
WD
228 }
229- past_space = strchr(bp, ' ') + 1;
230+ past_space = strchr(strchr(bp, ' ') + 1, ' ') + 1;
231 len = j - read_pos - (past_space - bp) + 1; /* count the '\0' */
232 read_pos = j + 1;
233
ccdb48f6 234@@ -399,15 +410,15 @@ static int read_delay_line(char *buf)
ee76479d
WD
235
236 static void do_delayed_deletions(char *delbuf)
237 {
238- int mode;
239+ int mode, fileflags;
240
241 if (deldelay_fd >= 0) {
242 if (deldelay_cnt && !flush_delete_delay())
243 return;
244 lseek(deldelay_fd, 0, 0);
245 }
246- while ((mode = read_delay_line(delbuf)) >= 0)
247- delete_item(delbuf, mode, NULL, DEL_RECURSE);
248+ while ((mode = read_delay_line(delbuf, &fileflags)) >= 0)
249+ delete_item(delbuf, mode, fileflags, NULL, DEL_RECURSE);
250 if (deldelay_fd >= 0)
251 close(deldelay_fd);
252 }
ccdb48f6 253@@ -474,7 +485,7 @@ static void delete_in_dir(char *fbuf, st
ee76479d
WD
254 if (!remember_delete(fp, delbuf))
255 break;
256 } else
f813befd 257- delete_item(delbuf, fp->mode, NULL, DEL_RECURSE);
7d006b5b 258+ delete_item(delbuf, fp->mode, FF_PTR(fp), NULL, DEL_RECURSE);
f90a882e
WD
259 }
260 }
261
7c4c2959 262@@ -1263,7 +1274,7 @@ static void recv_generator(char *fname,
2ad6fab3
WD
263 * full later (right before we handle its contents). */
264 if (statret == 0
265 && (S_ISDIR(sx.st.st_mode)
266- || delete_item(fname, sx.st.st_mode, "directory", del_opts) != 0))
267+ || delete_item(fname, sx.st.st_mode, FF_STAT(sx.st), "directory", del_opts) != 0))
268 goto cleanup; /* Any errors get reported later. */
269 if (do_mkdir(fname, file->mode & 0700) == 0)
270 file->flags |= FLAG_DIR_CREATED;
7c4c2959 271@@ -1275,7 +1286,7 @@ static void recv_generator(char *fname,
0461b869 272 * we need to delete it. If it doesn't exist, then
f90a882e 273 * (perhaps recursively) create it. */
ffc18846
WD
274 if (statret == 0 && !S_ISDIR(sx.st.st_mode)) {
275- if (delete_item(fname, sx.st.st_mode, "directory", del_opts) != 0)
7d006b5b 276+ if (delete_item(fname, sx.st.st_mode, FF_STAT(sx.st), "directory", del_opts) != 0)
99650e0d 277 goto skipping_dir_contents;
f90a882e
WD
278 statret = -1;
279 }
7c4c2959 280@@ -1404,7 +1415,7 @@ static void recv_generator(char *fname,
f90a882e
WD
281 }
282 /* Not the right symlink (or not a symlink), so
283 * delete it. */
ffc18846 284- if (delete_item(fname, sx.st.st_mode, "symlink", del_opts) != 0)
7d006b5b 285+ if (delete_item(fname, sx.st.st_mode, FF_STAT(sx.st), "symlink", del_opts) != 0)
ffc18846 286 goto cleanup;
05031682 287 } else if (basis_dir[0] != NULL) {
ffc18846 288 int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
7c4c2959 289@@ -1483,7 +1494,7 @@ static void recv_generator(char *fname,
05031682 290 goto return_with_success;
ffc18846 291 goto cleanup;
05031682 292 }
ffc18846 293- if (delete_item(fname, sx.st.st_mode, t, del_opts) != 0)
7d006b5b 294+ if (delete_item(fname, sx.st.st_mode, FF_STAT(sx.st), t, del_opts) != 0)
ffc18846 295 goto cleanup;
05031682 296 } else if (basis_dir[0] != NULL) {
ffc18846 297 int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &sx,
7c4c2959 298@@ -1574,7 +1585,7 @@ static void recv_generator(char *fname,
f90a882e
WD
299 fnamecmp_type = FNAMECMP_FNAME;
300
ffc18846
WD
301 if (statret == 0 && !S_ISREG(sx.st.st_mode)) {
302- if (delete_item(fname, sx.st.st_mode, "regular file", del_opts) != 0)
7d006b5b 303+ if (delete_item(fname, sx.st.st_mode, FF_STAT(sx.st), "regular file", del_opts) != 0)
ffc18846 304 goto cleanup;
f90a882e
WD
305 statret = -1;
306 stat_errno = ENOENT;
31611b92
WD
307--- old/options.c
308+++ new/options.c
52e09c4e 309@@ -51,6 +51,7 @@ int preserve_hard_links = 0;
ffc18846 310 int preserve_acls = 0;
5795bf59 311 int preserve_xattrs = 0;
31611b92 312 int preserve_perms = 0;
5e3c6c93 313+int preserve_fileflags = 0;
31611b92
WD
314 int preserve_executability = 0;
315 int preserve_devices = 0;
316 int preserve_specials = 0;
52e09c4e 317@@ -221,6 +222,7 @@ static void print_rsync_version(enum log
31611b92 318 char const *links = "no ";
58b399b9 319 char const *iconv = "no ";
31611b92 320 char const *ipv6 = "no ";
8871c12a 321+ char const *fileflags = "no ";
31611b92
WD
322 STRUCT_STAT *dumstat;
323
ac2da598 324 #if SUBPROTOCOL_VERSION != 0
52e09c4e 325@@ -250,6 +252,9 @@ static void print_rsync_version(enum log
58b399b9
WD
326 #ifdef ICONV_OPTION
327 iconv = "";
31611b92 328 #endif
31611b92 329+#ifdef SUPPORT_FLAGS
5e3c6c93 330+ fileflags = "";
31611b92 331+#endif
ac2da598
WD
332
333 rprintf(f, "%s version %s protocol version %d%s\n",
334 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION, subprotocol);
52e09c4e 335@@ -263,8 +268,8 @@ static void print_rsync_version(enum log
5e3c6c93
WD
336 (int)(sizeof (int64) * 8));
337 rprintf(f, " %ssocketpairs, %shardlinks, %ssymlinks, %sIPv6, batchfiles, %sinplace,\n",
338 got_socketpair, hardlinks, links, ipv6, have_inplace);
58b399b9
WD
339- rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv\n",
340- have_inplace, acls, xattrs, iconv);
341+ rprintf(f, " %sappend, %sACLs, %sxattrs, %siconv, %sfile-flags\n",
342+ have_inplace, acls, xattrs, iconv, fileflags);
5e3c6c93 343
31611b92 344 #ifdef MAINTAINER_MODE
5e3c6c93 345 rprintf(f, "Panic Action: \"%s\"\n", get_panic_action());
52e09c4e 346@@ -331,6 +336,7 @@ void usage(enum logcode F)
31611b92
WD
347 rprintf(F," -K, --keep-dirlinks treat symlinked dir on receiver as dir\n");
348 rprintf(F," -H, --hard-links preserve hard links\n");
349 rprintf(F," -p, --perms preserve permissions\n");
5e3c6c93 350+ rprintf(F," --fileflags preserve file-flags\n");
31611b92 351 rprintf(F," -E, --executability preserve the file's executability\n");
063cf77b 352 rprintf(F," --chmod=CHMOD affect file and/or directory permissions\n");
ffc18846 353 #ifdef SUPPORT_ACLS
ccdb48f6 354@@ -470,6 +476,8 @@ static struct poptOption long_options[]
31611b92
WD
355 {"perms", 'p', POPT_ARG_VAL, &preserve_perms, 1, 0, 0 },
356 {"no-perms", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
357 {"no-p", 0, POPT_ARG_VAL, &preserve_perms, 0, 0, 0 },
5e3c6c93
WD
358+ {"fileflags", 0, POPT_ARG_VAL, &preserve_fileflags, 1, 0, 0 },
359+ {"no-fileflags", 0, POPT_ARG_VAL, &preserve_fileflags, 0, 0, 0 },
31611b92 360 {"executability", 'E', POPT_ARG_NONE, &preserve_executability, 0, 0, 0 },
ffc18846
WD
361 {"acls", 'A', POPT_ARG_NONE, 0, 'A', 0, 0 },
362 {"no-acls", 0, POPT_ARG_VAL, &preserve_acls, 0, 0, 0 },
2ad6fab3 363@@ -1284,6 +1292,15 @@ int parse_arguments(int *argc_p, const c
31611b92
WD
364 }
365 #endif
366
367+#ifndef SUPPORT_FLAGS
5e3c6c93 368+ if (preserve_fileflags) {
31611b92
WD
369+ snprintf(err_buf, sizeof err_buf,
370+ "file flags are not supported on this %s\n",
371+ am_server ? "server" : "client");
372+ return 0;
373+ }
374+#endif
375+
376 if (write_batch && read_batch) {
377 snprintf(err_buf, sizeof err_buf,
378 "--write-batch and --read-batch can not be used together\n");
7c4c2959 379@@ -1792,6 +1809,9 @@ void server_options(char **args, int *ar
31611b92
WD
380 if (xfer_dirs && !recurse && delete_mode && am_sender)
381 args[ac++] = "--no-r";
382
5e3c6c93 383+ if (preserve_fileflags)
31611b92
WD
384+ args[ac++] = "--flags";
385+
386 if (do_compression && def_compress_level != Z_DEFAULT_COMPRESSION) {
387 if (asprintf(&arg, "--compress-level=%d", def_compress_level) < 0)
388 goto oom;
389--- old/rsync.c
390+++ new/rsync.c
52e09c4e 391@@ -32,6 +32,7 @@ extern int dry_run;
ffc18846 392 extern int preserve_acls;
5795bf59 393 extern int preserve_xattrs;
31611b92 394 extern int preserve_perms;
5e3c6c93 395+extern int preserve_fileflags;
31611b92
WD
396 extern int preserve_executability;
397 extern int preserve_times;
9c25eef5 398 extern int am_root;
52e09c4e 399@@ -60,6 +61,16 @@ iconv_t ic_chck = (iconv_t)-1;
ccdb48f6
WD
400 iconv_t ic_send = (iconv_t)-1, ic_recv = (iconv_t)-1;
401 # endif
ffc18846 402
9fa103b8
WD
403+#ifdef SUPPORT_FLAGS
404+#ifndef UF_NOUNLINK
405+#define UF_NOUNLINK 0
406+#endif
407+#ifndef SF_NOUNLINK
408+#define SF_NOUNLINK 0
409+#endif
ffc18846 410+#define NOCHANGE_FLAGS (UF_IMMUTABLE|UF_APPEND|UF_NOUNLINK|SF_IMMUTABLE|SF_APPEND|SF_NOUNLINK)
9fa103b8 411+#endif
ffc18846 412+
58b399b9
WD
413 static const char *default_charset(void)
414 {
ccdb48f6 415 # if defined HAVE_LIBCHARSET_H && defined HAVE_LOCALE_CHARSET
52e09c4e 416@@ -337,6 +348,41 @@ mode_t dest_mode(mode_t flist_mode, mode
f90a882e
WD
417 return new_mode;
418 }
419
420+#ifdef SUPPORT_FLAGS
421+/* Set a file's st_flags. */
422+static int set_fileflags(const char *fname, uint32 fileflags)
423+{
424+ if (do_chflags(fname, fileflags) != 0) {
425+ rsyserr(FERROR, errno,
426+ "failed to set file flags on %s",
427+ full_fname(fname));
428+ return 0;
429+ }
430+
431+ return 1;
432+}
433+
434+/* Remove immutable flags from an object, so it can be altered/removed. */
2ad6fab3 435+void make_mutable(const char *fname, mode_t mode, uint32 fileflags)
f90a882e 436+{
5e3c6c93 437+ if (!preserve_fileflags && S_ISLNK(mode))
f90a882e
WD
438+ return;
439+
440+ if (fileflags & NOCHANGE_FLAGS)
441+ set_fileflags(fname, fileflags & ~NOCHANGE_FLAGS);
442+}
443+
444+/* Undo a prior make_mutable() call. */
2ad6fab3 445+void undo_make_mutable(const char *fname, mode_t mode, uint32 fileflags)
f90a882e 446+{
5e3c6c93 447+ if (!preserve_fileflags && S_ISLNK(mode))
f90a882e
WD
448+ return;
449+
450+ if (fileflags & NOCHANGE_FLAGS)
451+ set_fileflags(fname, fileflags);
452+}
453+#endif
454+
52e09c4e 455 int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
5795bf59 456 const char *fnamecmp, int flags)
f90a882e 457 {
52e09c4e 458@@ -468,6 +514,15 @@ int set_file_attrs(const char *fname, st
31611b92
WD
459 }
460 #endif
461
462+#ifdef SUPPORT_FLAGS
7d006b5b
WD
463+ if (preserve_fileflags && !S_ISLNK(sxp->st.st_mode)
464+ && sxp->st.st_flags != F_FFLAGS(file)) {
465+ if (!set_fileflags(fname, F_FFLAGS(file)))
31611b92 466+ return 0;
31611b92
WD
467+ updated = 1;
468+ }
469+#endif
470+
471 if (verbose > 1 && flags & ATTRS_REPORT) {
ff318e90
WD
472 if (updated)
473 rprintf(FCLIENT, "%s\n", fname);
52e09c4e 474@@ -527,6 +582,9 @@ void finish_transfer(const char *fname,
5795bf59 475 set_file_attrs(fnametmp, file, NULL, fnamecmp,
f90a882e 476 ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
31611b92
WD
477
478+#ifdef SUPPORT_FLAGS
7d006b5b 479+ make_mutable(fnametmp, file->mode, F_FFLAGS(file));
31611b92 480+#endif
f90a882e
WD
481 /* move tmp file over real file */
482 if (verbose > 2)
483 rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname);
52e09c4e 484@@ -541,6 +599,9 @@ void finish_transfer(const char *fname,
f90a882e
WD
485 }
486 if (ret == 0) {
487 /* The file was moved into place (not copied), so it's done. */
31611b92 488+#ifdef SUPPORT_FLAGS
7d006b5b 489+ undo_make_mutable(fname, file->mode, F_FFLAGS(file));
31611b92 490+#endif
f90a882e
WD
491 return;
492 }
493 /* The file was copied, so tweak the perms of the copied file. If it
31611b92
WD
494--- old/rsync.h
495+++ new/rsync.h
99650e0d 496@@ -57,6 +57,7 @@
caf38d8d 497 #define XMIT_RDEV_MINOR_8_pre30 (1<<11) /* protocols 28 - 29 */
9c25eef5
WD
498 #define XMIT_GROUP_NAME_FOLLOWS (1<<11) /* protocols 30 - now */
499 #define XMIT_HLINK_FIRST (1<<12) /* protocols 30 - now (HLINKED files only) */
500+#define XMIT_SAME_FLAGS (1<<14) /* protocols ?? - now */
31611b92
WD
501
502 /* These flags are used in the live flist data. */
503
6240d1e4 504@@ -435,6 +436,10 @@ typedef unsigned int size_t;
7e27b6c0 505 #endif
6e6f514c
WD
506 #endif
507
508+#ifdef HAVE_CHFLAGS
509+#define SUPPORT_FLAGS 1
510+#endif
511+
512 /* Find a variable that is either exactly 32-bits or longer.
513 * If some code depends on 32-bit truncation, it will need to
514 * take special action in a "#if SIZEOF_INT32 > 4" section. */
6240d1e4 515@@ -602,6 +607,7 @@ extern int file_extra_cnt;
9c25eef5 516 extern int inc_recurse;
898a2112
WD
517 extern int uid_ndx;
518 extern int gid_ndx;
519+extern int fileflags_ndx;
520 extern int acls_ndx;
521 extern int xattrs_ndx;
fdf967c7 522
6240d1e4 523@@ -639,6 +645,7 @@ extern int xattrs_ndx;
1aa236e1 524 /* When the associated option is on, all entries will have these present: */
898a2112
WD
525 #define F_OWNER(f) REQ_EXTRA(f, uid_ndx)->unum
526 #define F_GROUP(f) REQ_EXTRA(f, gid_ndx)->unum
527+#define F_FFLAGS(f) REQ_EXTRA(f, fileflags_ndx)->unum
528 #define F_ACL(f) REQ_EXTRA(f, acls_ndx)->num
529 #define F_XATTR(f) REQ_EXTRA(f, xattrs_ndx)->num
58b399b9 530 #define F_NDX(f) REQ_EXTRA(f, ic_ndx)->num
31611b92
WD
531--- old/rsync.yo
532+++ new/rsync.yo
2ad6fab3 533@@ -338,6 +338,7 @@ to the detailed description below for a
1ed0b5c9
WD
534 -K, --keep-dirlinks treat symlinked dir on receiver as dir
535 -H, --hard-links preserve hard links
31611b92 536 -p, --perms preserve permissions
31611b92 537+ --flags preserve file flags
1ed0b5c9 538 -E, --executability preserve executability
063cf77b 539 --chmod=CHMOD affect file and/or directory permissions
ffc18846 540 -A, --acls preserve ACLs (implies -p)
2ad6fab3 541@@ -539,7 +540,9 @@ specified, in which case bf(-r) is not i
31611b92
WD
542
543 Note that bf(-a) bf(does not preserve hardlinks), because
544 finding multiply-linked files is expensive. You must separately
545-specify bf(-H).
546+specify bf(-H). Note also that for compatibility, bf(-a)
547+currently bf(does not include --flags) (see there) to include preserving
548+change file flags (if supported by the OS).
549
550 dit(--no-OPTION) You may turn off one or more implied options by prefixing
551 the option name with "no-". Not all options may be prefixed with a "no-":
2ad6fab3 552@@ -900,6 +903,13 @@ super-user copies all namespaces except
c8a8b4a7
WD
553 the user.* namespace. To be able to backup and restore non-user namespaces as
554 a normal user, see the bf(--fake-super) option.
31611b92
WD
555
556+dit(bf(--flags)) This option causes rsync to update the change file flags
f90a882e 557+to be the same as the source file, if your OS supports the bf(chflags)(2)
31611b92
WD
558+system call. In any case, an attempt is made to remove flags that would
559+prevent a file to be altered. Some flags can only be altered by the
560+super-user and can only be unset below a certain secure-level (usually
561+single-user mode).
562+
563 dit(bf(--chmod)) This option tells rsync to apply one or more
564 comma-separated "chmod" strings to the permission of the files in the
565 transfer. The resulting value is treated as though it was the permissions
566--- old/syscall.c
567+++ new/syscall.c
7c4c2959 568@@ -173,6 +173,15 @@ int do_chmod(const char *path, mode_t mo
31611b92
WD
569 }
570 #endif
571
572+#ifdef SUPPORT_FLAGS
573+int do_chflags(const char *path, u_long flags)
574+{
575+ if (dry_run) return 0;
576+ RETURN_ERROR_IF_RO_OR_LO;
577+ return chflags(path, flags);
578+}
579+#endif
580+
581 int do_rename(const char *fname1, const char *fname2)
582 {
583 if (dry_run) return 0;