Annotation of embedaddon/rsync/patches/filter-attribute-mods.diff, revision 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>