Call `OpenSSL_add_all_algorithms()` on older openssl versions.
[rsync.git] / exclude.c
index e5991009e4b3211654b2a2dbfdd1172c3c6041eb..ffe55b167edfdbe1325bcfdbe178f8b7b7017504 100644 (file)
--- a/exclude.c
+++ b/exclude.c
@@ -33,18 +33,15 @@ extern int recurse;
 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 read_batch;
-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;
@@ -55,6 +52,7 @@ filter_rule_list daemon_filter_list = { .debug_type = " [daemon]" };
 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. */
@@ -80,6 +78,10 @@ static filter_rule **mergelist_parents;
 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
@@ -222,6 +224,7 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
                                slash_cnt++;
                }
        }
+       rule->elide = 0;
        strlcpy(rule->pattern + pre_len, pat, pat_len + 1);
        pat_len += pre_len;
        if (suf_len) {
@@ -302,100 +305,237 @@ static void add_rule(filter_rule_list *listp, const char *pat, unsigned int pat_
        }
 }
 
+/* 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 || read_batch || filesfrom_host != NULL)
+       if (trust_sender_args)
                return;
+       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)
+       } 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) {
+                               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++;
+                               }
+                               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++ = *cp++;
+                               break;
+                       }
+               }
+               *p = '\0';
+               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, rule->pattern) == 0) {
+                                               if (ent != rule && strcmp(ent->pattern, new_pat) == 0) {
                                                        found = 1;
                                                        break;
                                                }
                                        }
-                                       if (!found) {
-                                               filter_rule *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(rule->pattern, "*[?\\"))
-                                                       R_rule->rflags |= FILTRULE_WILD;
-                                               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 (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 = '/';
                                }
-                               slash_cnt++;
-                               *p++ = *cp++;
-                               break;
-                         default:
-                               *p++ = *cp++;
-                               break;
                        }
                }
-               *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);
        }
 
        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_WILD2;
@@ -403,8 +543,7 @@ void add_implied_include(const char *arg)
                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++;
@@ -416,8 +555,7 @@ void add_implied_include(const char *arg)
                                p += arg_len;
                        }
                }
-               if (p[-1] != '/')
-                       *p++ = '/';
+               *p++ = '/';
                *p++ = '*';
                if (recurse)
                        *p++ = '*';
@@ -427,6 +565,8 @@ void add_implied_include(const char *arg)
                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);
        }
 }
 
@@ -767,7 +907,7 @@ static int rule_matches(const char *fname, filter_rule *ex, int name_flags)
        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))
@@ -883,6 +1023,15 @@ int name_is_excluded(const char *fname, int name_flags, int filter_level)
        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,
@@ -1438,7 +1587,7 @@ char *get_rule_prefix(filter_rule *rule, const char *pat, int for_xfer,
 
 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;
@@ -1453,21 +1602,15 @@ static void send_rules(int f_out, filter_rule_list *flp)
                 * 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)) {
@@ -1495,7 +1638,6 @@ static void send_rules(int f_out, filter_rule_list *flp)
                if (dlen)
                        write_byte(f_out, '/');
        }
-       flp->tail = prev;
 }
 
 /* This is only called by the client. */