struct filter_list_struct cvs_filter_list = { .debug_type = " [global CVS]" };
struct filter_list_struct daemon_filter_list = { .debug_type = " [daemon]" };
-/* Need room enough for ":MODS " prefix plus some room to grow. */
-#define MAX_RULE_PREFIX (16)
+struct filter_struct *last_hit_filter;
-#define MODIFIERS_MERGE_FILE "-+Cenw"
-#define MODIFIERS_INCL_EXCL "/!Crsp"
-#define MODIFIERS_HIDE_PROTECT "/!p"
+/* Need room enough for ":MODS " prefix, which can now include
+ * chmod/user/group values. */
+#define MAX_RULE_PREFIX (256)
#define SLASH_WILD3_SUFFIX "/***"
mergelist_cnt--;
}
+static struct filter_chmod_struct *ref_filter_chmod(struct filter_chmod_struct *chmod)
+{
+ chmod->ref_cnt++;
+ assert(chmod->ref_cnt != 0); /* Catch overflow. */
+ return chmod;
+}
+
+static void unref_filter_chmod(struct filter_chmod_struct *chmod)
+{
+ chmod->ref_cnt--;
+ if (chmod->ref_cnt == 0) {
+ free(chmod->modestr);
+ free_chmod_mode(chmod->modes);
+ free(chmod);
+ }
+}
+
static void free_filter(struct filter_struct *ex)
{
+ if (ex->flags & MATCHFLG_CHMOD)
+ unref_filter_chmod(ex->chmod);
free(ex->pattern);
free(ex);
}
/*
* Return -1 if file "name" is defined to be excluded by the specified
* exclude list, 1 if it is included, and 0 if it was not matched.
+ * Sets last_hit_filter to the filter that was hit, or NULL if none.
*/
int check_filter(struct filter_list_struct *listp, enum logcode code,
const char *name, int name_is_dir)
if (rule_matches(name, ent, name_is_dir)) {
report_filter_result(code, name, ent, name_is_dir,
listp->debug_type);
+ last_hit_filter = ent;
return ent->flags & MATCHFLG_INCLUDE ? 1 : -1;
}
}
+ last_hit_filter = NULL;
return 0;
}
return NULL;
}
+static char *grab_paren_value(const uchar **s_ptr)
+{
+ const uchar *start, *end;
+ int val_sz;
+ char *val;
+
+ if ((*s_ptr)[1] != '(')
+ return NULL;
+ start = (*s_ptr) + 2;
+
+ for (end = start; *end != ')'; end++)
+ if (!*end || *end == ' ' || *end == '_')
+ return NULL;
+
+ val_sz = end - start + 1;
+ val = new_array(char, val_sz);
+ strlcpy(val, (const char *)start, val_sz);
+ *s_ptr = end; /* remember ++s in parse_rule_tok */
+ return val;
+}
+
+static struct filter_chmod_struct *make_chmod_struct(char *modestr)
+{
+ struct filter_chmod_struct *chmod;
+ struct chmod_mode_struct *modes = NULL;
+
+ if (!parse_chmod(modestr, &modes))
+ return NULL;
+
+ if (!(chmod = new(struct filter_chmod_struct)))
+ out_of_memory("make_chmod_struct");
+ chmod->ref_cnt = 1;
+ chmod->modestr = modestr;
+ chmod->modes = modes;
+ return chmod;
+}
+
#define MATCHFLGS_FROM_CONTAINER (MATCHFLG_ABS_PATH | MATCHFLG_INCLUDE \
| MATCHFLG_DIRECTORY | MATCHFLG_NEGATE \
- | MATCHFLG_PERISHABLE)
+ | MATCHFLG_PERISHABLE | MATCHFLGS_ATTRS)
/* Gets the next include/exclude rule from *rulestr_ptr and advances
* *rulestr_ptr to point beyond it. Stores the pattern's start (within
const char **pat_ptr, unsigned int *pat_len_ptr)
{
const uchar *s = (const uchar *)*rulestr_ptr;
+ char *val;
struct filter_struct *filter;
unsigned int len;
/* Inherit from the template. Don't inherit MATCHFLGS_SIDES; we check
* that later. */
filter->flags = template->flags & MATCHFLGS_FROM_CONTAINER;
+ if (template->flags & MATCHFLG_CHMOD)
+ filter->chmod = ref_filter_chmod(template->chmod);
+ if (template->flags & MATCHFLG_FORCE_OWNER)
+ filter->force_uid = template->force_uid;
+ if (template->flags & MATCHFLG_FORCE_GROUP)
+ filter->force_gid = template->force_gid;
/* 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
goto invalid;
filter->flags |= MATCHFLG_EXCLUDE_SELF;
break;
+ case 'g': {
+ gid_t gid;
+
+ if (!(val = grab_paren_value(&s)))
+ goto invalid;
+ if (name_to_gid(val, &gid, True)) {
+ filter->flags |= MATCHFLG_FORCE_GROUP;
+ filter->force_gid = gid;
+ } else {
+ rprintf(FERROR,
+ "unknown group '%s' in filter rule: %s\n",
+ val, *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ free(val);
+ break;
+ }
+ case 'm': {
+ struct filter_chmod_struct *chmod;
+
+ if (!(val = grab_paren_value(&s)))
+ goto invalid;
+ if ((chmod = make_chmod_struct(val))) {
+ if (filter->flags & MATCHFLG_CHMOD)
+ unref_filter_chmod(filter->chmod);
+ filter->flags |= MATCHFLG_CHMOD;
+ filter->chmod = chmod;
+ } else {
+ rprintf(FERROR,
+ "unparseable chmod string '%s' in filter rule: %s\n",
+ val, *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ break;
+ }
case 'n':
if (!(filter->flags & MATCHFLG_MERGE_FILE))
goto invalid;
filter->flags |= MATCHFLG_NO_INHERIT;
break;
+ case 'o': {
+ uid_t uid;
+
+ if (!(val = grab_paren_value(&s)))
+ goto invalid;
+ if (name_to_uid(val, &uid, True)) {
+ filter->flags |= MATCHFLG_FORCE_OWNER;
+ filter->force_uid = uid;
+ } else {
+ rprintf(FERROR,
+ "unknown user '%s' in filter rule: %s\n",
+ val, *rulestr_ptr);
+ exit_cleanup(RERR_SYNTAX);
+ }
+ free(val);
+ break;
+ }
case 'p':
filter->flags |= MATCHFLG_PERISHABLE;
break;
else if (am_sender)
return NULL;
}
+ if (filter->flags & MATCHFLGS_ATTRS) {
+ if (!for_xfer || protocol_version >= 31) {
+ if (filter->flags & MATCHFLG_CHMOD)
+ if (!snappendf(&op, (buf + sizeof buf) - op,
+ "m(%s)", filter->chmod->modestr))
+ return NULL;
+ if (filter->flags & MATCHFLG_FORCE_OWNER)
+ if (!snappendf(&op, (buf + sizeof buf) - op,
+ "o(%u)", (unsigned)filter->force_uid))
+ return NULL;
+ if (filter->flags & MATCHFLG_FORCE_GROUP)
+ if (!snappendf(&op, (buf + sizeof buf) - op,
+ "g(%u)", (unsigned)filter->force_gid))
+ return NULL;
+ } else if (!am_sender)
+ return NULL;
+ }
if (op - buf > legal_len)
return NULL;
if (legal_len)