Changed "statx" to "stat_x" to try to work around a build problem on AIX.
[rsync/rsync.git] / generator.c
index 0dda9f4..e6e5003 100644 (file)
@@ -7,8 +7,9 @@
  * Copyright (C) 2003-2007 Wayne Davison
  *
  * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3 as
- * published by the Free Software Foundation.
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -42,7 +43,6 @@ extern int preserve_specials;
 extern int preserve_hard_links;
 extern int preserve_perms;
 extern int preserve_times;
-extern int omit_dir_times;
 extern int uid_ndx;
 extern int gid_ndx;
 extern int delete_mode;
@@ -81,7 +81,6 @@ extern int copy_dest;
 extern int link_dest;
 extern int whole_file;
 extern int list_only;
-extern int new_root_dir;
 extern int read_batch;
 extern int safe_symlinks;
 extern long block_size; /* "long" because popt can't set an int32. */
@@ -516,7 +515,7 @@ static void do_delete_pass(void)
                rprintf(FINFO, "                    \r");
 }
 
-int unchanged_attrs(const char *fname, struct file_struct *file, statx *sxp)
+int unchanged_attrs(const char *fname, struct file_struct *file, stat_x *sxp)
 {
        if (preserve_perms && !BITS_EQUAL(sxp->st.st_mode, file->mode, CHMOD_BITS))
                return 0;
@@ -548,12 +547,12 @@ int unchanged_attrs(const char *fname, struct file_struct *file, statx *sxp)
 }
 
 void itemize(const char *fnamecmp, struct file_struct *file, int ndx, int statret,
-            statx *sxp, int32 iflags, uchar fnamecmp_type,
+            stat_x *sxp, int32 iflags, uchar fnamecmp_type,
             const char *xname)
 {
        if (statret >= 0) { /* A from-dest-dir statret can == 1! */
                int keep_time = !preserve_times ? 0
-                   : S_ISDIR(file->mode) ? !omit_dir_times
+                   : S_ISDIR(file->mode) ? preserve_times > 1
                    : !S_ISLNK(file->mode);
 
                if (S_ISREG(file->mode) && F_LENGTH(file) != sxp->st.st_size)
@@ -830,7 +829,7 @@ static int find_fuzzy(struct file_struct *file, struct file_list *dirlist)
  * handling the file, -1 if no dest-linking occurred, or a non-negative
  * value if we found an alternate basis file. */
 static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
-                        char *cmpbuf, statx *sxp, int itemizing,
+                        char *cmpbuf, stat_x *sxp, int itemizing,
                         enum logcode code)
 {
        int best_match = -1;
@@ -881,7 +880,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
                        if (!hard_link_one(file, fname, cmpbuf, 1))
                                goto try_a_copy;
                        if (preserve_hard_links && F_IS_HLINKED(file))
-                               finish_hard_link(file, fname, &sxp->st, itemizing, code, j);
+                               finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, j);
                        if (itemizing && (verbose > 1 || stdout_format_has_i > 1)) {
                                itemize(cmpbuf, file, ndx, 1, sxp,
                                        ITEM_LOCAL_CHANGE | ITEM_XNAME_FOLLOWS,
@@ -900,7 +899,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
 #ifdef SUPPORT_HARD_LINKS
          try_a_copy: /* Copy the file locally. */
 #endif
-               if (copy_file(cmpbuf, fname, file->mode) < 0) {
+               if (copy_file(cmpbuf, fname, file->mode, 0) < 0) {
                        if (verbose) {
                                rsyserr(FINFO, errno, "copy_file %s => %s",
                                        full_fname(cmpbuf), fname);
@@ -923,7 +922,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
                }
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
-                       finish_hard_link(file, fname, &sxp->st, itemizing, code, -1);
+                       finish_hard_link(file, fname, ndx, &sxp->st, itemizing, code, -1);
 #endif
                return -2;
        }
@@ -935,7 +934,7 @@ static int try_dests_reg(struct file_struct *file, char *fname, int ndx,
  * handling the file, or -1 if no dest-linking occurred, or a non-negative
  * value if we found an alternate basis file. */
 static int try_dests_non(struct file_struct *file, char *fname, int ndx,
-                        char *cmpbuf, statx *sxp, int itemizing,
+                        char *cmpbuf, stat_x *sxp, int itemizing,
                         enum logcode code)
 {
        char lnk[MAXPATHLEN];
@@ -1051,7 +1050,7 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
                                return j;
                        }
                        if (preserve_hard_links && F_IS_HLINKED(file))
-                               finish_hard_link(file, fname, NULL, itemizing, code, -1);
+                               finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
                } else
 #endif
                        match_level = 2;
@@ -1073,6 +1072,35 @@ static int try_dests_non(struct file_struct *file, char *fname, int ndx,
        return j;
 }
 
+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));
+       }
+}
+
 static int phase = 0;
 static int dflt_perms;
 
