Mention that --keep-dirlinks can be dangerous if there are
[rsync/rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index 57d1596..afb1836 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -335,7 +335,9 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
 {
        static time_t modtime;
        static mode_t mode;
+#ifdef SUPPORT_HARD_LINKS
        static int64 dev;
+#endif
        static dev_t rdev;
        static uint32 rdev_major;
        static uid_t uid;
@@ -376,16 +378,22 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
 #endif
                f_name(file, fname);
 
-       xflags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */
+       /* Initialize starting value of xflags. */
+       if (protocol_version >= 30 && S_ISDIR(file->mode)) {
+               if (file->flags & FLAG_CONTENT_DIR)
+                       xflags = file->flags & FLAG_TOP_DIR;
+               else if (file->flags & FLAG_IMPLIED_DIR)
+                       xflags = XMIT_TOP_DIR | XMIT_NO_CONTENT_DIR;
+               else
+                       xflags = XMIT_NO_CONTENT_DIR;
+       } else
+               xflags = file->flags & FLAG_TOP_DIR; /* FLAG_TOP_DIR == XMIT_TOP_DIR */
 
        if (file->mode == mode)
                xflags |= XMIT_SAME_MODE;
        else
                mode = file->mode;
 
-       if (protocol_version >= 30 && S_ISDIR(mode) && !(file->flags & FLAG_XFER_DIR))
-               xflags |= XMIT_NON_XFER_DIR;
-
        if ((preserve_devices && IS_DEVICE(mode))
         || (preserve_specials && IS_SPECIAL(mode))) {
                if (protocol_version < 28) {
@@ -593,7 +601,9 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 {
        static int64 modtime;
        static mode_t mode;
+#ifdef SUPPORT_HARD_LINKS
        static int64 dev;
+#endif
        static dev_t rdev;
        static uint32 rdev_major;
        static uid_t uid;
@@ -864,22 +874,26 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
        if (S_ISDIR(mode)) {
                if (basename_len == 1+1 && *basename == '.') /* +1 for '\0' */
                        F_DEPTH(file)--;
-               if (xflags & XMIT_TOP_DIR) {
+               if (protocol_version >= 30) {
+                       if (!(xflags & XMIT_NO_CONTENT_DIR)) {
+                               if (xflags & XMIT_TOP_DIR)
+                                       file->flags |= FLAG_TOP_DIR;
+                               file->flags |= FLAG_CONTENT_DIR;
+                       } else if (xflags & XMIT_TOP_DIR)
+                               file->flags |= FLAG_IMPLIED_DIR;
+               } else if (xflags & XMIT_TOP_DIR) {
                        in_del_hier = recurse;
                        del_hier_name_len = F_DEPTH(file) == 0 ? 0 : l1 + l2;
                        if (relative_paths && del_hier_name_len > 2
                            && lastname[del_hier_name_len-1] == '.'
                            && lastname[del_hier_name_len-2] == '/')
                                del_hier_name_len -= 2;
-                       file->flags |= FLAG_TOP_DIR | FLAG_XFER_DIR;
-               } else if (protocol_version >= 30) {
-                       if (!(xflags & XMIT_NON_XFER_DIR))
-                               file->flags |= FLAG_XFER_DIR;
+                       file->flags |= FLAG_TOP_DIR | FLAG_CONTENT_DIR;
                } else if (in_del_hier) {
                        if (!relative_paths || !del_hier_name_len
                         || (l1 >= del_hier_name_len
                          && lastname[del_hier_name_len] == '/'))
-                               file->flags |= FLAG_XFER_DIR;
+                               file->flags |= FLAG_CONTENT_DIR;
                        else
                                in_del_hier = 0;
                }
@@ -1044,23 +1058,21 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                /* -x only affects dirs because we need to avoid recursing
                 * into a mount-point directory, not to avoid copying a
                 * symlinked file if -L (or similar) was specified. */
-               if (one_file_system && flags & FLAG_XFER_DIR) {
-                       if (flags & FLAG_TOP_DIR)
-                               filesystem_dev = st.st_dev;
-                       else if (st.st_dev != filesystem_dev) {
-                               if (one_file_system > 1) {
-                                       if (verbose > 2) {
-                                               rprintf(FINFO,
-                                                   "skipping mount-point dir %s\n",
-                                                   thisname);
-                                       }
-                                       return NULL;
+               if (one_file_system && st.st_dev != filesystem_dev
+                && BITS_SETnUNSET(flags, FLAG_CONTENT_DIR, FLAG_TOP_DIR)) {
+                       if (one_file_system > 1) {
+                               if (verbose > 1) {
+                                       rprintf(FINFO,
+                                           "[%s] skipping mount-point dir %s\n",
+                                           who_am_i(), thisname);
                                }
-                               flags |= FLAG_MOUNT_DIR;
+                               return NULL;
                        }
+                       flags |= FLAG_MOUNT_DIR;
+                       flags &= ~FLAG_CONTENT_DIR;
                }
        } else
-               flags &= ~FLAG_XFER_DIR;
+               flags &= ~FLAG_CONTENT_DIR;
 
        if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
                if (ignore_perishable)
@@ -1235,7 +1247,7 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
 {
        struct file_struct *file;
 #if defined SUPPORT_ACLS || defined SUPPORT_XATTRS
-       statx sx;
+       stat_x sx;
 #endif
 
        file = make_file(fname, flist, stp, flags, filter_level);
@@ -1399,9 +1411,6 @@ static void add_dirs_to_tree(int parent_ndx, struct file_list *from_flist,
                dir_flist->files[dir_flist->used++] = file;
                dir_cnt--;
 
-               if (file->flags & FLAG_MOUNT_DIR)
-                       continue;
-
                if (dp)
                        DIR_NEXT_SIBLING(dp) = dir_flist->used - 1;
                else if (parent_dp)
@@ -1500,7 +1509,7 @@ static void send_implied_dirs(int f, struct file_list *flist, char *fname,
        char *slash;
        int len, need_new_dir;
 
-       flags &= ~FLAG_XFER_DIR;
+       flags = (flags | FLAG_IMPLIED_DIR) & ~(FLAG_TOP_DIR | FLAG_CONTENT_DIR);
 
        if (inc_recurse) {
                if (lastpath_struct && F_PATHNAME(lastpath_struct) == pathname
@@ -1566,7 +1575,7 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
 {
        char fbuf[MAXPATHLEN];
        item_list *relname_list;
-       int len, dlen, flags = FLAG_DIVERT_DIRS | FLAG_XFER_DIR;
+       int len, dlen, flags = FLAG_DIVERT_DIRS | FLAG_CONTENT_DIR;
        size_t j;
 
        f_name(file, fbuf);
@@ -1579,8 +1588,19 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
 
        change_local_filter_dir(fbuf, dlen, send_dir_depth);
 
-       if (file->flags & FLAG_XFER_DIR)
+       if (file->flags & FLAG_CONTENT_DIR) {
+               if (one_file_system) {
+                       STRUCT_STAT st;
+                       if (link_stat(fbuf, &st, copy_dirlinks) != 0) {
+                               io_error |= IOERR_GENERAL;
+                               rsyserr(FERROR, errno, "link_stat %s failed",
+                                       full_fname(fbuf));
+                               return;
+                       }
+                       filesystem_dev = st.st_dev;
+               }
                send_directory(f, flist, fbuf, dlen, flags);
+       }
 
        if (!relative_paths)
                return;
@@ -1614,14 +1634,9 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
                                        full_fname(fbuf));
                                continue;
                        }
-                       send_file_name(f, flist, fbuf, &st,
-                           recurse || xfer_dirs ? FLAG_TOP_DIR | flags : flags,
-                           ALL_FILTERS);
-               } else {
-                       send_file_name(f, flist, fbuf, NULL,
-                           recurse ? FLAG_TOP_DIR | flags : flags,
-                           ALL_FILTERS);
-               }
+                       send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS);
+               } else
+                       send_file_name(f, flist, fbuf, NULL, FLAG_TOP_DIR | flags, ALL_FILTERS);
        }
 
        free(relname_list);
@@ -1674,8 +1689,8 @@ void send_extra_file_list(int f, int at_least)
                        send_dir_ndx = dir_ndx;
                        file = dir_flist->sorted[dir_ndx];
                        /* Try to avoid some duplicate scanning of identical dirs. */
-                       if (F_PATHNAME(file) == pathname && prev_flags & FLAG_XFER_DIR)
-                               file->flags &= ~FLAG_XFER_DIR;
+                       if (F_PATHNAME(file) == pathname && prev_flags & FLAG_CONTENT_DIR)
+                               file->flags &= ~FLAG_CONTENT_DIR;
                        send1extra(f, file, flist);
                        prev_flags = file->flags;
                        dp = F_DIR_NODE_P(file);
@@ -1743,7 +1758,8 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        struct timeval start_tv, end_tv;
        int64 start_write;
        int use_ff_fd = 0;
-       int flags, disable_buffering;
+       int disable_buffering;
+       int flags = recurse ? FLAG_CONTENT_DIR : 0;
        int reading_remotely = filesfrom_host != NULL;
        int rl_flags = (reading_remotely ? 0 : RL_DUMP_COMMENTS)
 #ifdef ICONV_OPTION
@@ -1760,6 +1776,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        start_write = stats.total_written;
        gettimeofday(&start_tv, NULL);
 
+       if (relative_paths && protocol_version >= 30)
+               implied_dirs = 1; /* We send flagged implied dirs */
+
 #ifdef SUPPORT_HARD_LINKS
        if (preserve_hard_links && protocol_version >= 30 && !cur_flist)
                init_hard_links();
@@ -1768,11 +1787,9 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        flist = cur_flist = flist_new(0, "send_file_list");
        if (inc_recurse) {
                dir_flist = flist_new(FLIST_TEMP, "send_file_list");
-               flags = FLAG_DIVERT_DIRS | FLAG_XFER_DIR;
-       } else {
+               flags |= FLAG_DIVERT_DIRS;
+       } else
                dir_flist = cur_flist;
-               flags = FLAG_XFER_DIR;
-       }
 
        disable_buffering = io_start_buffering_out(f);
        if (filesfrom_fd >= 0) {
@@ -1873,9 +1890,14 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                                } else
                                        break;
                        }
-                       if (len == 1 && fn[0] == '/')
-                               fn[len++] = '.';
                        fn[len] = '\0';
+                       if (len == 1) {
+                               if (fn[0] == '/') {
+                                       fn = "/.";
+                                       len = 2;
+                               } else if (fn[0] == '.')
+                                       is_dot_dir = 1;
+                       }
                        /* Reject a ".." dir in the active part of the path. */
                        for (p = fn; (p = strstr(p, "..")) != NULL; p += 2) {
                                if ((p[2] == '/' || p[2] == '\0')
@@ -1891,6 +1913,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                if (!*fn) {
                        len = 1;
                        fn = ".";
+                       is_dot_dir = 1;
                }
 
                dirlen = dir ? strlen(dir) : 0;
@@ -1938,9 +1961,12 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                                send_implied_dirs(f, flist, fbuf, slash, p, flags, 0);
                }
 
+               if (one_file_system)
+                       filesystem_dev = st.st_dev;
+
                if (recurse || (xfer_dirs && is_dot_dir)) {
                        struct file_struct *file;
-                       int top_flags = FLAG_TOP_DIR | flags;
+                       int top_flags = FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags;
                        file = send_file_name(f, flist, fbuf, &st,
                                              top_flags, ALL_FILTERS);
                        if (file && !inc_recurse)
@@ -2392,8 +2418,10 @@ static void clean_flist(struct file_list *flist, int strip_root)
                                else {
                                        if (am_sender)
                                                file->flags |= FLAG_DUPLICATE;
-                                       else    /* Make sure we don't lose vital flags. */
-                                               fp->flags |= file->flags & (FLAG_TOP_DIR|FLAG_XFER_DIR);
+                                       else { /* Make sure we merge our vital flags. */
+                                               fp->flags |= file->flags & (FLAG_TOP_DIR|FLAG_CONTENT_DIR);
+                                               fp->flags &= file->flags | ~FLAG_IMPLIED_DIR;
+                                       }
                                        keep = j, drop = i;
                                }
                        } else