#include "rsync.h"
extern int verbose;
+extern int am_server;
extern int eol_nulls;
extern int list_only;
extern int recurse;
extern int io_error;
extern int sanitize_paths;
extern int protocol_version;
+extern int module_id;
extern char curr_dir[];
extern unsigned int curr_dir_len;
/* Need room enough for ":MODS " prefix plus some room to grow. */
#define MAX_RULE_PREFIX (16)
+#define MODIFIERS_MERGE_FILE "-+Cenw"
+#define MODIFIERS_INCL_EXCL "/!"
+
/* The dirbuf is set by push_local_filters() to the current subdirectory
* relative to curr_dir that is being processed. The path always has a
* trailing slash appended, and the variable dirbuf_len contains the length
/* Build a filter structure given a filter pattern. The value in "pat"
* is not null-terminated. */
-static void make_filter(struct filter_list_struct *listp, const char *pat,
- unsigned int pat_len, unsigned int mflags)
+static void filter_rule(struct filter_list_struct *listp, const char *pat,
+ unsigned int pat_len, unsigned mflags, int xflags)
{
struct filter_struct *ret;
const char *cp;
unsigned int ex_len;
if (verbose > 2) {
- rprintf(FINFO, "[%s] make_filter(%.*s, %s%s)\n",
+ rprintf(FINFO, "[%s] filter_rule(%.*s, %s%s)\n",
who_am_i(), (int)pat_len, pat,
mflags & MATCHFLG_PERDIR_MERGE ? "per-dir-merge"
: mflags & MATCHFLG_INCLUDE ? "include" : "exclude",
ret = new(struct filter_struct);
if (!ret)
- out_of_memory("make_filter");
-
+ out_of_memory("filter_rule");
memset(ret, 0, sizeof ret[0]);
- if (mflags & MATCHFLG_ABS_PATH) {
- if (*pat != '/') {
- mflags &= ~MATCHFLG_ABS_PATH;
- ex_len = 0;
- } else
- ex_len = dirbuf_len - module_dirlen - 1;
+ if (xflags & XFLG_ANCHORED2ABS && *pat == '/'
+ && !(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE))) {
+ mflags |= MATCHFLG_ABS_PATH;
+ ex_len = dirbuf_len - module_dirlen - 1;
} else
ex_len = 0;
ret->pattern = new_array(char, ex_len + pat_len + 1);
if (!ret->pattern)
- out_of_memory("make_filter");
+ out_of_memory("filter_rule");
if (ex_len)
memcpy(ret->pattern, dirbuf + module_dirlen, ex_len);
strlcpy(ret->pattern + ex_len, pat, pat_len + 1);
struct filter_struct *ex = mergelist_parents[i];
const char *s = strrchr(ex->pattern, '/');
if (s)
- s++;
+ s++;
else
- s = ex->pattern;
+ s = ex->pattern;
len = strlen(s);
if (len == pat_len - (cp - ret->pattern)
&& memcmp(s, cp, len) == 0) {
}
if (!(lp = new_array(struct filter_list_struct, 1)))
- out_of_memory("make_filter");
+ out_of_memory("filter_rule");
lp->head = lp->tail = NULL;
if (asprintf(&lp->debug_type, " (per-dir %s)", cp) < 0)
- out_of_memory("make_filter");
+ out_of_memory("filter_rule");
ret->u.mergelist = lp;
if (mergelist_cnt == mergelist_size) {
struct filter_struct *,
mergelist_size);
if (!mergelist_parents)
- out_of_memory("make_filter");
+ out_of_memory("filter_rule");
}
mergelist_parents[mergelist_cnt++] = ret;
} else {
* dirs from that point through the parent dir of the transfer dir looking
* for the per-dir merge-file in each one. */
static BOOL setup_merge_file(struct filter_struct *ex,
- struct filter_list_struct *lp, int flags)
+ struct filter_list_struct *lp)
{
char buf[MAXPATHLEN];
char *x, *y, *pat = ex->pattern;
*y = '\0';
dirbuf_len = y - dirbuf;
strlcpy(x, ex->pattern, MAXPATHLEN - (x - buf));
- add_filter_file(lp, buf, flags | XFLG_ABS_PATH);
+ add_filter_file(lp, buf, ex->match_flags, XFLG_ANCHORED2ABS);
if (ex->match_flags & MATCHFLG_NO_INHERIT)
lp->head = NULL;
lp->tail = NULL;
for (i = 0; i < mergelist_cnt; i++) {
struct filter_struct *ex = mergelist_parents[i];
struct filter_list_struct *lp = ex->u.mergelist;
- int flags = 0;
if (verbose > 2) {
rprintf(FINFO, "[%s] pushing filter list%s\n",
lp->tail = NULL; /* Switch any local rules to inherited. */
if (ex->match_flags & MATCHFLG_NO_INHERIT)
lp->head = NULL;
- if (ex->match_flags & MATCHFLG_WORD_SPLIT)
- flags |= XFLG_WORD_SPLIT;
- if (ex->match_flags & MATCHFLG_NO_PREFIXES)
- flags |= XFLG_NO_PREFIXES;
- if (ex->match_flags & MATCHFLG_INCLUDE)
- flags |= XFLG_DEF_INCLUDE;
- else if (ex->match_flags & MATCHFLG_NO_PREFIXES)
- flags |= XFLG_DEF_EXCLUDE;
if (ex->match_flags & MATCHFLG_FINISH_SETUP) {
ex->match_flags &= ~MATCHFLG_FINISH_SETUP;
- if (setup_merge_file(ex, lp, flags))
+ if (setup_merge_file(ex, lp))
set_filter_dir(dir, dirlen);
}
if (strlcpy(dirbuf + dirbuf_len, ex->pattern,
- MAXPATHLEN - dirbuf_len) < MAXPATHLEN - dirbuf_len)
- add_filter_file(lp, dirbuf, flags | XFLG_ABS_PATH);
- else {
+ MAXPATHLEN - dirbuf_len) < MAXPATHLEN - dirbuf_len) {
+ add_filter_file(lp, dirbuf, ex->match_flags,
+ XFLG_ANCHORED2ABS);
+ } else {
io_error |= IOERR_GENERAL;
rprintf(FINFO,
"cannot add local filter rules in long-named directory: %s\n",
{
char *p, full_name[MAXPATHLEN];
int match_start = 0;
+ int ret_match = ex->match_flags & MATCHFLG_NEGATE ? 0 : 1;
char *pattern = ex->pattern;
if (!*name)
}
if (ex->match_flags & MATCHFLG_DIRECTORY && !name_is_dir)
- return 0;
+ return !ret_match;
if (*pattern == '/') {
match_start = 1;
name = p+1;
}
if (wildmatch(pattern, name))
- return 1;
+ return ret_match;
if (ex->match_flags & MATCHFLG_WILD2_PREFIX) {
/* If the **-prefixed pattern has a '/' as the next
* character, then try to match the rest of the
* pattern at the root. */
if (pattern[2] == '/' && wildmatch(pattern+3, name))
- return 1;
+ return ret_match;
}
else if (!match_start && ex->match_flags & MATCHFLG_WILD2) {
/* A non-anchored match with an infix or trailing "**"
while ((name = strchr(name, '/')) != NULL) {
name++;
if (wildmatch(pattern, name))
- return 1;
+ return ret_match;
}
}
} else if (match_start) {
if (strcmp(name,pattern) == 0)
- return 1;
+ return ret_match;
} else {
int l1 = strlen(name);
int l2 = strlen(pattern);
if (l2 <= l1 &&
strcmp(name+(l1-l2),pattern) == 0 &&
(l1==l2 || name[l1-(l2+1)] == '/')) {
- return 1;
+ return ret_match;
}
}
- return 0;
+ return !ret_match;
}
int name_is_dir, const char *type)
{
/* If a trailing slash is present to match only directories,
- * then it is stripped out by make_filter. So as a special
+ * then it is stripped out by filter_rule. So as a special
* case we add it back in here. */
if (verbose >= 2) {
* be '\0' terminated, so use the returned length to limit the string.
* Also, be sure to add this length to the returned pointer before passing
* it back to ask for the next token. This routine parses the "!" (list-
- * clearing) token and (if xflags does NOT contain XFLG_NO_PREFIXES) the
- * +/- prefixes for overriding the include/exclude mode. The *flag_ptr
- * value will also be set to the MATCHFLG_* bits for the current token.
- */
-static const char *get_filter_tok(const char *p, int xflags,
- unsigned int *len_ptr, unsigned int *flag_ptr)
+ * clearing) token and (depending on the mflags) the various prefixes.
+ * The *mflags_ptr value will be set on exit to the new MATCHFLG_* bits
+ * for the current token. */
+static const char *get_filter_tok(const char *p, unsigned mflags, int xflags,
+ unsigned int *len_ptr, unsigned int *mflags_ptr)
{
const unsigned char *s = (const unsigned char *)p;
- unsigned int len, mflags = 0;
+ unsigned int len, new_mflags;
int empty_pat_is_OK = 0;
- if (xflags & XFLG_WORD_SPLIT) {
+ if (mflags & MATCHFLG_WORD_SPLIT) {
/* Skip over any initial whitespace. */
while (isspace(*s))
s++;
if (!*s)
return NULL;
- /* Figure out what kind of a filter rule "s" is pointing at. */
- if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE))) {
+ new_mflags = mflags & MATCHFLGS_FROM_CONTAINER;
+
+ /* Figure out what kind of a filter rule "s" is pointing at. Note
+ * that if MATCHFLG_NO_PREFIXES is set, the rule is either an include
+ * or an exclude based on the inheritance of the MATCHFLG_INCLUDE
+ * flag (above). XFLG_OLD_PREFIXES indicates a compatibility mode
+ * for old include/exclude patterns where just "+ " and "- " are
+ * allowed as optional prefixes. */
+ if (mflags & MATCHFLG_NO_PREFIXES) {
+ if (*s == '!')
+ new_mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
+ } else if (xflags & XFLG_OLD_PREFIXES) {
+ if (*s == '-' && s[1] == ' ') {
+ new_mflags &= ~MATCHFLG_INCLUDE;
+ s += 2;
+ } else if (*s == '+' && s[1] == ' ') {
+ new_mflags |= MATCHFLG_INCLUDE;
+ s += 2;
+ }
+ if (*s == '!')
+ new_mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
+ } else {
char *mods = "";
switch (*s) {
case ':':
- mflags |= MATCHFLG_PERDIR_MERGE
- | MATCHFLG_FINISH_SETUP;
+ new_mflags |= MATCHFLG_PERDIR_MERGE
+ | MATCHFLG_FINISH_SETUP;
/* FALL THROUGH */
case '.':
- mflags |= MATCHFLG_MERGE_FILE;
- mods = "-+Cens";
+ new_mflags |= MATCHFLG_MERGE_FILE;
+ mods = MODIFIERS_INCL_EXCL MODIFIERS_MERGE_FILE;
break;
case '+':
- mflags |= MATCHFLG_INCLUDE;
- break;
+ new_mflags |= MATCHFLG_INCLUDE;
+ /* FALL THROUGH */
case '-':
+ mods = MODIFIERS_INCL_EXCL;
break;
case '!':
- mflags |= MATCHFLG_CLEAR_LIST;
+ new_mflags |= MATCHFLG_CLEAR_LIST;
mods = NULL;
break;
default:
rprintf(FERROR, "Unknown filter rule: %s\n", p);
exit_cleanup(RERR_SYNTAX);
}
- while (mods && *++s && *s != ' ' && *s != '=' && *s != '_') {
+ while (mods && *++s && *s != ' ' && *s != '_') {
if (strchr(mods, *s) == NULL) {
- if (xflags & XFLG_WORD_SPLIT && isspace(*s)) {
+ if (mflags & MATCHFLG_WORD_SPLIT && isspace(*s)) {
s--;
break;
}
+ invalid:
rprintf(FERROR,
- "unknown option '%c' in filter rule: %s\n",
+ "invalid modifier sequence at '%c' in filter rule: %s\n",
*s, p);
exit_cleanup(RERR_SYNTAX);
}
switch (*s) {
case '-':
- mflags |= MATCHFLG_NO_PREFIXES;
+ if (new_mflags & MATCHFLG_NO_PREFIXES)
+ goto invalid;
+ new_mflags |= MATCHFLG_NO_PREFIXES;
break;
case '+':
- mflags |= MATCHFLG_NO_PREFIXES
- | MATCHFLG_INCLUDE;
+ if (new_mflags & MATCHFLG_NO_PREFIXES)
+ goto invalid;
+ new_mflags |= MATCHFLG_NO_PREFIXES
+ | MATCHFLG_INCLUDE;
+ break;
+ case '/':
+ new_mflags |= MATCHFLG_ABS_PATH;
+ break;
+ case '!':
+ new_mflags |= MATCHFLG_NEGATE;
break;
case 'C':
+ if (new_mflags & MATCHFLG_NO_PREFIXES)
+ goto invalid;
empty_pat_is_OK = 1;
- mflags |= MATCHFLG_NO_PREFIXES
- | MATCHFLG_WORD_SPLIT
- | MATCHFLG_NO_INHERIT;
+ new_mflags |= MATCHFLG_NO_PREFIXES
+ | MATCHFLG_WORD_SPLIT
+ | MATCHFLG_NO_INHERIT;
break;
case 'e':
- mflags |= MATCHFLG_EXCLUDE_SELF;
+ new_mflags |= MATCHFLG_EXCLUDE_SELF;
break;
case 'n':
- mflags |= MATCHFLG_NO_INHERIT;
+ new_mflags |= MATCHFLG_NO_INHERIT;
break;
- case 's':
- mflags |= MATCHFLG_WORD_SPLIT;
+ case 'w':
+ new_mflags |= MATCHFLG_WORD_SPLIT;
break;
}
}
if (*s)
s++;
- } else if (!(xflags & XFLG_NO_PREFIXES)
- && (*s == '-' || *s == '+') && s[1] == ' ') {
- if (*s == '+')
- mflags |= MATCHFLG_INCLUDE;
- s += 2;
- } else {
- if (xflags & XFLG_DEF_INCLUDE)
- mflags |= MATCHFLG_INCLUDE;
- if (*s == '!')
- mflags |= MATCHFLG_CLEAR_LIST; /* Tentative! */
}
- if (xflags & XFLG_DIRECTORY)
- mflags |= MATCHFLG_DIRECTORY;
-
- if (xflags & XFLG_WORD_SPLIT) {
+ if (mflags & MATCHFLG_WORD_SPLIT) {
const unsigned char *cp = s;
/* Token ends at whitespace or the end of the string. */
while (!isspace(*cp) && *cp != '\0')
} else
len = strlen((char*)s);
- if (mflags & MATCHFLG_CLEAR_LIST) {
- if (!(xflags & (XFLG_DEF_INCLUDE | XFLG_DEF_EXCLUDE)) && len) {
+ if (new_mflags & MATCHFLG_CLEAR_LIST) {
+ if (!(xflags & XFLG_OLD_PREFIXES) && len) {
rprintf(FERROR,
"'!' rule has trailing characters: %s\n", p);
exit_cleanup(RERR_SYNTAX);
}
if (len > 1)
- mflags &= ~MATCHFLG_CLEAR_LIST;
+ new_mflags &= ~MATCHFLG_CLEAR_LIST;
} else if (!len && !empty_pat_is_OK) {
rprintf(FERROR, "unexpected end of filter rule: %s\n", p);
exit_cleanup(RERR_SYNTAX);
}
- if (xflags & XFLG_ABS_PATH)
- mflags |= MATCHFLG_ABS_PATH;
-
*len_ptr = len;
- *flag_ptr = mflags;
+ *mflags_ptr = new_mflags;
return (const char *)s;
}
void add_filter(struct filter_list_struct *listp, const char *pattern,
- int xflags)
+ unsigned mflags, int xflags)
{
- unsigned int pat_len, mflags;
+ unsigned int pat_len, new_mflags;
const char *cp, *p;
if (!pattern)
while (1) {
/* Remember that the returned string is NOT '\0' terminated! */
- cp = get_filter_tok(pattern, xflags, &pat_len, &mflags);
+ cp = get_filter_tok(pattern, mflags, xflags,
+ &pat_len, &new_mflags);
if (!cp)
break;
if (pat_len >= MAXPATHLEN) {
}
pattern = cp + pat_len;
- if (mflags & MATCHFLG_CLEAR_LIST) {
+ if (new_mflags & MATCHFLG_CLEAR_LIST) {
if (verbose > 2) {
rprintf(FINFO,
"[%s] clearing filter list%s\n",
pat_len = 10;
}
- if (mflags & MATCHFLG_MERGE_FILE) {
+ if (new_mflags & MATCHFLG_MERGE_FILE) {
unsigned int len = pat_len;
- if (mflags & MATCHFLG_EXCLUDE_SELF) {
+ if (new_mflags & MATCHFLG_EXCLUDE_SELF) {
const char *name = strrchr(cp, '/');
if (name)
len -= ++name - cp;
else
name = cp;
- make_filter(listp, name, len, 0);
- mflags &= ~MATCHFLG_EXCLUDE_SELF;
+ filter_rule(listp, name, len, 0, 0);
+ new_mflags &= ~MATCHFLG_EXCLUDE_SELF;
len = pat_len;
}
- if (mflags & MATCHFLG_PERDIR_MERGE) {
+ if (new_mflags & MATCHFLG_PERDIR_MERGE) {
if (parent_dirscan) {
- if (!(p = parse_merge_name(cp, &len, module_dirlen)))
+ if (!(p = parse_merge_name(cp, &len,
+ module_dirlen)))
continue;
- make_filter(listp, p, len, mflags);
+ filter_rule(listp, p, len,
+ new_mflags, 0);
continue;
}
} else {
- int flgs = XFLG_FATAL_ERRORS;
if (!(p = parse_merge_name(cp, &len, 0)))
continue;
- if (mflags & MATCHFLG_INCLUDE)
- flgs |= XFLG_DEF_INCLUDE;
- else if (mflags & MATCHFLG_NO_PREFIXES)
- flgs |= XFLG_DEF_EXCLUDE;
- add_filter_file(listp, p, flgs);
+ add_filter_file(listp, p, new_mflags,
+ XFLG_FATAL_ERRORS);
continue;
}
}
- make_filter(listp, cp, pat_len, mflags);
+ filter_rule(listp, cp, pat_len, new_mflags, xflags);
}
}
void add_filter_file(struct filter_list_struct *listp, const char *fname,
- int xflags)
+ unsigned mflags, int xflags)
{
FILE *fp;
char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */
char *eob = line + sizeof line - 1;
- int word_split = xflags & XFLG_WORD_SPLIT;
+ int word_split = mflags & MATCHFLG_WORD_SPLIT;
if (!fname || !*fname)
return;
- if (*fname != '-' || fname[1])
- fp = fopen(fname, "rb");
- else
+ if (*fname != '-' || fname[1] || am_server) {
+ if (server_filter_list.head) {
+ strlcpy(line, fname, sizeof line);
+ clean_fname(line, 1);
+ if (check_filter(&server_filter_list, line, 0) < 0)
+ fp = NULL;
+ else
+ fp = fopen(line, "rb");
+ } else
+ fp = fopen(fname, "rb");
+ } else
fp = stdin;
+
+ if (verbose > 2) {
+ rprintf(FINFO, "[%s] add_filter_file(%s,%x,%x)%s\n",
+ who_am_i(), safe_fname(fname), mflags, xflags,
+ fp ? "" : " [not found]");
+ }
+
if (!fp) {
if (xflags & XFLG_FATAL_ERRORS) {
rsyserr(FERROR, errno,
"failed to open %sclude file %s",
- xflags & XFLG_DEF_INCLUDE ? "in" : "ex",
+ mflags & MATCHFLG_INCLUDE ? "in" : "ex",
safe_fname(fname));
exit_cleanup(RERR_FILEIO);
}
}
dirbuf[dirbuf_len] = '\0';
- if (verbose > 2) {
- rprintf(FINFO, "[%s] add_filter_file(%s,%d)\n",
- who_am_i(), safe_fname(fname), xflags);
- }
-
while (1) {
char *s = line;
int ch, overflow = 0;
*s = '\0';
/* Skip an empty token and (when line parsing) comments. */
if (*line && (word_split || (*line != ';' && *line != '#')))
- add_filter(listp, line, xflags);
+ add_filter(listp, line, mflags, xflags);
if (ch == EOF)
break;
}
/* This is a complete hack - blame Rusty. FIXME!
* Remove this hack when older rsyncs (below 2.6.4) are gone. */
if (list_only == 1 && !recurse)
- add_filter(&filter_list, "/*/*", XFLG_DEF_EXCLUDE);
+ add_filter(&filter_list, "/*/*", MATCHFLG_NO_PREFIXES, 0);
for (ent = filter_list.head; ent; ent = ent->next) {
unsigned int len, plen, dlen;
void recv_filter_list(int f)
{
char line[MAXPATHLEN+MAX_RULE_PREFIX+1]; /* +1 for trailing slash. */
- unsigned int xflags = protocol_version >= 29 ? 0 : XFLG_DEF_EXCLUDE;
+ unsigned int xflags = protocol_version >= 29 ? 0 : XFLG_OLD_PREFIXES;
unsigned int l;
while ((l = read_int(f)) != 0) {
if (l >= sizeof line)
overflow("recv_filter_list");
read_sbuf(f, line, l);
- add_filter(&filter_list, line, xflags);
+ add_filter(&filter_list, line, 0, xflags);
}
}
void add_cvs_excludes(void)
{
- static unsigned int cvs_flags = XFLG_WORD_SPLIT | XFLG_NO_PREFIXES
- | XFLG_DEF_EXCLUDE;
+ static unsigned cvs_mflags = MATCHFLG_WORD_SPLIT|MATCHFLG_NO_PREFIXES;
char fname[MAXPATHLEN];
- char *p;
+ char *p = module_id >= 0 && lp_use_chroot(module_id)
+ ? "/" : getenv("HOME");
- add_filter(&filter_list, ":C", 0);
- add_filter(&filter_list, default_cvsignore, cvs_flags);
+ add_filter(&filter_list, ":C", 0, 0);
+ add_filter(&filter_list, default_cvsignore, cvs_mflags, 0);
- if ((p = getenv("HOME"))
- && pathjoin(fname, sizeof fname, p, ".cvsignore") < sizeof fname) {
- add_filter_file(&filter_list, fname, cvs_flags);
- }
+ if (p && pathjoin(fname, MAXPATHLEN, p, ".cvsignore") < MAXPATHLEN)
+ add_filter_file(&filter_list, fname, cvs_mflags, 0);
- add_filter(&filter_list, getenv("CVSIGNORE"), cvs_flags);
+ add_filter(&filter_list, getenv("CVSIGNORE"), cvs_mflags, 0);
}