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 8b08ff6..1819e39 100644 (file)
--- a/flist.c
+++ b/flist.c
@@ -87,6 +87,14 @@ extern int filesfrom_convert;
 extern iconv_t ic_send, ic_recv;
 #endif
 
+#ifdef HAVE_UTIMENSAT
+#ifdef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
+#define ST_MTIME_NSEC st_mtim.tv_nsec
+#elif defined(HAVE_STRUCT_STAT_ST_MTIMENSEC)
+#define ST_MTIME_NSEC st_mtimensec
+#endif
+#endif
+
 #define PTR_SIZE (sizeof (struct file_struct *))
 
 int io_error;
@@ -190,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)
@@ -200,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
@@ -213,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);
@@ -293,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;
@@ -495,6 +507,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                xflags |= XMIT_SAME_TIME;
        else
                modtime = file->modtime;
+       if (NSEC_BUMP(file) && protocol_version >= 31)
+               xflags |= XMIT_MOD_NSEC;
 
 #ifdef SUPPORT_HARD_LINKS
        if (tmp_dev != 0) {
@@ -577,6 +591,8 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                else
                        write_int(f, modtime);
        }
+       if (xflags & XMIT_MOD_NSEC)
+               write_varint(f, F_MOD_NSEC(file));
        if (!(xflags & XMIT_SAME_MODE))
                write_int(f, to_wire_mode(mode));
        if (preserve_uid && !(xflags & XMIT_SAME_UID)) {
@@ -662,8 +678,7 @@ static void send_file_entry(int f, const char *fname, struct file_struct *file,
                stats.total_size += F_LENGTH(file);
 }
 
-static struct file_struct *recv_file_entry(struct file_list *flist,
-                                          int xflags, int f)
+static struct file_struct *recv_file_entry(int f, struct file_list *flist, int xflags)
 {
        static int64 modtime;
        static mode_t mode;
@@ -685,6 +700,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
        int extra_len = file_extra_cnt * EXTRA_LEN;
        int first_hlink_ndx = -1;
        int64 file_length;
+       uint32 modtime_nsec;
        const char *basename;
        struct file_struct *file;
        alloc_pool_t *pool;
@@ -768,6 +784,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                        struct file_struct *first = flist->files[first_hlink_ndx - flist->ndx_start];
                        file_length = F_LENGTH(first);
                        modtime = first->modtime;
+                       modtime_nsec = F_MOD_NSEC(first);
                        mode = first->mode;
                        if (preserve_uid)
                                uid = F_OWNER(first);
@@ -801,6 +818,10 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                } else
                        modtime = read_int(f);
        }
+       if (xflags & XMIT_MOD_NSEC)
+               modtime_nsec = read_varint(f);
+       else
+               modtime_nsec = 0;
        if (!(xflags & XMIT_SAME_MODE))
                mode = from_wire_mode(read_int(f));
 
@@ -898,6 +919,10 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 #if SIZEOF_INT64 >= 8
        if (file_length > 0xFFFFFFFFu && S_ISREG(mode))
                extra_len += EXTRA_LEN;
+#endif
+#ifdef HAVE_UTIMENSAT
+       if (modtime_nsec)
+               extra_len += EXTRA_LEN;
 #endif
        if (file_length < 0) {
                rprintf(FERROR, "Offset underflow: file-length is negative\n");
@@ -934,6 +959,12 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                file->flags |= FLAG_HLINKED;
 #endif
        file->modtime = (time_t)modtime;
+#ifdef HAVE_UTIMENSAT
+       if (modtime_nsec) {
+               file->flags |= FLAG_MOD_NSEC;
+               OPT_EXTRA(file, 0)->unum = modtime_nsec;
+       }
+#endif
        file->len32 = (uint32)file_length;
 #if SIZEOF_INT64 >= 8
        if (file_length > 0xFFFFFFFFu && S_ISREG(mode)) {
@@ -942,7 +973,7 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
                exit_cleanup(RERR_UNSUPPORTED);
 #else
                file->flags |= FLAG_LENGTH64;
-               OPT_EXTRA(file, 0)->unum = (uint32)(file_length >> 32);
+               OPT_EXTRA(file, NSEC_BUMP(file))->unum = (uint32)(file_length >> 32);
 #endif
        }
 #endif
@@ -1088,11 +1119,11 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 
 #ifdef SUPPORT_ACLS
        if (preserve_acls && !S_ISLNK(mode))
-               receive_acl(file, f);
+               receive_acl(f, file);
 #endif
 #ifdef SUPPORT_XATTRS
        if (preserve_xattrs)
-               receive_xattr(file, f );
+               receive_xattr(f, file);
 #endif
 
        if (S_ISREG(mode) || S_ISLNK(mode))
@@ -1104,6 +1135,12 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 /* 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.
  *
@@ -1112,7 +1149,8 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
  * "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;
@@ -1140,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
@@ -1272,6 +1310,10 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
        linkname_len = 0;
 #endif
 
+#ifdef ST_MTIME_NSEC
+       if (st.ST_MTIME_NSEC && protocol_version >= 31)
+               extra_len += EXTRA_LEN;
+#endif
 #if SIZEOF_CAPITAL_OFF_T >= 8
        if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode))
                extra_len += EXTRA_LEN;
@@ -1283,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;
@@ -1326,11 +1371,17 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
 
        file->flags = flags;
        file->modtime = st.st_mtime;
+#ifdef ST_MTIME_NSEC
+       if (st.ST_MTIME_NSEC && protocol_version >= 31) {
+               file->flags |= FLAG_MOD_NSEC;
+               OPT_EXTRA(file, 0)->unum = st.ST_MTIME_NSEC;
+       }
+#endif
        file->len32 = (uint32)st.st_size;
 #if SIZEOF_CAPITAL_OFF_T >= 8
        if (st.st_size > 0xFFFFFFFFu && S_ISREG(st.st_mode)) {
                file->flags |= FLAG_LENGTH64;
-               OPT_EXTRA(file, 0)->unum = (uint32)(st.st_size >> 32);
+               OPT_EXTRA(file, NSEC_BUMP(file))->unum = (uint32)(st.st_size >> 32);
        }
 #endif
        file->mode = st.st_mode;
@@ -1361,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;
 
@@ -1375,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;
 
@@ -1493,13 +1548,13 @@ static struct file_struct *send_file_name(int f, struct file_list *flist,
 
 #ifdef SUPPORT_ACLS
                if (preserve_acls && !S_ISLNK(file->mode)) {
-                       send_acl(&sx, f);
+                       send_acl(f, &sx);
                        free_acl(&sx);
                }
 #endif
 #ifdef SUPPORT_XATTRS
                if (preserve_xattrs) {
-                       F_XATTR(file) = send_xattr(&sx, f);
+                       F_XATTR(file) = send_xattr(f, &sx);
                        free_xattr(&sx);
                }
 #endif
@@ -1533,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)
@@ -1666,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;
@@ -1715,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';
@@ -1735,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)
 {
@@ -1789,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;
@@ -1860,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)
@@ -1893,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);
@@ -2137,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
@@ -2241,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) {
@@ -2250,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);
@@ -2403,7 +2467,7 @@ struct file_list *recv_file_list(int f)
                }
 
                flist_expand(flist, 1);
-               file = recv_file_entry(flist, flags, f);
+               file = recv_file_entry(f, flist, flags);
 
                if (S_ISREG(file->mode)) {
                        /* Already counted */
@@ -3118,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))