Hi, When I made snapshot listing, I thought 'subvol' parameter is not enough as G.Baroncelli wrote. I think nice feature too. (I tried to expand subvol parameter feature, by iterating dentry search(lookup_one_len) but I cannot treat dentries correctly yet. :)
But, is 'subvol=name' not prohibited yet? When I specified 'subvol=name', mount command simply mounts filesystem root without error. Josef Bacik wrote: > This work is in preperation for being able to set a different root as the > default mounting root. > > There is currently a problem with how we mount subvolumes. We cannot > currently > mount a subvolume of a subvolume, you can only mount subvolumes/snapshots of > the > default subvolume. So say you take a snapshot of the default subvolume and > call > it snap1, and then take a snapshot of snap1 and call it snap2, so now you have > > / > /snap1 > /snap1/snap2 > > as your available volumes. Currently you can only mount / and /snap1, you > cannot mount /snap1/snap2. To fix this problem instead of passing > subvol=<name> > you must pass in subvol=<treeid>, where <treeid> is the tree id that gets spit > out via the subvolume listing you get from the subvolume listing patches > (btrfsctl -l). This allows us to mount /, /snap1 and /snap1/snap2 as the root > volume. > > In addition to the above, we also now read the default dir item in the tree > root > to get the root key that it points to. For now this just points at what has > always been the default subvolme, but later on I plan to change it to point at > whatever root you want to be the new default root, so you can just set the > default mount and not have to mount with -o subvol=<treeid>. I tested this > out > with the above scenario and it worked perfectly. Thanks, > > Signed-off-by: Josef Bacik <jo...@redhat.com> > --- > fs/btrfs/ctree.h | 2 +- > fs/btrfs/export.c | 4 +- > fs/btrfs/inode.c | 10 ++- > fs/btrfs/relocation.c | 2 +- > fs/btrfs/super.c | 167 +++++++++++++++++++++++++++++++++++------------- > fs/btrfs/tree-log.c | 2 +- > 6 files changed, 133 insertions(+), 54 deletions(-) > > diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h > index 444b3e9..464f688 100644 > --- a/fs/btrfs/ctree.h > +++ b/fs/btrfs/ctree.h > @@ -2318,7 +2318,7 @@ int btrfs_init_cachep(void); > void btrfs_destroy_cachep(void); > long btrfs_ioctl_trans_end(struct file *file); > struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, > - struct btrfs_root *root); > + struct btrfs_root *root, int *was_new); > int btrfs_commit_write(struct file *file, struct page *page, > unsigned from, unsigned to); > struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, > diff --git a/fs/btrfs/export.c b/fs/btrfs/export.c > index ba5c3fd..951ef09 100644 > --- a/fs/btrfs/export.c > +++ b/fs/btrfs/export.c > @@ -95,7 +95,7 @@ static struct dentry *btrfs_get_dentry(struct super_block > *sb, u64 objectid, > btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY); > key.offset = 0; > > - inode = btrfs_iget(sb, &key, root); > + inode = btrfs_iget(sb, &key, root, NULL); > if (IS_ERR(inode)) { > err = PTR_ERR(inode); > goto fail; > @@ -223,7 +223,7 @@ static struct dentry *btrfs_get_parent(struct dentry > *child) > > key.type = BTRFS_INODE_ITEM_KEY; > key.offset = 0; > - dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root)); > + dentry = d_obtain_alias(btrfs_iget(root->fs_info->sb, &key, root, > NULL)); > if (!IS_ERR(dentry)) > dentry->d_op = &btrfs_dentry_operations; > return dentry; > diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c > index b3ad168..b383e53 100644 > --- a/fs/btrfs/inode.c > +++ b/fs/btrfs/inode.c > @@ -2131,7 +2131,7 @@ void btrfs_orphan_cleanup(struct btrfs_root *root) > found_key.objectid = found_key.offset; > found_key.type = BTRFS_INODE_ITEM_KEY; > found_key.offset = 0; > - inode = btrfs_iget(root->fs_info->sb, &found_key, root); > + inode = btrfs_iget(root->fs_info->sb, &found_key, root, NULL); > if (IS_ERR(inode)) > break; > > @@ -3609,7 +3609,7 @@ static struct inode *btrfs_iget_locked(struct > super_block *s, > * Returns in *is_new if the inode was read from disk > */ > struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, > - struct btrfs_root *root) > + struct btrfs_root *root, int *new) > { > struct inode *inode; > > @@ -3624,6 +3624,8 @@ struct inode *btrfs_iget(struct super_block *s, struct > btrfs_key *location, > > inode_tree_add(inode); > unlock_new_inode(inode); > + if (new) > + *new = 1; > } > > return inode; > @@ -3676,7 +3678,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, > struct dentry *dentry) > return NULL; > > if (location.type == BTRFS_INODE_ITEM_KEY) { > - inode = btrfs_iget(dir->i_sb, &location, root); > + inode = btrfs_iget(dir->i_sb, &location, root, NULL); > return inode; > } > > @@ -3691,7 +3693,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, > struct dentry *dentry) > else > inode = new_simple_dir(dir->i_sb, &location, sub_root); > } else { > - inode = btrfs_iget(dir->i_sb, &location, sub_root); > + inode = btrfs_iget(dir->i_sb, &location, sub_root, NULL); > } > srcu_read_unlock(&root->fs_info->subvol_srcu, index); > > diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c > index cfcc93c..69fc10b 100644 > --- a/fs/btrfs/relocation.c > +++ b/fs/btrfs/relocation.c > @@ -3478,7 +3478,7 @@ static struct inode *create_reloc_inode(struct > btrfs_fs_info *fs_info, > key.objectid = objectid; > key.type = BTRFS_INODE_ITEM_KEY; > key.offset = 0; > - inode = btrfs_iget(root->fs_info->sb, &key, root); > + inode = btrfs_iget(root->fs_info->sb, &key, root, NULL); > BUG_ON(IS_ERR(inode) || is_bad_inode(inode)); > BTRFS_I(inode)->index_cnt = group->key.objectid; > > diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c > index 752a546..bcbdc1b 100644 > --- a/fs/btrfs/super.c > +++ b/fs/btrfs/super.c > @@ -72,7 +72,7 @@ enum { > > static match_table_t tokens = { > {Opt_degraded, "degraded"}, > - {Opt_subvol, "subvol=%s"}, > + {Opt_subvol, "subvol=%d"}, > {Opt_device, "device=%s"}, > {Opt_nodatasum, "nodatasum"}, > {Opt_nodatacow, "nodatacow"}, > @@ -277,12 +277,13 @@ int btrfs_parse_options(struct btrfs_root *root, char > *options) > * only when we need to allocate a new super block. > */ > static int btrfs_parse_early_options(const char *options, fmode_t flags, > - void *holder, char **subvol_name, > + void *holder, u64 *subvol_objectid, > struct btrfs_fs_devices **fs_devices) > { > substring_t args[MAX_OPT_ARGS]; > char *opts, *p; > int error = 0; > + int intarg; > > if (!options) > goto out; > @@ -303,7 +304,10 @@ static int btrfs_parse_early_options(const char > *options, fmode_t flags, > token = match_token(p, tokens, args); > switch (token) { > case Opt_subvol: > - *subvol_name = match_strdup(&args[0]); > + intarg = 0; > + match_int(&args[0], &intarg); > + if (intarg) > + *subvol_objectid = intarg; > break; > case Opt_device: > error = btrfs_scan_one_device(match_strdup(&args[0]), > @@ -319,17 +323,110 @@ static int btrfs_parse_early_options(const char > *options, fmode_t flags, > out_free_opts: > kfree(opts); > out: > + return error; > +} > + > +static struct dentry *get_default_root(struct super_block *sb, u64 > subvol_objectid) > +{ > + struct btrfs_root *root = sb->s_fs_info; > + struct btrfs_root *new_root; > + struct btrfs_dir_item *di; > + struct btrfs_path *path; > + struct btrfs_key location; > + struct inode *inode; > + struct dentry *dentry; > + u64 dir_id; > + int new = 0; > + > /* > - * If no subvolume name is specified we use the default one. Allocate > - * a copy of the string "." here so that code later in the > - * mount path doesn't care if it's the default volume or another one. > + * We have a specific subvol we want to mount, just setup location and > + * go look up the root. > */ > - if (!*subvol_name) { > - *subvol_name = kstrdup(".", GFP_KERNEL); > - if (!*subvol_name) > - return -ENOMEM; > + if (subvol_objectid) { > + location.objectid = subvol_objectid; > + location.type = BTRFS_ROOT_ITEM_KEY; > + location.offset = (u64)-1; > + goto find_root; > } > - return error; > + > + path = btrfs_alloc_path(); > + if (!path) > + return ERR_PTR(-ENOMEM); > + path->leave_spinning = 1; > + > + /* > + * Find the "default" dir item which points to the root item that we > + * will mount by default if we haven't been given a specific subvolume > + * to mount. > + */ > + dir_id = btrfs_super_root_dir(&root->fs_info->super_copy); > + di = btrfs_lookup_dir_item(NULL, root, path, dir_id, "default", 7, 0); > + 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. > + */ > + btrfs_free_path(path); > + dir_id = BTRFS_FIRST_FREE_OBJECTID; > + new_root = root->fs_info->fs_root; > + goto setup_root; > + } > + > + 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(root->fs_info, &location); > + if (IS_ERR(new_root)) > + return ERR_PTR(PTR_ERR(new_root)); > + > + if (btrfs_root_refs(&new_root->root_item) == 0) > + return ERR_PTR(-ENOENT); > + > + 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 (!inode) > + return ERR_PTR(-ENOMEM); > + > + /* > + * 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); > + } > + > + if (new) { > + const struct qstr name = { .name = "/", .len = 1 }; > + > + /* > + * New inode, we need to make the dentry a sibling of s_root so > + * everything gets cleaned up properly on unmount. > + */ > + dentry = d_alloc(sb->s_root, &name); > + if (!dentry) { > + iput(inode); > + return ERR_PTR(-ENOMEM); > + } > + d_splice_alias(inode, dentry); > + } else { > + /* > + * We found the inode in cache, just find a dentry for it and > + * put the reference to the inode we just got. > + */ > + dentry = d_find_alias(inode); > + iput(inode); > + } > + > + return dentry; > } > > static int btrfs_fill_super(struct super_block *sb, > @@ -365,7 +462,7 @@ static int btrfs_fill_super(struct super_block *sb, > key.objectid = BTRFS_FIRST_FREE_OBJECTID; > key.type = BTRFS_INODE_ITEM_KEY; > key.offset = 0; > - inode = btrfs_iget(sb, &key, tree_root->fs_info->fs_root); > + inode = btrfs_iget(sb, &key, tree_root->fs_info->fs_root, NULL); > if (IS_ERR(inode)) { > err = PTR_ERR(inode); > goto fail_close; > @@ -377,12 +474,6 @@ static int btrfs_fill_super(struct super_block *sb, > err = -ENOMEM; > goto fail_close; > } > -#if 0 > - /* this does the super kobj at the same time */ > - err = btrfs_sysfs_add_super(tree_root->fs_info); > - if (err) > - goto fail_close; > -#endif > > sb->s_root = root_dentry; > > @@ -472,29 +563,30 @@ static int btrfs_test_super(struct super_block *s, void > *data) > static int btrfs_get_sb(struct file_system_type *fs_type, int flags, > const char *dev_name, void *data, struct vfsmount *mnt) > { > - char *subvol_name = NULL; > struct block_device *bdev = NULL; > struct super_block *s; > struct dentry *root; > struct btrfs_fs_devices *fs_devices = NULL; > fmode_t mode = FMODE_READ; > + u64 subvol_objectid = 0; > int error = 0; > + int found = 0; > > if (!(flags & MS_RDONLY)) > mode |= FMODE_WRITE; > > error = btrfs_parse_early_options(data, mode, fs_type, > - &subvol_name, &fs_devices); > + &subvol_objectid, &fs_devices); > if (error) > return error; > > error = btrfs_scan_one_device(dev_name, mode, fs_type, &fs_devices); > if (error) > - goto error_free_subvol_name; > + goto error; > > error = btrfs_open_devices(fs_devices, mode, fs_type); > if (error) > - goto error_free_subvol_name; > + goto error; > > if (!(flags & MS_RDONLY) && fs_devices->rw_devices == 0) { > error = -EACCES; > @@ -513,6 +605,7 @@ static int btrfs_get_sb(struct file_system_type *fs_type, > int flags, > goto error_close_devices; > } > > + found = 1; > btrfs_close_devices(fs_devices); > } else { > char b[BDEVNAME_SIZE]; > @@ -523,46 +616,30 @@ static int btrfs_get_sb(struct file_system_type > *fs_type, int flags, > flags & MS_SILENT ? 1 : 0); > if (error) { > deactivate_locked_super(s); > - goto error_free_subvol_name; > + goto error; > } > > btrfs_sb(s)->fs_info->bdev_holder = fs_type; > s->s_flags |= MS_ACTIVE; > } > > - if (!strcmp(subvol_name, ".")) > - root = dget(s->s_root); > - else { > - mutex_lock(&s->s_root->d_inode->i_mutex); > - root = lookup_one_len(subvol_name, s->s_root, > - strlen(subvol_name)); > - mutex_unlock(&s->s_root->d_inode->i_mutex); > - > - if (IS_ERR(root)) { > - deactivate_locked_super(s); > - error = PTR_ERR(root); > - goto error_free_subvol_name; > - } > - if (!root->d_inode) { > - dput(root); > - deactivate_locked_super(s); > - error = -ENXIO; > - goto error_free_subvol_name; > - } > + root = get_default_root(s, subvol_objectid); > + if (IS_ERR(root)) { > + error = PTR_ERR(root); > + deactivate_locked_super(s); > + goto error; > } > > mnt->mnt_sb = s; > mnt->mnt_root = root; > > - kfree(subvol_name); > return 0; > > error_s: > error = PTR_ERR(s); > error_close_devices: > btrfs_close_devices(fs_devices); > -error_free_subvol_name: > - kfree(subvol_name); > +error: > return error; > } > > diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c > index 741666a..c0c2c1c 100644 > --- a/fs/btrfs/tree-log.c > +++ b/fs/btrfs/tree-log.c > @@ -445,7 +445,7 @@ static noinline struct inode *read_one_inode(struct > btrfs_root *root, > key.objectid = objectid; > key.type = BTRFS_INODE_ITEM_KEY; > key.offset = 0; > - inode = btrfs_iget(root->fs_info->sb, &key, root); > + inode = btrfs_iget(root->fs_info->sb, &key, root, NULL); > if (IS_ERR(inode)) { > inode = NULL; > } else if (is_bad_inode(inode)) { -- taruisi -- To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html