diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c index 8d073ff8cbd3..0c7ae8c0a296 100644 --- a/module/os/linux/zfs/zpl_inode.c +++ b/module/os/linux/zfs/zpl_inode.c @@ -511,8 +511,18 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry, zuserns_t *user_ns = NULL; #endif - /* We don't have renameat2(2) support */ + /* + * (1) RENAME_NOREPLACE: this flag indicates that if the target of + * the rename exists the rename should fail with -EEXIST instead of + * replacing the target. The VFS already checks for existence, so + * for local filesystems the RENAME_NOREPLACE implementation is + * equivalent to plain rename. + */ +#ifdef RENAME_NOREPLACE /* Appeared in 3.15 */ + if (flags != 0 && flags != RENAME_NOREPLACE) +#else if (flags) +#endif return (-EINVAL); crhold(cr); diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index 65b64f4fa8cd..7fce4b194f7d 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -802,8 +802,8 @@ tests = ['removal_all_vdev', 'removal_cancel', 'removal_check_space', tags = ['functional', 'removal'] [tests/functional/rename_dirs] -tests = ['rename_dirs_001_pos'] -tags = ['functional', 'rename_dirs'] +tests = ['rename_dirs_001_pos', 'rename_no_replace_002_pos'] +tags = ['functional', 'rename_dirs', 'rename_no_replace'] [tests/functional/replacement] tests = ['attach_import', 'attach_multiple', 'attach_rebuild', diff --git a/tests/zfs-tests/cmd/.gitignore b/tests/zfs-tests/cmd/.gitignore index 0ec450e248db..58ec79332523 100644 --- a/tests/zfs-tests/cmd/.gitignore +++ b/tests/zfs-tests/cmd/.gitignore @@ -36,6 +36,7 @@ /write_dos_attributes /xattrtest /zed_fd_spill-zedlet +/rename_no_replace /suid_write_to_file /cp_files /ctime diff --git a/tests/zfs-tests/cmd/Makefile.am b/tests/zfs-tests/cmd/Makefile.am index 673a18b4c083..753b8cb61ce5 100644 --- a/tests/zfs-tests/cmd/Makefile.am +++ b/tests/zfs-tests/cmd/Makefile.am @@ -108,8 +108,8 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/edonr_test %D%/skein_test \ libicp.la \ libspl.la \ libspl_assert.la -%C%_sha2_test_LDADD = $(%C%_skein_test_LDADD) -%C%_edonr_test_LDADD = $(%C%_skein_test_LDADD) +%C%_sha2_test_LDADD = $(%C%_skein_test_LDADD) +%C%_edonr_test_LDADD = $(%C%_skein_test_LDADD) %C%_blake3_test_LDADD = $(%C%_skein_test_LDADD) @@ -119,6 +119,7 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/user_ns_exec scripts_zfs_tests_bin_PROGRAMS += %D%/xattrtest scripts_zfs_tests_bin_PROGRAMS += %D%/zed_fd_spill-zedlet scripts_zfs_tests_bin_PROGRAMS += %D%/idmap_util +scripts_zfs_tests_bin_PROGRAMS += %D%/rename_no_replace %C%_idmap_util_LDADD = libspl.la diff --git a/tests/zfs-tests/cmd/rename_no_replace.c b/tests/zfs-tests/cmd/rename_no_replace.c new file mode 100644 index 000000000000..9e05b592d81a --- /dev/null +++ b/tests/zfs-tests/cmd/rename_no_replace.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: 0BSD + +#undef NDEBUG +#include +#include +#include +#include +#include +#include + +#ifdef RENAME_NOREPLACE +static void makeff(int in, size_t sz, struct stat *madeff) { + int ff = openat(in, "from", + O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_CLOEXEC, 0644); + assert(write(ff, "from", sz) == sz); + fstat(ff, madeff); + close(ff); +} + +int main(int argc, const char **argv) { + int from = argc > 1 ? + open(argv[1], O_PATH | O_DIRECTORY | O_CLOEXEC) : AT_FDCWD; + int to = argc > 2 ? + open(argv[2], O_PATH | O_DIRECTORY | O_CLOEXEC) : AT_FDCWD; + assert(from != -1 && to != -1); + + struct stat ffbuf, frombuf, tobuf; + makeff(from, 3, &ffbuf); + fstatat(from, "from", &frombuf, 0); + assert(!memcmp(&ffbuf, &frombuf, sizeof (struct stat))); + assert(!renameat2(from, "from", to, "to", RENAME_NOREPLACE)); + assert(!fstatat(to, "to", &tobuf, 0)); + assert(!memcmp(&ffbuf, &tobuf, sizeof (struct stat))); + + struct stat ffbuf2, frombuf2, from2buf2, tobuf2; + makeff(from, 4, &ffbuf2); + fstatat(from, "from", &frombuf2, 0); + assert(!memcmp(&ffbuf2, &frombuf2, sizeof (struct stat))); + assert(memcmp(&ffbuf, &ffbuf2, sizeof (struct stat))); + assert(renameat2(from, "from", to, "to", RENAME_NOREPLACE) == -1 && + errno == EEXIST); + assert(!fstatat(from, "from", &from2buf2, 0)); + assert(!memcmp(&frombuf2, &from2buf2, sizeof (struct stat))); + assert(!fstatat(to, "to", &tobuf2, 0)); + assert(!memcmp(&tobuf, &tobuf2, sizeof (struct stat))); +} +#else +int main(void) { + abort(); +} +#endif diff --git a/tests/zfs-tests/include/commands.cfg b/tests/zfs-tests/include/commands.cfg index 30514361ad57..8a18d6a9431c 100644 --- a/tests/zfs-tests/include/commands.cfg +++ b/tests/zfs-tests/include/commands.cfg @@ -216,6 +216,7 @@ export ZFSTEST_FILES='badsend write_dos_attributes xattrtest stride_dd + rename_no_replace zed_fd_spill-zedlet suid_write_to_file cp_files diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 2d99027754c4..ed1def8dcfca 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -270,6 +270,8 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \ functional/history/sparc.migratedpool.DAT.Z \ functional/history/sparc.orig_history.txt \ functional/history/zfs-pool-v4.dat.Z \ + functional/idmap_mount/idmap_mount.cfg \ + functional/idmap_mount/idmap_mount_common.kshlib \ functional/inheritance/config001.cfg \ functional/inheritance/config002.cfg \ functional/inheritance/config003.cfg \ @@ -378,9 +380,7 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \ functional/zvol/zvol_common.shlib \ functional/zvol/zvol_ENOSPC/zvol_ENOSPC.cfg \ functional/zvol/zvol_misc/zvol_misc_common.kshlib \ - functional/zvol/zvol_swap/zvol_swap.cfg \ - functional/idmap_mount/idmap_mount.cfg \ - functional/idmap_mount/idmap_mount_common.kshlib + functional/zvol/zvol_swap/zvol_swap.cfg nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/acl/off/cleanup.ksh \ @@ -414,10 +414,10 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/alloc_class/alloc_class_013_pos.ksh \ functional/alloc_class/cleanup.ksh \ functional/alloc_class/setup.ksh \ - functional/append/file_append.ksh \ - functional/append/threadsappend_001_pos.ksh \ functional/append/cleanup.ksh \ + functional/append/file_append.ksh \ functional/append/setup.ksh \ + functional/append/threadsappend_001_pos.ksh \ functional/arc/arcstats_runtime_tuning.ksh \ functional/arc/cleanup.ksh \ functional/arc/dbufstats_001_pos.ksh \ @@ -761,6 +761,8 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_root/zfs_receive/zfs_receive_014_pos.ksh \ functional/cli_root/zfs_receive/zfs_receive_015_pos.ksh \ functional/cli_root/zfs_receive/zfs_receive_016_pos.ksh \ + functional/cli_root/zfs_receive/zfs_receive_compressed_corrective.ksh \ + functional/cli_root/zfs_receive/zfs_receive_corrective.ksh \ functional/cli_root/zfs_receive/zfs_receive_-e.ksh \ functional/cli_root/zfs_receive/zfs_receive_from_encrypted.ksh \ functional/cli_root/zfs_receive/zfs_receive_from_zstd.ksh \ @@ -770,8 +772,6 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_root/zfs_receive/zfs_receive_raw.ksh \ functional/cli_root/zfs_receive/zfs_receive_to_encrypted.ksh \ functional/cli_root/zfs_receive/zfs_receive_-wR-encrypted-mix.ksh \ - functional/cli_root/zfs_receive/zfs_receive_corrective.ksh \ - functional/cli_root/zfs_receive/zfs_receive_compressed_corrective.ksh \ functional/cli_root/zfs_rename/cleanup.ksh \ functional/cli_root/zfs_rename/setup.ksh \ functional/cli_root/zfs_rename/zfs_rename_001_pos.ksh \ @@ -1234,7 +1234,6 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_user/misc/arcstat_001_pos.ksh \ functional/cli_user/misc/arc_summary_001_pos.ksh \ functional/cli_user/misc/arc_summary_002_neg.ksh \ - functional/cli_user/misc/zilstat_001_pos.ksh \ functional/cli_user/misc/cleanup.ksh \ functional/cli_user/misc/setup.ksh \ functional/cli_user/misc/zdb_001_neg.ksh \ @@ -1258,6 +1257,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/cli_user/misc/zfs_unmount_001_neg.ksh \ functional/cli_user/misc/zfs_unshare_001_neg.ksh \ functional/cli_user/misc/zfs_upgrade_001_neg.ksh \ + functional/cli_user/misc/zilstat_001_pos.ksh \ functional/cli_user/misc/zpool_001_neg.ksh \ functional/cli_user/misc/zpool_add_001_neg.ksh \ functional/cli_user/misc/zpool_attach_001_neg.ksh \ @@ -1423,6 +1423,12 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/history/history_009_pos.ksh \ functional/history/history_010_pos.ksh \ functional/history/setup.ksh \ + functional/idmap_mount/cleanup.ksh \ + functional/idmap_mount/idmap_mount_001.ksh \ + functional/idmap_mount/idmap_mount_002.ksh \ + functional/idmap_mount/idmap_mount_003.ksh \ + functional/idmap_mount/idmap_mount_004.ksh \ + functional/idmap_mount/setup.ksh \ functional/inheritance/cleanup.ksh \ functional/inheritance/inherit_001_pos.ksh \ functional/inuse/inuse_001_pos.ksh \ @@ -1693,6 +1699,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/removal/removal_with_export.ksh \ functional/removal/removal_with_faulted.ksh \ functional/removal/removal_with_ganging.ksh \ + functional/removal/removal_with_indirect.ksh \ functional/removal/removal_with_remove.ksh \ functional/removal/removal_with_scrub.ksh \ functional/removal/removal_with_send.ksh \ @@ -1708,6 +1715,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/removal/remove_raidz.ksh \ functional/rename_dirs/cleanup.ksh \ functional/rename_dirs/rename_dirs_001_pos.ksh \ + functional/rename_dirs/rename_no_replace_001_pos.ksh \ functional/rename_dirs/setup.ksh \ functional/replacement/attach_import.ksh \ functional/replacement/attach_multiple.ksh \ @@ -2000,10 +2008,4 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/zvol/zvol_swap/zvol_swap_003_pos.ksh \ functional/zvol/zvol_swap/zvol_swap_004_pos.ksh \ functional/zvol/zvol_swap/zvol_swap_005_pos.ksh \ - functional/zvol/zvol_swap/zvol_swap_006_pos.ksh \ - functional/idmap_mount/cleanup.ksh \ - functional/idmap_mount/setup.ksh \ - functional/idmap_mount/idmap_mount_001.ksh \ - functional/idmap_mount/idmap_mount_002.ksh \ - functional/idmap_mount/idmap_mount_003.ksh \ - functional/idmap_mount/idmap_mount_004.ksh + functional/zvol/zvol_swap/zvol_swap_006_pos.ksh diff --git a/tests/zfs-tests/tests/functional/rename_dirs/rename_no_replace_001_pos.ksh b/tests/zfs-tests/tests/functional/rename_dirs/rename_no_replace_001_pos.ksh new file mode 100755 index 000000000000..c17beb46578d --- /dev/null +++ b/tests/zfs-tests/tests/functional/rename_dirs/rename_no_replace_001_pos.ksh @@ -0,0 +1,22 @@ +#!/bin/ksh -p +# SPDX-License-Identifier: 0BSD + +. $STF_SUITE/include/libtest.shlib + +is_linux || [ $(linux_version) -ge $(linux_version "3.15.0") ] || log_unsupported "renameat2(2) is Linux-only" + +verify_runnable "both" + +log_assert "renameat2(RENAME_NOREPLACE) works" +log_onexit log_must rm -r "$TESTDIR"/* + +mkdir "$TESTDIR"/{a,b,c} + +log_must rename_no_replace "$TESTDIR" "$TESTDIR" ; rm "$TESTDIR"/from +log_must rename_no_replace "$TESTDIR" "$TESTDIR"/a + +cd "$TESTDIR"/b +log_must rename_no_replace ../c; rm to +log_must rename_no_replace + +log_pass