The branch main has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=7aaec5f3faecf98e377c97e24dddb9c65f4b2e75
commit 7aaec5f3faecf98e377c97e24dddb9c65f4b2e75 Author: Konstantin Belousov <[email protected]> AuthorDate: 2026-02-26 18:57:24 +0000 Commit: Konstantin Belousov <[email protected]> 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) ||
