Fixed several issues with preserving xattrs when using --backup.
[rsync/rsync.git] / generator.c
index c06ea0d..27e5b97 100644 (file)
@@ -97,7 +97,7 @@ extern char *backup_dir;
 extern char *backup_suffix;
 extern int backup_suffix_len;
 extern struct file_list *cur_flist, *first_flist, *dir_flist;
-extern struct filter_list_struct server_filter_list;
+extern struct filter_list_struct daemon_filter_list;
 
 int ignore_perishable = 0;
 int non_perishable_cnt = 0;
@@ -557,10 +557,13 @@ static void do_delete_pass(void)
        for (j = 0; j < cur_flist->used; j++) {
                struct file_struct *file = cur_flist->sorted[j];
 
-               if (!(file->flags & FLAG_CONTENT_DIR))
+               f_name(file, fbuf);
+
+               if (!(file->flags & FLAG_CONTENT_DIR)) {
+                       change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(file));
                        continue;
+               }
 
-               f_name(file, fbuf);
                if (verbose > 1 && file->flags & FLAG_TOP_DIR)
                        rprintf(FINFO, "deleting in %s\n", fbuf);
 
@@ -1213,6 +1216,14 @@ static void list_file_entry(struct file_struct *f)
 static int phase = 0;
 static int dflt_perms;
 
+static int implied_dirs_are_missing;
+/* Helper for recv_generator's skip_dir and dry_missing_dir tests. */
+static BOOL is_below(struct file_struct *file, struct file_struct *subtree)
+{
+       return F_DEPTH(file) > F_DEPTH(subtree)
+               && (!implied_dirs_are_missing || f_name_has_prefix(file, subtree));
+}
+
 /* Acts on the indicated item in cur_flist whose name is fname.  If a dir,
  * make sure it exists, and has the right permissions/timestamp info.  For
  * all other non-regular files (symlinks, etc.) we create them here.  For
@@ -1227,9 +1238,12 @@ static int dflt_perms;
 static void recv_generator(char *fname, struct file_struct *file, int ndx,
                           int itemizing, enum logcode code, int f_out)
 {
-       static int missing_below = -1;
        static const char *parent_dirname = "";
-       static struct file_struct *missing_dir = NULL;
+       /* Missing dir not created due to --dry-run; will still be scanned. */
+       static struct file_struct *dry_missing_dir = NULL;
+       /* Missing dir whose contents are skipped altogether due to
+        * --ignore-non-existing, daemon exclude, or mkdir failure. */
+       static struct file_struct *skip_dir = NULL;
        static struct file_list *fuzzy_dirlist = NULL;
        static int need_fuzzy_dirlist = 0;
        struct file_struct *fuzzy_file = NULL;
@@ -1241,7 +1255,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        char *fnamecmp, *partialptr, *backupptr = NULL;
        char fnamecmpbuf[MAXPATHLEN];
        uchar fnamecmp_type;
-       int implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30;
        int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
        int is_dir = !S_ISDIR(file->mode) ? 0
                   : inc_recurse && ndx != cur_flist->ndx_start - 1 ? -1
@@ -1258,25 +1271,21 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                return;
        }
 
