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 @@ -1129,6 +1129,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