fusefs: fix a permission handling bug during VOP_RENAME
If the file to be renamed is a directory and it's going to get a new parent, then the user must have write permissions to that directory, because the ".." dirent must be changed. Reported by: pjdfstest Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
d943c93e76
commit
8e45ec4e64
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/projects/fuse2/; revision=347372
@ -1423,7 +1423,8 @@ fuse_vnop_rename(struct vop_rename_args *ap)
|
||||
struct vnode *tvp = ap->a_tvp;
|
||||
struct componentname *tcnp = ap->a_tcnp;
|
||||
struct fuse_data *data;
|
||||
|
||||
bool newparent = fdvp != tdvp;
|
||||
bool isdir = fvp->v_type == VDIR;
|
||||
int err = 0;
|
||||
|
||||
if (fuse_isdeadfs(fdvp)) {
|
||||
@ -1442,7 +1443,17 @@ fuse_vnop_rename(struct vop_rename_args *ap)
|
||||
* under the source directory in the file system tree.
|
||||
* Linux performs this check at VFS level.
|
||||
*/
|
||||
/*
|
||||
* If source is a directory, and it will get a new parent, user must
|
||||
* have write permission to it, so ".." can be modified.
|
||||
*/
|
||||
data = fuse_get_mpdata(vnode_mount(tdvp));
|
||||
if (data->dataflags & FSESS_DEFAULT_PERMISSIONS && isdir && newparent) {
|
||||
err = fuse_internal_access(fvp, VWRITE,
|
||||
tcnp->cn_thread, tcnp->cn_cred);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
sx_xlock(&data->rename_lock);
|
||||
err = fuse_internal_rename(fdvp, fcnp, tdvp, tcnp);
|
||||
if (err == 0) {
|
||||
|
@ -833,6 +833,49 @@ TEST_F(Rename, eperm_on_sticky_srcdir)
|
||||
ASSERT_EQ(EPERM, errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* A user cannot move out a subdirectory that he does not own, because that
|
||||
* would require changing the subdirectory's ".." dirent
|
||||
*/
|
||||
TEST_F(Rename, eperm_for_subdirectory)
|
||||
{
|
||||
const char FULLDST[] = "mountpoint/d/dst";
|
||||
const char FULLSRC[] = "mountpoint/src";
|
||||
const char RELDSTDIR[] = "d";
|
||||
const char RELDST[] = "dst";
|
||||
const char RELSRC[] = "src";
|
||||
uint64_t ino = 42;
|
||||
uint64_t dstdir_ino = 43;
|
||||
|
||||
expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0);
|
||||
expect_lookup(RELSRC, ino, S_IFDIR | 0755, UINT64_MAX, 0);
|
||||
expect_lookup(RELDSTDIR, dstdir_ino, S_IFDIR | 0777, UINT64_MAX, 0);
|
||||
EXPECT_LOOKUP(dstdir_ino, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
|
||||
ASSERT_EQ(-1, rename(FULLSRC, FULLDST));
|
||||
ASSERT_EQ(EACCES, errno);
|
||||
}
|
||||
|
||||
/*
|
||||
* A user _can_ rename a subdirectory to which he lacks write permissions, if
|
||||
* it will keep the same parent
|
||||
*/
|
||||
TEST_F(Rename, subdirectory_to_same_dir)
|
||||
{
|
||||
const char FULLDST[] = "mountpoint/dst";
|
||||
const char FULLSRC[] = "mountpoint/src";
|
||||
const char RELDST[] = "dst";
|
||||
const char RELSRC[] = "src";
|
||||
uint64_t ino = 42;
|
||||
|
||||
expect_getattr(1, S_IFDIR | 0777, UINT64_MAX, 1, 0);
|
||||
expect_lookup(RELSRC, ino, S_IFDIR | 0755, UINT64_MAX, 0);
|
||||
EXPECT_LOOKUP(1, RELDST).WillOnce(Invoke(ReturnErrno(ENOENT)));
|
||||
expect_rename(0);
|
||||
|
||||
ASSERT_EQ(0, rename(FULLSRC, FULLDST)) << strerror(errno);
|
||||
}
|
||||
|
||||
TEST_F(Rename, eperm_on_sticky_dstdir)
|
||||
{
|
||||
const char FULLDST[] = "mountpoint/d/dst";
|
||||
|
Loading…
Reference in New Issue
Block a user