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

Reply via email to