Ensure that the generator gets notified about an I/O error for the dir
[rsync.git] / exclude.c
index 11b187c6e344544012865452524b4f1db8c99105..3538a74b0335927fb172bc70f23a0f0a9e81e335 100644 (file)
--- a/exclude.c
+++ b/exclude.c
@@ -4,11 +4,11 @@
  * Copyright (C) 1996-2001 Andrew Tridgell <tridge@samba.org>
  * Copyright (C) 1996 Paul Mackerras
  * Copyright (C) 2002 Martin Pool
- * Copyright (C) 2003, 2004, 2005, 2006 Wayne Davison
+ * Copyright (C) 2003-2009 Wayne Davison
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
@@ -17,8 +17,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+ * with this program; if not, visit the http://fsf.org website.
  */
 
 #include "rsync.h"
@@ -27,8 +26,6 @@ extern int verbose;
 extern int am_server;
 extern int am_sender;
 extern int eol_nulls;
-extern int list_only;
-extern int recurse;
 extern int io_error;
 extern int local_server;
 extern int prune_empty_dirs;
@@ -46,7 +43,7 @@ extern unsigned int module_dirlen;
 
 struct filter_list_struct filter_list = { 0, 0, "" };
 struct filter_list_struct cvs_filter_list = { 0, 0, " [global CVS]" };
-struct filter_list_struct server_filter_list = { 0, 0, " [daemon]" };
+struct filter_list_struct daemon_filter_list = { 0, 0, " [daemon]" };
 
 /* Need room enough for ":MODS " prefix plus some room to grow. */
 #define MAX_RULE_PREFIX (16)
@@ -55,6 +52,8 @@ struct filter_list_struct server_filter_list = { 0, 0, " [daemon]" };
 #define MODIFIERS_INCL_EXCL "/!Crsp"
 #define MODIFIERS_HIDE_PROTECT "/!p"
 
+#define SLASH_WILD3_SUFFIX "/***"
+
 /* 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
@@ -122,7 +121,7 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
 {
        struct filter_struct *ret;
        const char *cp;
-       unsigned int ex_len;
+       unsigned int pre_len, suf_len, slash_cnt = 0;
 
        if (verbose > 2) {
                rprintf(FINFO, "[%s] add_rule(%s%.*s%s)%s\n",
@@ -145,26 +144,54 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
                }
        }
 
-       if (!(ret = new(struct filter_struct)))
+       if (!(ret = new0(struct filter_struct)))
                out_of_memory("add_rule");
-       memset(ret, 0, sizeof ret[0]);
+
+       if (pat_len > 1 && pat[pat_len-1] == '/') {
+               pat_len--;
+               mflags |= MATCHFLG_DIRECTORY;
+       }
+
+       for (cp = pat; cp < pat + pat_len; cp++) {
+               if (*cp == '/')
+                       slash_cnt++;
+       }
 
        if (!(mflags & (MATCHFLG_ABS_PATH | MATCHFLG_MERGE_FILE))
         && ((xflags & (XFLG_ANCHORED2ABS|XFLG_ABS_IF_SLASH) && *pat == '/')
-         || (xflags & XFLG_ABS_IF_SLASH && strchr(pat, '/') != NULL))) {
+         || (xflags & XFLG_ABS_IF_SLASH && slash_cnt))) {
                mflags |= MATCHFLG_ABS_PATH;
                if (*pat == '/')
-                       ex_len = dirbuf_len - module_dirlen - 1;
+                       pre_len = dirbuf_len - module_dirlen - 1;
                else
-                       ex_len = 0;
+                       pre_len = 0;
+       } else
+               pre_len = 0;
+
+       /* The daemon wants dir-exclude rules to get an appended "/" + "***". */
+       if (xflags & XFLG_DIR2WILD3
+        && BITS_SETnUNSET(mflags, MATCHFLG_DIRECTORY, MATCHFLG_INCLUDE)) {
+               mflags &= ~MATCHFLG_DIRECTORY;
+               suf_len = sizeof SLASH_WILD3_SUFFIX - 1;
        } else