-       if (missing_below >= 0) {
-               if (F_DEPTH(file) <= missing_below
-                || (implied_dirs_are_missing && !f_name_has_prefix(file, missing_dir))) {
-                       if (dry_run)
-                               dry_run--;
-                       missing_below = -1;
-               } else if (!dry_run) {
+       if (skip_dir) {
+               if (is_below(file, skip_dir)) {
                        if (is_dir)
                                file->flags |= FLAG_MISSING_DIR;
 #ifdef SUPPORT_HARD_LINKS
-                       if (F_IS_HLINKED(file))
+                       else if (F_IS_HLINKED(file))
                                handle_skipped_hlink(file, itemizing, code, f_out);
 #endif
                        return;
                }
+               skip_dir = NULL;
        }
 
-       if (server_filter_list.head) {
-               if (check_filter(&server_filter_list, fname, is_dir) < 0) {
+       if (daemon_filter_list.head && (*fname != '.' || fname[1])) {
+               if (check_filter(&daemon_filter_list, FLOG, fname, is_dir) < 0) {
                        if (is_dir < 0)
                                return;
 #ifdef SUPPORT_HARD_LINKS
@@ -1298,7 +1307,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 #ifdef SUPPORT_XATTRS
        sx.xattr = NULL;
 #endif
-       if (dry_run > 1) {
+       if (dry_run > 1 || (dry_missing_dir && is_below(file, dry_missing_dir))) {
+         parent_is_dry_missing:
                if (fuzzy_dirlist) {
                        flist_free(fuzzy_dirlist);
                        fuzzy_dirlist = NULL;
@@ -1308,13 +1318,17 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                stat_errno = ENOENT;
        } else {
                const char *dn = file->dirname ? file->dirname : ".";
+               dry_missing_dir = NULL;
                if (parent_dirname != dn && strcmp(parent_dirname, dn) != 0) {
                        if (relative_paths && !implied_dirs
-                        && do_stat(dn, &sx.st) < 0
-                        && create_directory_path(fname) < 0) {
-                               rsyserr(FERROR_XFER, errno,
-                                       "recv_generator: mkdir %s failed",
-                                       full_fname(dn));
+                        && do_stat(dn, &sx.st) < 0) {
+                               if (dry_run)
+                                       goto parent_is_dry_missing;
+                               if (create_directory_path(fname) < 0) {
+                                       rsyserr(FERROR_XFER, errno,
+                                               "recv_generator: mkdir %s failed",
+                                               full_fname(dn));
+                               }
                        }
                        if (fuzzy_dirlist) {
                                flist_free(fuzzy_dirlist);
@@ -1343,12 +1357,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                if (is_dir) {
                        if (is_dir < 0)
                                return;
-                       if (missing_below < 0) {
-                               if (dry_run)
-                                       dry_run++;
-                               missing_below = F_DEPTH(file);
-                               missing_dir = file;
-                       }
+                       skip_dir = file;
                        file->flags |= FLAG_MISSING_DIR;
                }
 #ifdef SUPPORT_HARD_LINKS
@@ -1404,10 +1413,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                goto skipping_dir_contents;
                        statret = -1;
                }
-               if (dry_run && statret != 0 && missing_below < 0) {
-                       missing_below = F_DEPTH(file);
-                       missing_dir = file;
-                       dry_run++;
+               if (dry_run && statret != 0) {
+                       if (!dry_missing_dir)
+                               dry_missing_dir = file;
+                       file->flags |= FLAG_MISSING_DIR;
                }
                real_ret = statret;
                real_sx = sx;
@@ -1440,8 +1449,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                          skipping_dir_contents:
                                rprintf(FERROR,
                                    "*** Skipping any contents from this failed directory ***\n");
-                               missing_below = F_DEPTH(file);
-                               missing_dir = file;
+                               skip_dir = file;
                                file->flags |= FLAG_MISSING_DIR;
                                goto cleanup;
                        }
@@ -1474,9 +1482,13 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                DEV_MINOR(devp) = minor(real_sx.st.st_dev);
                        }
                }
-               else if (delete_during && f_out != -1 && !phase && dry_run < 2
-                   && (file->flags & FLAG_CONTENT_DIR))
-                       delete_in_dir(fname, file, &real_sx.st.st_dev);
+               else if (delete_during && f_out != -1 && !phase
+                   && !(file->flags & FLAG_MISSING_DIR)) {
+                       if (file->flags & FLAG_CONTENT_DIR)
+                               delete_in_dir(fname, file, &real_sx.st.st_dev);
+                       else
+                               change_local_filter_dir(fname, strlen(fname), F_DEPTH(file));
+               }
                goto cleanup;
        }
 
@@ -1729,7 +1741,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        } else
                partialptr = NULL;
 
-       if (statret != 0 && fuzzy_dirlist && dry_run <= 1) {
+       if (statret != 0 && fuzzy_dirlist) {
                int j = find_fuzzy(file, fuzzy_dirlist);
                if (j >= 0) {
                        fuzzy_file = fuzzy_dirlist->files[j];
@@ -1856,15 +1868,21 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        close(fd);
                        goto cleanup;
                }
-               if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0
-                && (errno != ENOENT || make_bak_dir(backupptr) < 0
-                 || (f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0)) {
-                       rsyserr(FERROR_XFER, errno, "open %s",
-                               full_fname(backupptr));
-                       unmake_file(back_file);
-                       back_file = NULL;
-                       close(fd);
-                       goto cleanup;
+               if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0) {
+                       int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
+                       if (errno == ENOENT && make_bak_dir(backupptr) == 0) {
+                               if ((f_copy = do_open(backupptr, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0600)) < 0)
+                                       save_errno = errno ? errno : save_errno;
+                               else
+                                       save_errno = 0;
+                       }
+                       if (save_errno) {
+                               rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(backupptr));
+                               unmake_file(back_file);
+                               back_file = NULL;
+                               close(fd);
+                               goto cleanup;
+                       }
                }
                fnamecmp_type = FNAMECMP_BACKUP;
        }
@@ -1926,9 +1944,16 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 
   cleanup:
        if (back_file) {
+               int save_preserve_xattrs = preserve_xattrs;
                if (f_copy >= 0)
                        close(f_copy);
+#ifdef SUPPORT_XATTRS
+               if (preserve_xattrs)
+                       copy_xattrs(fname, backupptr);
+#endif
+               preserve_xattrs = 0;
                set_file_attrs(backupptr, back_file, NULL, NULL, 0);
+               preserve_xattrs = save_preserve_xattrs;
                if (verbose > 1) {
                        rprintf(FINFO, "backed up %s to %s\n",
                                fname, backupptr);
@@ -2125,6 +2150,7 @@ void generate_files(int f_out, const char *local_name)
        lull_mod = allowed_lull * 5;
        symlink_timeset_failed_flags = ITEM_REPORT_TIME
            | (protocol_version >= 30 || !am_server ? ITEM_REPORT_TIMEFAIL : 0);
+       implied_dirs_are_missing = relative_paths && !implied_dirs && protocol_version < 30;
 
        if (verbose > 2)
                rprintf(FINFO, "generator starting pid=%ld\n", (long)getpid());
@@ -2169,16 +2195,18 @@ void generate_files(int f_out, const char *local_name)
                        f_name(fp, fbuf);
                        ndx = cur_flist->ndx_start - 1;
                        recv_generator(fbuf, fp, ndx, itemizing, code, f_out);
-                       if (delete_during && dry_run < 2 && !list_only) {
-                               if (BITS_SETnUNSET(fp->flags, FLAG_CONTENT_DIR, FLAG_MISSING_DIR)) {
+                       if (delete_during && dry_run < 2 && !list_only
+                        && !(fp->flags & FLAG_MISSING_DIR)) {
+                               if (fp->flags & FLAG_CONTENT_DIR) {
                                        dev_t dirdev;
                                        if (one_file_system) {
                                                uint32 *devp = F_DIR_DEV_P(fp);
                                                dirdev = MAKEDEV(DEV_MAJOR(devp), DEV_MINOR(devp));
                                        } else
                                                dirdev = MAKEDEV(0, 0);
-                                       delete_in_dir(f_name(fp, fbuf), fp, &dirdev);
-                               }
+                                       delete_in_dir(fbuf, fp, &dirdev);
+                               } else
+                                       change_local_filter_dir(fbuf, strlen(fbuf), F_DEPTH(fp));
                        }
                }
                for (i = cur_flist->low; i <= cur_flist->high; i++) {