Added get_xattr_acl(), set_xattr_acl(), and del_def_xattr_acl().
[rsync/rsync.git] / flist.c
diff --git a/flist.c b/flist.c
index dfa81c4..60ce2ba 100644 (file)
--- a/flist.c
+++ b/flist.c
  */
 
 #include "rsync.h"
+#include "ifuncs.h"
 #include "rounding.h"
 #include "io.h"
 
 extern int verbose;
-extern int list_only;
 extern int am_root;
 extern int am_server;
 extern int am_daemon;
@@ -171,35 +171,6 @@ void show_flist_stats(void)
        /* Nothing yet */
 }
 
-static void list_file_entry(struct file_struct *f)
-{
-       char permbuf[PERMSTRING_SIZE];
-       double len;
-
-       if (!F_IS_ACTIVE(f)) {
-               /* this can happen if duplicate names were removed */
-               return;
-       }
-
-       permstring(permbuf, f->mode);
-       len = F_LENGTH(f);
-
-       /* TODO: indicate '+' if the entry has an ACL. */
-
-#ifdef SUPPORT_LINKS
-       if (preserve_links && S_ISLNK(f->mode)) {
-               rprintf(FINFO, "%s %11.0f %s %s -> %s\n",
-                       permbuf, len, timestring(f->modtime),
-                       f_name(f, NULL), F_SYMLINK(f));
-       } else
-#endif
-       {
-               rprintf(FINFO, "%s %11.0f %s %s\n",
-                       permbuf, len, timestring(f->modtime),
-                       f_name(f, NULL));
-       }
-}
-
 /* Stat either a symlink or its referent, depending on the settings of
  * copy_links, copy_unsafe_links, etc.  Returns -1 on error, 0 on success.
  *
@@ -1070,23 +1041,26 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                        rprintf(FINFO, "skipping directory %s\n", thisname);
                        return NULL;
                }
-       } else
-               flags &= ~FLAG_XFER_DIR;
-
-       /* -x only affects directories 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 && st.st_dev != filesystem_dev
-        && S_ISDIR(st.st_mode)) {
-               if (one_file_system > 1) {
-                       if (verbose > 2) {
-                               rprintf(FINFO, "skipping mount-point dir %s\n",
-                                       thisname);
+               /* -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 > 1) {
+                                               rprintf(FINFO,
+                                                   "[%s] skipping mount-point dir %s\n",
+                                                   who_am_i(), thisname);
+                                       }
+                                       return NULL;
+                               }
+                               flags |= FLAG_MOUNT_DIR;
                        }
-                       return NULL;
                }
-               flags |= FLAG_MOUNT_DIR;
-       }
+       } else
+               flags &= ~FLAG_XFER_DIR;
 
        if (is_excluded(thisname, S_ISDIR(st.st_mode) != 0, filter_level)) {
                if (ignore_perishable)
@@ -1110,6 +1084,8 @@ struct file_struct *make_file(const char *fname, struct file_list *flist,
                 && flags & FLAG_DIVERT_DIRS) {
                        /* Room for parent/sibling/next-child info. */
                        extra_len += DIRNODE_EXTRA_CNT * EXTRA_LEN;
+                       if (relative_paths)
+                               extra_len += PTR_EXTRA_CNT * EXTRA_LEN;
                        dir_count++;
                        pool = dir_flist->file_pool;
                } else
@@ -1259,7 +1235,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);
@@ -1423,10 +1399,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_XFER_DIR)
-                || file->flags & FLAG_MOUNT_DIR)
-                       continue;
-
                if (dp)
                        DIR_NEXT_SIBLING(dp) = dir_flist->used - 1;
                else if (parent_dp)
@@ -1512,10 +1484,87 @@ static void send_directory(int f, struct file_list *flist, char *fbuf, int len,
        }
 }
 
