The branch main has been updated by kib: URL: https://cgit.FreeBSD.org/src/commit/?id=5e0b96e728af9916ac59d65e37155a95e06c3b5e
commit 5e0b96e728af9916ac59d65e37155a95e06c3b5e Author: Konstantin Belousov <[email protected]> AuthorDate: 2026-06-18 20:01:35 +0000 Commit: Konstantin Belousov <[email protected]> CommitDate: 2026-07-11 22:04:43 +0000 tmpfs: extract tmpfs_rename_check/set_parent() from tmpfs_rename() 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 | 167 ++++++++++++++++++++++++--------------------- 1 file changed, 89 insertions(+), 78 deletions(-) diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/sys/fs/tmpfs/tmpfs_vnops.c index b635450f205f..8021e42e7c06 100644 --- a/sys/fs/tmpfs/tmpfs_vnops.c +++ b/sys/fs/tmpfs/tmpfs_vnops.c @@ -962,6 +962,89 @@ releout: return (error); } +static int +tmpfs_rename_check_parent(struct tmpfs_mount *tmp, struct tmpfs_node *fdnode, + struct vnode *fvp, struct tmpfs_node *fnode, struct tmpfs_dirent *de, + struct tmpfs_node *tdnode, struct ucred *tcred) +{ + struct tmpfs_node *n; + int error; + + TMPFS_NODE_LOCK(fnode); + error = tmpfs_access_locked(fvp, fnode, VWRITE, tcred); + TMPFS_NODE_UNLOCK(fnode); + if (error != 0) + return (error); + + /* + * Ensure the target directory is not a child of the + * directory being moved. Otherwise, we'd end up + * with stale nodes. + * + * TMPFS_LOCK guarantees that no nodes are freed while + * traversing the list. Nodes can only be marked as + * removed: tn_parent == NULL. + */ + n = tdnode; + TMPFS_LOCK(tmp); + TMPFS_NODE_LOCK(n); + while (n != n->tn_dir.tn_parent) { + struct tmpfs_node *parent; + + if (n == fnode) { + TMPFS_NODE_UNLOCK(n); + TMPFS_UNLOCK(tmp); + return (EINVAL); + } + parent = n->tn_dir.tn_parent; + TMPFS_NODE_UNLOCK(n); + if (parent == NULL) { + n = NULL; + break; + } + TMPFS_NODE_LOCK(parent); + if (parent->tn_dir.tn_parent == NULL) { + TMPFS_NODE_UNLOCK(parent); + n = NULL; + break; + } + n = parent; + } + TMPFS_UNLOCK(tmp); + if (n == NULL) + return (EINVAL); + + TMPFS_NODE_UNLOCK(n); + + return (0); +} + +static void +tmpfs_rename_set_parent(struct tmpfs_node *fdnode, struct tmpfs_node *fnode, + struct tmpfs_dirent *de, struct tmpfs_node *tdnode) +{ + /* Adjust the parent pointer. */ + TMPFS_VALIDATE_DIR(fnode); + TMPFS_NODE_LOCK(de->td_node); + de->td_node->tn_dir.tn_parent = tdnode; + TMPFS_NODE_UNLOCK(de->td_node); + + /* + * As a result of changing the target of the '..' + * entry, the link count of the source and target + * directories has to be adjusted. + */ + TMPFS_NODE_LOCK(tdnode); + TMPFS_ASSERT_LOCKED(tdnode); + tdnode->tn_links++; + TMPFS_NODE_UNLOCK(tdnode); + + TMPFS_NODE_LOCK(fdnode); + TMPFS_ASSERT_LOCKED(fdnode); + fdnode->tn_links--; + TMPFS_NODE_UNLOCK(fdnode); +} + static int tmpfs_rename(struct vop_rename_args *v) { @@ -1129,86 +1212,14 @@ tmpfs_rename(struct vop_rename_args *v) * In case we are moving a directory, we have to adjust its * parent to point to the new parent. */ - if (de->td_node->tn_type == VDIR) { - struct tmpfs_node *n; - - TMPFS_NODE_LOCK(fnode); - error = tmpfs_access_locked(fvp, fnode, VWRITE, - tcnp->cn_cred); - TMPFS_NODE_UNLOCK(fnode); - if (error) { - if (newname != NULL) - free(newname, M_TMPFSNAME); + if (fnode->tn_type == VDIR) { + error = tmpfs_rename_check_parent(tmp, fdnode, fvp, + fnode, de, tdnode, tcnp->cn_cred); + if (error != 0) { + free(newname, M_TMPFSNAME); goto out_locked; } - - /* - * Ensure the target directory is not a child of the - * directory being moved. Otherwise, we'd end up - * with stale nodes. - */ - n = tdnode; - /* - * TMPFS_LOCK guaranties that no nodes are freed while - * traversing the list. Nodes can only be marked as - * removed: tn_parent == NULL. - */ - TMPFS_LOCK(tmp); - TMPFS_NODE_LOCK(n); - while (n != n->tn_dir.tn_parent) { - struct tmpfs_node *parent; - - if (n == fnode) { - TMPFS_NODE_UNLOCK(n); - TMPFS_UNLOCK(tmp); - error = EINVAL; - if (newname != NULL) - free(newname, M_TMPFSNAME); - goto out_locked; - } - parent = n->tn_dir.tn_parent; - TMPFS_NODE_UNLOCK(n); - if (parent == NULL) { - n = NULL; - break; - } - TMPFS_NODE_LOCK(parent); - if (parent->tn_dir.tn_parent == NULL) { - TMPFS_NODE_UNLOCK(parent); - n = NULL; - break; - } - n = parent; - } - TMPFS_UNLOCK(tmp); - if (n == NULL) { - error = EINVAL; - if (newname != NULL) - free(newname, M_TMPFSNAME); - goto out_locked; - } - TMPFS_NODE_UNLOCK(n); - - /* Adjust the parent pointer. */ - TMPFS_VALIDATE_DIR(fnode); - TMPFS_NODE_LOCK(de->td_node); - de->td_node->tn_dir.tn_parent = tdnode; - TMPFS_NODE_UNLOCK(de->td_node); - - /* - * As a result of changing the target of the '..' - * entry, the link count of the source and target - * directories has to be adjusted. - */ - TMPFS_NODE_LOCK(tdnode); - TMPFS_ASSERT_LOCKED(tdnode); - tdnode->tn_links++; - TMPFS_NODE_UNLOCK(tdnode); - - TMPFS_NODE_LOCK(fdnode); - TMPFS_ASSERT_LOCKED(fdnode); - fdnode->tn_links--; - TMPFS_NODE_UNLOCK(fdnode); + tmpfs_rename_set_parent(fdnode, fnode, de, tdnode); } }
