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 &quot;<code>--chown=foo:bar</code>&quot;, this is exactly the same as specifying
                    545:  &quot;<code>--usermap=*:foo --groupmap=*:bar</code>&quot;, 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 &quot;CVS&quot; and &quot;<code>*.o</code>&quot; 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: +&quot;<code>+o(root),g(root),m(go=) *~</code>&quot; would ensure that all &quot;backup&quot;
                    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>