7a362152efdd016dffb0d5761b9320679375d244
[rsync/rsync-patches.git] / flags.diff
1 This patch provides --flags, which preserves the st_flags 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/flags.diff
7     ./prepare-source
8     ./configure
9     make
10
11 --- old/configure.in
12 +++ new/configure.in
13 @@ -527,7 +527,7 @@ AC_CHECK_FUNCS(waitpid wait4 getcwd strd
14      memmove lchown vsnprintf snprintf vasprintf asprintf setsid glob strpbrk \
15      strlcat strlcpy strtol mallinfo getgroups setgroups geteuid getegid \
16      setlocale setmode open64 lseek64 mkstemp64 mtrace va_copy __va_copy \
17 -    strerror putenv iconv_open locale_charset nl_langinfo \
18 +    chflags strerror putenv iconv_open locale_charset nl_langinfo \
19      sigaction sigprocmask)
20  
21  AC_CHECK_FUNCS(getpgrp tcgetpgrp)
22 --- old/flist.c
23 +++ new/flist.c
24 @@ -44,6 +44,7 @@ extern int preserve_links;
25  extern int preserve_hard_links;
26  extern int preserve_devices;
27  extern int preserve_specials;
28 +extern int preserve_flags;
29  extern int preserve_uid;
30  extern int preserve_gid;
31  extern int relative_paths;
32 @@ -305,6 +306,9 @@ static void send_file_entry(struct file_
33         unsigned short flags;
34         static time_t modtime;
35         static mode_t mode;
36 +#ifdef SUPPORT_FLAGS
37 +       static uint32 fileflags;
38 +#endif
39         static int64 dev;
40         static dev_t rdev;
41         static uint32 rdev_major;
42 @@ -335,6 +339,12 @@ static void send_file_entry(struct file_
43                 flags |= XMIT_SAME_MODE;
44         else
45                 mode = file->mode;
46 +#ifdef SUPPORT_FLAGS
47 +       if (file->fileflags == fileflags)
48 +               flags |= XMIT_SAME_FLAGS;
49 +       else
50 +               fileflags = file->fileflags;
51 +#endif
52         if ((preserve_devices && IS_DEVICE(mode))
53          || (preserve_specials && IS_SPECIAL(mode))) {
54                 if (protocol_version < 28) {
55 @@ -418,6 +428,10 @@ static void send_file_entry(struct file_
56                 write_int(f, modtime);
57         if (!(flags & XMIT_SAME_MODE))
58                 write_int(f, to_wire_mode(mode));
59 +#ifdef SUPPORT_FLAGS
60 +       if (preserve_flags && !(flags & XMIT_SAME_FLAGS))
61 +               write_int(f, (int)fileflags);
62 +#endif
63         if (preserve_uid && !(flags & XMIT_SAME_UID)) {
64                 if (!numeric_ids)
65                         add_uid(uid);
66 @@ -485,6 +499,9 @@ static struct file_struct *receive_file_
67  {
68         static time_t modtime;
69         static mode_t mode;
70 +#ifdef SUPPORT_FLAGS
71 +       static uint32 fileflags;
72 +#endif
73         static int64 dev;
74         static dev_t rdev;
75         static uint32 rdev_major;
76 @@ -558,9 +575,12 @@ static struct file_struct *receive_file_
77                 modtime = (time_t)read_int(f);
78         if (!(flags & XMIT_SAME_MODE))
79                 mode = from_wire_mode(read_int(f));
80 -
81         if (chmod_modes && !S_ISLNK(mode))
82                 mode = tweak_mode(mode, chmod_modes);
83 +#ifdef SUPPORT_FLAGS
84 +       if (preserve_flags && !(flags & XMIT_SAME_FLAGS))
85 +               fileflags = (uint32)read_int(f);
86 +#endif
87  
88         if (preserve_uid && !(flags & XMIT_SAME_UID))
89                 uid = (uid_t)read_int(f);
90 @@ -611,6 +631,9 @@ static struct file_struct *receive_file_
91         file->modtime = modtime;
92         file->length = file_length;
93         file->mode = mode;
94 +#ifdef SUPPORT_FLAGS
95 +       file->fileflags = fileflags;
96 +#endif
97         file->uid = uid;
98         file->gid = gid;
99  
100 @@ -871,6 +894,9 @@ struct file_struct *make_file(char *fnam
101         file->modtime = st.st_mtime;
102         file->length = st.st_size;
103         file->mode = st.st_mode;
104 +#ifdef SUPPORT_FLAGS
105 +       file->fileflags = st.st_flags;
106 +#endif
107         file->uid = st.st_uid;
108         file->gid = st.st_gid;
109  
110 --- old/generator.c
111 +++ new/generator.c
112 @@ -98,6 +98,12 @@ int non_perishable_cnt = 0;
113  
114  static int deletion_count = 0; /* used to implement --max-delete */
115  
116 +#ifdef SUPPORT_FLAGS
117 +#define FILEFLAGS(ff) ff
118 +#else
119 +#define FILEFLAGS(ff) 0
120 +#endif
121 +
122  /* For calling delete_item() and delete_dir_contents(). */
123  #define DEL_RECURSE            (1<<1) /* recurse */
124  #define DEL_DIR_IS_EMPTY       (1<<2) /* internal delete_FUNCTIONS use only */
125 @@ -113,7 +119,6 @@ enum delret {
126  /* Forward declaration for delete_item(). */
127  static enum delret delete_dir_contents(char *fname, int flags);
128  
129 -
130  static int is_backup_file(char *fn)
131  {
132         int k = strlen(fn) - backup_suffix_len;
133 @@ -126,17 +131,20 @@ static int is_backup_file(char *fn)
134   * Note that fname must point to a MAXPATHLEN buffer if the mode indicates it's
135   * a directory! (The buffer is used for recursion, but returned unchanged.)
136   */
137 -static enum delret delete_item(char *fname, int mode, char *replace, int flags)
138 +static enum delret delete_item(char *fname, int mode, uint32 fileflags, char *replace, int flags)
139  {
140         enum delret ret;
141         char *what;
142         int ok;
143  
144         if (verbose > 2) {
145 -               rprintf(FINFO, "delete_item(%s) mode=%o flags=%d\n",
146 -                       fname, mode, flags);
147 +               rprintf(FINFO, "delete_item(%s) mode=%o fileflags=%o flags=%d\n",
148 +                       fname, mode, fileflags, flags);
149         }
150  
151 +#ifdef SUPPORT_FLAGS
152 +       make_mutable(fname, mode, fileflags);
153 +#endif
154         if (S_ISDIR(mode) && !(flags & DEL_DIR_IS_EMPTY)) {
155                 ignore_perishable = 1;
156                 /* If DEL_RECURSE is not set, this just reports emptiness. */
157 @@ -248,7 +256,7 @@ static enum delret delete_dir_contents(c
158                 if (S_ISDIR(fp->mode)
159                  && delete_dir_contents(fname, flags | DEL_RECURSE) != DR_SUCCESS)
160                         ret = DR_NOT_EMPTY;
161 -               if (delete_item(fname, fp->mode, NULL, flags) != DR_SUCCESS)
162 +               if (delete_item(fname, fp->mode, FILEFLAGS(fp->fileflags), NULL, flags) != DR_SUCCESS)
163                         ret = DR_NOT_EMPTY;
164         }
165  
166 @@ -338,7 +346,7 @@ static void delete_in_dir(struct file_li
167                 }
168                 if (flist_find(flist, fp) < 0) {
169                         f_name(fp, delbuf);
170 -                       delete_item(delbuf, fp->mode, NULL, DEL_RECURSE);
171 +                       delete_item(delbuf, fp->mode, FILEFLAGS(fp->fileflags), NULL, DEL_RECURSE);
172                 }
173         }
174  
175 @@ -1036,7 +1044,7 @@ static void recv_generator(char *fname, 
176                  * we need to delete it.  If it doesn't exist, then
177                  * (perhaps recursively) create it. */
178                 if (statret == 0 && !S_ISDIR(st.st_mode)) {
179 -                       if (delete_item(fname, st.st_mode, "directory", del_opts) != 0)
180 +                       if (delete_item(fname, st.st_mode, FILEFLAGS(st.st_flags), "directory", del_opts) != 0)
181                                 return;
182                         statret = -1;
183                 }
184 @@ -1130,7 +1138,7 @@ static void recv_generator(char *fname, 
185                         }
186                         /* Not the right symlink (or not a symlink), so
187                          * delete it. */
188 -                       if (delete_item(fname, st.st_mode, "symlink", del_opts) != 0)
189 +                       if (delete_item(fname, st.st_mode, FILEFLAGS(st.st_flags), "symlink", del_opts) != 0)
190                                 return;
191                 } else if (basis_dir[0] != NULL) {
192                         int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &st,
193 @@ -1201,7 +1209,7 @@ static void recv_generator(char *fname, 
194                                         goto return_with_success;
195                                 return;
196                         }
197 -                       if (delete_item(fname, st.st_mode, t, del_opts) != 0)
198 +                       if (delete_item(fname, st.st_mode, FILEFLAGS(st.st_flags), t, del_opts) != 0)
199                                 return;
200                 } else if (basis_dir[0] != NULL) {
201                         int j = try_dests_non(file, fname, ndx, fnamecmpbuf, &st,
202 @@ -1287,7 +1295,7 @@ static void recv_generator(char *fname, 
203         fnamecmp_type = FNAMECMP_FNAME;
204  
205         if (statret == 0 && !S_ISREG(st.st_mode)) {
206 -               if (delete_item(fname, st.st_mode, "regular file", del_opts) != 0)
207 +               if (delete_item(fname, st.st_mode, FILEFLAGS(st.st_flags), "regular file", del_opts) != 0)
208                         return;
209                 statret = -1;
210                 stat_errno = ENOENT;
211 --- old/options.c
212 +++ new/options.c
213 @@ -48,6 +48,7 @@ int copy_links = 0;
214  int preserve_links = 0;
215  int preserve_hard_links = 0;
216  int preserve_perms = 0;
217 +int preserve_flags = 0;
218  int preserve_executability = 0;
219  int preserve_devices = 0;
220  int preserve_specials = 0;
221 @@ -201,6 +202,7 @@ static void print_rsync_version(enum log
222         char const *hardlinks = "no ";
223         char const *links = "no ";
224         char const *ipv6 = "no ";
225 +       char const *flags = "no ";
226         STRUCT_STAT *dumstat;
227  
228  #ifdef HAVE_SOCKETPAIR
229 @@ -223,6 +225,10 @@ static void print_rsync_version(enum log
230         ipv6 = "";
231  #endif
232  
233 +#ifdef SUPPORT_FLAGS
234 +       flags = "";
235 +#endif
236 +
237         rprintf(f, "%s  version %s  protocol version %d\n",
238                 RSYNC_NAME, RSYNC_VERSION, PROTOCOL_VERSION);
239         rprintf(f, "Copyright (C) 1996-2006 by Andrew Tridgell, Wayne Davison, and others.\n");
240 @@ -235,9 +241,9 @@ static void print_rsync_version(enum log
241         /* Note that this field may not have type ino_t.  It depends
242          * on the complicated interaction between largefile feature
243          * macros. */
244 -       rprintf(f, "              %sinplace, %sIPv6, "
245 +       rprintf(f, "              %sinplace, %sIPv6, %sfile flags, "
246                 "%d-bit system inums, %d-bit internal inums\n",
247 -               have_inplace, ipv6,
248 +               have_inplace, ipv6, flags,
249                 (int) (sizeof dumstat->st_ino * 8),
250                 (int) (sizeof (int64) * 8));
251  #ifdef MAINTAINER_MODE
252 @@ -304,6 +310,7 @@ void usage(enum logcode F)
253    rprintf(F," -K, --keep-dirlinks         treat symlinked dir on receiver as dir\n");
254    rprintf(F," -H, --hard-links            preserve hard links\n");
255    rprintf(F," -p, --perms                 preserve permissions\n");
256 +  rprintf(F,"     --flags                 preserve file flags\n");
257    rprintf(F," -E, --executability         preserve the file's executability\n");
258    rprintf(F,"     --chmod=CHMOD           affect file and/or directory permissions\n");
259    rprintf(F," -o, --owner                 preserve owner (super-user only)\n");
260 @@ -424,6 +431,8 @@ static struct poptOption long_options[] 
261    {"perms",           'p', POPT_ARG_VAL,    &preserve_perms, 1, 0, 0 },
262    {"no-perms",         0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
263    {"no-p",             0,  POPT_ARG_VAL,    &preserve_perms, 0, 0, 0 },
264 +  {"flags",            0,  POPT_ARG_VAL,    &preserve_flags, 1, 0, 0 },
265 +  {"no-flags",         0,  POPT_ARG_VAL,    &preserve_flags, 0, 0, 0 },
266    {"executability",   'E', POPT_ARG_NONE,   &preserve_executability, 0, 0, 0 },
267    {"times",           't', POPT_ARG_VAL,    &preserve_times, 1, 0, 0 },
268    {"no-times",         0,  POPT_ARG_VAL,    &preserve_times, 0, 0, 0 },
269 @@ -1128,6 +1137,15 @@ int parse_arguments(int *argc, const cha
270         }
271  #endif
272  
273 +#ifndef SUPPORT_FLAGS
274 +       if (preserve_flags) {
275 +               snprintf(err_buf, sizeof err_buf,
276 +                        "file flags are not supported on this %s\n",
277 +                        am_server ? "server" : "client");
278 +               return 0;
279 +       }
280 +#endif
281 +
282         if (write_batch && read_batch) {
283                 snprintf(err_buf, sizeof err_buf,
284                         "--write-batch and --read-batch can not be used together\n");
285 @@ -1581,6 +1599,9 @@ void server_options(char **args,int *arg
286         if (xfer_dirs && !recurse && delete_mode && am_sender)
287                 args[ac++] = "--no-r";
288  
289 +       if (preserve_flags)
290 +               args[ac++] = "--flags";
291 +
292         if (do_compression && def_compress_level != Z_DEFAULT_COMPRESSION) {
293                 if (asprintf(&arg, "--compress-level=%d", def_compress_level) < 0)
294                         goto oom;
295 --- old/rsync.c
296 +++ new/rsync.c
297 @@ -30,9 +30,12 @@
298  #include <langinfo.h>
299  #endif
300  
301 +#define NOCHANGE_FLAGS (UF_IMMUTABLE|UF_APPEND|UF_NOUNLINK|SF_IMMUTABLE|SF_APPEND|SF_NOUNLINK)
302 +
303  extern int verbose;
304  extern int dry_run;
305  extern int preserve_perms;
306 +extern int preserve_flags;
307  extern int preserve_executability;
308  extern int preserve_times;
309  extern int omit_dir_times;
310 @@ -123,6 +126,41 @@ mode_t dest_mode(mode_t flist_mode, mode
311         return new_mode;
312  }
313  
314 +#ifdef SUPPORT_FLAGS
315 +/* Set a file's st_flags. */
316 +static int set_fileflags(const char *fname, uint32 fileflags)
317 +{
318 +       if (do_chflags(fname, fileflags) != 0) {
319 +               rsyserr(FERROR, errno,
320 +                       "failed to set file flags on %s",
321 +                       full_fname(fname));
322 +               return 0;
323 +       }
324 +
325 +       return 1;
326 +}
327 +
328 +/* Remove immutable flags from an object, so it can be altered/removed. */
329 +void make_mutable(char *fname, mode_t mode, uint32 fileflags)
330 +{
331 +       if (!preserve_flags && S_ISLNK(mode))
332 +               return;
333 +
334 +       if (fileflags & NOCHANGE_FLAGS)
335 +               set_fileflags(fname, fileflags & ~NOCHANGE_FLAGS);
336 +}
337 +
338 +/* Undo a prior make_mutable() call. */
339 +void undo_make_mutable(char *fname, mode_t mode, uint32 fileflags)
340 +{
341 +       if (!preserve_flags && S_ISLNK(mode))
342 +               return;
343 +
344 +       if (fileflags & NOCHANGE_FLAGS)
345 +               set_fileflags(fname, fileflags);
346 +}
347 +#endif
348 +
349  int set_file_attrs(char *fname, struct file_struct *file, STRUCT_STAT *st,
350                    int flags)
351  {
352 @@ -221,6 +259,15 @@ int set_file_attrs(char *fname, struct f
353         }
354  #endif
355  
356 +#ifdef SUPPORT_FLAGS
357 +       if (preserve_flags && !S_ISLNK(st->st_mode)
358 +        && st->st_flags != file->fileflags) {
359 +               if (!set_fileflags(fname, file->fileflags))
360 +                       return 0;
361 +               updated = 1;
362 +       }
363 +#endif
364 +
365         if (verbose > 1 && flags & ATTRS_REPORT) {
366                 if (updated)
367                         rprintf(FCLIENT, "%s\n", fname);
368 @@ -268,6 +315,9 @@ void finish_transfer(char *fname, char *
369         set_file_attrs(fnametmp, file, NULL,
370                        ok_to_set_time ? 0 : ATTRS_SKIP_MTIME);
371  
372 +#ifdef SUPPORT_FLAGS
373 +       make_mutable(fnametmp, file->mode, file->fileflags);
374 +#endif
375         /* move tmp file over real file */
376         if (verbose > 2)
377                 rprintf(FINFO, "renaming %s to %s\n", fnametmp, fname);
378 @@ -282,6 +332,9 @@ void finish_transfer(char *fname, char *
379         }
380         if (ret == 0) {
381                 /* The file was moved into place (not copied), so it's done. */
382 +#ifdef SUPPORT_FLAGS
383 +               undo_make_mutable(fname, file->mode, file->fileflags);
384 +#endif
385                 return;
386         }
387         /* The file was copied, so tweak the perms of the copied file.  If it
388 --- old/rsync.h
389 +++ new/rsync.h
390 @@ -54,6 +54,7 @@
391  #define XMIT_HAS_IDEV_DATA (1<<9)
392  #define XMIT_SAME_DEV (1<<10)
393  #define XMIT_RDEV_MINOR_IS_SMALL (1<<11)
394 +#define XMIT_SAME_FLAGS (1<<12)
395  
396  /* These flags are used in the live flist data. */
397  
398 @@ -345,6 +346,10 @@ enum msgcode {
399  #define schar char
400  #endif
401  
402 +#ifdef HAVE_CHFLAGS
403 +#define SUPPORT_FLAGS 1
404 +#endif
405 +
406  /* Find a variable that is either exactly 32-bits or longer.
407   * If some code depends on 32-bit truncation, it will need to
408   * take special action in a "#if SIZEOF_INT32 > 4" section. */
409 @@ -528,6 +533,9 @@ struct file_struct {
410                 struct hlink *links;
411         } link_u;
412         time_t modtime;
413 +#ifdef SUPPORT_FLAGS
414 +       uint32 fileflags;
415 +#endif
416         uid_t uid;
417         gid_t gid;
418         mode_t mode;
419 --- old/rsync.yo
420 +++ new/rsync.yo
421 @@ -321,6 +321,7 @@ to the detailed description below for a 
422   -K, --keep-dirlinks         treat symlinked dir on receiver as dir
423   -H, --hard-links            preserve hard links
424   -p, --perms                 preserve permissions
425 +     --flags                 preserve file flags
426   -E, --executability         preserve executability
427       --chmod=CHMOD           affect file and/or directory permissions
428   -o, --owner                 preserve owner (super-user only)
429 @@ -509,7 +510,9 @@ specified, in which case bf(-r) is not i
430  
431  Note that bf(-a) bf(does not preserve hardlinks), because
432  finding multiply-linked files is expensive.  You must separately
433 -specify bf(-H).
434 +specify bf(-H).  Note also that for compatibility, bf(-a)
435 +currently bf(does not include --flags) (see there) to include preserving
436 +change file flags (if supported by the OS).
437  
438  dit(--no-OPTION) You may turn off one or more implied options by prefixing
439  the option name with "no-".  Not all options may be prefixed with a "no-":
440 @@ -804,6 +807,13 @@ quote(itemization(
441  
442  If bf(--perms) is enabled, this option is ignored.
443  
444 +dit(bf(--flags)) This option causes rsync to update the change file flags
445 +to be the same as the source file, if your OS supports the bf(chflags)(2)
446 +system call.  In any case, an attempt is made to remove flags that would
447 +prevent a file to be altered.  Some flags can only be altered by the
448 +super-user and can only be unset below a certain secure-level (usually
449 +single-user mode).
450 +
451  dit(bf(--chmod)) This option tells rsync to apply one or more
452  comma-separated "chmod" strings to the permission of the files in the
453  transfer.  The resulting value is treated as though it was the permissions
454 --- old/syscall.c
455 +++ new/syscall.c
456 @@ -152,6 +152,15 @@ int do_chmod(const char *path, mode_t mo
457  }
458  #endif
459  
460 +#ifdef SUPPORT_FLAGS
461 +int do_chflags(const char *path, u_long flags)
462 +{
463 +       if (dry_run) return 0;
464 +       RETURN_ERROR_IF_RO_OR_LO;
465 +       return chflags(path, flags);
466 +}
467 +#endif
468 +
469  int do_rename(const char *fname1, const char *fname2)
470  {
471         if (dry_run) return 0;