rsync
rsync copied to clipboard
directory permissions set too early when copying read-only directory with -r
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.