A simple change to change_pathname() to ensure that the error output
[rsync/rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index 673148e..b0b05dc 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -97,7 +97,7 @@ int flist_eof = 0; /* all the file-lists are now known */
 
 #define NORMAL_NAME 0
 #define SLASH_ENDING_NAME 1
-#define DOT_NAME 2
+#define DOTDIR_NAME 2
 
 /* Starting from protocol version 26, we always use 64-bit ino_t and dev_t
  * internally, even if this platform does not allow files to have 64-bit inums.
@@ -244,7 +244,7 @@ static inline int is_daemon_excluded(const char *fname, int is_dir)
 
 static inline int path_is_daemon_excluded(char *path, int ignore_filename)
 {
-       if (daemon_filter_list.head && path) {
+       if (daemon_filter_list.head) {
                char *slash = path;
 
                while ((slash = strchr(slash+1, '/')) != NULL) {
@@ -277,17 +277,6 @@ static int is_excluded(const char *fname, int is_dir, int filter_level)
        if (filter_level == NO_FILTERS)
                return 0;
 #endif
-       if (fname) {
-               /* never exclude '.', even if somebody does --exclude '*' */
-               if (fname[0] == '.' && !fname[1])
-                       return 0;
-               /* Handle the -R version of the '.' dir. */
-               if (fname[0] == '/') {
-                       int len = strlen(fname);
-                       if (fname[len-1] == '.' && fname[len-2] == '/')
-                               return 0;
-               }
-       }
        if (is_daemon_excluded(fname, is_dir))
                return 1;
        if (filter_level != ALL_FILTERS)
@@ -304,7 +293,6 @@ static void send_directory(int f, struct file_list *flist,
 static const char *pathname, *orig_dir;
 static int pathname_len;
 
-
 /* Make sure flist can hold at least flist->used + extra entries. */
 static void flist_expand(struct file_list *flist, int extra)
 {
@@ -350,30 +338,51 @@ static void flist_done_allocating(struct file_list *flist)
                flist->pool_boundary = ptr;
 }
 
-int push_pathname(const char *dir, int len)
+/* Call this with EITHER (1) "file, NULL, 0" to chdir() to the file's
+ * F_PATHNAME(), or (2) "NULL, dir, dirlen" to chdir() to the supplied dir,
+ * with dir == NULL taken to be the starting directory, and dirlen < 0
+ * indicating that strdup(dir) should be called and then the -dirlen length
+ * value checked to ensure that it is not daemon-excluded. */
+int change_pathname(struct file_struct *file, const char *dir, int dirlen)
 {
-       if (dir == pathname)
-               return 1;
+       if (dirlen < 0) {
+               char *cpy = strdup(dir);
+               if (*cpy != '/')
+                       change_dir(orig_dir, CD_SKIP_CHDIR);
+               if (path_is_daemon_excluded(cpy, 0))
+                       goto chdir_error;
+               dir = cpy;
+               dirlen = -dirlen;
+       } else {
+               if (file) {
+                       if (pathname == F_PATHNAME(file))
+                               return 1;
+                       dir = F_PATHNAME(file);
+                       if (dir)
+                               dirlen = strlen(dir);
+               } else if (pathname == dir)
+                       return 1;
+               if (dir && *dir != '/')
+                       change_dir(orig_dir, CD_SKIP_CHDIR);
+       }
 
-       if (!orig_dir)
-               orig_dir = strdup(curr_dir);
+       pathname = dir;
+       pathname_len = dirlen;
 
-       if (pathname && !pop_dir(orig_dir)) {
-               rsyserr(FERROR, errno, "pop_dir %s failed",
-                       full_fname(orig_dir));
-               exit_cleanup(RERR_FILESELECT);
-       }
+       if (!dir)
+               dir = orig_dir;
 
-       if (dir && !push_dir(dir, 0)) {
+       if (!change_dir(dir, CD_NORMAL)) {
+         chdir_error:
                io_error |= IOERR_GENERAL;
-               rsyserr(FERROR, errno, "push_dir %s failed in %s",
-                       full_fname(dir), curr_dir);
+               rsyserr(FERROR, errno, "change_dir %s failed", full_fname(dir));
+               if (dir != orig_dir)
+                       change_dir(orig_dir, CD_NORMAL);
+               pathname = NULL;
+               pathname_len = 0;
                return 0;
        }
 
-       pathname = dir;
-       pathname_len = len >= 0 ? len : dir ? (int)strlen(dir) : 0;
-
        return 1;
 }
 
@@ -429,28 +438,24 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                }
        } else if (protocol_version < 28)
                rdev = MAKEDEV(0, 0);
