The branch main has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=8e6e77ec25bc5809500d29bf318a843b40205657

commit 8e6e77ec25bc5809500d29bf318a843b40205657
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2026-06-18 18:12:01 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2026-07-11 22:04:43 +0000

    renameat(2): add AT_RENAME_EXCHANGE flag
    
    Currently no filesystems support it.
    
    Reviewed by:    mckusick
    Discussed with: markj
    Tested by:      pho
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D57658
---
 sys/kern/vfs_syscalls.c | 70 ++++++++++++++++++++++++++++++++++++++++---------
 sys/sys/fcntl.h         |  2 ++
 2 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
index 59d0c9c40927..29d503bbb3fa 100644
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -3752,12 +3752,13 @@ sys_renameat2(struct thread *td, struct renameat2_args 
*uap)
 #ifdef MAC
 static int
 kern_renameat_mac(struct thread *td, int oldfd, const char *old, int newfd,
-    const char *new, enum uio_seg pathseg, struct nameidata *fromnd)
+    const char *new, enum uio_seg pathseg, struct nameidata *fromnd, int op,
+    int ndflags)
 {
        int error;
 
-       NDINIT_ATRIGHTS(fromnd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1,
-           pathseg, old, oldfd, &cap_renameat_source_rights);
+       NDINIT_ATRIGHTS(fromnd, op, LOCKPARENT | LOCKLEAF | AUDITVNODE1 |
+           ndflags, pathseg, old, oldfd, &cap_renameat_source_rights);
        if ((error = namei(fromnd)) != 0)
                return (error);
        error = mac_vnode_check_rename_from(td->td_ucred, fromnd->ni_dvp,
@@ -3782,23 +3783,35 @@ kern_renameat(struct thread *td, int oldfd, const char 
*old, int newfd,
        struct vnode *tvp, *fvp, *tdvp;
        struct nameidata fromnd, tond;
        uint64_t tondflags;
-       int error;
+       int error, fndflags, op;
        short irflag;
+       bool exchange;
 
-       if ((flags & ~(AT_RENAME_NOREPLACE)) != 0)
+       if ((flags & ~(AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE)) != 0)
+               return (EINVAL);
+       if ((flags & (AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE)) ==
+           (AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE))
                return (EINVAL);
+       if ((flags & AT_RENAME_EXCHANGE) != 0) {
+               op = RENAME;
+               exchange = true;
+       } else {
+               op = DELETE;
+               exchange = false;
+       }
+       fndflags = 0;
 again:
        tmp = mp = NULL;
        bwillwrite();
 #ifdef MAC
        if (mac_vnode_check_rename_from_enabled()) {
                error = kern_renameat_mac(td, oldfd, old, newfd, new, pathseg,
-                   &fromnd);
+                   &fromnd, op, fndflags);
                if (error != 0)
                        return (error);
        } else {
 #endif
-       NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | AUDITVNODE1,
+       NDINIT_ATRIGHTS(&fromnd, op, WANTPARENT | AUDITVNODE1 | fndflags,
            pathseg, old, oldfd, &cap_renameat_source_rights);
        if ((error = namei(&fromnd)) != 0)
                return (error);
@@ -3806,6 +3819,11 @@ again:
        }
 #endif
        fvp = fromnd.ni_vp;
+       if (exchange && fvp == NULL) {
+               NDFREE_PNBUF(&fromnd);
+               vrele(fromnd.ni_dvp);
+               return (ENOENT);
+       }
        tondflags = LOCKPARENT | LOCKLEAF | NOCACHE | AUDITVNODE2;
        if (fromnd.ni_vp->v_type == VDIR)
                tondflags |= WILLBEDIR;
@@ -3844,6 +3862,29 @@ again:
                error = EEXIST;
                goto out;
        }
+       if (exchange) {
+               if (tvp == NULL) {
+                       error = ENOENT;
+                       goto out;
+               }
+               if (tvp->v_type == VDIR && fndflags == 0) {
+                       fndflags = WILLBEDIR;
+again2:
+                       NDFREE_PNBUF(&fromnd);
+                       vrele(fromnd.ni_dvp);
+                       vrele(fvp);
+                       NDFREE_PNBUF(&tond);
+                       VOP_VPUT_PAIR(tdvp, &tvp, true);
+                       error = sig_intr();
+                       if (error != 0)
+                               goto out1;
+                       goto again;
+               }
+               if (tvp->v_type != VDIR && fndflags != 0) {
+                       fndflags = 0;
+                       goto again2;
+               }
+       }
        error = vn_start_write(fvp, &mp, V_NOWAIT);
        if (error != 0) {
 again1:
@@ -3886,12 +3927,15 @@ again1:
                goto out;
        }
        if (tvp != NULL) {
-               if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
-                       error = ENOTDIR;
-                       goto out;
-               } else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
-                       error = EISDIR;
-                       goto out;
+               if (!exchange) {
+                       if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
+                               error = ENOTDIR;
+                               goto out;
+                       } else if (fvp->v_type != VDIR &&
+                           tvp->v_type == VDIR) {
+                               error = EISDIR;
+                               goto out;
+                       }
                }
 #ifdef CAPABILITIES
                if (newfd != AT_FDCWD && (tond.ni_resflags & NIRES_ABS) == 0) {
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
index 80cbca4ea753..44f43f745762 100644
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -260,7 +260,9 @@ typedef     __pid_t         pid_t;
 #define        AT_EMPTY_PATH           0x4000  /* Operate on dirfd if path is 
empty */
 
 #define        AT_RENAME_NOREPLACE     0x0001  /* Fail rename if target exists 
*/
+#define        AT_RENAME_EXCHANGE      0x0002  /* Atomically exchange 'from' 
and 'to' */
 #define        RENAME_NOREPLACE        AT_RENAME_NOREPLACE
+#define        RENAME_EXCHANGE         AT_RENAME_EXCHANGE
 #endif /* __BSD_VISIBLE */
 
 /*

Reply via email to