-               ex_len = 0;
-       if (!(ret->pattern = new_array(char, ex_len + pat_len + 1)))
+               suf_len = 0;
+
+       if (!(ret->pattern = new_array(char, pre_len + pat_len + suf_len + 1)))
                out_of_memory("add_rule");
-       if (ex_len)
-               memcpy(ret->pattern, dirbuf + module_dirlen, ex_len);
-       strlcpy(ret->pattern + ex_len, pat, pat_len + 1);
-       pat_len += ex_len;
+       if (pre_len) {
+               memcpy(ret->pattern, dirbuf + module_dirlen, pre_len);
+               for (cp = ret->pattern; cp < ret->pattern + pre_len; cp++) {
+                       if (*cp == '/')
+                               slash_cnt++;
+               }
+       }
+       strlcpy(ret->pattern + pre_len, pat, pat_len + 1);
+       pat_len += pre_len;
+       if (suf_len) {
+               memcpy(ret->pattern + pat_len, SLASH_WILD3_SUFFIX, suf_len+1);
+               pat_len += suf_len;
+               slash_cnt++;
+       }
 
        if (strpbrk(ret->pattern, "*[?")) {
                mflags |= MATCHFLG_WILD;
@@ -182,11 +209,6 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
                }
        }
 
-       if (pat_len > 1 && ret->pattern[pat_len-1] == '/') {
-               ret->pattern[pat_len-1] = 0;
-               mflags |= MATCHFLG_DIRECTORY;
-       }
-
        if (mflags & MATCHFLG_PERDIR_MERGE) {
                struct filter_list_struct *lp;
                unsigned int len;
@@ -230,10 +252,8 @@ static void add_rule(struct filter_list_struct *listp, const char *pat,
                                out_of_memory("add_rule");
                }
                mergelist_parents[mergelist_cnt++] = ret;
-       } else {
-               for (cp = ret->pattern; (cp = strchr(cp, '/')) != NULL; cp++)
-                       ret->u.slash_cnt++;
-       }
+       } else
+               ret->u.slash_cnt = slash_cnt;
 
        ret->match_flags = mflags;
 
@@ -299,7 +319,7 @@ static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
                        strlcpy(to, merge_file, *len_ptr + 1);
                        merge_file = to;
                }
