The branch main has been updated by kib:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=7e06c33151e7664f6d664ee282d8da1985ce94aa

commit 7e06c33151e7664f6d664ee282d8da1985ce94aa
Author:     Konstantin Belousov <[email protected]>
AuthorDate: 2026-06-18 18:40:40 +0000
Commit:     Konstantin Belousov <[email protected]>
CommitDate: 2026-07-11 22:04:43 +0000

    tmpfs: implement AT_RENAME_EXCHANGE
    
    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/fs/tmpfs/tmpfs_vnops.c | 90 ++++++++++++++++++++++++++++++++++------------
 1 file changed, 67 insertions(+), 23 deletions(-)

diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c
index 8021e42e7c06..fe8507b4936a 100644
--- a/sys/fs/tmpfs/tmpfs_vnops.c
+++ b/sys/fs/tmpfs/tmpfs_vnops.c
@@ -1055,7 +1055,7 @@ tmpfs_rename(struct vop_rename_args *v)
        struct vnode *tvp = v->a_tvp;
        struct componentname *tcnp = v->a_tcnp;
        char *newname;
-       struct tmpfs_dirent *de;
+       struct tmpfs_dirent *de, *tde;
        struct tmpfs_mount *tmp;
        struct tmpfs_node *fdnode;
        struct tmpfs_node *fnode;
@@ -1063,8 +1063,11 @@ tmpfs_rename(struct vop_rename_args *v)
        struct tmpfs_node *tdnode;
        int error;
        bool want_seqc_end;
+       bool exchange;
 
        want_seqc_end = false;
+       newname = NULL;
+       error = 0;
 
        /*
         * Disallow cross-device renames.
@@ -1076,16 +1079,15 @@ tmpfs_rename(struct vop_rename_args *v)
                goto out;
        }
 
-       if ((v->a_flags & ~(AT_RENAME_NOREPLACE)) != 0) {
+       if ((v->a_flags & ~(AT_RENAME_NOREPLACE | AT_RENAME_EXCHANGE)) != 0) {
                error = EOPNOTSUPP;
                goto out;
        }
 
        /* If source and target are the same file, there is nothing to do. */
-       if (fvp == tvp) {
-               error = 0;
+       if (fvp == tvp)
                goto out;
-       }
+       exchange = (v->a_flags & AT_RENAME_EXCHANGE) != 0;
 
        /*
         * If we need to move the directory between entries, lock the
@@ -1138,6 +1140,8 @@ tmpfs_rename(struct vop_rename_args *v)
        fdnode = VP_TO_TMPFS_DIR(fdvp);
        fnode = VP_TO_TMPFS_NODE(fvp);
        de = tmpfs_dir_lookup(fdnode, fnode, fcnp);
+       if (exchange)
+               tde = tmpfs_dir_lookup(tdnode, tnode, tcnp);
 
        /*
         * Entry can disappear before we lock fdvp.
@@ -1151,6 +1155,13 @@ tmpfs_rename(struct vop_rename_args *v)
                goto out_locked;
        }
        MPASS(de->td_node == fnode);
+       if (exchange) {
+               if (tde == NULL) {
+                       error = ENOENT;
+                       goto out_locked;
+               }
+               MPASS(tde->td_node == tnode);
+       }
 
        /*
         * If re-naming a directory to another preexisting directory
@@ -1168,22 +1179,27 @@ tmpfs_rename(struct vop_rename_args *v)
                        goto out_locked;
                }
 
-               if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
-                       if (tnode->tn_size != 0 &&
-                           ((tcnp->cn_flags & IGNOREWHITEOUT) == 0 ||
-                           tnode->tn_size > tnode->tn_dir.tn_wht_size)) {
-                               error = ENOTEMPTY;
+               if (!exchange) {
+                       if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
+                               if (tnode->tn_size != 0 &&
+                                   ((tcnp->cn_flags & IGNOREWHITEOUT) == 0 ||
+                                   tnode->tn_size >
+                                   tnode->tn_dir.tn_wht_size)) {
+                                       error = ENOTEMPTY;
+                                       goto out_locked;
+                               }
+                       } else if (fnode->tn_type == VDIR &&
+                           tnode->tn_type != VDIR) {
+                               error = ENOTDIR;
                                goto out_locked;
+                       } else if (fnode->tn_type != VDIR &&
+                           tnode->tn_type == VDIR) {
+                               error = EISDIR;
+                               goto out_locked;
+                       } else {
+                               MPASS(fnode->tn_type != VDIR &&
+                                   tnode->tn_type != VDIR);
                        }
-               } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
-                       error = ENOTDIR;
-                       goto out_locked;
-               } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
-                       error = EISDIR;
-                       goto out_locked;
-               } else {
-                       MPASS(fnode->tn_type != VDIR &&
-                               tnode->tn_type != VDIR);
                }
        }
 
@@ -1197,11 +1213,9 @@ tmpfs_rename(struct vop_rename_args *v)
         * Ensure that we have enough memory to hold the new name, if it
         * has to be changed.
         */
-       if (fcnp->cn_namelen != tcnp->cn_namelen ||
-           bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0) {
+       if (!exchange && (fcnp->cn_namelen != tcnp->cn_namelen ||
+           bcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fcnp->cn_namelen) != 0))
                newname = malloc(tcnp->cn_namelen, M_TMPFSNAME, M_WAITOK);
-       } else
-               newname = NULL;
 
        /*
         * If the node is being moved to another directory, we have to do
@@ -1219,8 +1233,38 @@ tmpfs_rename(struct vop_rename_args *v)
                                free(newname, M_TMPFSNAME);
                                goto out_locked;
                        }
+               }
+               if (exchange && tnode->tn_type == VDIR) {
+                       MPASS(newname == NULL);
+                       error = tmpfs_rename_check_parent(tmp, tdnode, tvp,
+                           tnode, tde, fdnode, fcnp->cn_cred);
+                       if (error != 0)
+                               goto out_locked;
+               }
+               if (fnode->tn_type == VDIR)
                        tmpfs_rename_set_parent(fdnode, fnode, de, tdnode);
+               if (exchange && tnode->tn_type == VDIR)
+                       tmpfs_rename_set_parent(tdnode, tnode, tde, fdnode);
+       }
+       if (exchange) {
+               de->td_node = tnode;
+               tde->td_node = fnode;
+               if (tmpfs_use_nc(fvp)) {
+                       cache_purge(fvp);
+                       cache_purge(tvp);
+                       cache_enter(tdvp, fvp, tcnp);
+                       cache_enter(fdvp, tvp, fcnp);
+               }
+               fdnode->tn_status |= TMPFS_NODE_MODIFIED | TMPFS_NODE_CHANGED;
+               fdnode->tn_accessed = true;
+               tmpfs_update(fdvp);
+               if (fdvp != tdvp) {
+                       tdnode->tn_status |= TMPFS_NODE_MODIFIED |
+                           TMPFS_NODE_CHANGED;
+                       tdnode->tn_accessed = true;
+                       tmpfs_update(tdvp);
                }
+               goto out_locked;
        }
 
        /*

Reply via email to