Annotation of embedaddon/rsync/patches/filter-attribute-mods.diff, revision 1.1.1.1
1.1 misho 1: From: Matt McCutchen <matt@mattmccutchen.net>
2:
3: Implement the "m", "o", "g" include modifiers to tweak the permissions,
4: owner, or group of matching files.
5:
6: To use this patch, run these commands for a successful build:
7:
8: patch -p1 <patches/filter-attribute-mods.diff
9: ./configure (optional if already run)
10: make
11:
12: based-on: e94bad1c156fc3910f24e2b3b71a81b0b0bdeb70
13: diff --git a/exclude.c b/exclude.c
14: --- a/exclude.c
15: +++ b/exclude.c
16: @@ -45,10 +45,13 @@ filter_rule_list filter_list = { .debug_type = "" };
17: filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };
18: filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
19:
20: +filter_rule *last_hit_filter_rule;
21: +
22: int saw_xattr_filter = 0;
23:
24: -/* Need room enough for ":MODS " prefix plus some room to grow. */
25: -#define MAX_RULE_PREFIX (16)
26: +/* Need room enough for ":MODS " prefix, which can now include
27: + * chmod/user/group values. */
28: +#define MAX_RULE_PREFIX (256)
29:
30: #define SLASH_WILD3_SUFFIX "/***"
31:
32: @@ -127,8 +130,27 @@ static void teardown_mergelist(filter_rule *ex)
33: mergelist_cnt--;
34: }
35:
36: +static struct filter_chmod_struct *ref_filter_chmod(struct filter_chmod_struct *chmod)
37: +{
38: + chmod->ref_cnt++;
39: + assert(chmod->ref_cnt != 0); /* Catch overflow. */
40: + return chmod;
41: +}
42: +
43: +static void unref_filter_chmod(struct filter_chmod_struct *chmod)
44: +{
45: + chmod->ref_cnt--;
46: + if (chmod->ref_cnt == 0) {
47: + free(chmod->modestr);
48: + free_chmod_mode(chmod->modes);
49: + free(chmod);
50: + }
51: +}
52: +
53: static void free_filter(filter_rule *ex)
54: {
55: + if (ex->rflags & FILTRULE_CHMOD)
56: + unref_filter_chmod(ex->chmod);
57: if (ex->rflags & FILTRULE_PERDIR_MERGE)
58: teardown_mergelist(ex);
59: free(ex->pattern);
60: @@ -722,7 +744,9 @@ static void report_filter_result(enum logcode code, char const *name,
61:
62: /* This function is used to check if a file should be included/excluded
63: * from the list of files based on its name and type etc. The value of
64: - * filter_level is set to either SERVER_FILTERS or ALL_FILTERS. */
65: + * filter_level is set to either SERVER_FILTERS or ALL_FILTERS.
66: + * "last_hit_filter_rule" will be set to the operative filter, or NULL if none. */
67: +
68: int name_is_excluded(const char *fname, int name_flags, int filter_level)
69: {
70: if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, name_flags) < 0) {
71: @@ -731,6 +755,9 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
72: return 1;
73: }
74:
75: + /* Don't leave a daemon include in last_hit_filter_rule. */
76: + last_hit_filter_rule = NULL;
77: +
78: if (filter_level != ALL_FILTERS)
79: return 0;
80:
81: @@ -741,7 +768,8 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
82: }
83:
84: /* Return -1 if file "name" is defined to be excluded by the specified
85: - * exclude list, 1 if it is included, and 0 if it was not matched. */
86: + * exclude list, 1 if it is included, and 0 if it was not matched.
87: + * Sets last_hit_filter_rule to the filter that was hit, or NULL if none. */
88: int check_filter(filter_rule_list *listp, enum logcode code,
89: const char *name, int name_flags)
90: {
91: @@ -764,10 +792,12 @@ int check_filter(filter_rule_list *listp, enum logcode code,
92: }
93: if (rule_matches(name, ent, name_flags)) {
94: report_filter_result(code, name, ent, name_flags, listp->debug_type);
95: + last_hit_filter_rule = ent;
96: return ent->rflags & FILTRULE_INCLUDE ? 1 : -1;
97: }
98: }
99:
100: + last_hit_filter_rule = NULL;
101: return 0;
102: }
103:
104: @@ -784,9 +814,45 @@ static const uchar *rule_strcmp(const uchar *str, const char *rule, int rule_len
105: return NULL;
106: }
107:
108: +static char *grab_paren_value(const uchar **s_ptr)
109: +{
110: + const uchar *start, *end;
111: + int val_sz;
112: + char *val;
113: +
114: + if ((*s_ptr)[1] != '(')
115: + return NULL;
116: + start = (*s_ptr) + 2;
117: +
118: + for (end = start; *end != ')'; end++)
119: + if (!*end || *end == ' ' || *end == '_')
120: + return NULL;
121: +
122: + val_sz = end - start + 1;
123: + val = new_array(char, val_sz);
124: + strlcpy(val, (const char *)start, val_sz);
125: + *s_ptr = end; /* remember ++s in parse_rule_tok */
126: + return val;
127: +}
128: +
129: +static struct filter_chmod_struct *make_chmod_struct(char *modestr)
130: +{
131: + struct filter_chmod_struct *chmod;
132: + struct chmod_mode_struct *modes = NULL;
133: +
134: + if (!parse_chmod(modestr, &modes))
135: + return NULL;
136: +
137: + chmod = new(struct filter_chmod_struct);
138: + chmod->ref_cnt = 1;
139: + chmod->modestr = modestr;
140: + chmod->modes = modes;
141: + return chmod;
142: +}
143: +
144: #define FILTRULES_FROM_CONTAINER (FILTRULE_ABS_PATH | FILTRULE_INCLUDE \
145: | FILTRULE_DIRECTORY | FILTRULE_NEGATE \
146: - | FILTRULE_PERISHABLE)
147: + | FILTRULE_PERISHABLE | FILTRULES_ATTRS)
148:
149: /* Gets the next include/exclude rule from *rulestr_ptr and advances
150: * *rulestr_ptr to point beyond it. Stores the pattern's start (within
151: @@ -801,6 +867,7 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
152: const char **pat_ptr, unsigned int *pat_len_ptr)
153: {
154: const uchar *s = (const uchar *)*rulestr_ptr;
155: + char *val;
156: filter_rule *rule;
157: unsigned int len;
158:
159: @@ -819,6 +886,12 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
160: /* Inherit from the template. Don't inherit FILTRULES_SIDES; we check
161: * that later. */
162: rule->rflags = template->rflags & FILTRULES_FROM_CONTAINER;
163: + if (template->rflags & FILTRULE_CHMOD)
164: + rule->chmod = ref_filter_chmod(template->chmod);
165: + if (template->rflags & FILTRULE_FORCE_OWNER)
166: + rule->force_uid = template->force_uid;
167: + if (template->rflags & FILTRULE_FORCE_GROUP)
168: + rule->force_gid = template->force_gid;
169:
170: /* Figure out what kind of a filter rule "s" is pointing at. Note
171: * that if FILTRULE_NO_PREFIXES is set, the rule is either an include
172: @@ -964,11 +1037,63 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
173: goto invalid;
174: rule->rflags |= FILTRULE_EXCLUDE_SELF;
175: break;
176: + case 'g': {
177: + gid_t gid;
178: +
179: + if (!(val = grab_paren_value(&s)))
180: + goto invalid;
181: + if (group_to_gid(val, &gid, True)) {
182: + rule->rflags |= FILTRULE_FORCE_GROUP;
183: + rule->force_gid = gid;
184: + } else {
185: + rprintf(FERROR,
186: + "unknown group '%s' in filter rule: %s\n",
187: + val, *rulestr_ptr);
188: + exit_cleanup(RERR_SYNTAX);
189: + }
190: + free(val);
191: + break;
192: + }
193: + case 'm': {
194: + struct filter_chmod_struct *chmod;
195: +
196: + if (!(val = grab_paren_value(&s)))
197: + goto invalid;
198: + if ((chmod = make_chmod_struct(val))) {
199: + if (rule->rflags & FILTRULE_CHMOD)
200: + unref_filter_chmod(rule->chmod);
201: + rule->rflags |= FILTRULE_CHMOD;
202: + rule->chmod = chmod;
203: + } else {
204: + rprintf(FERROR,
205: + "unparseable chmod string '%s' in filter rule: %s\n",
206: + val, *rulestr_ptr);
207: + exit_cleanup(RERR_SYNTAX);
208: + }
209: + break;
210: + }
211: case 'n':
212: if (!(rule->rflags & FILTRULE_MERGE_FILE))
213: goto invalid;
214: rule->rflags |= FILTRULE_NO_INHERIT;
215: break;
216: + case 'o': {
217: + uid_t uid;
218: +
219: + if (!(val = grab_paren_value(&s)))
220: + goto invalid;
221: + if (user_to_uid(val, &uid, True)) {
222: + rule->rflags |= FILTRULE_FORCE_OWNER;
223: + rule->force_uid = uid;
224: + } else {
225: + rprintf(FERROR,
226: + "unknown user '%s' in filter rule: %s\n",
227: + val, *rulestr_ptr);
228: + exit_cleanup(RERR_SYNTAX);
229: + }
230: + free(val);
231: + break;
232: + }
233: case 'p':
234: rule->rflags |= FILTRULE_PERISHABLE;
235: break;
236: @@ -1282,6 +1407,23 @@ char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
237: else if (am_sender)
238: return NULL;
239: }
240: + if (rule->rflags & FILTRULES_ATTRS) {
241: + if (!for_xfer || protocol_version >= 31) {
242: + if (rule->rflags & FILTRULE_CHMOD)
243: + if (!snappendf(&op, (buf + sizeof buf) - op,
244: + "m(%s)", rule->chmod->modestr))
245: + return NULL;
246: + if (rule->rflags & FILTRULE_FORCE_OWNER)
247: + if (!snappendf(&op, (buf + sizeof buf) - op,
248: + "o(%u)", (unsigned)rule->force_uid))
249: + return NULL;
250: + if (rule->rflags & FILTRULE_FORCE_GROUP)
251: + if (!snappendf(&op, (buf + sizeof buf) - op,
252: + "g(%u)", (unsigned)rule->force_gid))
253: + return NULL;
254: + } else if (!am_sender)
255: + return NULL;
256: + }
257: if (op - buf > legal_len)
258: return NULL;
259: if (legal_len)
260: diff --git a/flist.c b/flist.c
261: --- a/flist.c
262: +++ b/flist.c
263: @@ -84,6 +84,7 @@ extern struct chmod_mode_struct *chmod_modes;
264:
265: extern filter_rule_list filter_list;
266: extern filter_rule_list daemon_filter_list;
267: +extern filter_rule *last_hit_filter_rule;
268:
269: #ifdef ICONV_OPTION
270: extern int filesfrom_convert;
271: @@ -1229,7 +1230,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
272: } else if (readlink_stat(thisname, &st, linkname) != 0) {
273: int save_errno = errno;
274: /* See if file is excluded before reporting an error. */
275: - if (filter_level != NO_FILTERS
276: + if (filter_level != NO_FILTERS && filter_level != ALL_FILTERS_NO_EXCLUDE
277: && (is_excluded(thisname, 0, filter_level)
278: || is_excluded(thisname, 1, filter_level))) {
279: if (ignore_perishable && save_errno != ENOENT)
280: @@ -1274,6 +1275,12 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
281:
282: if (filter_level == NO_FILTERS)
283: goto skip_filters;
284: + if (filter_level == ALL_FILTERS_NO_EXCLUDE) {
285: + /* Call only for the side effect of setting last_hit_filter_rule to
286: + * any operative include filter, which might affect attributes. */
287: + is_excluded(thisname, S_ISDIR(st.st_mode) != 0, ALL_FILTERS);
288: + goto skip_filters;
289: + }
290:
291: if (S_ISDIR(st.st_mode)) {
292: if (!xfer_dirs) {
293: @@ -1494,12 +1501,23 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
294: int flags, int filter_level)
295: {
296: struct file_struct *file;
297: + BOOL can_tweak_mode;
298:
299: file = make_file(fname, flist, stp, flags, filter_level);
300: if (!file)
301: return NULL;
302:
303: - if (chmod_modes && !S_ISLNK(file->mode) && file->mode)
304: + can_tweak_mode = !S_ISLNK(file->mode) && file->mode;
305: + if ((filter_level == ALL_FILTERS || filter_level == ALL_FILTERS_NO_EXCLUDE)
306: + && last_hit_filter_rule) {
307: + if ((last_hit_filter_rule->rflags & FILTRULE_CHMOD) && can_tweak_mode)
308: + file->mode = tweak_mode(file->mode, last_hit_filter_rule->chmod->modes);
309: + if ((last_hit_filter_rule->rflags & FILTRULE_FORCE_OWNER) && uid_ndx)
310: + F_OWNER(file) = last_hit_filter_rule->force_uid;
311: + if ((last_hit_filter_rule->rflags & FILTRULE_FORCE_GROUP) && gid_ndx)
312: + F_GROUP(file) = last_hit_filter_rule->force_gid;
313: + }
314: + if (chmod_modes && can_tweak_mode)
315: file->mode = tweak_mode(file->mode, chmod_modes);
316:
317: if (f >= 0) {
318: @@ -2399,7 +2417,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
319: struct file_struct *file;
320: file = send_file_name(f, flist, fbuf, &st,
321: FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags,
322: - NO_FILTERS);
323: + ALL_FILTERS_NO_EXCLUDE);
324: if (!file)
325: continue;
326: if (inc_recurse) {
327: @@ -2413,7 +2431,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
328: } else
329: send_if_directory(f, flist, file, fbuf, len, flags);
330: } else
331: - send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS);
332: + send_file_name(f, flist, fbuf, &st, flags, ALL_FILTERS_NO_EXCLUDE);
333: }
334:
335: if (reenable_multiplex >= 0)
336: diff --git a/rsync.1.md b/rsync.1.md
337: --- a/rsync.1.md
338: +++ b/rsync.1.md
339: @@ -1285,7 +1285,9 @@ your home directory (remove the '=' for that).
340: > --chmod=D2775,F664
341:
342: It is also legal to specify multiple `--chmod` options, as each additional
343: - option is just appended to the list of changes to make.
344: + option is just appended to the list of changes to make. To change
345: + permissions of files matching a pattern, use an include filter with the `m`
346: + modifier, which takes effect before any `--chmod` options.
347:
348: See the `--perms` and `--executability` options for how the resulting
349: permission value can be applied to the files in the transfer.
350: @@ -2636,6 +2638,10 @@ your home directory (remove the '=' for that).
351: "`--usermap=*:foo --groupmap=*:bar`", only easier. If your shell complains
352: about the wildcards, use `--protect-args` (`-s`).
353:
354: + To change ownership of files matching a pattern, use an include filter with
355: + a `o` or `g` modifier, which take effect before uid/gid mapping and
356: + therefore *can* be mixed with `--usermap` and `--groupmap`.
357: +
358: 0. `--timeout=SECONDS`
359:
360: This option allows you to set a maximum I/O timeout in seconds. If no data
361: @@ -3633,6 +3639,15 @@ The following modifiers are accepted after a "`+`" or "`-`":
362: rules that exclude things like "CVS" and "`*.o`" are marked as perishable,
363: and will not prevent a directory that was removed on the source from being
364: deleted on the destination.
365: +- An `m(CHMOD)` on an include rule tweaks the permissions of matching
366: + source files in the same way as `--chmod`. This happens before any tweaks
367: + requested via `--chmod` options.
368: +- An `o(USER)` on an include rule pretends that matching source files are
369: + owned by `USER` (a name or numeric uid). This happens before any uid mapping
370: + by name or `--usermap`.
371: +- A `g(GROUP)` on an include rule pretends that matching source files are
372: + owned by `GROUP` (a name or numeric gid). This happens before any gid
373: + mapping by name or `--groupmap`.
374: - An `x` indicates that a rule affects xattr names in xattr copy/delete
375: operations (and is thus ignored when matching file/dir names). If no
376: xattr-matching rules are specified, a default xattr filtering rule is used
377: @@ -3690,6 +3705,12 @@ The following modifiers are accepted after a merge or dir-merge rule:
378: rules in the file must not specify sides (via a modifier or a rule prefix
379: such as `hide`).
380:
381: +The attribute-affecting modifiers `m`, `o`, and `g` work only in client filters
382: +(not in daemon filters), and only the modifiers of the first matching rule are
383: +applied. As an example, assuming `--super` is enabled, the rule
384: +"`+o(root),g(root),m(go=) *~`" would ensure that all "backup"
385: +files belong to root and are not accessible to anyone else.
386: +
387: Per-directory rules are inherited in all subdirectories of the directory where
388: the merge-file was found unless the 'n' modifier was used. Each subdirectory's
389: rules are prefixed to the inherited per-directory rules from its parents, which
390: diff --git a/rsync.h b/rsync.h
391: --- a/rsync.h
392: +++ b/rsync.h
393: @@ -171,6 +171,9 @@
394: #define NO_FILTERS 0
395: #define SERVER_FILTERS 1
396: #define ALL_FILTERS 2
397: +/* Don't let the file be excluded, but check for a filter that might affect
398: + * its attributes via FILTRULES_ATTRS. */
399: +#define ALL_FILTERS_NO_EXCLUDE 3
400:
401: #define XFLG_FATAL_ERRORS (1<<0)
402: #define XFLG_OLD_PREFIXES (1<<1)
403: @@ -966,6 +969,8 @@ struct map_struct {
404: int status; /* first errno from read errors */
405: };
406:
407: +struct chmod_mode_struct;
408: +
409: #define NAME_IS_FILE (0) /* filter name as a file */
410: #define NAME_IS_DIR (1<<0) /* filter name as a dir */
411: #define NAME_IS_XATTR (1<<2) /* filter name as an xattr */
412: @@ -991,8 +996,18 @@ struct map_struct {
413: #define FILTRULE_CLEAR_LIST (1<<18)/* this item is the "!" token */
414: #define FILTRULE_PERISHABLE (1<<19)/* perishable if parent dir goes away */
415: #define FILTRULE_XATTR (1<<20)/* rule only applies to xattr names */
416: +#define FILTRULE_CHMOD (1<<21)/* chmod-tweak matching files */
417: +#define FILTRULE_FORCE_OWNER (1<<22)/* force owner of matching files */
418: +#define FILTRULE_FORCE_GROUP (1<<23)/* force group of matching files */
419:
420: #define FILTRULES_SIDES (FILTRULE_SENDER_SIDE | FILTRULE_RECEIVER_SIDE)
421: +#define FILTRULES_ATTRS (FILTRULE_CHMOD | FILTRULE_FORCE_OWNER | FILTRULE_FORCE_GROUP)
422: +
423: +struct filter_chmod_struct {
424: + unsigned int ref_cnt;
425: + char *modestr;
426: + struct chmod_mode_struct *modes;
427: +};
428:
429: typedef struct filter_struct {
430: struct filter_struct *next;
431: @@ -1002,6 +1017,11 @@ typedef struct filter_struct {
432: int slash_cnt;
433: struct filter_list_struct *mergelist;
434: } u;
435: + /* TODO: Use an "extras" mechanism to avoid
436: + * allocating this memory when we don't need it. */
437: + struct filter_chmod_struct *chmod;
438: + uid_t force_uid;
439: + gid_t force_gid;
440: } filter_rule;
441:
442: typedef struct filter_list_struct {
443: diff --git a/util.c b/util.c
444: --- a/util.c
445: +++ b/util.c
446: @@ -884,6 +884,25 @@ size_t stringjoin(char *dest, size_t destsize, ...)
447: return ret;
448: }
449:
450: +/* Append formatted text at *dest_ptr up to a maximum of sz (like snprintf).
451: + * On success, advance *dest_ptr and return True; on overflow, return False. */
452: +BOOL snappendf(char **dest_ptr, size_t sz, const char *format, ...)
453: +{
454: + va_list ap;
455: + size_t len;
456: +
457: + va_start(ap, format);
458: + len = vsnprintf(*dest_ptr, sz, format, ap);
459: + va_end(ap);
460: +
461: + if (len >= sz)
462: + return False;
463: + else {
464: + *dest_ptr += len;
465: + return True;
466: + }
467: +}
468: +
469: int count_dir_elements(const char *p)
470: {
471: int cnt = 0, new_component = 1;
472: diff -Nurp a/rsync.1 b/rsync.1
473: --- a/rsync.1
474: +++ b/rsync.1
475: @@ -1378,7 +1378,9 @@ Using octal mode numbers is also allowed
476: .RE
477: .IP
478: It is also legal to specify multiple \fB\-\-chmod\fP options, as each additional
479: -option is just appended to the list of changes to make.
480: +option is just appended to the list of changes to make. To change
481: +permissions of files matching a pattern, use an include filter with the \fBm\fP
482: +modifier, which takes effect before any \fB\-\-chmod\fP options.
483: .IP
484: See the \fB\-\-perms\fP and \fB\-\-executability\fP options for how the resulting
485: permission value can be applied to the files in the transfer.
486: @@ -2686,6 +2688,10 @@ USER is empty, a leading colon must be s
487: If you specify "\fB\-\-chown=foo:bar\fP", this is exactly the same as specifying
488: "\fB\-\-usermap=*:foo\ \-\-groupmap=*:bar\fP", only easier. If your shell complains
489: about the wildcards, use \fB\-\-protect-args\fP (\fB\-s\fP).
490: +.IP
491: +To change ownership of files matching a pattern, use an include filter with
492: +a \fBo\fP or \fBg\fP modifier, which take effect before uid/gid mapping and
493: +therefore \fIcan\fP be mixed with \fB\-\-usermap\fP and \fB\-\-groupmap\fP.
494: .IP "\fB\-\-timeout=SECONDS\fP"
495: This option allows you to set a maximum I/O timeout in seconds. If no data
496: is transferred for the specified time then rsync will exit. The default is
497: @@ -3704,6 +3710,18 @@ rules that exclude things like "CVS" and
498: and will not prevent a directory that was removed on the source from being
499: deleted on the destination.
500: .IP o
501: +An \fBm(CHMOD)\fP on an include rule tweaks the permissions of matching
502: +source files in the same way as \fB\-\-chmod\fP. This happens before any tweaks
503: +requested via \fB\-\-chmod\fP options.
504: +.IP o
505: +An \fBo(USER)\fP on an include rule pretends that matching source files are
506: +owned by \fBUSER\fP (a name or numeric uid). This happens before any uid mapping
507: +by name or \fB\-\-usermap\fP.
508: +.IP o
509: +A \fBg(GROUP)\fP on an include rule pretends that matching source files are
510: +owned by \fBGROUP\fP (a name or numeric gid). This happens before any gid
511: +mapping by name or \fB\-\-groupmap\fP.
512: +.IP o
513: An \fBx\fP indicates that a rule affects xattr names in xattr copy/delete
514: operations (and is thus ignored when matching file/dir names). If no
515: xattr-matching rules are specified, a default xattr filtering rule is used
516: @@ -3772,6 +3790,12 @@ specifies sides to affect (via the \fBs\
517: rules in the file must not specify sides (via a modifier or a rule prefix
518: such as \fBhide\fP).
519: .P
520: +The attribute-affecting modifiers \fBm\fP, \fBo\fP, and \fBg\fP work only in client filters
521: +(not in daemon filters), and only the modifiers of the first matching rule are
522: +applied. As an example, assuming \fB\-\-super\fP is enabled, the rule
523: +"\fB+o(root),g(root),m(go=)\ *~\fP" would ensure that all "backup"
524: +files belong to root and are not accessible to anyone else.
525: +.P
526: Per-directory rules are inherited in all subdirectories of the directory where
527: the merge-file was found unless the 'n' modifier was used. Each subdirectory's
528: rules are prefixed to the inherited per-directory rules from its parents, which
529: diff -Nurp a/rsync.1.html b/rsync.1.html
530: --- a/rsync.1.html
531: +++ b/rsync.1.html
532: @@ -1242,7 +1242,9 @@ consistent executability across all bits
533: </code></pre>
534: </blockquote>
535: <p>It is also legal to specify multiple <code>--chmod</code> options, as each additional
536: -option is just appended to the list of changes to make.</p>
537: +option is just appended to the list of changes to make. To change
538: +permissions of files matching a pattern, use an include filter with the <code>m</code>
539: +modifier, which takes effect before any <code>--chmod</code> options.</p>
540: <p>See the <code>--perms</code> and <code>--executability</code> options for how the resulting
541: permission value can be applied to the files in the transfer.</p>
542: </dd>
543: @@ -2490,6 +2492,9 @@ USER is empty, a leading colon must be s
544: <p>If you specify "<code>--chown=foo:bar</code>", this is exactly the same as specifying
545: "<code>--usermap=*:foo --groupmap=*:bar</code>", only easier. If your shell complains
546: about the wildcards, use <code>--protect-args</code> (<code>-s</code>).</p>
547: +<p>To change ownership of files matching a pattern, use an include filter with
548: +a <code>o</code> or <code>g</code> modifier, which take effect before uid/gid mapping and
549: +therefore <u>can</u> be mixed with <code>--usermap</code> and <code>--groupmap</code>.</p>
550: </dd>
551:
552: <dt><code>--timeout=SECONDS</code></dt><dd>
553: @@ -3431,6 +3436,15 @@ directories that are being deleted. For
554: rules that exclude things like "CVS" and "<code>*.o</code>" are marked as perishable,
555: and will not prevent a directory that was removed on the source from being
556: deleted on the destination.</li>
557: +<li>An <code>m(CHMOD)</code> on an include rule tweaks the permissions of matching
558: +source files in the same way as <code>--chmod</code>. This happens before any tweaks
559: +requested via <code>--chmod</code> options.</li>
560: +<li>An <code>o(USER)</code> on an include rule pretends that matching source files are
561: +owned by <code>USER</code> (a name or numeric uid). This happens before any uid mapping
562: +by name or <code>--usermap</code>.</li>
563: +<li>A <code>g(GROUP)</code> on an include rule pretends that matching source files are
564: +owned by <code>GROUP</code> (a name or numeric gid). This happens before any gid
565: +mapping by name or <code>--groupmap</code>.</li>
566: <li>An <code>x</code> indicates that a rule affects xattr names in xattr copy/delete
567: operations (and is thus ignored when matching file/dir names). If no
568: xattr-matching rules are specified, a default xattr filtering rule is used
569: @@ -3486,6 +3500,11 @@ specifies sides to affect (via the <code
570: rules in the file must not specify sides (via a modifier or a rule prefix
571: such as <code>hide</code>).</li>
572: </ul>
573: +<p>The attribute-affecting modifiers <code>m</code>, <code>o</code>, and <code>g</code> work only in client filters
574: +(not in daemon filters), and only the modifiers of the first matching rule are
575: +applied. As an example, assuming <code>--super</code> is enabled, the rule
576: +"<code>+o(root),g(root),m(go=) *~</code>" would ensure that all "backup"
577: +files belong to root and are not accessible to anyone else.</p>
578: <p>Per-directory rules are inherited in all subdirectories of the directory where
579: the merge-file was found unless the 'n' modifier was used. Each subdirectory's
580: rules are prefixed to the inherited per-directory rules from its parents, which
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>