Attempt to avoid a problem with --copy-unsafe-links where a symlink was
[rsync/rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index a167c3b..1819e39 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -198,7 +198,8 @@ void show_flist_stats(void)
  *
  * The stat structure pointed to by stp will contain information about the
  * link or the referent as appropriate, if they exist. */
-static int readlink_stat(const char *path, STRUCT_STAT *stp, char *linkbuf)
+static int readlink_stat(const char *path, STRUCT_STAT *stp, char *linkbuf,
+                        unsigned int *phys_depth_p)
 {
 #ifdef SUPPORT_LINKS
        if (link_stat(path, stp, copy_dirlinks) < 0)
@@ -208,11 +209,12 @@ static int readlink_stat(const char *path, STRUCT_STAT *stp, char *linkbuf)
                if (llen < 0)
                        return -1;
                linkbuf[llen] = '\0';
-               if (copy_unsafe_links && unsafe_symlink(linkbuf, path)) {
+               if (copy_unsafe_links && unsafe_symlink(linkbuf, *phys_depth_p)) {
                        if (INFO_GTE(SYMSAFE, 1)) {
                                rprintf(FINFO,"copying unsafe symlink \"%s\" -> \"%s\"\n",
                                        path, linkbuf);
                        }
+                       *phys_depth_p = 0;
                        return x_stat(path, stp, NULL);
                }
                if (munge_symlinks && am_sender && llen > SYMLINK_PREFIX_LEN
@@ -221,6 +223,8 @@ static int readlink_stat(const char *path, STRUCT_STAT *stp, char *linkbuf)
                                llen - SYMLINK_PREFIX_LEN + 1);
                }
        }
+       if (copy_unsafe_links && S_ISDIR(stp->st_mode))
+               (*phys_depth_p)++;
        return 0;
 #else
        return x_stat(path, stp, NULL);
@@ -301,7 +305,7 @@ static int is_excluded(const char *fname, int is_dir, int filter_level)
 }
 
 static void send_directory(int f, struct file_list *flist,
-                          char *fbuf, int len, int flags);
+                          char *fbuf, int len, int flags, unsigned int phys_depth);
 
 static const char *pathname, *orig_dir;
 static int pathname_len;
@@ -1131,6 +1135,12 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
 /* Create a file_struct for a named file by reading its stat() information
  * and performing extensive checks against global options.
  *
+ * If we're the sender and --copy-unsafe-links is on, we want to consider a
+ * symlink unsafe if it leaves the subtree established by another unsafe
+ * symlink, but that can't be determined based on "fname".  So the depth of
+ * real directories since the last unsafe symlink is passed as "phys_depth".
+ * In other cases, "phys_depth" is ignored.
+ *
  * Returns a pointer to the new file struct, or NULL if there was an error
  * or this file should be excluded.
  *
@@ -1139,7 +1149,8 @@ static struct file_struct *recv_file_entry(int f, struct file_list *flist, int x
  * "io_error |= IOERR_GENERAL" to avoid deletion of the file from the
  * destination if --delete is on. */
 struct file_struct *make_file(const char *fname, struct file_list *flist,
-                             STRUCT_STAT *stp, int flags, int filter_level)
+                             STRUCT_STAT *stp, int flags, int filter_level,
+                             unsigned int phys_depth)
 {
        static char *lastdir;
        static int lastdir_len = -1;
@@ -1167,7 +1178,7 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                 * dir, or a request to delete a specific file. */
                st = *stp;
                *linkname = '\0'; /* make IBM code checker happy */
-       } else if (readlink_stat(thisname, &st, linkname) != 0) {
+       } else if (readlink_stat(thisname, &st, linkname, &phys_depth) != 0) {
                int save_errno = errno;
                /* See if file is excluded before reporting an error. */
                if (filter_level != NO_FILTERS
@@ -1314,6 +1325,9 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                        extra_len += SUM_EXTRA_CNT * EXTRA_LEN;
        }
 
+       if (copy_unsafe_links && S_ISDIR(st.st_mode))
+               extra_len += EXTRA_LEN; // F_DIR_PHYS_DEPTH
+
 #if EXTRA_ROUNDING > 0
        if (extra_len & (EXTRA_ROUNDING * EXTRA_LEN))
                extra_len = (extra_len | (EXTRA_ROUNDING * EXTRA_LEN)) + EXTRA_LEN;
@@ -1398,6 +1412,9 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        if (sender_keeps_checksum && S_ISREG(st.st_mode))
                memcpy(F_SUM(file), tmp_sum, checksum_len);
 
+       if (copy_unsafe_links && S_ISDIR(st.st_mode))
+               F_DIR_PHYS_DEPTH(file) = phys_depth; /* as adjusted by readlink_stat */
+
        if (unsort_ndx)
                F_NDX(file) = stats.num_dirs;
 
@@ -1412,11 +1429,12 @@ void unmake_file(struct file_struct *file)
 
 static struct file_struct *send_file_name(int f, struct file_list *flist,
                                          const char *fname, STRUCT_STAT *stp,
-                                         int flags, int filter_level)
+                                         int flags, int filter_level,
+                                         unsigned int phys_depth)
 {
        struct file_struct *file;
 
-       file = make_file(fname, flist, stp, flags, filter_level);
+       file = make_file(fname, flist, stp, flags, filter_level, phys_depth);
        if (!file)
                return NULL;
 
@@ -1570,7 +1588,7 @@ static void send_if_directory(int f, struct file_list *flist,
                        return;
                }
                save_filters = push_local_filters(fbuf, len);
-               send_directory(f, flist, fbuf, len, flags);
+               send_directory(f, flist, fbuf, len, flags, F_DIR_PHYS_DEPTH(file));
                pop_local_filters(save_filters);
                fbuf[ol] = '\0';
                if (is_dot_dir)
@@ -1703,7 +1721,7 @@ static void interpret_stat_error(const char *fname, int is_dir)
  * might call this with f set to -2, which also indicates that local filter
  * rules should be ignored. */
 static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
-                          int flags)
+                          int flags, unsigned int phys_depth)
 {
        struct dirent *di;
        unsigned remainder;
@@ -1752,7 +1770,7 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
                        continue;
                }
 