@@ -1091,35 +1119,46 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 {
        static int missing_below = -1, excluded_below = -1;
        static const char *parent_dirname = "";
+       static struct file_struct *missing_dir = NULL, *excluded_dir = NULL;
        static struct file_list *fuzzy_dirlist = NULL;
        static int need_fuzzy_dirlist = 0;
        struct file_struct *fuzzy_file = NULL;
        int fd = -1, f_copy = -1;
-       statx sx, real_sx;
+       stat_x sx, real_sx;
        STRUCT_STAT partial_st;
        struct file_struct *back_file = NULL;
        int statret, real_ret, stat_errno;
        char *fnamecmp, *partialptr, *backupptr = NULL;
        char fnamecmpbuf[MAXPATHLEN];
        uchar fnamecmp_type;
+       int implied_dirs_are_missing = relative_paths && !implied_dirs && !inc_recurse;
        int del_opts = delete_mode || force_delete ? DEL_RECURSE : 0;
 
-       if (list_only)
-               return;
-
        if (verbose > 2)
                rprintf(FINFO, "recv_generator(%s,%d)\n", fname, ndx);
 
+       if (list_only) {
+               if (S_ISDIR(file->mode)
+                && ((!implied_dirs && !(file->flags & FLAG_XFER_DIR))
+                 || (inc_recurse && ndx != cur_flist->ndx_start - 1)))
+                       return;
+               list_file_entry(file);
+               return;
+       }
+
        if (server_filter_list.head) {
                if (excluded_below >= 0) {
-                       if (F_DEPTH(file) > excluded_below)
+                       if (F_DEPTH(file) > excluded_below
+                        && (!implied_dirs_are_missing || f_name_has_prefix(file, excluded_dir)))
                                goto skipping;
                        excluded_below = -1;
                }
                if (check_filter(&server_filter_list, fname,
                                 S_ISDIR(file->mode)) < 0) {
-                       if (S_ISDIR(file->mode))
+                       if (S_ISDIR(file->mode)) {
                                excluded_below = F_DEPTH(file);
+                               excluded_dir = file;
+                       }
                  skipping:
                        if (verbose) {
                                rprintf(FINFO,
@@ -1131,7 +1170,8 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        }
 
        if (missing_below >= 0) {
-               if (F_DEPTH(file) <= missing_below) {
+               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;
@@ -1200,6 +1240,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                if (dry_run)
                                        dry_run++;
                                missing_below = F_DEPTH(file);
+                               missing_dir = file;
                        }
                        file->flags |= FLAG_MISSING_DIR;
                }
@@ -1207,6 +1248,22 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        }
 
        if (S_ISDIR(file->mode)) {
+               if (!implied_dirs && !(file->flags & FLAG_XFER_DIR))
+                       goto cleanup;
+               if (inc_recurse && ndx != cur_flist->ndx_start - 1) {
+                       /* In inc_recurse mode we want ot make sure any missing
+                        * directories get created while we're still processing
+                        * the parent dir (which allows us to touch the parent
+                        * dir's mtime right away).  We will handle the dir in
+                        * full later (right before we handle its contents). */
+                       if (statret == 0
+                        && (S_ISDIR(sx.st.st_mode)
+                         || delete_item(fname, sx.st.st_mode, "directory", del_opts) != 0))
+                               goto cleanup; /* Any errors get reported later. */
+                       if (do_mkdir(fname, file->mode & 0700) == 0)
+                               file->flags |= FLAG_DIR_CREATED;
+                       goto cleanup;
+               }
                /* The file to be received is a directory, so we need
                 * to prepare appropriately.  If there is already a
                 * file of that name and it is *not* a directory, then
@@ -1214,20 +1271,18 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                 * (perhaps recursively) create it. */
                if (statret == 0 && !S_ISDIR(sx.st.st_mode)) {
                        if (delete_item(fname, sx.st.st_mode, "directory", del_opts) != 0)
-                               return;
+                               goto skipping_dir_contents;
                        statret = -1;
                }
                if (dry_run && statret != 0 && missing_below < 0) {
                        missing_below = F_DEPTH(file);
+                       missing_dir = file;
                        dry_run++;
                }
                real_ret = statret;
                real_sx = sx;
-               if (new_root_dir) {
-                       if (*fname == '.' && fname[1] == '\0')
-                               statret = -1;
-                       new_root_dir = 0;
-               }
+               if (file->flags & FLAG_DIR_CREATED)
+                       statret = -1;
                if (!preserve_perms) { /* See comment in non-dir code below. */
                        file->mode = dest_mode(file->mode, sx.st.st_mode,
                                               dflt_perms, statret == 0);
@@ -1252,9 +1307,11 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                rsyserr(FERROR, errno,
                                        "recv_generator: mkdir %s failed",
                                        full_fname(fname));
+                         skipping_dir_contents:
                                rprintf(FERROR,
                                    "*** Skipping any contents from this failed directory ***\n");
                                missing_below = F_DEPTH(file);
+                               missing_dir = file;
                                file->flags |= FLAG_MISSING_DIR;
                                goto cleanup;
                        }
@@ -1334,7 +1391,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                        itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
 #ifdef SUPPORT_HARD_LINKS
                                if (preserve_hard_links && F_IS_HLINKED(file))
-                                       finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
+                                       finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
 #endif
                                if (remove_source_files == 1)
                                        goto return_with_success;
@@ -1361,8 +1418,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                statret = 1;
                }
 #ifdef SUPPORT_HARD_LINKS
-               if (preserve_hard_links && F_HLINK_NOT_LAST(file))
+               if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+                       cur_flist->in_progress++;
                        goto cleanup;
+               }
 #endif
                if (do_symlink(sl, fname) != 0) {
                        rsyserr(FERROR, errno, "symlink %s -> \"%s\" failed",
@@ -1377,7 +1436,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                rprintf(code, "%s -> %s\n", fname, sl);
 #ifdef SUPPORT_HARD_LINKS
                        if (preserve_hard_links && F_IS_HLINKED(file))
-                               finish_hard_link(file, fname, NULL, itemizing, code, -1);
+                               finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
 #endif
                        /* This does not check remove_source_files == 1
                         * because this is one of the items that the old
@@ -1413,7 +1472,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                        itemize(fname, file, ndx, 0, &sx, 0, 0, NULL);
 #ifdef SUPPORT_HARD_LINKS
                                if (preserve_hard_links && F_IS_HLINKED(file))
-                                       finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
+                                       finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
 #endif
                                if (remove_source_files == 1)
                                        goto return_with_success;
@@ -1438,8 +1497,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                statret = 1;
                }
 #ifdef SUPPORT_HARD_LINKS
-               if (preserve_hard_links && F_HLINK_NOT_LAST(file))
+               if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+                       cur_flist->in_progress++;
                        goto cleanup;
+               }
 #endif
                if (verbose > 2) {
                        rprintf(FINFO, "mknod(%s, 0%o, [%ld,%ld])\n",
@@ -1459,7 +1520,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                                rprintf(code, "%s\n", fname);
 #ifdef SUPPORT_HARD_LINKS
                        if (preserve_hard_links && F_IS_HLINKED(file))
-                               finish_hard_link(file, fname, NULL, itemizing, code, -1);
+                               finish_hard_link(file, fname, ndx, NULL, itemizing, code, -1);
 #endif
                        if (remove_source_files == 1)
                                goto return_with_success;
@@ -1558,8 +1619,10 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
 
        if (statret != 0) {
 #ifdef SUPPORT_HARD_LINKS
-               if (preserve_hard_links && F_HLINK_NOT_LAST(file))
+               if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+                       cur_flist->in_progress++;
                        goto cleanup;
+               }
 #endif
                if (stat_errno == ENOENT)
                        goto notify_others;
@@ -1568,7 +1631,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                goto cleanup;
        }
 
-       if (append_mode > 0 && sx.st.st_size > F_LENGTH(file))
+       if (append_mode > 0 && sx.st.st_size >= F_LENGTH(file))
                goto cleanup;
 
        if (fnamecmp_type <= FNAMECMP_BASIS_DIR_HIGH)
@@ -1585,7 +1648,7 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        itemize(fnamecmp, file, ndx, statret, &sx, 0, 0, NULL);
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
-                       finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
+                       finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
 #endif
                if (remove_source_files != 1)
                        goto cleanup;
@@ -1603,8 +1666,23 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                statret = 0;
        }
 
-       if (!do_xfers || read_batch || whole_file)
+       if (!do_xfers)
+               goto notify_others;
+
+       if (read_batch || whole_file) {
+               if (inplace && make_backups > 0 && fnamecmp_type == FNAMECMP_FNAME) {
+                       if (!(backupptr = get_backup_name(fname)))
+                               goto cleanup;
+                       if (!(back_file = make_file(fname, NULL, NULL, 0, NO_FILTERS)))
+                               goto pretend_missing;
+                       if (copy_file(fname, backupptr, back_file->mode, 1) < 0) {
+                               unmake_file(back_file);
+                               back_file = NULL;
+                               goto cleanup;
+                       }
+               }
                goto notify_others;
+       }
 
        if (fuzzy_dirlist) {
                int j = flist_find(fuzzy_dirlist, file);
@@ -1613,16 +1691,16 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        }
 
        /* open the file */
-       fd = do_open(fnamecmp, O_RDONLY, 0);
-
-       if (fd == -1) {
+       if ((fd = do_open(fnamecmp, O_RDONLY, 0)) < 0) {
                rsyserr(FERROR, errno, "failed to open %s, continuing",
                        full_fname(fnamecmp));
          pretend_missing:
                /* pretend the file didn't exist */
 #ifdef SUPPORT_HARD_LINKS
-               if (preserve_hard_links && F_HLINK_NOT_LAST(file))
+               if (preserve_hard_links && F_HLINK_NOT_LAST(file)) {
+                       cur_flist->in_progress++;
                        goto cleanup;
+               }
 #endif
                statret = real_ret = -1;
                goto notify_others;
@@ -1641,14 +1719,17 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                        rsyserr(FERROR, errno, "unlink %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) {
+               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, errno, "open %s",
                                full_fname(backupptr));
                        unmake_file(back_file);
+                       back_file = NULL;
                        close(fd);
                        goto cleanup;
                }
@@ -1696,22 +1777,24 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
        if (!do_xfers) {
 #ifdef SUPPORT_HARD_LINKS
                if (preserve_hard_links && F_IS_HLINKED(file))
-                       finish_hard_link(file, fname, &sx.st, itemizing, code, -1);
+                       finish_hard_link(file, fname, ndx, &sx.st, itemizing, code, -1);
 #endif
                goto cleanup;
        }
        if (read_batch)
                goto cleanup;
 
-       if (statret != 0 || whole_file) {
+       if (statret != 0 || whole_file)
                write_sum_head(f_out, NULL);
-               goto cleanup;
+       else {
+               generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy);
+               close(fd);
        }
 
-       generate_and_send_sums(fd, sx.st.st_size, f_out, f_copy);
-
-       if (f_copy >= 0) {
-               close(f_copy);
+  cleanup:
+       if (back_file) {
+               if (f_copy >= 0)
+                       close(f_copy);
                set_file_attrs(backupptr, back_file, NULL, NULL, 0);
                if (verbose > 1) {
                        rprintf(FINFO, "backed up %s to %s\n",
@@ -1720,9 +1803,6 @@ static void recv_generator(char *fname, struct file_struct *file, int ndx,
                unmake_file(back_file);
        }
 
-       close(fd);
-
-  cleanup:
 #ifdef SUPPORT_ACLS
        if (preserve_acls)
                free_acl(&sx);
@@ -1751,15 +1831,16 @@ static void touch_up_dirs(struct file_list *flist, int ndx)
         * transfer and/or re-set any tweaked modified-time values. */
        for (i = start; i <= end; i++, counter++) {
                file = flist->files[i];
+               if (!S_ISDIR(file->mode)
+                || (relative_paths && !implied_dirs && !(file->flags & FLAG_XFER_DIR)))
+                       continue;
                if (verbose > 3) {
                        fname = f_name(file, NULL);
                        rprintf(FINFO, "touch_up_dirs: %s (%d)\n",
                                NS(fname), i);
                }
-               if (!F_IS_ACTIVE(file) || !S_ISDIR(file->mode)
-                || file->flags & FLAG_MISSING_DIR)
-                       continue;
-               if (!need_retouch_dir_times && file->mode & S_IWUSR)
+               if (!F_IS_ACTIVE(file) || file->flags & FLAG_MISSING_DIR
+                || (!need_retouch_dir_times && file->mode & S_IWUSR))
                        continue;
                fname = f_name(file, NULL);
                if (!(file->mode & S_IWUSR))
@@ -1784,9 +1865,9 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
        while (preserve_hard_links && (ndx = get_hlink_num()) != -1) {
                flist = flist_for_ndx(ndx);
                assert(flist != NULL);
-               file = flist->files[ndx];
+               file = flist->files[ndx - flist->ndx_start];
                assert(file->flags & FLAG_HLINKED);
-               finish_hard_link(file, f_name(file, fbuf), NULL, itemizing, code, -1);
+               finish_hard_link(file, f_name(file, fbuf), ndx, NULL, itemizing, code, -1);
        }
 #endif
 
@@ -1838,6 +1919,11 @@ void check_for_finished_files(int itemizing, enum logcode code, int check_redo)
                        maybe_flush_socket(1);
                }
 
+               if (delete_during == 2 || !dir_tweaking) {
+                       /* Skip directory touch-up. */
+               } else if (first_flist->ndx_start != 0)
+                       touch_up_dirs(dir_flist, first_flist->parent_ndx);
+
                flist_free(first_flist); /* updates first_flist */
        }
 }
@@ -1869,7 +1955,7 @@ void generate_files(int f_out, const char *local_name)
        }
        solo_file = local_name;
        dir_tweaking = !(list_only || solo_file || dry_run);
-       need_retouch_dir_times = preserve_times && !omit_dir_times;
+       need_retouch_dir_times = preserve_times > 1;
        lull_mod = allowed_lull * 5;
 
        if (verbose > 2)
@@ -1903,12 +1989,19 @@ void generate_files(int f_out, const char *local_name)
        dflt_perms = (ACCESSPERMS & ~orig_umask);
 
        do {
+#ifdef SUPPORT_HARD_LINKS
+               if (preserve_hard_links && inc_recurse) {
+                       while (!flist_eof && file_total < FILECNT_LOOKAHEAD/2)
+                               wait_for_receiver();
+               }
+#endif
+
                if (inc_recurse && cur_flist->ndx_start) {
                        struct file_struct *fp = dir_flist->files[cur_flist->parent_ndx];
                        f_name(fp, fbuf);
                        ndx = cur_flist->ndx_start - 1;
                        recv_generator(fbuf, fp, ndx, itemizing, code, f_out);
-                       if (delete_during) {
+                       if (delete_during && dry_run < 2) {
                                if (BITS_SETnUNSET(fp->flags, FLAG_XFER_DIR, FLAG_MISSING_DIR)) {
                                        dev_t dirdev;
                                        if (one_file_system) {
@@ -1926,14 +2019,6 @@ void generate_files(int f_out, const char *local_name)
                        if (!F_IS_ACTIVE(file))
                                continue;
 
-                       if (inc_recurse && S_ISDIR(file->mode)) {
-                               /* Regular dirs are at the end, so we can stop. */
-                               if (F_DEPTH(file))
-                                       break;
-                               /* A dot-dir is at the start, so just skip it. */
-                               continue;
-                       }
-
 #ifdef ICONV_OPTION
                        if (ic_ndx)
                                ndx = F_NDX(file);
@@ -2015,7 +2100,8 @@ void generate_files(int f_out, const char *local_name)
        if (delete_after && !solo_file && file_total > 0)
                do_delete_pass();
 
-       if ((need_retouch_dir_perms || need_retouch_dir_times) && dir_tweaking)
+       if ((need_retouch_dir_perms || need_retouch_dir_times)
+        && dir_tweaking && (!inc_recurse || delete_during == 2))
                touch_up_dirs(dir_flist, -1);
 
        if (max_delete >= 0 && deletion_count > max_delete) {