[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <5524C556.1040607@cn.fujitsu.com>
Date: Wed, 8 Apr 2015 14:06:14 +0800
From: Qu Wenruo <quwenruo@...fujitsu.com>
To: Omar Sandoval <osandov@...ndov.com>, Chris Mason <clm@...com>,
Josef Bacik <jbacik@...com>, David Sterba <dsterba@...e.cz>,
<linux-btrfs@...r.kernel.org>
CC: <linux-kernel@...r.kernel.org>
Subject: Re: [PATCH 2/3] Btrfs: unify subvol= and subvolid= mounting
-------- Original Message --------
Subject: [PATCH 2/3] Btrfs: unify subvol= and subvolid= mounting
From: Omar Sandoval <osandov@...ndov.com>
To: Chris Mason <clm@...com>, Josef Bacik <jbacik@...com>, David Sterba
<dsterba@...e.cz>, <linux-btrfs@...r.kernel.org>
Date: 2015年04月08日 13:34
> Currently, mounting a subvolume with subvolid= takes a different code
> path than mounting with subvol=. This isn't really a big deal except for
> the fact that mounts done with subvolid= or the default subvolume don't
> have a dentry that's connected to the dentry tree like in the subvol=
> case. To unify the code paths, when given subvolid= or using the default
> subvolume ID, translate it into a subvolume name by walking
> ROOT_BACKREFs in the root tree and INODE_REFs in the filesystem trees.
Oh, this patch is what I have tried long long ago, and want to do the
same thing, to show subvolume mount for btrfs.
But it came to me that, superblock->show_path() is a better method to do it.
You can implement btrfs_show_path() to allow mountinfo to get the
subvolume name from subvolid, and don't change the mount routine much.
Thanks,
Qu
>
> Signed-off-by: Omar Sandoval <osandov@...ndov.com>
> ---
> fs/btrfs/super.c | 347 ++++++++++++++++++++++++++++++++++++-------------------
> 1 file changed, 225 insertions(+), 122 deletions(-)
>
> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> index d38be09..5ab9801 100644
> --- a/fs/btrfs/super.c
> +++ b/fs/btrfs/super.c
> @@ -841,33 +841,153 @@ out:
> return error;
> }
>
> -static struct dentry *get_default_root(struct super_block *sb,
> - u64 subvol_objectid)
> +static char *get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info,
> + u64 subvol_objectid)
> {
> - struct btrfs_fs_info *fs_info = btrfs_sb(sb);
> struct btrfs_root *root = fs_info->tree_root;
> - struct btrfs_root *new_root;
> - struct btrfs_dir_item *di;
> - struct btrfs_path *path;
> - struct btrfs_key location;
> - struct inode *inode;
> - u64 dir_id;
> - int new = 0;
> + struct btrfs_root *fs_root;
> + struct btrfs_root_ref *root_ref;
> + struct btrfs_inode_ref *inode_ref;
> + struct btrfs_key key;
> + struct btrfs_path *path = NULL;
> + char *name = NULL, *ptr;
> + u64 dirid;
> + int len;
> + int ret;
> +
> + path = btrfs_alloc_path();
> + if (!path) {
> + ret = -ENOMEM;
> + goto err;
> + }
> + path->leave_spinning = 1;
> +
> + name = kmalloc(PATH_MAX, GFP_NOFS);
> + if (!name) {
> + ret = -ENOMEM;
> + goto err;
> + }
> + ptr = name + PATH_MAX - 1;
> + ptr[0] = '\0';
>
> /*
> - * We have a specific subvol we want to mount, just setup location and
> - * go look up the root.
> + * Walk up the subvolume trees in the tree of tree roots by root
> + * backrefs until we hit the top-level subvolume.
> */
> - if (subvol_objectid) {
> - location.objectid = subvol_objectid;
> - location.type = BTRFS_ROOT_ITEM_KEY;
> - location.offset = (u64)-1;
> - goto find_root;
> + while (subvol_objectid != BTRFS_FS_TREE_OBJECTID) {
> + key.objectid = subvol_objectid;
> + key.type = BTRFS_ROOT_BACKREF_KEY;
> + key.offset = (u64)-1;
> +
> + ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
> + if (ret < 0) {
> + goto err;
> + } else if (ret > 0) {
> + ret = btrfs_previous_item(root, path, subvol_objectid,
> + BTRFS_ROOT_BACKREF_KEY);
> + if (ret < 0) {
> + goto err;
> + } else if (ret > 0) {
> + ret = -ENOENT;
> + goto err;
> + }
> + }
> +
> + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
> + subvol_objectid = key.offset;
> +
> + root_ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
> + struct btrfs_root_ref);
> + len = btrfs_root_ref_name_len(path->nodes[0], root_ref);
> + ptr -= len + 1;
> + if (ptr < name) {
> + ret = -ENAMETOOLONG;
> + goto err;
> + }
> + read_extent_buffer(path->nodes[0], ptr + 1,
> + (unsigned long)(root_ref + 1), len);
> + ptr[0] = '/';
> + dirid = btrfs_root_ref_dirid(path->nodes[0], root_ref);
> + btrfs_release_path(path);
> +
> + key.objectid = subvol_objectid;
> + key.type = BTRFS_ROOT_ITEM_KEY;
> + key.offset = (u64)-1;
> + fs_root = btrfs_read_fs_root_no_name(fs_info, &key);
> + if (IS_ERR(fs_root)) {
> + ret = PTR_ERR(fs_root);
> + goto err;
> + }
> +
> + /*
> + * Walk up the filesystem tree by inode refs until we hit the
> + * root directory.
> + */
> + while (dirid != BTRFS_FIRST_FREE_OBJECTID) {
> + key.objectid = dirid;
> + key.type = BTRFS_INODE_REF_KEY;
> + key.offset = (u64)-1;
> +
> + ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
> + if (ret < 0) {
> + goto err;
> + } else if (ret > 0) {
> + ret = btrfs_previous_item(fs_root, path, dirid,
> + BTRFS_INODE_REF_KEY);
> + if (ret < 0) {
> + goto err;
> + } else if (ret > 0) {
> + ret = -ENOENT;
> + goto err;
> + }
> + }
> +
> + btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
> + dirid = key.offset;
> +
> + inode_ref = btrfs_item_ptr(path->nodes[0],
> + path->slots[0],
> + struct btrfs_inode_ref);
> + len = btrfs_inode_ref_name_len(path->nodes[0],
> + inode_ref);
> + ptr -= len + 1;
> + if (ptr < name) {
> + ret = -ENAMETOOLONG;
> + goto err;
> + }
> + read_extent_buffer(path->nodes[0], ptr + 1,
> + (unsigned long)(inode_ref + 1), len);
> + ptr[0] = '/';
> + btrfs_release_path(path);
> + }
> + }
> +
> + btrfs_free_path(path);
> + if (ptr == name + PATH_MAX - 1) {
> + name[0] = '/';
> + name[1] = '\0';
> + } else {
> + memmove(name, ptr, name + PATH_MAX - ptr);
> }
> + return name;
> +
> +err:
> + btrfs_free_path(path);
> + kfree(name);
> + return ERR_PTR(ret);
> +}
> +
> +static int get_default_subvol_objectid(struct btrfs_fs_info *fs_info, u64 *objectid)
> +{
> + struct btrfs_root *root = fs_info->tree_root;
> + struct btrfs_dir_item *di;
> + struct btrfs_path *path;
> + struct btrfs_key location;
> + u64 dir_id;
>
> path = btrfs_alloc_path();
> if (!path)
> - return ERR_PTR(-ENOMEM);
> + return -ENOMEM;
> path->leave_spinning = 1;
>
> /*
> @@ -879,49 +999,23 @@ static struct dentry *get_default_root(struct super_block *sb,
> di = btrfs_lookup_dir_item(NULL, root, path, dir_id, "default", 7, 0);
> if (IS_ERR(di)) {
> btrfs_free_path(path);
> - return ERR_CAST(di);
> + return PTR_ERR(di);
> }
> if (!di) {
> /*
> * Ok the default dir item isn't there. This is weird since
> * it's always been there, but don't freak out, just try and
> - * mount to root most subvolume.
> + * mount the top-level subvolume.
> */
> btrfs_free_path(path);
> - dir_id = BTRFS_FIRST_FREE_OBJECTID;
> - new_root = fs_info->fs_root;
> - goto setup_root;
> + *objectid = BTRFS_FS_TREE_OBJECTID;
> + return 0;
> }
>
> btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location);
> btrfs_free_path(path);
> -
> -find_root:
> - new_root = btrfs_read_fs_root_no_name(fs_info, &location);
> - if (IS_ERR(new_root))
> - return ERR_CAST(new_root);
> -
> - dir_id = btrfs_root_dirid(&new_root->root_item);
> -setup_root:
> - location.objectid = dir_id;
> - location.type = BTRFS_INODE_ITEM_KEY;
> - location.offset = 0;
> -
> - inode = btrfs_iget(sb, &location, new_root, &new);
> - if (IS_ERR(inode))
> - return ERR_CAST(inode);
> -
> - /*
> - * If we're just mounting the root most subvol put the inode and return
> - * a reference to the dentry. We will have already gotten a reference
> - * to the inode in btrfs_fill_super so we're good to go.
> - */
> - if (!new && sb->s_root->d_inode == inode) {
> - iput(inode);
> - return dget(sb->s_root);
> - }
> -
> - return d_obtain_root(inode);
> + *objectid = location.objectid;
> + return 0;
> }
>
> static int btrfs_fill_super(struct super_block *sb,
> @@ -1129,109 +1223,123 @@ static inline int is_subvolume_inode(struct inode *inode)
> }
>
> /*
> - * This will strip out the subvol=%s argument for an argument string and add
> - * subvolid=0 to make sure we get the actual tree root for path walking to the
> - * subvol we want.
> + * This will add subvolid=0 to the argument string while removing any subvol=
> + * and subvolid= arguments to make sure we get the top-level root for path
> + * walking to the subvol we want.
> */
> static char *setup_root_args(char *args)
> {
> - unsigned len = strlen(args) + 2 + 1;
> - char *src, *dst, *buf;
> -
> - /*
> - * We need the same args as before, but with this substitution:
> - * s!subvol=[^,]+!subvolid=0!
> - *
> - * Since the replacement string is up to 2 bytes longer than the
> - * original, allocate strlen(args) + 2 + 1 bytes.
> - */
> + char *p, *dst, *buf;
>
> - src = strstr(args, "subvol=");
> - /* This shouldn't happen, but just in case.. */
> - if (!src)
> - return NULL;
> + if (!args)
> + return kstrdup("subvolid=0", GFP_NOFS);
>
> - buf = dst = kmalloc(len, GFP_NOFS);
> + /* The worst case is that we add ",subvolid=0" to the end. */
> + buf = dst = kmalloc(strlen(args) + strlen(",subvolid=0") + 1, GFP_NOFS);
> if (!buf)
> return NULL;
>
> - /*
> - * If the subvol= arg is not at the start of the string,
> - * copy whatever precedes it into buf.
> - */
> - if (src != args) {
> - *src++ = '\0';
> - strcpy(buf, args);
> - dst += strlen(args);
> + while (1) {
> + p = strchrnul(args, ',');
> + if (strncmp(args, "subvol=", strlen("subvol=")) != 0 &&
> + strncmp(args, "subvolid=", strlen("subvolid=")) != 0) {
> + memcpy(dst, args, p - args);
> + dst += p - args;
> + *dst++ = ',';
> + }
> + if (*p)
> + args = p + 1;
> + else
> + break;
> }
> -
> strcpy(dst, "subvolid=0");
> - dst += strlen("subvolid=0");
> -
> - /*
> - * If there is a "," after the original subvol=... string,
> - * copy that suffix into our buffer. Otherwise, we're done.
> - */
> - src = strchr(src, ',');
> - if (src)
> - strcpy(dst, src);
>
> return buf;
> }
>
> -static struct dentry *mount_subvol(const char *subvol_name, int flags,
> - const char *device_name, char *data)
> +static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
> + int flags, const char *device_name,
> + char *data)
> {
> struct dentry *root;
> - struct vfsmount *mnt;
> + struct vfsmount *mnt = NULL;
> char *newargs;
> + int ret;
>
> newargs = setup_root_args(data);
> - if (!newargs)
> - return ERR_PTR(-ENOMEM);
> - mnt = vfs_kern_mount(&btrfs_fs_type, flags, device_name,
> - newargs);
> + if (!newargs) {
> + root = ERR_PTR(-ENOMEM);
> + goto out;
> + }
>
> - if (PTR_RET(mnt) == -EBUSY) {
> + mnt = vfs_kern_mount(&btrfs_fs_type, flags, device_name, newargs);
> + if (PTR_ERR_OR_ZERO(mnt) == -EBUSY) {
> if (flags & MS_RDONLY) {
> - mnt = vfs_kern_mount(&btrfs_fs_type, flags & ~MS_RDONLY, device_name,
> - newargs);
> + mnt = vfs_kern_mount(&btrfs_fs_type, flags & ~MS_RDONLY,
> + device_name, newargs);
> } else {
> - int r;
> - mnt = vfs_kern_mount(&btrfs_fs_type, flags | MS_RDONLY, device_name,
> - newargs);
> + mnt = vfs_kern_mount(&btrfs_fs_type, flags | MS_RDONLY,
> + device_name, newargs);
> if (IS_ERR(mnt)) {
> - kfree(newargs);
> - return ERR_CAST(mnt);
> + root = ERR_CAST(mnt);
> + mnt = NULL;
> + goto out;
> }
>
> down_write(&mnt->mnt_sb->s_umount);
> - r = btrfs_remount(mnt->mnt_sb, &flags, NULL);
> + ret = btrfs_remount(mnt->mnt_sb, &flags, NULL);
> up_write(&mnt->mnt_sb->s_umount);
> - if (r < 0) {
> - /* FIXME: release vfsmount mnt ??*/
> - kfree(newargs);
> - return ERR_PTR(r);
> + if (ret < 0) {
> + root = ERR_PTR(ret);
> + goto out;
> }
> }
> }
> + if (IS_ERR(mnt)) {
> + root = ERR_CAST(mnt);
> + mnt = NULL;
> + goto out;
> + }
>
> - kfree(newargs);
> + if (!subvol_name) {
> + if (!subvol_objectid) {
> + ret = get_default_subvol_objectid(btrfs_sb(mnt->mnt_sb),
> + &subvol_objectid);
> + if (ret) {
> + root = ERR_PTR(ret);
> + goto out;
> + }
> + }
> + subvol_name = get_subvol_name_from_objectid(btrfs_sb(mnt->mnt_sb),
> + subvol_objectid);
> + if (IS_ERR(subvol_name)) {
> + root = ERR_CAST(subvol_name);
> + subvol_name = NULL;
> + goto out;
> + }
>
> - if (IS_ERR(mnt))
> - return ERR_CAST(mnt);
> + }
>
> root = mount_subtree(mnt, subvol_name);
> + mnt = NULL; /* mount_subtree drops our reference on the vfsmount. */
>
> if (!IS_ERR(root) && !is_subvolume_inode(root->d_inode)) {
> struct super_block *s = root->d_sb;
> dput(root);
> root = ERR_PTR(-EINVAL);
> deactivate_locked_super(s);
> - printk(KERN_ERR "BTRFS: '%s' is not a valid subvolume\n",
> - subvol_name);
> + pr_err("BTRFS: '%s' is not a valid subvolume\n", subvol_name);
> + }
> + if (!IS_ERR(root) && subvol_objectid &&
> + BTRFS_I(root->d_inode)->root->root_key.objectid != subvol_objectid) {
> + pr_warn("BTRFS: subvol '%s' does not match subvolid %llu\n",
> + subvol_name, subvol_objectid);
> }
>
> +out:
> + mntput(mnt);
> + kfree(newargs);
> + kfree(subvol_name);
> return root;
> }
>
> @@ -1296,7 +1404,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
> {
> struct block_device *bdev = NULL;
> struct super_block *s;
> - struct dentry *root;
> struct btrfs_fs_devices *fs_devices = NULL;
> struct btrfs_fs_info *fs_info = NULL;
> struct security_mnt_opts new_sec_opts;
> @@ -1316,10 +1423,10 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
> return ERR_PTR(error);
> }
>
> - if (subvol_name) {
> - root = mount_subvol(subvol_name, flags, device_name, data);
> - kfree(subvol_name);
> - return root;
> + if (subvol_name || subvol_objectid != BTRFS_FS_TREE_OBJECTID) {
> + /* mount_subvol() will free subvol_name. */
> + return mount_subvol(subvol_name, subvol_objectid, flags,
> + device_name, data);
> }
>
> security_init_mnt_opts(&new_sec_opts);
> @@ -1385,23 +1492,19 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
> error = btrfs_fill_super(s, fs_devices, data,
> flags & MS_SILENT ? 1 : 0);
> }
> -
> - root = !error ? get_default_root(s, subvol_objectid) : ERR_PTR(error);
> - if (IS_ERR(root)) {
> + if (error) {
> deactivate_locked_super(s);
> - error = PTR_ERR(root);
> goto error_sec_opts;
> }
>
> fs_info = btrfs_sb(s);
> error = setup_security_options(fs_info, s, &new_sec_opts);
> if (error) {
> - dput(root);
> deactivate_locked_super(s);
> goto error_sec_opts;
> }
>
> - return root;
> + return dget(s->s_root);
>
> error_close_devices:
> btrfs_close_devices(fs_devices);
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists