Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 86 additions & 1 deletion fs/namespace.c
Original file line number Diff line number Diff line change
Expand Up @@ -2293,6 +2293,19 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
return attach_recursive_mnt(mnt, p, mp, false);
}

static int may_change_propagation(const struct mount *m)
{
struct mnt_namespace *ns = m->mnt_ns;

// it must be mounted in some namespace
if (IS_ERR_OR_NULL(ns)) // is_mounted()
return -EINVAL;
// and the caller must be admin in userns of that namespace
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
return -EPERM;
return 0;
}

/*
* Sanity check the flags to change_mnt_propagation.
*/
Expand Down Expand Up @@ -2329,6 +2342,10 @@ static int do_change_type(struct path *path, int ms_flags)
return -EINVAL;

namespace_lock();
err = may_change_propagation(mnt);
if (err)
goto out_unlock;

if (type == MS_SHARED) {
err = invent_group_ids(mnt, recurse);
if (err)
Expand Down Expand Up @@ -2701,6 +2718,71 @@ static bool check_for_nsfs_mounts(struct mount *subtree)
return ret;
}

static int do_set_group(struct path *from_path, struct path *to_path)
{
struct mount *from, *to;
int err;

from = real_mount(from_path->mnt);
to = real_mount(to_path->mnt);

namespace_lock();

err = may_change_propagation(from);
if (err)
goto out;
err = may_change_propagation(to);
if (err)
goto out;

err = -EINVAL;
/* To and From paths should be mount roots */
if (from_path->dentry != from_path->mnt->mnt_root)
goto out;
if (to_path->dentry != to_path->mnt->mnt_root)
goto out;

/* Setting sharing groups is only allowed across same superblock */
if (from->mnt.mnt_sb != to->mnt.mnt_sb)
goto out;

/* From mount root should be wider than To mount root */
if (!is_subdir(to->mnt.mnt_root, from->mnt.mnt_root))
goto out;

/* From mount should not have locked children in place of To's root */
if (has_locked_children(from, to->mnt.mnt_root))
goto out;

/* Setting sharing groups is only allowed on private mounts */
if (IS_MNT_SHARED(to) || IS_MNT_SLAVE(to))
goto out;

/* From should not be private */
if (!IS_MNT_SHARED(from) && !IS_MNT_SLAVE(from))
goto out;

if (IS_MNT_SLAVE(from)) {
struct mount *m = from->mnt_master;

list_add(&to->mnt_slave, &from->mnt_slave);
to->mnt_master = m;
}

if (IS_MNT_SHARED(from)) {
to->mnt_group_id = from->mnt_group_id;
list_add(&to->mnt_share, &from->mnt_share);
lock_mount_hash();
set_mnt_shared(to);
unlock_mount_hash();
}

err = 0;
out:
namespace_unlock();
return err;
}

static int do_move_mount(struct path *old_path, struct path *new_path)
{
struct mnt_namespace *ns;
Expand Down Expand Up @@ -3685,7 +3767,10 @@ SYSCALL_DEFINE5(move_mount,
if (ret < 0)
goto out_to;

ret = do_move_mount(&from_path, &to_path);
if (flags & MOVE_MOUNT_SET_GROUP)
ret = do_set_group(&from_path, &to_path);
else
ret = do_move_mount(&from_path, &to_path);

out_to:
path_put(&to_path);
Expand Down
3 changes: 2 additions & 1 deletion include/uapi/linux/mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@
#define MOVE_MOUNT_T_SYMLINKS 0x00000010 /* Follow symlinks on to path */
#define MOVE_MOUNT_T_AUTOMOUNTS 0x00000020 /* Follow automounts on to path */
#define MOVE_MOUNT_T_EMPTY_PATH 0x00000040 /* Empty to path permitted */
#define MOVE_MOUNT__MASK 0x00000077
#define MOVE_MOUNT_SET_GROUP 0x00000100 /* Set sharing group instead */
#define MOVE_MOUNT__MASK 0x00000177

/*
* fsopen() flags.
Expand Down