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) ||

Reply via email to