+static char lastpath[MAXPATHLEN] = "";
+static int lastpath_len = 0;
+static struct file_struct *lastpath_struct;
+
+static void send_implied_dirs(int f, struct file_list *flist, char *fname,
+                             char *start, char *limit, int flags, int is_dot_dir)
+{
+       struct file_struct *file;
+       item_list *relname_list;
+       relnamecache **rnpp;
+       char *slash;
+       int len, need_new_dir;
+
+       flags &= ~FLAG_XFER_DIR;
+
+       if (inc_recurse) {
+               if (lastpath_struct && F_PATHNAME(lastpath_struct) == pathname
+                && lastpath_len == limit - fname
+                && strncmp(lastpath, fname, lastpath_len) == 0)
+                       need_new_dir = 0;
+               else
+                       need_new_dir = 1;
+       } else
+               need_new_dir = 1;
+
+       if (need_new_dir) {
+               int save_copy_links = copy_links;
+               int save_xfer_dirs = xfer_dirs;
+
+               copy_links = xfer_dirs = 1;
+
+               *limit = '\0';
+
+               for (slash = start; (slash = strchr(slash+1, '/')) != NULL; ) {
+                       *slash = '\0';
+                       send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+                       *slash = '/';
+               }
+
+               file = send_file_name(f, flist, fname, NULL, flags, ALL_FILTERS);
+               if (inc_recurse) {
+                       if (file && !S_ISDIR(file->mode))
+                               file = NULL;
+                       lastpath_struct = file;
+               }
+
+               strlcpy(lastpath, fname, sizeof lastpath);
+               lastpath_len = limit - fname;
+
+               *limit = '/';
+
+               copy_links = save_copy_links;
+               xfer_dirs = save_xfer_dirs;
+
+               if (!inc_recurse)
+                       return;
+       }
+
+       if (!lastpath_struct)
+               return; /* dir must have vanished */
+
+       len = strlen(limit+1);
+       memcpy(&relname_list, F_DIR_RELNAMES_P(lastpath_struct), sizeof relname_list);
+       if (!relname_list) {
+               if (!(relname_list = new0(item_list)))
+                       out_of_memory("send_implied_dirs");
+               memcpy(F_DIR_RELNAMES_P(lastpath_struct), &relname_list, sizeof relname_list);
+       }
+       rnpp = EXPAND_ITEM_LIST(relname_list, relnamecache *, 32);
+       if (!(*rnpp = (relnamecache*)new_array(char, sizeof (relnamecache) + len)))
+               out_of_memory("send_implied_dirs");
+       (*rnpp)->is_dot_dir = is_dot_dir;
+       strlcpy((*rnpp)->fname, limit+1, len + 1);
+}
+
 static void send1extra(int f, struct file_struct *file, struct file_list *flist)
 {
        char fbuf[MAXPATHLEN];
-       int dlen;
+       item_list *relname_list;
+       int len, dlen, flags = FLAG_DIVERT_DIRS | FLAG_XFER_DIR;
+       size_t j;
 
        f_name(file, fbuf);
        dlen = strlen(fbuf);
@@ -1527,13 +1576,59 @@ static void send1extra(int f, struct file_struct *file, struct file_list *flist)
 
        change_local_filter_dir(fbuf, dlen, send_dir_depth);
 
-       send_directory(f, flist, fbuf, dlen, FLAG_DIVERT_DIRS | FLAG_XFER_DIR);
+       if (BITS_SETnUNSET(file->flags, FLAG_XFER_DIR, FLAG_MOUNT_DIR))
+               send_directory(f, flist, fbuf, dlen, flags);
+
+       if (!relative_paths)
+               return;
+
+       memcpy(&relname_list, F_DIR_RELNAMES_P(file), sizeof relname_list);
+       if (!relname_list)
+               return;
+
+       for (j = 0; j < relname_list->count; j++) {
+               char *slash;
+               relnamecache *rnp = ((relnamecache**)relname_list->items)[j];
+               int is_dot_dir = rnp->is_dot_dir;
+
+               fbuf[dlen] = '/';
+               len = strlcpy(fbuf + dlen + 1, rnp->fname, sizeof fbuf - dlen - 1);
+               free(rnp);
+               if (len >= (int)sizeof fbuf)
+                       continue; /* Impossible... */
+
+               slash = strchr(fbuf+dlen+1, '/');
+               if (slash) {
+                       send_implied_dirs(f, flist, fbuf, fbuf+dlen+1, slash, flags, is_dot_dir);
+                       continue;
+               }
+
+               if (is_dot_dir) {
+                       STRUCT_STAT st;
+                       if (link_stat(fbuf, &st, 1) != 0) {
+                               io_error |= IOERR_GENERAL;
+                               rsyserr(FERROR, errno, "link_stat %s failed",
+                                       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);
+               }
+       }
+
+       free(relname_list);
 }
 
 void send_extra_file_list(int f, int at_least)
 {
        struct file_list *flist;
        int64 start_write;
+       uint16 prev_flags;
        int future_cnt, save_io_error = io_error;
 
        if (flist_eof)
@@ -1565,6 +1660,7 @@ void send_extra_file_list(int f, int at_least)
                flist->parent_ndx = dir_ndx;
 
                send1extra(f, file, flist);
+               prev_flags = file->flags;
                dp = F_DIR_NODE_P(file);
 
                /* If there are any duplicate directory names that follow, we
@@ -1574,8 +1670,11 @@ void send_extra_file_list(int f, int at_least)
                    && dir_flist->sorted[dir_ndx]->flags & FLAG_DUPLICATE) {
                        send_dir_ndx = dir_ndx;
                        file = dir_flist->sorted[dir_ndx];
-                       if (F_PATHNAME(file) != pathname)
-                               send1extra(f, file, flist);
+                       /* Try to avoid some duplicate scanning of identical dirs. */
+                       if (F_PATHNAME(file) == pathname && prev_flags & FLAG_XFER_DIR)
+                               file->flags &= ~FLAG_XFER_DIR;
+                       send1extra(f, file, flist);
+                       prev_flags = file->flags;
                        dp = F_DIR_NODE_P(file);
                }
 
@@ -1637,7 +1736,6 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
        int len, dirlen;
        STRUCT_STAT st;
        char *p, *dir;
-       char lastpath[MAXPATHLEN] = "";
        struct file_list *flist;
        struct timeval start_tv, end_tv;
        int64 start_write;
@@ -1804,7 +1902,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                if (fn != fbuf)
                        memmove(fbuf, fn, len + 1);
 
-               if (link_stat(fbuf, &st, copy_dirlinks) != 0) {
+               if (link_stat(fbuf, &st, copy_dirlinks || is_dot_dir) != 0) {
                        io_error |= IOERR_GENERAL;
                        rsyserr(FERROR, errno, "link_stat %s failed",
                                full_fname(fbuf));
@@ -1816,7 +1914,12 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                        continue;
                }
 
-               if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) {
+               if (inc_recurse && relative_paths && *fbuf) {
+                       if ((p = strchr(fbuf+1, '/')) != NULL) {
+                               send_implied_dirs(f, flist, fbuf, fbuf, p, flags, is_dot_dir);
+                               continue;
+                       }
+               } else if (implied_dirs && (p=strrchr(fbuf,'/')) && p != fbuf) {
                        /* Send the implied directories at the start of the
                         * source spec, so we get their permissions right. */
                        char *lp = lastpath, *slash = fbuf;
@@ -1828,29 +1931,10 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                                        slash = fn;
                        }
                        *p = '/';
-                       if (fn != p || (*lp && *lp != '/')) {
-                               int save_copy_links = copy_links;
-                               int save_xfer_dirs = xfer_dirs;
-                               int dir_flags = flags & ~FLAG_XFER_DIR;
-                               copy_links |= copy_unsafe_links;
-                               xfer_dirs = 1;
-                               while ((slash = strchr(slash+1, '/')) != 0) {
-                                       *slash = '\0';
-                                       send_file_name(f, flist, fbuf, NULL,
-                                                      dir_flags, ALL_FILTERS);
-                                       *slash = '/';
-                               }
-                               copy_links = save_copy_links;
-                               xfer_dirs = save_xfer_dirs;
-                               *p = '\0';
-                               strlcpy(lastpath, fbuf, sizeof lastpath);
-                               *p = '/';
-                       }
+                       if (fn != p || (*lp && *lp != '/'))
+                               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;
@@ -2068,12 +2152,6 @@ struct file_list *recv_file_list(int f)
        if (verbose > 3)
                output_flist(flist);
 
-       if (list_only) {
-               int i;
-               for (i = flist->low; i <= flist->high; i++)
-                       list_file_entry(flist->files[i]);
-       }
-
        if (verbose > 2)
                rprintf(FINFO, "recv_file_list done\n");
 
@@ -2182,8 +2260,7 @@ struct file_list *flist_new(int flags, char *msg)
 {
        struct file_list *flist;
 
-       flist = new0(struct file_list);
-       if (!flist)
+       if (!(flist = new0(struct file_list)))
                out_of_memory(msg);
 
        if (flags & FLIST_TEMP) {
@@ -2307,11 +2384,13 @@ static void clean_flist(struct file_list *flist, int strip_root)
                         * list.  Otherwise keep the first one. */
                        if (S_ISDIR(file->mode)) {
                                struct file_struct *fp = flist->sorted[j];
-                               if (!S_ISDIR(fp->mode) || !(fp->flags & FLAG_XFER_DIR))
+                               if (!S_ISDIR(fp->mode))
                                        keep = i, drop = j;
                                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);
                                        keep = j, drop = i;
                                }
                        } else
@@ -2323,11 +2402,6 @@ static void clean_flist(struct file_list *flist, int strip_root)
                                            "removing duplicate name %s from file list (%d)\n",
                                            f_name(file, fbuf), drop + flist->ndx_start);
                                }
-                               /* Make sure we don't lose track of a user-specified
-                                * top directory. */
-                               flist->sorted[keep]->flags |= flist->sorted[drop]->flags
-                                                          & (FLAG_TOP_DIR|FLAG_XFER_DIR);
-
                                clear_file(flist->sorted[drop]);
                        }