-               if (!sanitize_path(fn, merge_file, r, dirbuf_depth, NULL)) {
+               if (!sanitize_path(fn, merge_file, r, dirbuf_depth, SP_DEFAULT)) {
                        rprintf(FERROR, "merge-file name overflows: %s\n",
                                merge_file);
                        return NULL;
@@ -307,18 +327,19 @@ static char *parse_merge_name(const char *merge_file, unsigned int *len_ptr,
                fn_len = strlen(fn);
        } else {
                strlcpy(fn, merge_file, len_ptr ? *len_ptr + 1 : MAXPATHLEN);
-               fn_len = clean_fname(fn, 1);
+               fn_len = clean_fname(fn, CFN_COLLAPSE_DOT_DOT_DIRS);
        }
 
        /* If the name isn't in buf yet, it's wasn't absolute. */
        if (fn != buf) {
-               if (dirbuf_len + fn_len >= MAXPATHLEN) {
+               int d_len = dirbuf_len - prefix_skip;
+               if (d_len + fn_len >= MAXPATHLEN) {
                        rprintf(FERROR, "merge-file name overflows: %s\n", fn);
                        return NULL;
                }
-               memcpy(buf, dirbuf + prefix_skip, dirbuf_len - prefix_skip);
-               memcpy(buf + dirbuf_len - prefix_skip, fn, fn_len + 1);
-               fn_len = clean_fname(buf, 1);
+               memcpy(buf, dirbuf + prefix_skip, d_len);
+               memcpy(buf + d_len, fn, fn_len + 1);
+               fn_len = clean_fname(buf, CFN_COLLAPSE_DOT_DOT_DIRS);
        }
 
        if (len_ptr)
@@ -340,7 +361,7 @@ void set_filter_dir(const char *dir, unsigned int dirlen)
                len = 0;
        memcpy(dirbuf + len, dir, dirlen);
        dirbuf[dirlen + len] = '\0';
-       dirbuf_len = clean_fname(dirbuf, 1);
+       dirbuf_len = clean_fname(dirbuf, CFN_COLLAPSE_DOT_DOT_DIRS);
        if (dirbuf_len > 1 && dirbuf[dirbuf_len-1] == '.'
            && dirbuf[dirbuf_len-2] == '/')
                dirbuf_len -= 2;
@@ -376,7 +397,7 @@ static BOOL setup_merge_file(struct filter_struct *ex,
        else
                pathjoin(buf, MAXPATHLEN, dirbuf, x);
 
-       len = clean_fname(buf, 1);
+       len = clean_fname(buf, CFN_COLLAPSE_DOT_DOT_DIRS);
        if (len != 1 && len < MAXPATHLEN-1) {
                buf[len++] = '/';
                buf[len] = '\0';
@@ -455,7 +476,7 @@ void *push_local_filters(const char *dir, unsigned int dirlen)
                                          XFLG_ANCHORED2ABS);
                } else {
                        io_error |= IOERR_GENERAL;
-                       rprintf(FINFO,
+                       rprintf(FERROR,
                            "cannot add local filter rules in long-named directory: %s\n",
                            full_fname(dirbuf));
                }
@@ -493,15 +514,42 @@ void pop_local_filters(void *mem)
        free(pop);
 }
 
-static int rule_matches(char *name, struct filter_struct *ex, int name_is_dir)
+void change_local_filter_dir(const char *dname, int dlen, int dir_depth)
+{
+       static int cur_depth = -1;
+       static void *filt_array[MAXPATHLEN/2+1];
+
+       if (!dname) {
+               for ( ; cur_depth >= 0; cur_depth--) {
+                       if (filt_array[cur_depth]) {
+                               pop_local_filters(filt_array[cur_depth]);
+                               filt_array[cur_depth] = NULL;
+                       }
+               }
+               return;
+       }
+
+       assert(dir_depth < MAXPATHLEN/2+1);
+
+       for ( ; cur_depth >= dir_depth; cur_depth--) {
+               if (filt_array[cur_depth]) {
+                       pop_local_filters(filt_array[cur_depth]);
+                       filt_array[cur_depth] = NULL;
+               }
+       }
+
+       cur_depth = dir_depth;
+       filt_array[cur_depth] = push_local_filters(dname, dlen);
+}
+
+static int rule_matches(const char *fname, struct filter_struct *ex, int name_is_dir)
 {
        int slash_handling, str_cnt = 0, anchored_match = 0;
        int ret_match = ex->match_flags & MATCHFLG_NEGATE ? 0 : 1;
        char *p, *pattern = ex->pattern;
        const char *strings[16]; /* more than enough */
+       const char *name = fname + (*fname == '/');
 
-       if (*name == '/')
-               name++;
        if (!*name)
                return 0;
 
@@ -511,13 +559,13 @@ static int rule_matches(char *name, struct filter_struct *ex, int name_is_dir)
                 * just match the name portion of the path. */
                if ((p = strrchr(name,'/')) != NULL)
                        name = p+1;
-       } else if (ex->match_flags & MATCHFLG_ABS_PATH && *name != '/'
+       } else if (ex->match_flags & MATCHFLG_ABS_PATH && *fname != '/'
            && curr_dir_len > module_dirlen + 1) {
                /* If we're matching against an absolute-path pattern,
                 * we need to prepend our full path info. */
                strings[str_cnt++] = curr_dir + module_dirlen + 1;
                strings[str_cnt++] = "/";
-       } else if (ex->match_flags & MATCHFLG_WILD2_PREFIX && *name != '/') {
+       } else if (ex->match_flags & MATCHFLG_WILD2_PREFIX && *fname != '/') {
                /* Allow "**"+"/" to match at the start of the string. */
                strings[str_cnt++] = "/";
        }
@@ -573,7 +621,7 @@ static int rule_matches(char *name, struct filter_struct *ex, int name_is_dir)
 }
 
 
-static void report_filter_result(char const *name,
+static void report_filter_result(enum logcode code, char const *name,
                                  struct filter_struct const *ent,
                                  int name_is_dir, const char *type)
 {
@@ -585,7 +633,7 @@ static void report_filter_result(char const *name,
                static char *actions[2][2]
                    = { {"show", "hid"}, {"risk", "protect"} };
                const char *w = who_am_i();
-               rprintf(FINFO, "[%s] %sing %s %s because of pattern %s%s%s\n",
+               rprintf(code, "[%s] %sing %s %s because of pattern %s%s%s\n",
                    w, actions[*w!='s'][!(ent->match_flags&MATCHFLG_INCLUDE)],
                    name_is_dir ? "directory" : "file", name, ent->pattern,
                    ent->match_flags & MATCHFLG_DIRECTORY ? "/" : "", type);
@@ -597,7 +645,8 @@ static void report_filter_result(char const *name,
  * 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(struct filter_list_struct *listp, char *name, int name_is_dir)
+int check_filter(struct filter_list_struct *listp, enum logcode code,
+                const char *name, int name_is_dir)
 {
        struct filter_struct *ent;
 
@@ -605,22 +654,22 @@ int check_filter(struct filter_list_struct *listp, char *name, int name_is_dir)
                if (ignore_perishable && ent->match_flags & MATCHFLG_PERISHABLE)
                        continue;
                if (ent->match_flags & MATCHFLG_PERDIR_MERGE) {
-                       int rc = check_filter(ent->u.mergelist, name,
+                       int rc = check_filter(ent->u.mergelist, code, name,
                                              name_is_dir);
                        if (rc)
                                return rc;
                        continue;
                }
                if (ent->match_flags & MATCHFLG_CVS_IGNORE) {
-                       int rc = check_filter(&cvs_filter_list, name,
+                       int rc = check_filter(&cvs_filter_list, code, name,
                                              name_is_dir);
                        if (rc)
                                return rc;
                        continue;
                }
                if (rule_matches(name, ent, name_is_dir)) {
-                       report_filter_result(name, ent, name_is_dir,
-                                             listp->debug_type);
+                       report_filter_result(code, name, ent, name_is_dir,
+                                            listp->debug_type);
                        return ent->match_flags & MATCHFLG_INCLUDE ? 1 : -1;
                }
        }
@@ -875,18 +924,20 @@ static char default_cvsignore[] =
        " *.a *.olb *.o *.obj *.so *.exe"
        " *.Z *.elc *.ln core"
        /* The rest we added to suit ourself. */
-       " .svn/ .bzr/";
+       " .svn/ .git/ .bzr/";
 
 static void get_cvs_excludes(uint32 mflags)
 {
-       char *p, fname[MAXPATHLEN];
        static int initialized = 0;
+       char *p, fname[MAXPATHLEN];
 
        if (initialized)
                return;
        initialized = 1;
 
-       parse_rule(&cvs_filter_list, default_cvsignore, mflags | MATCHFLG_PERISHABLE, 0);
+       parse_rule(&cvs_filter_list, default_cvsignore,
+                  mflags | (protocol_version >= 30 ? MATCHFLG_PERISHABLE : 0),
+                  0);
 
        p = module_id >= 0 && lp_use_chroot(module_id) ? "/" : getenv("HOME");
        if (p && pathjoin(fname, MAXPATHLEN, p, ".cvsignore") < MAXPATHLEN)
@@ -984,10 +1035,10 @@ void parse_filter_file(struct filter_list_struct *listp, const char *fname,
                return;
 
        if (*fname != '-' || fname[1] || am_server) {
-               if (server_filter_list.head) {
+               if (daemon_filter_list.head) {
                        strlcpy(line, fname, sizeof line);
-                       clean_fname(line, 1);
-                       if (check_filter(&server_filter_list, line, 0) < 0)
+                       clean_fname(line, CFN_COLLAPSE_DOT_DOT_DIRS);
+                       if (check_filter(&daemon_filter_list, FLOG, line, 0) < 0)
                                fp = NULL;
                        else
                                fp = fopen(line, "rb");
@@ -1187,11 +1238,6 @@ void send_filter_list(int f_out)
                parse_rule(&filter_list, "-C", 0, 0);
        }
 
-       /* 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)
-               parse_rule(&filter_list, "/*/*", MATCHFLG_NO_PREFIXES, 0);
-
        send_rules(f_out, &filter_list);
 
        if (f_out >= 0)