I've modified 'btrfs subvolume list' to show a subvolume's attributes, such as readonly and default, and adopted a new structure for args for subvol_getflags/setflags.
So here is the kernel side update. Signed-off-by: Liu Bo <[email protected]> --- fs/btrfs/ioctl.c | 100 ++++++++++++++++++++++++++++++++++++++++------------- fs/btrfs/ioctl.h | 5 +++ 2 files changed, 80 insertions(+), 25 deletions(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 60fff96..f9c2180 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1487,6 +1487,31 @@ out: return ret; } +static struct btrfs_root *__btrfs_subvol_get_root(struct btrfs_root *root, + u64 root_id) +{ + struct btrfs_key root_key; + struct btrfs_root *root_ret = NULL; + + if (root->objectid == root_id || !root_id) { + root_ret = root; + goto get_root; + } + + root_key.objectid = root_id; + root_key.type = BTRFS_ROOT_ITEM_KEY; + root_key.offset = (u64)-1; + root_ret = btrfs_read_fs_root_no_name(root->fs_info, &root_key); + /* root_ret won't be NULL */ + if (IS_ERR(root_ret)) + return root_ret; +get_root: + if (btrfs_root_refs(&root_ret->root_item) == 0) + return ERR_PTR(-ENOENT); + + return root_ret; +} + /* Return 1 for default, otherwise return 0. */ static int btrfs_root_default(struct btrfs_root *root) { @@ -1525,24 +1550,38 @@ static noinline int btrfs_ioctl_subvol_getflags(struct file *file, { struct inode *inode = fdentry(file)->d_inode; struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_root *new_root = NULL; int ret = 0; - u64 flags = 0; + struct btrfs_ioctl_get_set_flags_args *get_args; if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) return -EINVAL; - down_read(&root->fs_info->subvol_sem); - if (btrfs_root_readonly(root)) - flags |= BTRFS_SUBVOL_RDONLY; + get_args = memdup_user(arg, sizeof(*get_args)); + if (IS_ERR(get_args)) + return PTR_ERR(get_args); - ret = btrfs_root_default(root); - if (ret > 0) - flags |= BTRFS_SUBVOL_DEFAULT; - up_read(&root->fs_info->subvol_sem); + new_root = __btrfs_subvol_get_root(root, get_args->objectid); + if (IS_ERR(new_root)) { + ret = PTR_ERR(new_root); + goto out; + } - if (copy_to_user(arg, &flags, sizeof(flags))) + down_read(&new_root->fs_info->subvol_sem); + if (btrfs_root_readonly(new_root)) + get_args->flags |= BTRFS_SUBVOL_RDONLY; + ret = btrfs_root_default(new_root); + if (ret > 0) { + get_args->flags |= BTRFS_SUBVOL_DEFAULT; + ret = 0; + } + up_read(&new_root->fs_info->subvol_sem); + + if (copy_to_user(arg, get_args, sizeof(*get_args))) ret = -EFAULT; +out: + kfree(get_args); return ret; } @@ -1551,8 +1590,10 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, { struct inode *inode = fdentry(file)->d_inode; struct btrfs_root *root = BTRFS_I(inode)->root; + struct btrfs_root *new_root = NULL; struct btrfs_trans_handle *trans; - u64 root_flags; + struct btrfs_ioctl_get_set_flags_args *set_args = NULL; + u64 root_flags, new_root_flags; u64 flags; int ret = 0; @@ -1565,11 +1606,19 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, goto out_drop_write; } - if (copy_from_user(&flags, arg, sizeof(flags))) { - ret = -EFAULT; + set_args = memdup_user(arg, sizeof(*set_args)); + if (IS_ERR(set_args)) { + ret = PTR_ERR(set_args); + goto out_drop_write; + } + + new_root = __btrfs_subvol_get_root(root, set_args->objectid); + if (IS_ERR(new_root)) { + ret = PTR_ERR(new_root); goto out_drop_write; } + flags = set_args->flags; if (flags & BTRFS_SUBVOL_CREATE_ASYNC) { ret = -EINVAL; goto out_drop_write; @@ -1585,38 +1634,39 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, goto out_drop_write; } - down_write(&root->fs_info->subvol_sem); + down_write(&new_root->fs_info->subvol_sem); /* nothing to do */ - if (!!(flags & BTRFS_SUBVOL_RDONLY) == btrfs_root_readonly(root)) + if (!!(flags & BTRFS_SUBVOL_RDONLY) == btrfs_root_readonly(new_root)) goto out_drop_sem; - root_flags = btrfs_root_flags(&root->root_item); + new_root_flags = root_flags = btrfs_root_flags(&new_root->root_item); if (flags & BTRFS_SUBVOL_RDONLY) - btrfs_set_root_flags(&root->root_item, - root_flags | BTRFS_ROOT_SUBVOL_RDONLY); + new_root_flags |= BTRFS_ROOT_SUBVOL_RDONLY; else - btrfs_set_root_flags(&root->root_item, - root_flags & ~BTRFS_ROOT_SUBVOL_RDONLY); + new_root_flags &= ~BTRFS_ROOT_SUBVOL_RDONLY; - trans = btrfs_start_transaction(root, 1); + btrfs_set_root_flags(&new_root->root_item, new_root_flags); + + trans = btrfs_start_transaction(new_root, 1); if (IS_ERR(trans)) { ret = PTR_ERR(trans); goto out_reset; } - ret = btrfs_update_root(trans, root->fs_info->tree_root, - &root->root_key, &root->root_item); + ret = btrfs_update_root(trans, new_root->fs_info->tree_root, + &new_root->root_key, &new_root->root_item); - btrfs_commit_transaction(trans, root); + btrfs_commit_transaction(trans, new_root); out_reset: if (ret) - btrfs_set_root_flags(&root->root_item, root_flags); + btrfs_set_root_flags(&new_root->root_item, root_flags); out_drop_sem: - up_write(&root->fs_info->subvol_sem); + up_write(&new_root->fs_info->subvol_sem); out_drop_write: mnt_drop_write_file(file); out: + kfree(set_args); return ret; } diff --git a/fs/btrfs/ioctl.h b/fs/btrfs/ioctl.h index 3186d2d..1fa0ce2 100644 --- a/fs/btrfs/ioctl.h +++ b/fs/btrfs/ioctl.h @@ -45,6 +45,11 @@ struct btrfs_ioctl_vol_args_v2 { char name[BTRFS_SUBVOL_NAME_MAX + 1]; }; +struct btrfs_ioctl_get_set_flags_args { + __u64 objectid; + __u64 flags; +}; + /* * structure to report errors and progress to userspace, either as a * result of a finished scrub, a canceled scrub or a progress inquiry -- 1.6.5.2 -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
