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; } /*