-               send_file_name(f, flist, fbuf, NULL, flags, filter_level);
+               send_file_name(f, flist, fbuf, NULL, flags, filter_level, phys_depth);
        }
 
        fbuf[len] = '\0';
@@ -1772,6 +1790,10 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
        }
 }
 
+/* Used in a few places where we need to pass a phys_depth but can't easily
+ * determine what it should be.  FIXME! */
+#define FIXME_path_depth path_depth
+
 static void send_implied_dirs(int f, struct file_list *flist, char *fname,
                              char *start, char *limit, int flags, char name_type)
 {
@@ -1826,14 +1848,16 @@ static void send_implied_dirs(int f, struct file_list *flist, char *fname,
 
                for (slash = start; (slash = strchr(slash+1, '/')) != NULL; ) {
                        *slash = '\0';
-                       file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+                       file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS,
+                                             FIXME_path_depth(fname));
                        depth++;
                        if (!inc_recurse && file && S_ISDIR(file->mode))
                                change_local_filter_dir(fname, strlen(fname), depth);
                        *slash = '/';
                }
 
-               file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+               file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS,
+                                     FIXME_path_depth(fname));
                if (inc_recurse) {
                        if (file && !S_ISDIR(file->mode))
                                file = NULL;
@@ -1897,7 +1921,7 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
                        }
                        filesystem_dev = st.st_dev;
                }
-               send_directory(f, flist, fbuf, dlen, flags);
+               send_directory(f, flist, fbuf, dlen, flags, F_DIR_PHYS_DEPTH(file));
        }
 
        if (!relative_paths)
@@ -1930,9 +1954,11 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
                                interpret_stat_error(fbuf, True);
                                continue;
                        }
-                       send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS);
+                       send_file_name(f, flist, fbuf, &st, FLAG_TOP_DIR | flags, ALL_FILTERS,
+                                      FIXME_path_depth(fbuf));
                } else
-                       send_file_name(f, flist, fbuf, NULL, FLAG_TOP_DIR | flags, ALL_FILTERS);
+                       send_file_name(f, flist, fbuf, NULL, FLAG_TOP_DIR | flags, ALL_FILTERS,
+                                      FIXME_path_depth(fbuf));
        }
 
        free(relname_list);
@@ -2174,7 +2200,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                        if (*fn == '.' && fn[1] == '/' && !implied_dot_dir) {
                                send_file_name(f, flist, ".", NULL,
                                    (flags | FLAG_IMPLIED_DIR) & ~FLAG_CONTENT_DIR,
-                                   ALL_FILTERS);
+                                   ALL_FILTERS, 0);
                                implied_dot_dir = 1;
                        }
                        len = clean_fname(fn, CFN_KEEP_TRAILING_SLASH
@@ -2278,7 +2304,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                        struct file_struct *file;
                        file = send_file_name(f, flist, fbuf, &st,
                                              FLAG_TOP_DIR | FLAG_CONTENT_DIR | flags,
-                                             NO_FILTERS);
+                                             NO_FILTERS, FIXME_path_depth(fbuf));
                        if (!file)
                                continue;
                        if (inc_recurse) {
@@ -2287,12 +2313,13 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                                                send_dir_depth = 0;
                                                change_local_filter_dir(fbuf, len, send_dir_depth);
                                        }
-                                       send_directory(f, flist, fbuf, len, flags);
+                                       send_directory(f, flist, fbuf, len, flags, F_DIR_PHYS_DEPTH(file));
                                }
                        } else
                                send_if_directory(f, flist, file, fbuf, len, flags);
                } else
-                       send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS);
+                       send_file_name(f, flist, fbuf, &st, flags, NO_FILTERS,
+                                      FIXME_path_depth(fbuf));
        }
 
        gettimeofday(&end_tv, NULL);
@@ -3155,7 +3182,8 @@ struct file_list *get_dirlist(char *dirname, int dlen, int ignore_filter_rules)
 
        recurse = 0;
        xfer_dirs = 1;
-       send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen, FLAG_CONTENT_DIR);
+       send_directory(ignore_filter_rules ? -2 : -1, dirlist, dirname, dlen,
+                      FLAG_CONTENT_DIR, PHYS_DEPTH_N_A);
        xfer_dirs = save_xfer_dirs;
        recurse = save_recurse;
        if (INFO_GTE(PROGRESS, 1))