mirror of
https://github.com/versity/scoutfs.git
synced 2026-09-03 06:37:00 +00:00
Merge pull request #61 from bgly/bduffyly/rename2
Add basic renameat2 syscall support
This commit is contained in:
+26
-2
@@ -1615,8 +1615,9 @@ static int verify_ancestors(struct super_block *sb, u64 p1, u64 p2,
|
||||
* from using parent/child locking orders as two groups can have both
|
||||
* parent and child relationships to each other.
|
||||
*/
|
||||
static int scoutfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
struct inode *new_dir, struct dentry *new_dentry)
|
||||
static int scoutfs_rename_common(struct inode *old_dir,
|
||||
struct dentry *old_dentry, struct inode *new_dir,
|
||||
struct dentry *new_dentry, unsigned int flags)
|
||||
{
|
||||
struct super_block *sb = old_dir->i_sb;
|
||||
struct inode *old_inode = old_dentry->d_inode;
|
||||
@@ -1688,6 +1689,11 @@ static int scoutfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
|
||||
if ((flags & RENAME_NOREPLACE) && (new_inode != NULL)) {
|
||||
ret = -EEXIST;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (should_orphan(new_inode)) {
|
||||
ret = scoutfs_lock_orphan(sb, SCOUTFS_LOCK_WRITE_ONLY, 0, scoutfs_ino(new_inode),
|
||||
&orph_lock);
|
||||
@@ -1870,6 +1876,23 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scoutfs_rename(struct inode *old_dir,
|
||||
struct dentry *old_dentry, struct inode *new_dir,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
return scoutfs_rename_common(old_dir, old_dentry, new_dir, new_dentry, 0);
|
||||
}
|
||||
|
||||
static int scoutfs_rename2(struct inode *old_dir,
|
||||
struct dentry *old_dentry, struct inode *new_dir,
|
||||
struct dentry *new_dentry, unsigned int flags)
|
||||
{
|
||||
if (flags & ~RENAME_NOREPLACE)
|
||||
return -EINVAL;
|
||||
|
||||
return scoutfs_rename_common(old_dir, old_dentry, new_dir, new_dentry, flags);
|
||||
}
|
||||
|
||||
#ifdef KC_FMODE_KABI_ITERATE
|
||||
/* we only need this to set the iterate flag for kabi :/ */
|
||||
static int scoutfs_dir_open(struct inode *inode, struct file *file)
|
||||
@@ -1960,6 +1983,7 @@ const struct inode_operations_wrapper scoutfs_dir_iops = {
|
||||
.permission = scoutfs_permission,
|
||||
},
|
||||
.tmpfile = scoutfs_tmpfile,
|
||||
.rename2 = scoutfs_rename2,
|
||||
};
|
||||
|
||||
void scoutfs_dir_exit(void)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
src/*.d
|
||||
src/createmany
|
||||
src/dumb_renameat2
|
||||
src/dumb_setxattr
|
||||
src/handle_cat
|
||||
src/bulk_create_paths
|
||||
|
||||
@@ -3,6 +3,7 @@ SHELL := /usr/bin/bash
|
||||
|
||||
# each binary command is built from a single .c file
|
||||
BIN := src/createmany \
|
||||
src/dumb_renameat2 \
|
||||
src/dumb_setxattr \
|
||||
src/handle_cat \
|
||||
src/bulk_create_paths \
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
=== renameat2 noreplace flag test
|
||||
=== run two asynchronous calls to renameat2 NOREPLACE
|
||||
@@ -9,6 +9,8 @@ generic/011
|
||||
generic/013
|
||||
generic/014
|
||||
generic/020
|
||||
generic/023
|
||||
generic/024
|
||||
generic/028
|
||||
generic/032
|
||||
generic/034
|
||||
@@ -82,6 +84,7 @@ generic/016
|
||||
generic/018
|
||||
generic/021
|
||||
generic/022
|
||||
generic/025
|
||||
generic/026
|
||||
generic/031
|
||||
generic/033
|
||||
@@ -93,6 +96,7 @@ generic/060
|
||||
generic/061
|
||||
generic/063
|
||||
generic/064
|
||||
generic/078
|
||||
generic/079
|
||||
generic/081
|
||||
generic/082
|
||||
@@ -278,4 +282,4 @@ shared/004
|
||||
shared/032
|
||||
shared/051
|
||||
shared/289
|
||||
Passed all 73 tests
|
||||
Passed all 75 tests
|
||||
|
||||
@@ -37,4 +37,5 @@ createmany-parallel-mounts.sh
|
||||
archive-light-cycle.sh
|
||||
block-stale-reads.sh
|
||||
inode-deletion.sh
|
||||
renameat2-noreplace.sh
|
||||
xfstests.sh
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifndef RENAMEAT2_EXIST
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#if !defined(SYS_renameat2) && defined(__x86_64__)
|
||||
#define SYS_renameat2 316 /* from arch/x86/entry/syscalls/syscall_64.tbl */
|
||||
#endif
|
||||
|
||||
static int renameat2(int olddfd, const char *old_dir,
|
||||
int newdfd, const char *new_dir,
|
||||
unsigned int flags)
|
||||
{
|
||||
#ifdef SYS_renameat2
|
||||
return syscall(SYS_renameat2, olddfd, old_dir, newdfd, new_dir, flags);
|
||||
#else
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef RENAME_NOREPLACE
|
||||
#define RENAME_NOREPLACE (1 << 0) /* Don't overwrite newpath of rename */
|
||||
#endif
|
||||
#ifndef RENAME_EXCHANGE
|
||||
#define RENAME_EXCHANGE (1 << 1) /* Exchange oldpath and newpath */
|
||||
#endif
|
||||
#ifndef RENAME_WHITEOUT
|
||||
#define RENAME_WHITEOUT (1 << 2) /* Whiteout oldpath */
|
||||
#endif
|
||||
|
||||
static void exit_usage(char **argv)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [-n|-x|-w] old_path new_path\n"
|
||||
" -n noreplace\n"
|
||||
" -x exchange\n"
|
||||
" -w whiteout\n", argv[0]);
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const char *old_path = NULL;
|
||||
const char *new_path = NULL;
|
||||
unsigned int flags = 0;
|
||||
int ret;
|
||||
int c;
|
||||
|
||||
for (c = 1; c < argc; c++) {
|
||||
if (argv[c][0] == '-') {
|
||||
switch (argv[c][1]) {
|
||||
case 'n':
|
||||
flags |= RENAME_NOREPLACE;
|
||||
break;
|
||||
case 'x':
|
||||
flags |= RENAME_EXCHANGE;
|
||||
break;
|
||||
case 'w':
|
||||
flags |= RENAME_WHITEOUT;
|
||||
break;
|
||||
default:
|
||||
exit_usage(argv);
|
||||
}
|
||||
} else if (!old_path) {
|
||||
old_path = argv[c];
|
||||
} else if (!new_path) {
|
||||
new_path = argv[c];
|
||||
} else {
|
||||
exit_usage(argv);
|
||||
}
|
||||
}
|
||||
|
||||
if (!old_path || !new_path) {
|
||||
printf("specify the correct directory path\n");
|
||||
errno = ENOENT;
|
||||
return 1;
|
||||
}
|
||||
|
||||
ret = renameat2(AT_FDCWD, old_path, AT_FDCWD, new_path, flags);
|
||||
if (ret == -1) {
|
||||
perror("Error");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#
|
||||
# simple renameat2 NOREPLACE unit test
|
||||
#
|
||||
|
||||
t_require_commands dumb_renameat2
|
||||
t_require_mounts 2
|
||||
|
||||
echo "=== renameat2 noreplace flag test"
|
||||
|
||||
# give each mount their own dir (lock group) to minimize create contention
|
||||
mkdir $T_M0/dir0
|
||||
mkdir $T_M1/dir1
|
||||
|
||||
echo "=== run two asynchronous calls to renameat2 NOREPLACE"
|
||||
for i in $(seq 0 100); do
|
||||
# prepare inputs in isolation
|
||||
touch "$T_M0/dir0/old0"
|
||||
touch "$T_M1/dir1/old1"
|
||||
|
||||
# race doing noreplace renames, both can't succeed
|
||||
dumb_renameat2 -n "$T_M0/dir0/old0" "$T_M0/dir0/sharednew" 2> /dev/null &
|
||||
pid0=$!
|
||||
dumb_renameat2 -n "$T_M1/dir1/old1" "$T_M1/dir0/sharednew" 2> /dev/null &
|
||||
pid1=$!
|
||||
|
||||
wait $pid0
|
||||
rc0=$?
|
||||
wait $pid1
|
||||
rc1=$?
|
||||
|
||||
test "$rc0" == 0 -a "$rc1" == 0 && t_fail "both renames succeeded"
|
||||
|
||||
# blow away possible files for either race outcome
|
||||
rm -f "$T_M0/dir0/old0" "$T_M1/dir1/old1" "$T_M0/dir0/sharednew" "$T_M1/dir1/sharednew"
|
||||
done
|
||||
|
||||
t_pass
|
||||
@@ -60,13 +60,9 @@ EOF
|
||||
|
||||
cat << EOF > local.exclude
|
||||
generic/003 # missing atime update in buffered read
|
||||
generic/023 # renameat2 not implemented
|
||||
generic/024 # renameat2 not implemented
|
||||
generic/025 # renameat2 not implemented
|
||||
generic/029 # mmap missing
|
||||
generic/030 # mmap missing
|
||||
generic/075 # file content mismatch failures (fds, etc)
|
||||
generic/078 # renameat2 not implemented
|
||||
generic/080 # mmap missing
|
||||
generic/103 # enospc causes trans commit failures
|
||||
generic/105 # needs trigage: something about acls
|
||||
|
||||
Reference in New Issue
Block a user