./configure (optional if already run)
make
-based-on: 1c82a1e1e54eb585cd37c875604193f5b977d24e
+based-on: 194cee671d5e178f20c4494f41911fa8db942935
diff --git a/exclude.c b/exclude.c
--- a/exclude.c
+++ b/exclude.c
-@@ -44,10 +44,13 @@ filter_rule_list filter_list = { .debug_type = "" };
+@@ -46,10 +46,13 @@ filter_rule_list filter_list = { .debug_type = "" };
filter_rule_list cvs_filter_list = { .debug_type = " [global CVS]" };
filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
#define SLASH_WILD3_SUFFIX "/***"
-@@ -126,8 +129,27 @@ static void teardown_mergelist(filter_rule *ex)
+@@ -128,8 +131,27 @@ static void teardown_mergelist(filter_rule *ex)
mergelist_cnt--;
}
if (ex->rflags & FILTRULE_PERDIR_MERGE)
teardown_mergelist(ex);
free(ex->pattern);
-@@ -729,7 +751,9 @@ static void report_filter_result(enum logcode code, char const *name,
+@@ -723,7 +745,9 @@ static void report_filter_result(enum logcode code, char const *name,
/* This function is used to check if a file should be included/excluded
* from the list of files based on its name and type etc. The value of
int name_is_excluded(const char *fname, int name_flags, int filter_level)
{
if (daemon_filter_list.head && check_filter(&daemon_filter_list, FLOG, fname, name_flags) < 0) {
-@@ -738,6 +762,9 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
+@@ -732,6 +756,9 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
return 1;
}
if (filter_level != ALL_FILTERS)
return 0;
-@@ -748,7 +775,8 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
+@@ -742,7 +769,8 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
}
/* Return -1 if file "name" is defined to be excluded by the specified
int check_filter(filter_rule_list *listp, enum logcode code,
const char *name, int name_flags)
{
-@@ -771,10 +799,12 @@ int check_filter(filter_rule_list *listp, enum logcode code,
+@@ -765,10 +793,12 @@ int check_filter(filter_rule_list *listp, enum logcode code,
}
if (rule_matches(name, ent, name_flags)) {
report_filter_result(code, name, ent, name_flags, listp->debug_type);
return 0;
}
-@@ -791,9 +821,46 @@ static const uchar *rule_strcmp(const uchar *str, const char *rule, int rule_len
+@@ -785,9 +815,45 @@ static const uchar *rule_strcmp(const uchar *str, const char *rule, int rule_len
return NULL;
}
+ if (!parse_chmod(modestr, &modes))
+ return NULL;
+
-+ if (!(chmod = new(struct filter_chmod_struct)))
-+ out_of_memory("make_chmod_struct");
++ chmod = new(struct filter_chmod_struct);
+ chmod->ref_cnt = 1;
+ chmod->modestr = modestr;
+ chmod->modes = modes;
/* Gets the next include/exclude rule from *rulestr_ptr and advances
* *rulestr_ptr to point beyond it. Stores the pattern's start (within
-@@ -808,6 +875,7 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
+@@ -802,6 +868,7 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
const char **pat_ptr, unsigned int *pat_len_ptr)
{
const uchar *s = (const uchar *)*rulestr_ptr;
filter_rule *rule;
unsigned int len;
-@@ -827,6 +895,12 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
+@@ -820,6 +887,12 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
/* Inherit from the template. Don't inherit FILTRULES_SIDES; we check
* that later. */
rule->rflags = template->rflags & FILTRULES_FROM_CONTAINER;
/* Figure out what kind of a filter rule "s" is pointing at. Note
* that if FILTRULE_NO_PREFIXES is set, the rule is either an include
-@@ -972,11 +1046,63 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
+@@ -965,11 +1038,63 @@ static filter_rule *parse_rule_tok(const char **rulestr_ptr,
goto invalid;
rule->rflags |= FILTRULE_EXCLUDE_SELF;
break;
case 'p':
rule->rflags |= FILTRULE_PERISHABLE;
break;
-@@ -1301,6 +1427,23 @@ char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
+@@ -1283,6 +1408,23 @@ char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
else if (am_sender)
return NULL;
}
diff --git a/flist.c b/flist.c
--- a/flist.c
+++ b/flist.c
-@@ -82,6 +82,7 @@ extern struct chmod_mode_struct *chmod_modes;
+@@ -83,6 +83,7 @@ extern struct chmod_mode_struct *chmod_modes;
extern filter_rule_list filter_list;
extern filter_rule_list daemon_filter_list;
#ifdef ICONV_OPTION
extern int filesfrom_convert;
-@@ -1156,7 +1157,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1178,7 +1179,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
} else if (readlink_stat(thisname, &st, linkname) != 0) {
int save_errno = errno;
/* See if file is excluded before reporting an error. */
&& (is_excluded(thisname, 0, filter_level)
|| is_excluded(thisname, 1, filter_level))) {
if (ignore_perishable && save_errno != ENOENT)
-@@ -1201,6 +1202,12 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
+@@ -1223,6 +1224,12 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
if (filter_level == NO_FILTERS)
goto skip_filters;
if (S_ISDIR(st.st_mode)) {
if (!xfer_dirs) {
-@@ -1403,12 +1410,23 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
+@@ -1439,12 +1446,23 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
int flags, int filter_level)
{
struct file_struct *file;
file->mode = tweak_mode(file->mode, chmod_modes);
if (f >= 0) {
-@@ -2302,7 +2320,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
+@@ -2345,7 +2363,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
struct file_struct *file;
file = send_file_name(f, flist, fbuf, &st,
FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags,
if (!file)
continue;
if (inc_recurse) {
-@@ -2316,7 +2334,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
+@@ -2359,7 +2377,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
} else
send_if_directory(f, flist, file, fbuf, len, flags);
} else
}
if (reenable_multiplex >= 0)
+diff --git a/rsync.1.md b/rsync.1.md
+--- a/rsync.1.md
++++ b/rsync.1.md
+@@ -1247,7 +1247,9 @@ your home directory (remove the '=' for that).
+ > --chmod=D2775,F664
+
+ It is also legal to specify multiple `--chmod` options, as each additional
+- option is just appended to the list of changes to make.
++ option is just appended to the list of changes to make. To change
++ permissions of files matching a pattern, use an include filter with the `m`
++ modifier, which takes effect before any `--chmod` options.
+
+ See the `--perms` and `--executability` options for how the resulting
+ permission value can be applied to the files in the transfer.
+@@ -2548,6 +2550,10 @@ your home directory (remove the '=' for that).
+ If you specify "`--chown=foo:bar`", this is exactly the same as specifying
+ "`--usermap=*:foo --groupmap=*:bar`", only easier.
+
++ To change ownership of files matching a pattern, use an include filter with
++ the `o` and `g` modifiers, which take effect before uid/gid mapping and
++ therefore em(can) be mixed with `--usermap` and `--groupmap`.
++
+ 0. `--timeout=SECONDS`
+
+ This option allows you to set a maximum I/O timeout in seconds. If no data
+@@ -3499,6 +3505,15 @@ The following modifiers are accepted after a "`+`" or "`-`":
+ rules that exclude things like "CVS" and "`*.o`" are marked as perishable,
+ and will not prevent a directory that was removed on the source from being
+ deleted on the destination.
++- An `m+nop()(CHMOD)` on an include rule tweaks the permissions of matching
++ source files in the same way as `--chmod`. This happens before any tweaks
++ requested via `--chmod` options.
++- An `o+nop()(USER)` on an include rule pretends that matching source files are
++ owned by `USER` (a name or numeric uid). This happens before any uid mapping
++ by name or `--usermap`.
++- A `g+nop()(GROUP)` on an include rule pretends that matching source files are
++ owned by `GROUP` (a name or numeric gid). This happens before any gid
++ mapping by name or `--groupmap`.
+ - An `x` indicates that a rule affects xattr names in xattr copy/delete
+ operations (and is thus ignored when matching file/dir names). If no
+ xattr-matching rules are specified, a default xattr filtering rule is used
+@@ -3556,6 +3571,12 @@ The following modifiers are accepted after a merge or dir-merge rule:
+ rules in the file must not specify sides (via a modifier or a rule prefix
+ such as `hide`).
+
++The attribute-affecting modifiers `m`, `o`, and `g` work only in client filters
++(not in daemon filters), and only the modifiers of the first matching rule are
++applied. As an example, assuming `--super` is enabled, the rule
++"`+o+nop()(root)g+nop()(root)m+nop()(go=) *~`" would ensure that all "backup"
++files belong to root and are not accessible to anyone else.
++
+ Per-directory rules are inherited in all subdirectories of the directory where
+ the merge-file was found unless the 'n' modifier was used. Each subdirectory's
+ rules are prefixed to the inherited per-directory rules from its parents, which
diff --git a/rsync.h b/rsync.h
--- a/rsync.h
+++ b/rsync.h
-@@ -158,6 +158,9 @@
+@@ -171,6 +171,9 @@
#define NO_FILTERS 0
#define SERVER_FILTERS 1
#define ALL_FILTERS 2
#define XFLG_FATAL_ERRORS (1<<0)
#define XFLG_OLD_PREFIXES (1<<1)
-@@ -866,6 +869,8 @@ struct map_struct {
+@@ -920,6 +923,8 @@ struct map_struct {
int status; /* first errno from read errors */
};
#define NAME_IS_FILE (0) /* filter name as a file */
#define NAME_IS_DIR (1<<0) /* filter name as a dir */
#define NAME_IS_XATTR (1<<2) /* filter name as an xattr */
-@@ -891,8 +896,18 @@ struct map_struct {
+@@ -945,8 +950,18 @@ struct map_struct {
#define FILTRULE_CLEAR_LIST (1<<18)/* this item is the "!" token */
#define FILTRULE_PERISHABLE (1<<19)/* perishable if parent dir goes away */
#define FILTRULE_XATTR (1<<20)/* rule only applies to xattr names */
typedef struct filter_struct {
struct filter_struct *next;
-@@ -902,6 +917,11 @@ typedef struct filter_struct {
+@@ -956,6 +971,11 @@ typedef struct filter_struct {
int slash_cnt;
struct filter_list_struct *mergelist;
} u;
} filter_rule;
typedef struct filter_list_struct {
-diff --git a/rsync.yo b/rsync.yo
---- a/rsync.yo
-+++ b/rsync.yo
-@@ -1185,6 +1185,8 @@ quote(--chmod=D2775,F664)
-
- It is also legal to specify multiple bf(--chmod) options, as each
- additional option is just appended to the list of changes to make.
-+To change permissions of files matching a pattern, use an include filter with
-+the bf(m) modifier, which takes effect before any bf(--chmod) options.
-
- See the bf(--perms) and bf(--executability) options for how the resulting
- permission value can be applied to the files in the transfer.
-@@ -2157,6 +2159,10 @@ be omitted, but if USER is empty, a leading colon must be supplied.
- If you specify "--chown=foo:bar, this is exactly the same as specifying
- "--usermap=*:foo --groupmap=*:bar", only easier.
-
-+To change ownership of files matching a pattern, use an include filter with
-+the bf(o) and bf(g) modifiers, which take effect before uid/gid mapping and
-+therefore em(can) be mixed with bf(--usermap) and bf(--groupmap).
-+
- dit(bf(--timeout=TIMEOUT)) This option allows you to set a maximum I/O
- timeout in seconds. If no data is transferred for the specified time
- then rsync will exit. The default is 0, which means no timeout.
-@@ -3039,6 +3045,15 @@ itemization(
- option's default rules that exclude things like "CVS" and "*.o" are
- marked as perishable, and will not prevent a directory that was removed
- on the source from being deleted on the destination.
-+ it() An bf(m+nop()(CHMOD)) on an include rule tweaks the permissions of matching
-+ source files in the same way as bf(--chmod). This happens before any
-+ tweaks requested via bf(--chmod) options.
-+ it() An bf(o+nop()(USER)) on an include rule pretends that matching source files
-+ are owned by bf(USER) (a name or numeric uid). This happens before any uid
-+ mapping by name or bf(--usermap).
-+ it() A bf(g+nop()(GROUP)) on an include rule pretends that matching source files
-+ are owned by bf(GROUP) (a name or numeric gid). This happens before any gid
-+ mapping by name or bf(--groupmap).
- it() An bf(x) indicates that a rule affects xattr names in xattr copy/delete
- operations (and is thus ignored when matching file/dir names). If no
- xattr-matching rules are specified, a default xattr filtering rule is
-@@ -3104,6 +3119,12 @@ itemization(
- a rule prefix such as bf(hide)).
- )
-
-+The attribute-affecting modifiers bf(m), bf(o), and bf(g) work only in client
-+filters (not in daemon filters), and only the modifiers of the first matching
-+rule are applied. As an example, assuming bf(--super) is enabled, the
-+rule "+o+nop()(root)g+nop()(root)m+nop()(go=) *~" would ensure that all "backup" files belong to
-+root and are not accessible to anyone else.
-+
- Per-directory rules are inherited in all subdirectories of the directory
- where the merge-file was found unless the 'n' modifier was used. Each
- subdirectory's rules are prefixed to the inherited per-directory rules
diff --git a/util.c b/util.c
--- a/util.c
+++ b/util.c
-@@ -885,6 +885,25 @@ size_t stringjoin(char *dest, size_t destsize, ...)
+@@ -884,6 +884,25 @@ size_t stringjoin(char *dest, size_t destsize, ...)
return ret;
}