git: 7aaec5f3faec - main - renameat2(2): implement AT_RENAME_NOREPLACE flag
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 05 Mar 2026 23:48:20 UTC
The branch main has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=7aaec5f3faecf98e377c97e24dddb9c65f4b2e75
commit 7aaec5f3faecf98e377c97e24dddb9c65f4b2e75
Author: Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2026-02-26 18:57:24 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-03-05 23:46:53 +0000
renameat2(2): implement AT_RENAME_NOREPLACE flag
For msdosfs, tmpfs, and ufs.
Reviewed by: markj
Tested by: pho
Sponsored by: The FreeBSD Foundation
MFC after: 1 week
Differential revision: https://reviews.freebsd.org/D55539
---
sys/fs/msdosfs/msdosfs_vnops.c | 9 +++++++--
sys/fs/tmpfs/tmpfs_vnops.c | 9 +++++++--
sys/kern/vfs_syscalls.c | 24 ++++++++++++++++++++----
sys/sys/fcntl.h | 3 +++
sys/ufs/ufs/ufs_vnops.c | 7 ++++++-
5 files changed, 43 insertions(+), 9 deletions(-)
diff --git a/sys/fs/msdosfs/msdosfs_vnops.c b/sys/fs/msdosfs/msdosfs_vnops.c
index 3e28a7ce9d05..d626aed28076 100644
--- a/sys/fs/msdosfs/msdosfs_vnops.c
+++ b/sys/fs/msdosfs/msdosfs_vnops.c
@@ -49,12 +49,12 @@
* October 1992
*/
-#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/clock.h>
#include <sys/dirent.h>
+#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/lockf.h>
#include <sys/malloc.h>
@@ -970,7 +970,7 @@ msdosfs_rename(struct vop_rename_args *ap)
goto abortit;
}
- if (ap->a_flags != 0) {
+ if ((ap->a_flags & ~(AT_RENAME_NOREPLACE)) != 0) {
error = EOPNOTSUPP;
goto abortit;
}
@@ -1041,6 +1041,11 @@ relock:
vrele(tvp);
tvp = NULL;
}
+ if (error == 0 && tvp != NULL &&
+ (ap->a_flags & AT_RENAME_NOREPLACE) != 0) {
+ error = EEXIST;
+ goto unlock;
+ }
if (error == 0) {
nip = NULL;
error = deget(pmp, scn, blkoff, LK_EXCLUSIVE | LK_NOWAIT,
diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
index 6b32f53b3363..05d8f3e863e8 100644
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -993,7 +993,7 @@ tmpfs_rename(struct vop_rename_args *v)
goto out;
}
- if (v->a_flags != 0) {
+ if ((v->a_flags & ~(AT_RENAME_NOREPLACE)) != 0) {
error = EOPNOTSUPP;
goto out;
}
@@ -1018,9 +1018,14 @@ tmpfs_rename(struct vop_rename_args *v)
"tmpfs_rename: fdvp not locked");
ASSERT_VOP_ELOCKED(tdvp,
"tmpfs_rename: tdvp not locked");
- if (tvp != NULL)
+ if (tvp != NULL) {
ASSERT_VOP_ELOCKED(tvp,
"tmpfs_rename: tvp not locked");
+ if ((v->a_flags & AT_RENAME_NOREPLACE) != 0) {
+ error = EEXIST;
+ goto out_locked;
+ }
+ }
if (fvp == tvp) {
error = 0;
goto out_locked;
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 20780334a6b5..06500909589e 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -3783,7 +3783,7 @@ kern_renameat(struct thread *td, int oldfd, const char *old, int newfd,
int error;
short irflag;
- if (flags != 0)
+ if ((flags & ~(AT_RENAME_NOREPLACE)) != 0)
return (EINVAL);
again:
tmp = mp = NULL;
@@ -3820,6 +3820,19 @@ again:
}
tdvp = tond.ni_dvp;
tvp = tond.ni_vp;
+ if (tvp != NULL && (flags & AT_RENAME_NOREPLACE) != 0) {
+ /*
+ * Often filesystems need to relock the vnodes in
+ * VOP_RENAME(), which opens a window for invalidation
+ * of this check. Then, not all filesystems might
+ * implement AT_RENAME_NOREPLACE. This leads to
+ * situation where sometimes EOPNOTSUPP might be
+ * returned from the VOP due to race, while most of
+ * the time this check works.
+ */
+ error = EEXIST;
+ goto out;
+ }
error = vn_start_write(fvp, &mp, V_NOWAIT);
if (error != 0) {
again1:
@@ -3912,9 +3925,12 @@ out:
vrele(fromnd.ni_dvp);
vrele(fvp);
}
- lockmgr(&tmp->mnt_renamelock, LK_RELEASE, 0);
- vfs_rel(tmp);
- vn_finished_write(mp);
+ if (tmp != NULL) {
+ lockmgr(&tmp->mnt_renamelock, LK_RELEASE, 0);
+ vfs_rel(tmp);
+ }
+ if (mp != NULL)
+ vn_finished_write(mp);
out1:
if (error == ERESTART)
return (0);
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
index 18d3928e91c7..8f2fc1e2debb 100644
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -245,6 +245,9 @@ typedef __pid_t pid_t;
#define AT_RESOLVE_BENEATH 0x2000 /* Do not allow name resolution
to walk out of dirfd */
#define AT_EMPTY_PATH 0x4000 /* Operate on dirfd if path is empty */
+
+#define AT_RENAME_NOREPLACE 0x0001 /* Fail rename if target exists */
+#define RENAME_NOREPLACE AT_RENAME_NOREPLACE
#endif /* __BSD_VISIBLE */
/*
diff --git a/sys/ufs/ufs/ufs_vnops.c b/sys/ufs/ufs/ufs_vnops.c
index 429e6b5c8dd7..4abbd4ee807f 100644
--- a/sys/ufs/ufs/ufs_vnops.c
+++ b/sys/ufs/ufs/ufs_vnops.c
@@ -1294,7 +1294,7 @@ ufs_rename(
goto releout;
}
- if (ap->a_flags != 0) {
+ if ((ap->a_flags & ~(AT_RENAME_NOREPLACE)) != 0) {
error = EOPNOTSUPP;
mp = NULL;
goto releout;
@@ -1394,6 +1394,11 @@ relock:
}
}
+ if (tvp != NULL && (ap->a_flags & AT_RENAME_NOREPLACE) != 0) {
+ error = EEXIST;
+ goto unlockout;
+ }
+
if (DOINGSUJ(fdvp) &&
(seqc_in_modify(fdvp_s) || !vn_seqc_consistent(fdvp, fdvp_s) ||
seqc_in_modify(fvp_s) || !vn_seqc_consistent(fvp, fvp_s) ||