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
This commit is contained in:
Konstantin Belousov committed 2026-03-06 01:46:53 +02:00
1 parent 28599a1e5f
commit 7aaec5f3fa
5 files changed
+43 -9

No files matched your search

+7 -2
View File
@@ -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,
+7 -2
View File
@@ -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;
+20 -4
View File
@@ -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);
+3
View File
@@ -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 */
/*
+6 -1
View File
@@ -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) ||