From 691525073e0c9dfb7287769aaa347f79118957d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org> Date: Thu, 28 Nov 2024 18:39:53 +0100 Subject: [PATCH 1/6] s3:vfs_crossrename: avoid locking panic in copy_reg() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use low level backend functions that don't go through the FSA layer. Done via calling transfer_file() as it was in version before 5c18f07 BUG: https://bugzilla.samba.org/show_bug.cgi?id=15724 Signed-off-by: Pavel Filipenský <pfilipensky@samba.org> --- source3/modules/vfs_crossrename.c | 125 ++++++++++++++++++++---------- 1 file changed, 84 insertions(+), 41 deletions(-) diff --git a/source3/modules/vfs_crossrename.c b/source3/modules/vfs_crossrename.c index 79f13c28da59..c260bdee1948 100644 --- a/source3/modules/vfs_crossrename.c +++ b/source3/modules/vfs_crossrename.c @@ -54,10 +54,12 @@ static NTSTATUS copy_reg(vfs_handle_struct *handle, struct files_struct *dstfsp, const struct smb_filename *dest) { - NTSTATUS status; - struct smb_filename *full_fname_src = NULL; - struct smb_filename *full_fname_dst = NULL; + NTSTATUS status = NT_STATUS_OK; int ret; + off_t off; + int ifd = -1; + int ofd = -1; + struct timespec ts[2]; if (!VALID_STAT(source->st)) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; @@ -79,21 +81,6 @@ static NTSTATUS copy_reg(vfs_handle_struct *handle, goto out; } - full_fname_src = full_path_from_dirfsp_atname(talloc_tos(), - srcfsp, - source); - if (full_fname_src == NULL) { - status = NT_STATUS_NO_MEMORY; - goto out; - } - full_fname_dst = full_path_from_dirfsp_atname(talloc_tos(), - dstfsp, - dest); - if (full_fname_dst == NULL) { - status = NT_STATUS_NO_MEMORY; - goto out; - } - ret = SMB_VFS_NEXT_UNLINKAT(handle, dstfsp, dest, @@ -103,40 +90,96 @@ static NTSTATUS copy_reg(vfs_handle_struct *handle, goto out; } + ifd = openat(fsp_get_pathref_fd(srcfsp), + source->base_name, + O_RDONLY, + 0); + if (ifd < 0) { + status = map_nt_error_from_unix(errno); + goto out; + } + + ofd = openat(fsp_get_pathref_fd(dstfsp), + dest->base_name, + O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, + 0600); + if (ofd < 0) { + status = map_nt_error_from_unix(errno); + goto out; + } + + off = transfer_file(ifd, ofd, source->st.st_ex_size); + if (off == -1) { + status = map_nt_error_from_unix(errno); + goto out; + } + + ret = fchown(ofd, source->st.st_ex_uid, source->st.st_ex_gid); + if (ret == -1 && errno != EPERM) { + status = map_nt_error_from_unix(errno); + goto out; + } + /* - * copy_internals() takes attribute values from the NTrename call. - * - * From MS-CIFS: - * - * "If the attribute is 0x0000, then only normal files are renamed. - * If the system file or hidden attributes are specified, then the - * rename is inclusive of both special types." + * fchown turns off set[ug]id bits for non-root, + * so do the chmod last. */ - status = copy_internals(talloc_tos(), - handle->conn, - NULL, - srcfsp, /* src_dirfsp */ - full_fname_src, - dstfsp, /* dst_dirfsp */ - full_fname_dst, - FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM); - if (!NT_STATUS_IS_OK(status)) { + ret = fchmod(ofd, source->st.st_ex_mode & 07777); + if (ret == -1 && errno != EPERM) { + status = map_nt_error_from_unix(errno); goto out; } - ret = SMB_VFS_NEXT_UNLINKAT(handle, - srcfsp, - source, - 0); + /* Try to copy the old file's modtime and access time. */ + ts[0] = source->st.st_ex_atime; + ts[1] = source->st.st_ex_mtime; + ret = futimens(ofd, ts); + if (ret == -1) { + DBG_DEBUG("Updating the time stamp on destinaton '%s' failed " + "with '%s'. Rename operation can continue.\n", + dest->base_name, + strerror(errno)); + } + + ret = close(ifd); + if (ret == -1) { + status = map_nt_error_from_unix(errno); + goto out; + } + ifd = -1; + + ret = close(ofd); if (ret == -1) { status = map_nt_error_from_unix(errno); goto out; } + ofd = -1; + + ret = SMB_VFS_NEXT_UNLINKAT(handle, srcfsp, source, 0); + if (ret == -1) { + status = map_nt_error_from_unix(errno); + } - out: +out: + if (ifd != -1) { + ret = close(ifd); + if (ret == -1) { + DBG_DEBUG("Failed to close %s (%d): %s.\n", + source->base_name, + ifd, + strerror(errno)); + } + } + if (ofd != -1) { + ret = close(ofd); + if (ret == -1) { + DBG_DEBUG("Failed to close %s (%d): %s.\n", + dest->base_name, + ofd, + strerror(errno)); + } + } - TALLOC_FREE(full_fname_src); - TALLOC_FREE(full_fname_dst); return status; } -- GitLab From f3f2a2d6683d8294c56f4ad275bb23d7a03c7aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org> Date: Thu, 28 Nov 2024 18:32:25 +0100 Subject: [PATCH 2/6] s3:vfs_crossrename: crossrename_renameat() needs to return 0 if copy_reg() is successful MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG: https://bugzilla.samba.org/show_bug.cgi?id=15724 Signed-off-by: Pavel Filipenský <pfilipensky@samba.org> --- source3/modules/vfs_crossrename.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source3/modules/vfs_crossrename.c b/source3/modules/vfs_crossrename.c index c260bdee1948..5c6006931864 100644 --- a/source3/modules/vfs_crossrename.c +++ b/source3/modules/vfs_crossrename.c @@ -213,6 +213,7 @@ static int crossrename_renameat(vfs_handle_struct *handle, smb_fname_src, dstfsp, smb_fname_dst); + result = 0; if (!NT_STATUS_IS_OK(status)) { errno = map_errno_from_nt_status(status); result = -1; -- GitLab From cd26b847d044abd2ce0c3918a9c87bce1f4712c8 Mon Sep 17 00:00:00 2001 From: Jones Syue <jonessyue@qnap.com> Date: Thu, 26 Sep 2024 17:17:14 +0800 Subject: [PATCH 3/6] s3:vfs_crossrename: add back checking for errno ENOENT strace gives a clue: samba try to remove 'file.txt' in the dst folder but actually it is not existed yet, and got an errno = ENOENT, renameat(32, "file.txt", 31, "file.txt") = -1 EXDEV (Invalid cross-device link) unlinkat(31, "file.txt", 0) = -1 ENOENT (No such file or directory) Commit 5c18f074be92 ("s3: VFS: crossrename. Use real dirfsp for SMB_VFS_RENAMEAT()") seems unintentionally removed errno ENOENT checking, so add it back could address 1st issue. BUG: https://bugzilla.samba.org/show_bug.cgi?id=15724 Signed-off-by: Jones Syue <jonessyue@qnap.com> --- source3/modules/vfs_crossrename.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/modules/vfs_crossrename.c b/source3/modules/vfs_crossrename.c index 5c6006931864..0ce4b44898de 100644 --- a/source3/modules/vfs_crossrename.c +++ b/source3/modules/vfs_crossrename.c @@ -85,7 +85,7 @@ static NTSTATUS copy_reg(vfs_handle_struct *handle, dstfsp, dest, 0); - if (ret == -1) { + if (ret == -1 && errno != ENOENT) { status = map_nt_error_from_unix(errno); goto out; } -- GitLab From aa23df456abe4453e5baf839826434f363ca0711 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org> Date: Mon, 2 Dec 2024 22:27:39 +0100 Subject: [PATCH 4/6] docs:manpage: vfs_crossrename is not fully stackable VFS module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG: https://bugzilla.samba.org/show_bug.cgi?id=15724 Signed-off-by: Pavel Filipenský <pfilipensky@samba.org> --- docs-xml/manpages/vfs_crossrename.8.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs-xml/manpages/vfs_crossrename.8.xml b/docs-xml/manpages/vfs_crossrename.8.xml index 72d67d685b1f..7ea0b50cc9b1 100644 --- a/docs-xml/manpages/vfs_crossrename.8.xml +++ b/docs-xml/manpages/vfs_crossrename.8.xml @@ -62,7 +62,10 @@ </varlistentry> </variablelist> - <para>This module is stackable.</para> + <para> This module is not fully stackable. It can be combined with other + modules, but should be the last module in the <command>vfs objects</command> + list. It directly access the files in the OS filesystem. + </para> </refsect1> -- GitLab From 62741e4cdd368b162b961a8afef4b9dc3142739c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org> Date: Wed, 4 Dec 2024 11:02:18 +0100 Subject: [PATCH 5/6] selftest: Add test for vfs crossrename module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG: https://bugzilla.samba.org/show_bug.cgi?id=15724 Signed-off-by: Pavel Filipenský <pfilipensky@samba.org> --- selftest/target/Samba3.pm | 12 +++++ source3/script/tests/test_recycle.sh | 80 +++++++++++++++++++++++++++- source3/selftest/tests.py | 2 +- 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/selftest/target/Samba3.pm b/selftest/target/Samba3.pm index 05027b26f469..d7abda753547 100755 --- a/selftest/target/Samba3.pm +++ b/selftest/target/Samba3.pm @@ -2783,6 +2783,9 @@ sub provision($$) my $recycle_shrdir="$shrdir/recycle"; push(@dirs,$recycle_shrdir); + my $recycle_shrdir2="$shrdir/recycle2"; + push(@dirs,$recycle_shrdir2); + my $fakedircreatetimes_shrdir="$shrdir/fakedircreatetimes"; push(@dirs,$fakedircreatetimes_shrdir); @@ -3718,6 +3721,15 @@ sub provision($$) recycle : exclude = *.tmp recycle : directory_mode = 755 +[recycle2] + copy = tmp + path = $recycle_shrdir2 + vfs objects = recycle crossrename + recycle : repository = .trash + recycle : exclude = *.tmp + recycle : directory_mode = 755 + wide links = yes + [fakedircreatetimes] copy = tmp path = $fakedircreatetimes_shrdir diff --git a/source3/script/tests/test_recycle.sh b/source3/script/tests/test_recycle.sh index ba1d0a598b14..779683f4822f 100755 --- a/source3/script/tests/test_recycle.sh +++ b/source3/script/tests/test_recycle.sh @@ -29,7 +29,8 @@ export SAMBA_DEPRECATED_SUPPRESS # Define the test environment/filenames. # -share_test_dir="$LOCAL_PATH" +share_test_dir="$LOCAL_PATH/recycle" +share_test_dir2="$LOCAL_PATH/recycle2" # # Cleanup function. @@ -43,6 +44,13 @@ do_cleanup() rm -f testfile2.tmp rm -rf .trash ) + ( + #subshell. + cd "$share_test_dir2" || return + rm -f testfile3 + rm -f testfile4.tmp + rm -rf .trash + ) } # @@ -50,6 +58,25 @@ do_cleanup() # do_cleanup +# Setup .trash on a different filesystem to test crossrename +# /tmp or /dev/shm should provide tmpfs +# +for T in /tmp /dev/shm +do + if df --portability --print-type $T 2>/dev/null | grep -q tmpfs; then + TRASHDIR=$T + break + fi +done + +if [ -z $TRASHDIR ]; then + echo "No tmpfs filesystem found." + exit 1 +fi + +TRASHDIR=$(mktemp -d /$TRASHDIR/.trash_XXXXXX) +chmod 0755 $TRASHDIR +ln -s $TRASHDIR $share_test_dir2/.trash test_recycle() { @@ -90,12 +117,61 @@ quit return 0 } +test_recycle_crossrename() +{ + tmpfile=$PREFIX/smbclient_interactive_prompt_commands + echo " +put $tmpfile testfile3 +put $tmpfile testfile4.tmp +del testfile3 +del testfile4.tmp +quit +" > $tmpfile + cmd='CLI_FORCE_INTERACTIVE=yes $SMBCLIENT -U$USERNAME%$PASSWORD //$SERVER/recycle2 -I$SERVER_IP $ADDARGS < $tmpfile 2>&1' + eval echo "$cmd" + out=$(eval "$cmd") + ret=$? + rm -f "$tmpfile" + + if [ $ret != 0 ]; then + printf "%s\n" "$out" + printf "failed recycle smbclient run with error %s\n" "$ret" + return 1 + fi + + test -e "$share_test_dir2/.trash/testfile3" || { + printf ".trash/testfile3 expected to exist but does NOT exist\n" + return 1 + } + test -e "$share_test_dir2/.trash/testfile4.tmp" && { + printf ".trash/testfile4.tmp not expected to exist but DOES exist\n" + return 1 + } + deviceid1=`stat -c '%d' "$share_test_dir2/"` + deviceid2=`stat -c '%d' "$share_test_dir2/.trash/"` + test "$deviceid1=" != "$deviceid2" || { + printf ".trash/ should be on a different filesystem!\n" + return 1 + } + perm_want=755 + perm_is=`stat -c '%a' "$share_test_dir2/.trash/"` + test "$perm_is" = "$perm_want" || { + printf ".trash/ permission should be $perm_want but is $perm_is\n" + return 1 + } + return 0 +} + panic_count_0=$(grep -c PANIC $SMBD_TEST_LOG) testit "recycle" \ test_recycle || failed=$((failed + 1)) +testit "recycle_crossrename" \ + test_recycle_crossrename || + failed=$((failed + 1)) + panic_count_1=$(grep -c PANIC $SMBD_TEST_LOG) testit "check_panic" test $panic_count_0 -eq $panic_count_1 || failed=$(expr $failed + 1) @@ -103,5 +179,7 @@ testit "check_panic" test $panic_count_0 -eq $panic_count_1 || failed=$(expr $fa # # Cleanup. do_cleanup +# Cleanup above only deletes a symlink, delete also /tmp/.trash_XXXXXX dir +rm -rf "$TRASHDIR" testok "$0" "$failed" diff --git a/source3/selftest/tests.py b/source3/selftest/tests.py index 6b690e2fc1f2..4540aef24b52 100755 --- a/source3/selftest/tests.py +++ b/source3/selftest/tests.py @@ -784,7 +784,7 @@ for env in ["fileserver"]: plantestsuite("samba3.blackbox.force_create_mode", env, [os.path.join(samba3srcdir, "script/tests/test_force_create_mode.sh"), '$SERVER', '$DOMAIN', '$USERNAME', '$PASSWORD', '$PREFIX', env, smbclient3]) plantestsuite("samba3.blackbox.dropbox", env, [os.path.join(samba3srcdir, "script/tests/test_dropbox.sh"), '$SERVER', '$DOMAIN', 'gooduser', '$PASSWORD', '$PREFIX', env, smbclient3]) plantestsuite("samba3.blackbox.offline", env, [os.path.join(samba3srcdir, "script/tests/test_offline.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/offline', smbclient3]) - plantestsuite("samba3.blackbox.recycle", env, [os.path.join(samba3srcdir, "script/tests/test_recycle.sh"), '$SERVER', '$SERVER_IP', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/recycle', '$PREFIX', smbclient3]) + plantestsuite("samba3.blackbox.recycle", env, [os.path.join(samba3srcdir, "script/tests/test_recycle.sh"), '$SERVER', '$SERVER_IP', '$USERNAME', '$PASSWORD', '$LOCAL_PATH', '$PREFIX', smbclient3]) plantestsuite("samba3.blackbox.fakedircreatetimes", env, [os.path.join(samba3srcdir, "script/tests/test_fakedircreatetimes.sh"), '$SERVER', '$SERVER_IP', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/fakedircreatetimes', '$PREFIX', smbclient3]) plantestsuite("samba3.blackbox.shadow_copy2.NT1", env + "_smb1_done", [os.path.join(samba3srcdir, "script/tests/test_shadow_copy.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbclient3, '-m', 'NT1']) plantestsuite("samba3.blackbox.shadow_copy2.SMB3", env, [os.path.join(samba3srcdir, "script/tests/test_shadow_copy.sh"), '$SERVER', '$SERVER_IP', '$DOMAIN', '$USERNAME', '$PASSWORD', '$LOCAL_PATH/shadow', smbclient3, '-m', 'SMB3']) -- GitLab From c2cb4004f7a996863970c47471b55b9e6ee9caed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20Filipensk=C3=BD?= <pfilipensky@samba.org> Date: Wed, 11 Dec 2024 22:33:17 +0100 Subject: [PATCH 6/6] s3:open.c: Fix a typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pavel Filipenský <pfilipensky@samba.org> --- source3/smbd/open.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source3/smbd/open.c b/source3/smbd/open.c index b87b74988ae4..8aac03677a21 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -4749,7 +4749,7 @@ mkdir_first: &rhow); if (ret == -1 && errno == EINVAL) { /* - * This is the strategie we use without having + * This is the strategy we use without having * renameat2(RENAME_NOREPLACE): * * renameat() is able to replace a directory if the source is -- GitLab