zfs_rename: support RENAME_* flags

Implement support for Linux's RENAME_* flags (for renameat2). Aside from
being quite useful for userspace (providing race-free ways to exchange
paths and implement mv --no-clobber), they are used by overlayfs and are
thus required in order to use overlayfs-on-ZFS.

In order for us to represent the new renameat2(2) flags in the ZIL, we
create two new transaction types for the two flags which need
transactional-level support (RENAME_EXCHANGE and RENAME_WHITEOUT).
RENAME_NOREPLACE does not need any ZIL support because we know that if
the operation succeeded before creating the ZIL entry, there was no file
to be clobbered and thus it can be treated as a regular TX_RENAME.

Reviewed-by: Ryan Moeller <ryan@iXsystems.com>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Pavel Snajdr <snajpa@snajpa.net>
Signed-off-by: Aleksa Sarai <cyphar@cyphar.com>
Closes #12209
Closes #14070
This commit is contained in:
Aleksa Sarai
2019-06-22 10:35:11 +10:00
committed by Brian Behlendorf
parent e015d6cc0b
commit dbf6108b4d
33 changed files with 932 additions and 74 deletions
+1
View File
@@ -27,6 +27,7 @@
/randwritecomp
/read_dos_attributes
/readmmap
/renameat2
/rename_dir
/rm_lnkcnt_zero_file
/send_doall
+1 -2
View File
@@ -112,10 +112,10 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/edonr_test %D%/skein_test \
%C%_edonr_test_LDADD = $(%C%_skein_test_LDADD)
%C%_blake3_test_LDADD = $(%C%_skein_test_LDADD)
if BUILD_LINUX
scripts_zfs_tests_bin_PROGRAMS += %D%/getversion
scripts_zfs_tests_bin_PROGRAMS += %D%/user_ns_exec
scripts_zfs_tests_bin_PROGRAMS += %D%/renameat2
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
@@ -127,7 +127,6 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/read_dos_attributes %D%/write_dos_attribu
%C%_read_dos_attributes_SOURCES = %D%/linux_dos_attributes/read_dos_attributes.c
%C%_write_dos_attributes_SOURCES = %D%/linux_dos_attributes/write_dos_attributes.c
scripts_zfs_tests_bin_PROGRAMS += %D%/randfree_file
%C%_randfree_file_SOURCES = %D%/file/randfree_file.c
+128
View File
@@ -0,0 +1,128 @@
/* SPDX-License-Identifier: CDDL-1.0 OR MPL-2.0 */
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (C) 2019 Aleksa Sarai <cyphar@cyphar.com>
* Copyright (C) 2019 SUSE LLC
*/
/*
* mv(1) doesn't currently support RENAME_{EXCHANGE,WHITEOUT} so this is a very
* simple renameat2(2) wrapper for the OpenZFS self-tests.
*/
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syscall.h>
#ifndef SYS_renameat2
#ifdef __NR_renameat2
#define SYS_renameat2 __NR_renameat2
#elif defined(__x86_64__)
#define SYS_renameat2 316
#elif defined(__i386__)
#define SYS_renameat2 353
#elif defined(__arm__) || defined(__aarch64__)
#define SYS_renameat2 382
#else
#error "SYS_renameat2 not known for this architecture."
#endif
#endif
#ifndef RENAME_NOREPLACE
#define RENAME_NOREPLACE (1 << 0) /* Don't overwrite target */
#endif
#ifndef RENAME_EXCHANGE
#define RENAME_EXCHANGE (1 << 1) /* Exchange source and dest */
#endif
#ifndef RENAME_WHITEOUT
#define RENAME_WHITEOUT (1 << 2) /* Whiteout source */
#endif
/* glibc doesn't provide renameat2 wrapper, let's use our own */
static int
sys_renameat2(int olddirfd, const char *oldpath,
int newdirfd, const char *newpath, unsigned int flags)
{
int ret = syscall(SYS_renameat2, olddirfd, oldpath, newdirfd, newpath,
flags);
return ((ret < 0) ? -errno : ret);
}
static void
usage(void)
{
fprintf(stderr, "usage: renameat2 [-Cnwx] src dst\n");
exit(1);
}
static void
check(void)
{
int err = sys_renameat2(AT_FDCWD, ".", AT_FDCWD, ".", RENAME_EXCHANGE);
exit(err == -ENOSYS);
}
int
main(int argc, char **argv)
{
char *src, *dst;
int ch, err;
unsigned int flags = 0;
while ((ch = getopt(argc, argv, "Cnwx")) >= 0) {
switch (ch) {
case 'C':
check();
break;
case 'n':
flags |= RENAME_NOREPLACE;
break;
case 'w':
flags |= RENAME_WHITEOUT;
break;
case 'x':
flags |= RENAME_EXCHANGE;
break;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc != 2)
usage();
src = argv[0];
dst = argv[1];
err = sys_renameat2(AT_FDCWD, src, AT_FDCWD, dst, flags);
if (err < 0)
fprintf(stderr, "renameat2: %s", strerror(-err));
return (err != 0);
}
+1
View File
@@ -208,6 +208,7 @@ export ZFSTEST_FILES='badsend
randwritecomp
readmmap
read_dos_attributes
renameat2
rename_dir
rm_lnkcnt_zero_file
send_doall
@@ -0,0 +1,7 @@
pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/renameat2
dist_pkgdata_SCRIPTS = \
setup.ksh \
cleanup.ksh \
renameat2_noreplace.ksh \
renameat2_exchange.ksh \
renameat2_whiteout.ksh
+34
View File
@@ -0,0 +1,34 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
#
# Copyright (c) 2013 by Delphix. All rights reserved.
#
. $STF_SUITE/include/libtest.shlib
default_cleanup
@@ -0,0 +1,61 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (C) 2019 Aleksa Sarai <cyphar@cyphar.com>
# Copyright (C) 2019 SUSE LLC
#
. $STF_SUITE/include/libtest.shlib
verify_runnable "both"
function cleanup
{
log_must rm -rf $TESTDIR/*
}
log_assert "ZFS supports RENAME_EXCHANGE."
log_onexit cleanup
cd $TESTDIR
echo "foo" > foo
echo "bar" > bar
# Self-exchange is a no-op.
log_must renameat2 -x foo foo
log_must grep '^foo$' foo
# Basic exchange.
log_must renameat2 -x foo bar
log_must grep '^bar$' foo
log_must grep '^foo$' bar
# And exchange back.
log_must renameat2 -x foo bar
log_must grep '^foo$' foo
log_must grep '^bar$' bar
# Exchange with a bad path should fail.
log_mustnot renameat2 -x bar baz
log_pass "ZFS supports RENAME_EXCHANGE as expected."
@@ -0,0 +1,51 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (C) 2019 Aleksa Sarai <cyphar@cyphar.com>
# Copyright (C) 2019 SUSE LLC
#
. $STF_SUITE/include/libtest.shlib
verify_runnable "both"
function cleanup
{
log_must rm -rf $TESTDIR/*
}
log_assert "ZFS supports RENAME_NOREPLACE."
log_onexit cleanup
cd $TESTDIR
touch foo bar
# Clobbers should always fail.
log_mustnot renameat2 -n foo foo
log_mustnot renameat2 -n foo bar
log_mustnot renameat2 -n bar foo
# Regular renames should succeed.
log_must renameat2 -n bar baz
log_pass "ZFS supports RENAME_NOREPLACE as expected."
@@ -0,0 +1,50 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (C) 2019 Aleksa Sarai <cyphar@cyphar.com>
# Copyright (C) 2019 SUSE LLC
#
. $STF_SUITE/include/libtest.shlib
verify_runnable "both"
function cleanup
{
log_must rm -rf $TESTDIR/*
}
log_assert "ZFS supports RENAME_WHITEOUT."
log_onexit cleanup
cd $TESTDIR
echo "whiteout" > whiteout
# Straight-forward rename-with-whiteout.
log_must renameat2 -w whiteout new
# Check new file.
log_must grep '^whiteout$' new
# Check that the whiteout is actually a {0,0} char device.
log_must grep '^character special file:0:0$' <<<"$(stat -c '%F:%t:%T' whiteout)"
log_pass "ZFS supports RENAME_WHITEOUT as expected."
+37
View File
@@ -0,0 +1,37 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#
#
# Copyright (C) 2019 Aleksa Sarai <cyphar@cyphar.com>
# Copyright (C) 2019 SUSE LLC
#
. $STF_SUITE/include/libtest.shlib
if ! is_linux ; then
log_unsupported "renameat2 is linux-only"
elif ! renameat2 -C ; then
log_unsupported "renameat2 not supported on this (pre-3.15) linux kernel"
fi
DISK=${DISKS%% *}
default_setup $DISK
@@ -175,6 +175,29 @@ log_must ln /$TESTPOOL/$TESTFS/link_and_unlink \
/$TESTPOOL/$TESTFS/link_and_unlink.link
log_must rm /$TESTPOOL/$TESTFS/link_and_unlink.link
# We can't test RENAME_* flags without renameat2(2) support.
if ! is_linux ; then
log_note "renameat2 is linux-only"
elif ! renameat2 -C ; then
log_note "renameat2 not supported on this (pre-3.15) linux kernel"
else
# TX_RENAME_EXCHANGE
log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/xchg-a bs=1k count=1
log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/xchg-b bs=1k count=1
log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/xchg-c bs=1k count=1
log_must dd if=/dev/urandom of=/$TESTPOOL/$TESTFS/xchg-d bs=1k count=1
# rotate the files around
log_must renameat2 -x /$TESTPOOL/$TESTFS/xchg-{a,b}
log_must renameat2 -x /$TESTPOOL/$TESTFS/xchg-{b,c}
log_must renameat2 -x /$TESTPOOL/$TESTFS/xchg-{c,a}
# exchange same path
log_must renameat2 -x /$TESTPOOL/$TESTFS/xchg-{d,d}
# TX_RENAME_WHITEOUT
log_must mkfile 1k /$TESTPOOL/$TESTFS/whiteout
log_must renameat2 -w /$TESTPOOL/$TESTFS/whiteout{,-moved}
fi
#
# 4. Copy TESTFS to temporary location (TESTDIR/copy)
#