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 6963b46..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;
@@ -75,6 +75,7 @@ extern struct filter_list_struct server_filter_list;
 
 #ifdef ICONV_OPTION
 extern int ic_ndx;
+extern int filesfrom_convert;
 extern int need_unsorted_flist;
 extern iconv_t ic_send, ic_recv;
 #endif
@@ -115,6 +116,7 @@ static char tmp_sum[MAX_DIGEST_LEN];
 static char empty_sum[MAX_DIGEST_LEN];
 static int flist_count_offset; /* for --delete --progress */
 static int dir_count = 0;
+static int high_hlink_ndx;
 
 static void clean_flist(struct file_list *flist, int strip_root);
 static void output_flist(struct file_list *flist);
@@ -169,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.
  *
@@ -376,22 +349,21 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
 
 #ifdef ICONV_OPTION
        if (ic_send != (iconv_t)-1) {
-               ICONV_CONST char *ibuf;
-               char *obuf = fname;
-               size_t ocnt = MAXPATHLEN, icnt;
-
-               iconv(ic_send, NULL,0, NULL,0);
-               if ((ibuf = (ICONV_CONST char *)file->dirname) != NULL) {
-                   icnt = strlen(ibuf);
-                   ocnt--; /* pre-subtract the space for the '/' */
-                   if (iconv(ic_send, &ibuf,&icnt, &obuf,&ocnt) == (size_t)-1)
-                       goto convert_error;
-                   *obuf++ = '/';
-               }
-
-               ibuf = (ICONV_CONST char *)file->basename;
-               icnt = strlen(ibuf);
-               if (iconv(ic_send, &ibuf,&icnt, &obuf,&ocnt) == (size_t)-1) {
+               xbuf outbuf, inbuf;
+
+               INIT_CONST_XBUF(outbuf, fname);
+
+               if (file->dirname) {
+                       INIT_XBUF_STRLEN(inbuf, (char*)file->dirname);
+                       outbuf.size -= 2; /* Reserve room for '/' & 1 more char. */
+                       if (iconvbufs(ic_send, &inbuf, &outbuf, 0) < 0)
+                               goto convert_error;
+                       outbuf.size += 2;
+                       outbuf.buf[outbuf.len++] = '/';
+               }
+
+               INIT_XBUF_STRLEN(inbuf, (char*)file->basename);
+               if (iconvbufs(ic_send, &inbuf, &outbuf, 0) < 0) {
                  convert_error:
                        io_error |= IOERR_GENERAL;
                        rprintf(FINFO,
@@ -399,7 +371,7 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
                            who_am_i(), f_name(file, fname), strerror(errno));
                        return;
                }
-               *obuf = '\0';
+               outbuf.buf[outbuf.len] = '\0';
        } else
 #endif
                f_name(file, fname);
@@ -467,7 +439,8 @@ static void send_file_entry(int f, struct file_struct *file, int ndx, int first_
                        struct ht_int64_node *np = idev_find(tmp_dev, tmp_ino);
                        first_hlink_ndx = (int32)(long)np->data - 1;
                        if (first_hlink_ndx < 0) {
-                               np->data = (void*)(long)(ndx + first_ndx + 1);
+                               high_hlink_ndx = ndx + first_ndx;
+                               np->data = (void*)(long)(high_hlink_ndx + 1);
                                xflags |= XMIT_HLINK_FIRST;
                        }
                        xflags |= XMIT_HLINKED;
@@ -665,25 +638,19 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 
 #ifdef ICONV_OPTION
        if (ic_recv != (iconv_t)-1) {
-               char *obuf = thisname;
-               ICONV_CONST char *ibuf = (ICONV_CONST char *)lastname;
-               size_t ocnt = MAXPATHLEN, icnt = basename_len;
+               xbuf outbuf, inbuf;
 
-               if (icnt >= MAXPATHLEN) {
-                       errno = E2BIG;
-                       goto convert_error;
-               }
+               INIT_CONST_XBUF(outbuf, thisname);
+               INIT_XBUF(inbuf, lastname, basename_len, -1);
 
-               iconv(ic_recv, NULL,0, NULL,0);
-               if (iconv(ic_recv, &ibuf,&icnt, &obuf,&ocnt) == (size_t)-1) {
-                 convert_error:
+               if (iconvbufs(ic_recv, &inbuf, &outbuf, 0) < 0) {
                        io_error |= IOERR_GENERAL;
                        rprintf(FINFO,
                            "[%s] cannot convert filename: %s (%s)\n",
                            who_am_i(), lastname, strerror(errno));
-                       obuf = thisname;
+                       outbuf.len = 0;
                }
-               *obuf = '\0';
+               outbuf.buf[outbuf.len] = '\0';
        }
 #endif
 
@@ -941,9 +908,11 @@ static struct file_struct *recv_file_entry(struct file_list *flist,
 #ifdef SUPPORT_HARD_LINKS
        if (preserve_hard_links && xflags & XMIT_HLINKED) {
                if (protocol_version >= 30) {
-                       F_HL_GNUM(file) = xflags & XMIT_HLINK_FIRST
-                                       ? flist->ndx_start + flist->used
-                                       : first_hlink_ndx;
+                       if (xflags & XMIT_HLINK_FIRST) {
+                               high_hlink_ndx = flist->ndx_start + flist->used;
+                               F_HL_GNUM(file) = high_hlink_ndx;
+                       } else
+                               F_HL_GNUM(file) = first_hlink_ndx;
                } else {
                        static int32 cnt = 0;
                        struct ht_int64_node *np;
@@ -1072,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)
@@ -1112,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
@@ -1261,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);
@@ -1425,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)
@@ -1514,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);
@@ -1529,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)
@@ -1551,6 +1644,7 @@ void send_extra_file_list(int f, int at_least)
        while (future_cnt < at_least) {
                struct file_struct *file = dir_flist->sorted[send_dir_ndx];
                int dir_ndx, dstart = dir_count;
+               const char *pathname = F_PATHNAME(file);
                int32 *dp;
 
                flist = flist_new(0, "send_extra_file_list");
@@ -1566,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
@@ -1575,7 +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];
+                       /* 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,14 +1736,17 @@ 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;
        int use_ff_fd = 0;
        int flags, disable_buffering;
        int reading_remotely = filesfrom_host != NULL;
-       int rl_nulls = eol_nulls || reading_remotely;
+       int rl_flags = (reading_remotely ? 0 : RL_DUMP_COMMENTS)
+#ifdef ICONV_OPTION
+                    | (filesfrom_convert ? RL_CONVERT : 0)
+#endif
+                    | (eol_nulls || reading_remotely ? RL_EOL_NULLS : 0);
 
        rprintf(FLOG, "building file list\n");
        if (show_filelist_p())
@@ -1685,7 +1787,7 @@ struct file_list *send_file_list(int f, int argc, char *argv[])
                int is_dot_dir;
 
                if (use_ff_fd) {
-                       if (read_line(filesfrom_fd, fbuf, sizeof fbuf, !reading_remotely, rl_nulls) == 0)
+                       if (read_line(filesfrom_fd, fbuf, sizeof fbuf, rl_flags) == 0)
                                break;
                        sanitize_path(fbuf, fbuf, "", 0, NULL);
                } else {
@@ -1800,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));
@@ -1812,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;
@@ -1824,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;
@@ -2064,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");
 
@@ -2178,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) {
@@ -2200,6 +2281,8 @@ struct file_list *flist_new(int flags, char *msg)
                        flist->file_pool = first_flist->file_pool;
 
                        flist->ndx_start = first_flist->prev->ndx_end + 2;
+                       if (flist->ndx_start <= high_hlink_ndx)
+                               flist->ndx_start = high_hlink_ndx + 1;
 
                        flist->prev = first_flist->prev;
                        flist->prev->next = first_flist->prev = flist;
@@ -2298,26 +2381,27 @@ static void clean_flist(struct file_list *flist, int strip_root)
                        int keep, drop;
                        /* If one is a dir and the other is not, we want to
                         * keep the dir because it might have contents in the
-                        * list.  If both are dirs, keep the last one.
-                        * Otherwise keep the first one. */
-                       if (S_ISDIR(file->mode))
-                               keep = i, drop = j;
-                       else
+                        * list.  Otherwise keep the first one. */
+                       if (S_ISDIR(file->mode)) {
+                               struct file_struct *fp = flist->sorted[j];
+                               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
                                keep = j, drop = i;
 
-                       if (am_sender)
-                               flist->sorted[drop]->flags |= FLAG_DUPLICATE;
-                       else {
+                       if (!am_sender) {
                                if (verbose > 1) {
                                        rprintf(FINFO,
                                            "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]);
                        }