diff --git a/sys/fs/tmpfs/tmpfs_vnops.c b/fs/tmpfs/tmpfs_vnops.c --- a/sys/fs/tmpfs/tmpfs_vnops.c +++ b/fs/tmpfs/tmpfs_vnops.c @@ -1029,6 +1029,15 @@ } } + /* + * Avoid manipulating '.' and '..' entries. + */ + if ((fcnp->cn_flags & ISDOTDOT) != 0 || + (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')) { + error = EINVAL; + goto out_locked; + } + if (tvp != NULL) vn_seqc_write_begin(tvp); vn_seqc_write_begin(tdvp); @@ -1044,15 +1053,10 @@ de = tmpfs_dir_lookup(fdnode, fnode, fcnp); /* - * Entry can disappear before we lock fdvp, - * also avoid manipulating '.' and '..' entries. + * Entry can disappear before we lock fdvp. */ if (de == NULL) { - if ((fcnp->cn_flags & ISDOTDOT) != 0 || - (fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')) - error = EINVAL; - else - error = ENOENT; + error = ENOENT; goto out_locked; } MPASS(de->td_node == fnode); @@ -1118,6 +1122,23 @@ if (de->td_node->tn_type == VDIR) { struct tmpfs_node *n; + /* + * User owns the source sticky parent directory, + * but doesn't own the source directory. This fails when + * changing parent directory, because this will modify + * source directory inode (the .. link in it), but still + * can rename it without changing its parent directory. + */ + if ((fdnode->tn_mode & S_ISTXT) && + fcnp->cn_cred->cr_uid != 0 && + fcnp->cn_cred->cr_uid == fdnode->tn_uid && + fcnp->cn_cred->cr_uid != fnode->tn_uid) { + if (newname != NULL) + free(newname, M_TMPFSNAME); + error = EPERM; + goto out_locked; + } + /* * Ensure the target directory is not a child of the * directory being moved. Otherwise, we'd end up @@ -1225,6 +1246,9 @@ tde = tmpfs_dir_lookup(tdnode, tnode, tcnp); tmpfs_dir_detach(tdvp, tde); + tnode->tn_status |= TMPFS_NODE_CHANGED; + tmpfs_update(tvp); + /* * Free the directory entry we just deleted. Note that the * node referred by it will not be removed until the vnode is