rsync icon indicating copy to clipboard operation
rsync copied to clipboard

directory permissions set too early when copying read-only directory with -r

Open alaniwi opened this issue 2 months ago • 0 comments

When copying a read-only local directory containing two or more subdirectories to a local destination, using the -r flag alone, the destination copy is set to read-only after the first subdirectory has been created, so subsequent subdirectories cannot be created. If the -t flag is added, the problem goes away. (I have not tested with remote filesystems.)

Here is the test code:

#!/bin/sh

do_tests() {
    mkdir src src/a src/b
    chmod 555 src 

    echo "--- test 1 ---"
    rsync -r src dst1/

    echo    
    echo "--- test 2 ---"
    rsync -rt src dst2/
}

tidy_up() {
    for p in src dst1 dst2
    do
	if [ -e $p ]
	then
	    chmod -R u+w $p
	    rm -fr $p
	fi
    done
}

tidy_up
do_tests
tidy_up

This gives (when run in /tmp):

--- test 1 ---
rsync: [generator] recv_generator: mkdir "/tmp/dst1/src/b" failed: Permission denied (13)
*** Skipping any contents from this failed directory ***
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1338) [sender=3.3.0]

--- test 2 ---

or if -v flags are added, then it gives:

--- test 1 ---
sending incremental file list
created directory dst1
src/
rsync: [generator] recv_generator: mkdir "/tmp/dst1/src/b" failed: Permission denied (13)
*** Skipping any contents from this failed directory ***
src/a/
src/b/

sent 108 bytes  received 55 bytes  326.00 bytes/sec
total size is 0  speedup is 0.00
rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1338) [sender=3.3.0]

--- test 2 ---
sending incremental file list
created directory dst2
src/
src/a/
src/b/

sent 108 bytes  received 55 bytes  326.00 bytes/sec
total size is 0  speedup is 0.00

Tracing the process with strace, here is part of the output in the case without -t (I've added ==> at start of relevant lines):

==>  mkdir("src/a", 040775)                  = 0
     newfstatat(AT_FDCWD, "src/a", {st_mode=S_IFDIR|0775, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0
     openat(AT_FDCWD, "src", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = 0
     newfstatat(0, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
==>  chmod("/proc/self/fd/0", 0555)          = 0
     close(0)                                = 0
     munmap(0x72abd057e000, 266240)          = 0
     brk(0x579e03f1a000)                     = 0x579e03f1a000
     newfstatat(AT_FDCWD, "src/b", 0x7ffd64cf6700, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
==>  mkdir("src/b", 040775)                  = -1 EACCES (Permission denied)

whereas here part of the strace output when -t has been added, showing the chmod being deferred until later:

==>  mkdir("src/a", 0700)                    = 0
     munmap(0x7440b4eb6000, 266240)          = 0
     newfstatat(AT_FDCWD, "src/b", 0x7ffd694c0ca0, AT_SYMLINK_NOFOLLOW) = -1 ENOENT (No such file or directory)
==>  mkdir("src/b", 0700)                    = 0
     pselect6(4, [3], [1], [3], {tv_sec=60, tv_nsec=0}, NULL) = 2 (in [3], out [1], left {tv_sec=59, tv_nsec=999998683})
     read(3, "\20\0\0\7\1\0\0\0\0\0\2\0\0\0\0\0\376\377\377\377", 32768) = 20
     write(1, "\4\0\0\7\3\0`\0", 8)          = 8
     brk(0x594fafb8f000)                     = 0x594fafb8f000
     newfstatat(AT_FDCWD, "src/a", {st_mode=S_IFDIR|0700, st_size=4096, ...}, AT_SYMLINK_NOFOLLOW) = 0
     utimensat(AT_FDCWD, "src/a", [{tv_sec=1714657695, tv_nsec=263325211} /* 2024-05-02T14:48:15.263325211+0100 */, {tv_sec=1714657664, tv_nsec=475674223} /* 2024-05-02T14:47:44.475674223+0100 */], AT_SYMLINK_NOFOLLOW) = 0
     openat(AT_FDCWD, "src/a", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = 0
     newfstatat(0, "", {st_mode=S_IFDIR|0700, st_size=4096, ...}, AT_EMPTY_PATH) = 0
     chmod("/proc/self/fd/0", 0775)          = 0
     close(0)                                = 0
     openat(AT_FDCWD, "src", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = 0
     newfstatat(0, "", {st_mode=S_IFDIR|0755, st_size=4096, ...}, AT_EMPTY_PATH) = 0
==>  chmod("/proc/self/fd/0", 0555)          = 0
     close(0)                                = 0

Behaviour seen in rsync 3.3.0, 3.2.7, 3.2.3 and 3.1.2.

alaniwi avatar May 02 '24 14:05 alaniwi