-       if (preserve_uid) {
-               if ((uid_t)F_OWNER(file) == uid && *lastname)
-                       xflags |= XMIT_SAME_UID;
-               else {
-                       uid = F_OWNER(file);
-                       if (!numeric_ids) {
-                               user_name = add_uid(uid);
-                               if (inc_recurse && user_name)
-                                       xflags |= XMIT_USER_NAME_FOLLOWS;
-                       }
+       if (!preserve_uid || ((uid_t)F_OWNER(file) == uid && *lastname))
+               xflags |= XMIT_SAME_UID;
+       else {
+               uid = F_OWNER(file);
+               if (!numeric_ids) {
+                       user_name = add_uid(uid);
+                       if (inc_recurse && user_name)
+                               xflags |= XMIT_USER_NAME_FOLLOWS;
                }
        }
-       if (preserve_gid) {
-               if ((gid_t)F_GROUP(file) == gid && *lastname)
-                       xflags |= XMIT_SAME_GID;
-               else {
-                       gid = F_GROUP(file);
-                       if (!numeric_ids) {
-                               group_name = add_gid(gid);
-                               if (inc_recurse && group_name)
-                                       xflags |= XMIT_GROUP_NAME_FOLLOWS;
-                       }
+       if (!preserve_gid || ((gid_t)F_GROUP(file) == gid && *lastname))
+               xflags |= XMIT_SAME_GID;
+       else {
+               gid = F_GROUP(file);
+               if (!numeric_ids) {
+                       group_name = add_gid(gid);
+                       if (inc_recurse && group_name)
+                               xflags |= XMIT_GROUP_NAME_FOLLOWS;
                }
        }
        if (file->modtime == modtime)
@@ -467,15 +472,14 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                                np->data = (void*)(long)(first_ndx + ndx + 1);
                                xflags |= XMIT_HLINK_FIRST;
                        }
-                       xflags |= XMIT_HLINKED;
                } else {
                        if (tmp_dev == dev) {
                                if (protocol_version >= 28)
                                        xflags |= XMIT_SAME_DEV_pre30;
                        } else
                                dev = tmp_dev;
-                       xflags |= XMIT_HLINKED;
                }
+               xflags |= XMIT_HLINKED;
        }
 #endif
 
@@ -1019,6 +1023,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        const char *basename;
        alloc_pool_t *pool;
        STRUCT_STAT st;
+       int excl_ret;
        char *bp;
 
        if (strlcpy(thisname, fname, sizeof thisname) >= sizeof thisname) {
@@ -1101,7 +1106,17 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        } else
                flags &= ~FLAG_CONTENT_DIR;
 
-       if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
+       if (S_ISDIR(st.st_mode)) {
+               if (flags & FLAG_DOTDIR_NAME) {
+                       /* A "." fname (or "/." fname in relative mode) is
+                        * never excluded.  No other trailing-dotdir names
+                        * are possible. */
+                       excl_ret = 0;
+               } else
+                       excl_ret = is_excluded(thisname, 1, filter_level);
+       } else
+               excl_ret = is_excluded(thisname, 0, filter_level);
+       if (excl_ret) {
                if (ignore_perishable)
                        non_perishable_cnt++;
                return NULL;
@@ -1672,10 +1687,8 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
        f_name(file, fbuf);
        dlen = strlen(fbuf);
 
-       if (F_PATHNAME(file) != pathname) {
-               if (!push_pathname(F_PATHNAME(file), -1))
-                       exit_cleanup(RERR_FILESELECT);
-       }
+       if (!change_pathname(file, NULL, 0))
+               exit_cleanup(RERR_FILESELECT);
 
        change_local_filter_dir(fbuf, dlen, send_dir_depth);
 
@@ -1840,7 +1853,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        int64 start_write;
        int use_ff_fd = 0;
        int disable_buffering;
-       int flags = recurse ? FLAG_CONTENT_DIR : 0;
+       int arg_flags, flags = recurse ? FLAG_CONTENT_DIR : 0;
        int reading_remotely = filesfrom_host != NULL;
        int rl_flags = (reading_remotely ? 0 : RL_DUMP_COMMENTS)
 #ifdef ICONV_OPTION
@@ -1858,6 +1871,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        start_write = stats.total_written;
        gettimeofday(&start_tv, NULL);
 
+       if (!orig_dir)
+               orig_dir = strdup(curr_dir);
+
        if (relative_paths && protocol_version >= 30)
                implied_dirs = 1; /* We send flagged implied dirs */
 
@@ -1875,9 +1891,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
 
        disable_buffering = io_start_buffering_out(f);
        if (filesfrom_fd >= 0) {
-               if (argv[0] && !push_dir(argv[0], 0)) {
-                       rsyserr(FERROR_XFER, errno, "push_dir %s failed in %s",
-                               full_fname(argv[0]), curr_dir);
+               if (argv[0] && !change_dir(argv[0], CD_NORMAL)) {
+                       rsyserr(FERROR_XFER, errno, "change_dir %s failed",
+                               full_fname(argv[0]));
                        exit_cleanup(RERR_FILESELECT);
                }
                use_ff_fd = 1;
@@ -1912,7 +1928,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                                fbuf[len++] = '.';
                                fbuf[len] = '\0';
                        }
-                       name_type = DOT_NAME;
+                       name_type = DOTDIR_NAME;
                } else if (len > 1 && fbuf[len-1] == '.' && fbuf[len-2] == '.'
                    && (len == 2 || fbuf[len-3] == '/')) {
                        if (len + 2 >= MAXPATHLEN)
@@ -1920,9 +1936,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                        fbuf[len++] = '/';
                        fbuf[len++] = '.';
                        fbuf[len] = '\0';
-                       name_type = DOT_NAME;
+                       name_type = DOTDIR_NAME;
                } else if (fbuf[len-1] == '.' && (len == 1 || fbuf[len-2] == '/'))
-                       name_type = DOT_NAME;
+                       name_type = DOTDIR_NAME;
                else
                        name_type = NORMAL_NAME;
 
@@ -1970,13 +1986,13 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                                if (fn[0] == '/') {
                                        fn = "/.";
                                        len = 2;
-                                       name_type = DOT_NAME;
+                                       name_type = DOTDIR_NAME;
                                } else if (fn[0] == '.')
-                                       name_type = DOT_NAME;
+                                       name_type = DOTDIR_NAME;
                        } else if (fn[len-1] == '/') {
                                fn[--len] = '\0';
                                if (len == 1 && *fn == '.')
-                                       name_type = DOT_NAME;
+                                       name_type = DOTDIR_NAME;
                                else
                                        name_type = SLASH_ENDING_NAME;
                        }
@@ -1995,29 +2011,23 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                if (!*fn) {
                        len = 1;
                        fn = ".";
-                       name_type = DOT_NAME;
+                       name_type = DOTDIR_NAME;
                }
 
                dirlen = dir ? strlen(dir) : 0;
                if (dirlen != lastdir_len || memcmp(lastdir, dir, dirlen) != 0) {
-                       if (path_is_daemon_excluded(dir, 0)) {
-                               io_error |= IOERR_GENERAL;
-                               rsyserr(FERROR, errno, "push_dir %s failed in %s",
-                                       full_fname(dir), curr_dir);
-                               continue;
-                       }
-                       if (!push_pathname(dir ? strdup(dir) : NULL, dirlen))
+                       if (!change_pathname(NULL, dir, -dirlen))
                                continue;
                        lastdir = pathname;
                        lastdir_len = pathname_len;
-               } else if (!push_pathname(lastdir, lastdir_len))
+               } else if (!change_pathname(NULL, lastdir, lastdir_len))
                        continue;
 
                if (fn != fbuf)
                        memmove(fbuf, fn, len + 1);
 
                if (link_stat(fbuf, &st, copy_dirlinks || name_type != NORMAL_NAME) != 0
-                || is_daemon_excluded(fbuf, S_ISDIR(st.st_mode) != 0)
+                || (name_type != DOTDIR_NAME && is_daemon_excluded(fbuf, S_ISDIR(st.st_mode)))
                 || (relative_paths && path_is_daemon_excluded(fbuf, 1))) {
                        io_error |= IOERR_GENERAL;
                        rsyserr(FERROR_XFER, errno, "link_stat %s failed",
@@ -2050,15 +2060,17 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                if (one_file_system)
                        filesystem_dev = st.st_dev;
 
+               arg_flags = name_type == DOTDIR_NAME ? FLAG_DOTDIR_NAME : 0;
+
                if (recurse || (xfer_dirs && name_type != NORMAL_NAME)) {
                        struct file_struct *file;
-                       int top_flags = FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags;
+                       arg_flags |= FLAG_TOP_DIR | FLAG_CONTENT_DIR;
                        file = send_file_name(f, flist, fbuf, &st,
-                                             top_flags, ALL_FILTERS);
+                                             arg_flags | flags, ALL_FILTERS);
                        if (!file)
                                continue;
                        if (inc_recurse) {
-                               if (name_type == DOT_NAME) {
+                               if (name_type == DOTDIR_NAME) {
                                        if (send_dir_depth < 0) {
                                                send_dir_depth = 0;
                                                change_local_filter_dir(fbuf, len, send_dir_depth);
@@ -2068,7 +2080,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                        } else
                                send_if_directory(f, flist, file, fbuf, len, flags);
                } else
-                       send_file_name(f, flist, fbuf, &st, flags, ALL_FILTERS);
+                       send_file_name(f, flist, fbuf, &st, arg_flags | flags, ALL_FILTERS);
        }
 
        gettimeofday(&end_tv, NULL);