]> git.ipfire.org Git - thirdparty/rsync.git/commitdiff
main: fix --mkpath + --dry-run file-to-file copy (#880)
authorAndrew Tridgell <andrew@tridgell.net>
Fri, 5 Jun 2026 01:29:18 +0000 (11:29 +1000)
committerAndrew Tridgell <andrew@tridgell.net>
Fri, 5 Jun 2026 01:51:30 +0000 (11:51 +1000)
A single-file --mkpath copy whose destination parent does not exist
failed under --dry-run: make_path() only *reports* the directories it
would create in a dry run, so change_dir#3 then tried to chdir into a
parent that isn't there and aborted with "change_dir#3 ... failed".

When the parent is genuinely missing in a dry run, skip the chdir and
mark the destination as not-yet-present (dry_run++), exactly as the
multi-file/dir-creation path already does, so the generator doesn't
probe the missing tree.  Gating it on the missing-parent case keeps an
ordinary file-to-file dry run chdir'ing into and itemizing against an
existing destination.

Fixes: #880
Co-authored-by: Stiliyan Tonev (Bark) <stiliyan21@gmail.com>
main.c

diff --git a/main.c b/main.c
index 5aac6cfc6de558497c201c2584f1e092883f583d..9b52bbe6acf5d589bb5c23eb3341e174c8297581 100644 (file)
--- a/main.c
+++ b/main.c
@@ -832,7 +832,16 @@ static char *get_local_name(struct file_list *flist, char *dest_path)
                dest_path = "/";
 
        *cp = '\0';
-       if (!change_dir(dest_path, CD_NORMAL)) {
+       if (dry_run && mkpath_dest_arg && do_stat(dest_path, &st) < 0) {
+               /* --mkpath would have created this parent dir, but a dry run did
+                * not, so don't chdir into it; flag the destination as not yet
+                * present (as the dir-creation path above does) so the generator
+                * doesn't try to compare against the missing tree (#880).  Only
+                * the missing-parent case is touched, so an ordinary file-to-file
+                * dry run still itemizes against an existing destination. */
+               dry_run++;
+               change_dir(dest_path, CD_SKIP_CHDIR);
+       } else if (!change_dir(dest_path, CD_NORMAL)) {
                rsyserr(FERROR, errno, "change_dir#3 %s failed",
                        full_fname(dest_path));
                exit_cleanup(RERR_FILESELECT);