Commit 4d4340c9 authored by Christian Brauner's avatar Christian Brauner Committed by David Sterba

btrfs: allow idmapped SNAP_CREATE/SUBVOL_CREATE ioctls

Creating subvolumes and snapshots is one of the core features of btrfs
and is even available to unprivileged users. Make it possible to use
subvolume and snapshot creation on idmapped mounts. This is a fairly
straightforward operation since all the permission checking helpers are
already capable of handling idmapped mounts. So we just need to pass
down the mount's userns.
Reviewed-by: default avatarJosef Bacik <josef@toxicpanda.com>
Signed-off-by: default avatarChristian Brauner <christian.brauner@ubuntu.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 5474bf40
...@@ -3164,7 +3164,8 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end, ...@@ -3164,7 +3164,8 @@ int btrfs_set_extent_delalloc(struct btrfs_inode *inode, u64 start, u64 end,
struct extent_state **cached_state); struct extent_state **cached_state);
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
struct btrfs_root *new_root, struct btrfs_root *new_root,
struct btrfs_root *parent_root); struct btrfs_root *parent_root,
struct user_namespace *mnt_userns);
void btrfs_set_delalloc_extent(struct inode *inode, struct extent_state *state, void btrfs_set_delalloc_extent(struct inode *inode, struct extent_state *state,
unsigned *bits); unsigned *bits);
void btrfs_clear_delalloc_extent(struct inode *inode, void btrfs_clear_delalloc_extent(struct inode *inode,
......
...@@ -9028,7 +9028,8 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback) ...@@ -9028,7 +9028,8 @@ static int btrfs_truncate(struct inode *inode, bool skip_writeback)
*/ */
int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
struct btrfs_root *new_root, struct btrfs_root *new_root,
struct btrfs_root *parent_root) struct btrfs_root *parent_root,
struct user_namespace *mnt_userns)
{ {
struct inode *inode; struct inode *inode;
int err; int err;
...@@ -9039,7 +9040,7 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans, ...@@ -9039,7 +9040,7 @@ int btrfs_create_subvol_root(struct btrfs_trans_handle *trans,
if (err < 0) if (err < 0)
return err; return err;
inode = btrfs_new_inode(trans, new_root, &init_user_ns, NULL, "..", 2, inode = btrfs_new_inode(trans, new_root, mnt_userns, NULL, "..", 2,
ino, ino, ino, ino,
S_IFDIR | (~current_umask() & S_IRWXUGO), S_IFDIR | (~current_umask() & S_IRWXUGO),
&index); &index);
......
...@@ -499,8 +499,8 @@ int __pure btrfs_is_empty_uuid(u8 *uuid) ...@@ -499,8 +499,8 @@ int __pure btrfs_is_empty_uuid(u8 *uuid)
return 1; return 1;
} }
static noinline int create_subvol(struct inode *dir, static noinline int create_subvol(struct user_namespace *mnt_userns,
struct dentry *dentry, struct inode *dir, struct dentry *dentry,
const char *name, int namelen, const char *name, int namelen,
struct btrfs_qgroup_inherit *inherit) struct btrfs_qgroup_inherit *inherit)
{ {
...@@ -645,7 +645,7 @@ static noinline int create_subvol(struct inode *dir, ...@@ -645,7 +645,7 @@ static noinline int create_subvol(struct inode *dir,
goto fail; goto fail;
} }
ret = btrfs_create_subvol_root(trans, new_root, root); ret = btrfs_create_subvol_root(trans, new_root, root, mnt_userns);
btrfs_put_root(new_root); btrfs_put_root(new_root);
if (ret) { if (ret) {
/* We potentially lose an unused inode item here */ /* We potentially lose an unused inode item here */
...@@ -871,15 +871,16 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir) ...@@ -871,15 +871,16 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
} }
/* copy of may_create in fs/namei.c() */ /* copy of may_create in fs/namei.c() */
static inline int btrfs_may_create(struct inode *dir, struct dentry *child) static inline int btrfs_may_create(struct user_namespace *mnt_userns,
struct inode *dir, struct dentry *child)
{ {
if (d_really_is_positive(child)) if (d_really_is_positive(child))
return -EEXIST; return -EEXIST;
if (IS_DEADDIR(dir)) if (IS_DEADDIR(dir))
return -ENOENT; return -ENOENT;
if (!fsuidgid_has_mapping(dir->i_sb, &init_user_ns)) if (!fsuidgid_has_mapping(dir->i_sb, mnt_userns))
return -EOVERFLOW; return -EOVERFLOW;
return inode_permission(&init_user_ns, dir, MAY_WRITE | MAY_EXEC); return inode_permission(mnt_userns, dir, MAY_WRITE | MAY_EXEC);
} }
/* /*
...@@ -888,6 +889,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child) ...@@ -888,6 +889,7 @@ static inline int btrfs_may_create(struct inode *dir, struct dentry *child)
* inside this filesystem so it's quite a bit simpler. * inside this filesystem so it's quite a bit simpler.
*/ */
static noinline int btrfs_mksubvol(const struct path *parent, static noinline int btrfs_mksubvol(const struct path *parent,
struct user_namespace *mnt_userns,
const char *name, int namelen, const char *name, int namelen,
struct btrfs_root *snap_src, struct btrfs_root *snap_src,
bool readonly, bool readonly,
...@@ -902,12 +904,12 @@ static noinline int btrfs_mksubvol(const struct path *parent, ...@@ -902,12 +904,12 @@ static noinline int btrfs_mksubvol(const struct path *parent,
if (error == -EINTR) if (error == -EINTR)
return error; return error;
dentry = lookup_one_len(name, parent->dentry, namelen); dentry = lookup_one(mnt_userns, name, parent->dentry, namelen);
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto out_unlock; goto out_unlock;
error = btrfs_may_create(dir, dentry); error = btrfs_may_create(mnt_userns, dir, dentry);
if (error) if (error)
goto out_dput; goto out_dput;
...@@ -929,7 +931,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, ...@@ -929,7 +931,7 @@ static noinline int btrfs_mksubvol(const struct path *parent,
if (snap_src) if (snap_src)
error = create_snapshot(snap_src, dir, dentry, readonly, inherit); error = create_snapshot(snap_src, dir, dentry, readonly, inherit);
else else
error = create_subvol(dir, dentry, name, namelen, inherit); error = create_subvol(mnt_userns, dir, dentry, name, namelen, inherit);
if (!error) if (!error)
fsnotify_mkdir(dir, dentry); fsnotify_mkdir(dir, dentry);
...@@ -943,6 +945,7 @@ static noinline int btrfs_mksubvol(const struct path *parent, ...@@ -943,6 +945,7 @@ static noinline int btrfs_mksubvol(const struct path *parent,
} }
static noinline int btrfs_mksnapshot(const struct path *parent, static noinline int btrfs_mksnapshot(const struct path *parent,
struct user_namespace *mnt_userns,
const char *name, int namelen, const char *name, int namelen,
struct btrfs_root *root, struct btrfs_root *root,
bool readonly, bool readonly,
...@@ -972,7 +975,7 @@ static noinline int btrfs_mksnapshot(const struct path *parent, ...@@ -972,7 +975,7 @@ static noinline int btrfs_mksnapshot(const struct path *parent,
btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1); btrfs_wait_ordered_extents(root, U64_MAX, 0, (u64)-1);
ret = btrfs_mksubvol(parent, name, namelen, ret = btrfs_mksubvol(parent, mnt_userns, name, namelen,
root, readonly, inherit); root, readonly, inherit);
out: out:
if (snapshot_force_cow) if (snapshot_force_cow)
...@@ -1801,6 +1804,7 @@ static noinline int btrfs_ioctl_resize(struct file *file, ...@@ -1801,6 +1804,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,
} }
static noinline int __btrfs_ioctl_snap_create(struct file *file, static noinline int __btrfs_ioctl_snap_create(struct file *file,
struct user_namespace *mnt_userns,
const char *name, unsigned long fd, int subvol, const char *name, unsigned long fd, int subvol,
bool readonly, bool readonly,
struct btrfs_qgroup_inherit *inherit) struct btrfs_qgroup_inherit *inherit)
...@@ -1828,8 +1832,8 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, ...@@ -1828,8 +1832,8 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file,
} }
if (subvol) { if (subvol) {
ret = btrfs_mksubvol(&file->f_path, name, namelen, ret = btrfs_mksubvol(&file->f_path, mnt_userns, name,
NULL, readonly, inherit); namelen, NULL, readonly, inherit);
} else { } else {
struct fd src = fdget(fd); struct fd src = fdget(fd);
struct inode *src_inode; struct inode *src_inode;
...@@ -1843,14 +1847,15 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file, ...@@ -1843,14 +1847,15 @@ static noinline int __btrfs_ioctl_snap_create(struct file *file,
btrfs_info(BTRFS_I(file_inode(file))->root->fs_info, btrfs_info(BTRFS_I(file_inode(file))->root->fs_info,
"Snapshot src from another FS"); "Snapshot src from another FS");
ret = -EXDEV; ret = -EXDEV;
} else if (!inode_owner_or_capable(&init_user_ns, src_inode)) { } else if (!inode_owner_or_capable(mnt_userns, src_inode)) {
/* /*
* Subvolume creation is not restricted, but snapshots * Subvolume creation is not restricted, but snapshots
* are limited to own subvolumes only * are limited to own subvolumes only
*/ */
ret = -EPERM; ret = -EPERM;
} else { } else {
ret = btrfs_mksnapshot(&file->f_path, name, namelen, ret = btrfs_mksnapshot(&file->f_path, mnt_userns,
name, namelen,
BTRFS_I(src_inode)->root, BTRFS_I(src_inode)->root,
readonly, inherit); readonly, inherit);
} }
...@@ -1876,8 +1881,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file, ...@@ -1876,8 +1881,9 @@ static noinline int btrfs_ioctl_snap_create(struct file *file,
return PTR_ERR(vol_args); return PTR_ERR(vol_args);
vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';
ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file),
subvol, false, NULL); vol_args->name, vol_args->fd, subvol,
false, NULL);
kfree(vol_args); kfree(vol_args);
return ret; return ret;
...@@ -1935,8 +1941,9 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file, ...@@ -1935,8 +1941,9 @@ static noinline int btrfs_ioctl_snap_create_v2(struct file *file,
} }
} }
ret = __btrfs_ioctl_snap_create(file, vol_args->name, vol_args->fd, ret = __btrfs_ioctl_snap_create(file, file_mnt_user_ns(file),
subvol, readonly, inherit); vol_args->name, vol_args->fd, subvol,
readonly, inherit);
if (ret) if (ret)
goto free_inherit; goto free_inherit;
free_inherit: free_inherit:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment