extern int local_server;
extern int prune_empty_dirs;
extern int ignore_perishable;
-extern int old_style_args;
extern int relative_paths;
extern int delete_mode;
extern int delete_excluded;
extern int cvs_exclude;
extern int sanitize_paths;
extern int protocol_version;
-extern int list_only;
+extern int trust_sender_args;
extern int module_id;
-extern char *filesfrom_host;
extern char curr_dir[MAXPATHLEN];
extern unsigned int curr_dir_len;
extern unsigned int module_dirlen;
filter_rule_list implied_filter_list = { .debug_type = " [implied]" };
int saw_xattr_filter = 0;
+int trust_sender_args = 0;
int trust_sender_filter = 0;
/* Need room enough for ":MODS " prefix plus some room to grow. */
static int mergelist_cnt = 0;
static int mergelist_size = 0;
+#define LOCAL_RULE 1
+#define REMOTE_RULE 2
+static uchar cur_elide_value = REMOTE_RULE;
+
/* Each filter_list_struct describes a singly-linked list by keeping track
* of both the head and tail pointers. The list is slightly unusual in that
* a parent-dir's content can be appended to the end of the local list in a
slash_cnt++;
}
}
+ rule->elide = 0;
strlcpy(rule->pattern + pre_len, pat, pat_len + 1);
pat_len += pre_len;
if (suf_len) {
}
}
+/* If the wildcards failed, the remote shell might give us a file matching the literal
+ * wildcards. Since "*" & "?" already match themselves, this just needs to deal with
+ * failed "[foo]" idioms.
+ */
+static void maybe_add_literal_brackets_rule(filter_rule const *based_on, int arg_len)
+{
+ filter_rule *rule;
+ const char *arg = based_on->pattern, *cp;
+ char *p;
+ int cnt = 0;
+
+ if (arg_len < 0)
+ arg_len = strlen(arg);
+
+ for (cp = arg; *cp; cp++) {
+ if (*cp == '\\' && cp[1]) {
+ cp++;
+ } else if (*cp == '[')
+ cnt++;
+ }
+ if (!cnt)
+ return;
+
+ rule = new0(filter_rule);
+ rule->rflags = based_on->rflags;
+ rule->u.slash_cnt = based_on->u.slash_cnt;
+ p = rule->pattern = new_array(char, arg_len + cnt + 1);
+ for (cp = arg; *cp; ) {
+ if (*cp == '\\' && cp[1]) {
+ *p++ = *cp++;
+ } else if (*cp == '[')
+ *p++ = '\\';
+ *p++ = *cp++;
+ }
+ *p++ = '\0';
+
+ rule->next = implied_filter_list.head;
+ implied_filter_list.head = rule;
+ if (DEBUG_GTE(FILTER, 3)) {
+ rprintf(FINFO, "[%s] add_implied_include(%s%s)\n", who_am_i(), rule->pattern,
+ rule->rflags & FILTRULE_DIRECTORY ? "/" : "");
+ }
+}
+
+static char *partial_string_buf = NULL;
+static int partial_string_len = 0;
+void implied_include_partial_string(const char *s_start, const char *s_end)
+{
+ partial_string_len = s_end - s_start;
+ if (partial_string_len <= 0 || partial_string_len >= MAXPATHLEN) { /* too-large should be impossible... */
+ partial_string_len = 0;
+ return;
+ }
+ if (!partial_string_buf)
+ partial_string_buf = new_array(char, MAXPATHLEN);
+ memcpy(partial_string_buf, s_start, partial_string_len);
+}
+
+void free_implied_include_partial_string()
+{
+ if (partial_string_buf) {
+ if (partial_string_len)
+ add_implied_include("", 0);
+ free(partial_string_buf);
+ partial_string_buf = NULL;
+ }
+ partial_string_len = 0; /* paranoia */
+}
+
/* Each arg the client sends to the remote sender turns into an implied include
* that the receiver uses to validate the file list from the sender. */
-void add_implied_include(const char *arg)
+void add_implied_include(const char *arg, int skip_daemon_module)
{
- filter_rule *rule;
- int arg_len, saw_wild = 0, backslash_cnt = 0;
- int slash_cnt = 1; /* We know we're adding a leading slash. */
+ int arg_len, saw_wild = 0, saw_live_open_brkt = 0, backslash_cnt = 0;
+ int slash_cnt = 0;
const char *cp;
char *p;
- if (am_server || old_style_args || list_only || filesfrom_host != NULL)
+ if (trust_sender_args)
return;
- if (relative_paths) {
- cp = strstr(arg, "/./");
- if (cp)
- arg = cp+3;
- } else {
- if ((cp = strrchr(arg, '/')) != NULL)
+ if (partial_string_len) {
+ arg_len = strlen(arg);
+ if (partial_string_len + arg_len >= MAXPATHLEN) {
+ partial_string_len = 0;
+ return; /* Should be impossible... */
+ }
+ memcpy(partial_string_buf + partial_string_len, arg, arg_len + 1);
+ partial_string_len = 0;
+ arg = partial_string_buf;
+ }
+ if (skip_daemon_module) {
+ if ((cp = strchr(arg, '/')) != NULL)
arg = cp + 1;
+ else
+ arg = "";
}
+ if (relative_paths) {
+ if ((cp = strstr(arg, "/./")) != NULL)
+ arg = cp + 3;
+ } else if ((cp = strrchr(arg, '/')) != NULL) {
+ arg = cp + 1;
+ }
+ if (*arg == '.' && arg[1] == '\0')
+ arg++;
arg_len = strlen(arg);
if (arg_len) {
+ char *new_pat;
if (strpbrk(arg, "*[?")) {
/* We need to add room to escape backslashes if wildcard chars are present. */
- cp = arg;
- while ((cp = strchr(cp, '\\')) != NULL) {
+ for (cp = arg; (cp = strchr(cp, '\\')) != NULL; cp++)
arg_len++;
- cp++;
- }
saw_wild = 1;
}
arg_len++; /* Leave room for the prefixed slash */
- rule = new0(filter_rule);
- if (!implied_filter_list.head)
- implied_filter_list.head = implied_filter_list.tail = rule;
- else {
- rule->next = implied_filter_list.head;
- implied_filter_list.head = rule;
- }
- rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
- p = rule->pattern = new_array(char, arg_len + 1);
+ p = new_pat = new_array(char, arg_len + 1);
*p++ = '/';
- cp = arg;
- while (*cp) {
+ slash_cnt++;
+ for (cp = arg; *cp; ) {
switch (*cp) {
case '\\':
- backslash_cnt++;
- if (saw_wild)
- *p++ = '\\';
+ if (cp[1] == ']') {
+ if (!saw_wild)
+ cp++; /* A \] in a non-wild filter causes a problem, so drop the \ . */
+ } else if (!strchr("*[?", cp[1])) {
+ backslash_cnt++;
+ if (saw_wild)
+ *p++ = '\\';
+ }
*p++ = *cp++;
break;
case '/':
- if (p[-1] == '/') /* This is safe because of the initial slash. */
- break;
- if (relative_paths) {
- filter_rule const *ent;
- int found = 0;
- *p = '\0';
- for (ent = implied_filter_list.head; ent; ent = ent->next) {
- if (ent != rule && strcmp(ent->pattern, rule->pattern) == 0)
- found = 1;
- }
- if (!found) {
- filter_rule *R_rule = new0(filter_rule);
- R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY
- | (saw_wild ? FILTRULE_WILD : 0);
- R_rule->pattern = strdup(rule->pattern);
- R_rule->u.slash_cnt = slash_cnt;
- R_rule->next = implied_filter_list.head;
- implied_filter_list.head = R_rule;
- if (DEBUG_GTE(FILTER, 3)) {
- rprintf(FINFO, "[%s] add_implied_include(%s/)\n",
- who_am_i(), rule->pattern);
- }
+ if (p[-1] == '/') { /* This is safe because of the initial slash. */
+ if (*++cp == '\0') {
+ slash_cnt--;
+ p--;
}
+ } else if (cp[1] == '\0') {
+ cp++;
+ } else {
+ slash_cnt++;
+ *p++ = *cp++;
}
- slash_cnt++;
+ break;
+ case '.':
+ if (p[-1] == '/') {
+ if (cp[1] == '/') {
+ cp += 2;
+ if (!*cp) {
+ slash_cnt--;
+ p--;
+ }
+ } else if (cp[1] == '\0') {
+ cp++;
+ slash_cnt--;
+ p--;
+ } else
+ *p++ = *cp++;
+ } else
+ *p++ = *cp++;
+ break;
+ case '[':
+ saw_live_open_brkt = 1;
*p++ = *cp++;
break;
default:
}
}
*p = '\0';
- rule->u.slash_cnt = slash_cnt;
- arg = (const char *)rule->pattern;
- if (DEBUG_GTE(FILTER, 3))
- rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), rule->pattern);
+ arg_len = p - new_pat;
+ if (!arg_len)
+ free(new_pat);
+ else {
+ filter_rule *rule = new0(filter_rule);
+ rule->rflags = FILTRULE_INCLUDE + (saw_wild ? FILTRULE_WILD : 0);
+ rule->u.slash_cnt = slash_cnt;
+ arg = rule->pattern = new_pat;
+ if (!implied_filter_list.head)
+ implied_filter_list.head = implied_filter_list.tail = rule;
+ else {
+ rule->next = implied_filter_list.head;
+ implied_filter_list.head = rule;
+ }
+ if (DEBUG_GTE(FILTER, 3))
+ rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), arg);
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(rule, arg_len);
+ if (relative_paths && slash_cnt) {
+ int sub_slash_cnt = slash_cnt;
+ while ((p = strrchr(new_pat, '/')) != NULL && p != new_pat) {
+ filter_rule const *ent;
+ filter_rule *R_rule;
+ int found = 0;
+ *p = '\0';
+ for (ent = implied_filter_list.head; ent; ent = ent->next) {
+ if (ent != rule && strcmp(ent->pattern, new_pat) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ *p = '/';
+ break; /* We added all parent dirs already */
+ }
+ R_rule = new0(filter_rule);
+ R_rule->rflags = FILTRULE_INCLUDE | FILTRULE_DIRECTORY;
+ /* Check if our sub-path has wildcards or escaped backslashes */
+ if (saw_wild && strpbrk(new_pat, "*[?\\"))
+ R_rule->rflags |= FILTRULE_WILD;
+ R_rule->pattern = strdup(new_pat);
+ R_rule->u.slash_cnt = --sub_slash_cnt;
+ R_rule->next = implied_filter_list.head;
+ implied_filter_list.head = R_rule;
+ if (DEBUG_GTE(FILTER, 3)) {
+ rprintf(FINFO, "[%s] add_implied_include(%s/)\n",
+ who_am_i(), R_rule->pattern);
+ }
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(R_rule, -1);
+ }
+ for (p = new_pat; sub_slash_cnt < slash_cnt; sub_slash_cnt++) {
+ p += strlen(p);
+ *p = '/';
+ }
+ }
+ }
}
if (recurse || xfer_dirs) {
/* Now create a rule with an added "/" & "**" or "*" at the end */
- rule = new0(filter_rule);
+ filter_rule *rule = new0(filter_rule);
+ rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
if (recurse)
- rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD | FILTRULE_WILD2;
- else
- rule->rflags = FILTRULE_INCLUDE | FILTRULE_WILD;
- /* A +4 in the len leaves enough room for / * * \0 or / * \0 \0 */
+ rule->rflags |= FILTRULE_WILD2;
+ /* We must leave enough room for / * * \0. */
if (!saw_wild && backslash_cnt) {
/* We are appending a wildcard, so now the backslashes need to be escaped. */
p = rule->pattern = new_array(char, arg_len + backslash_cnt + 3 + 1);
- cp = arg;
- while (*cp) {
+ for (cp = arg; *cp; ) { /* Note that arg_len != 0 because backslash_cnt > 0 */
if (*cp == '\\')
*p++ = '\\';
*p++ = *cp++;
p += arg_len;
}
}
- if (p[-1] != '/')
- *p++ = '/';
+ *p++ = '/';
*p++ = '*';
if (recurse)
*p++ = '*';
implied_filter_list.head = rule;
if (DEBUG_GTE(FILTER, 3))
rprintf(FINFO, "[%s] add_implied_include(%s)\n", who_am_i(), rule->pattern);
+ if (saw_live_open_brkt)
+ maybe_add_literal_brackets_rule(rule, p - rule->pattern);
}
}
const char *strings[16]; /* more than enough */
const char *name = fname + (*fname == '/');
- if (!*name)
+ if (!*name || ex->elide == cur_elide_value)
return 0;
if (!(name_flags & NAME_IS_XATTR) ^ !(ex->rflags & FILTRULE_XATTR))
return 0;
}
+int check_server_filter(filter_rule_list *listp, enum logcode code, const char *name, int name_flags)
+{
+ int ret;
+ cur_elide_value = LOCAL_RULE;
+ ret = check_filter(listp, code, name, name_flags);
+ cur_elide_value = REMOTE_RULE;
+ return ret;
+}
+
/* 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. */
int check_filter(filter_rule_list *listp, enum logcode code,
static void send_rules(int f_out, filter_rule_list *flp)
{
- filter_rule *ent, *prev = NULL;
+ filter_rule *ent;
for (ent = flp->head; ent; ent = ent->next) {
unsigned int len, plen, dlen;
* merge files as an optimization (since they can only have
* include/exclude rules). */
if (ent->rflags & FILTRULE_SENDER_SIDE)
- elide = am_sender ? 1 : -1;
+ elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
if (ent->rflags & FILTRULE_RECEIVER_SIDE)
- elide = elide ? 0 : am_sender ? -1 : 1;
+ elide = elide ? 0 : am_sender ? REMOTE_RULE : LOCAL_RULE;
else if (delete_excluded && !elide
&& (!(ent->rflags & FILTRULE_PERDIR_MERGE)
|| ent->rflags & FILTRULE_NO_PREFIXES))
- elide = am_sender ? 1 : -1;
- if (elide < 0) {
- if (prev)
- prev->next = ent->next;
- else
- flp->head = ent->next;
- } else
- prev = ent;
- if (elide > 0)
+ elide = am_sender ? LOCAL_RULE : REMOTE_RULE;
+ ent->elide = elide;
+ if (elide == LOCAL_RULE)
continue;
if (ent->rflags & FILTRULE_CVS_IGNORE
&& !(ent->rflags & FILTRULE_MERGE_FILE)) {
if (dlen)
write_byte(f_out, '/');
}
- flp->tail = prev;
}
/* This is only called by the client. */