move_mount: allow to add a mount into an existing group
[linux-2.6-microblaze.git] / fs / namespace.c
index ab4174a..5d0b477 100644 (file)
@@ -2684,6 +2684,78 @@ out:
        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 = -EINVAL;
+       /* To and From must be mounted */
+       if (!is_mounted(&from->mnt))
+               goto out;
+       if (!is_mounted(&to->mnt))
+               goto out;
+
+       err = -EPERM;
+       /* We should be allowed to modify mount namespaces of both mounts */
+       if (!ns_capable(from->mnt_ns->user_ns, CAP_SYS_ADMIN))
+               goto out;
+       if (!ns_capable(to->mnt_ns->user_ns, CAP_SYS_ADMIN))
+               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, &m->mnt_slave_list);
+               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;
@@ -3669,7 +3741,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);