提交 73f73415 编写于 作者: J Josef Bacik 提交者: Chris Mason

Btrfs: change how we mount subvolumes

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
subvolid=<name> you must pass in subvolid=<treeid>, where <treeid> is
the tree id that gets spit out via the subvolume listing you get from
the subvolume listing patches (btrfs filesystem list).  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 subvolid=<treeid>.  I tested this out with the above scenario and it
worked perfectly.  Thanks,

mount -o subvol operates inside the selected subvolid.  For example:

mount -o subvol=snap1,subvolid=256 /dev/xxx /mnt

/mnt will have the snap1 directory for the subvolume with id
256.

mount -o subvol=snap /dev/xxx /mnt

/mnt will be the snap directory of whatever the default subvolume
is.
Signed-off-by: NJosef Bacik <josef@redhat.com>
Signed-off-by: NChris Mason <chris.mason@oracle.com>
上级 12534832
...@@ -2335,7 +2335,7 @@ int btrfs_init_cachep(void); ...@@ -2335,7 +2335,7 @@ int btrfs_init_cachep(void);
void btrfs_destroy_cachep(void); void btrfs_destroy_cachep(void);
long btrfs_ioctl_trans_end(struct file *file); long btrfs_ioctl_trans_end(struct file *file);
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, 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, int btrfs_commit_write(struct file *file, struct page *page,
unsigned from, unsigned to); unsigned from, unsigned to);
struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
......
...@@ -95,7 +95,7 @@ static struct dentry *btrfs_get_dentry(struct super_block *sb, u64 objectid, ...@@ -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); btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
key.offset = 0; key.offset = 0;
inode = btrfs_iget(sb, &key, root); inode = btrfs_iget(sb, &key, root, NULL);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
err = PTR_ERR(inode); err = PTR_ERR(inode);
goto fail; goto fail;
...@@ -223,7 +223,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child) ...@@ -223,7 +223,7 @@ static struct dentry *btrfs_get_parent(struct dentry *child)
key.type = BTRFS_INODE_ITEM_KEY; key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0; 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)) if (!IS_ERR(dentry))
dentry->d_op = &btrfs_dentry_operations; dentry->d_op = &btrfs_dentry_operations;
return dentry; return dentry;
......
...@@ -2153,7 +2153,7 @@ void btrfs_orphan_cleanup(struct btrfs_root *root) ...@@ -2153,7 +2153,7 @@ void btrfs_orphan_cleanup(struct btrfs_root *root)
found_key.objectid = found_key.offset; found_key.objectid = found_key.offset;
found_key.type = BTRFS_INODE_ITEM_KEY; found_key.type = BTRFS_INODE_ITEM_KEY;
found_key.offset = 0; 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)) if (IS_ERR(inode))
break; break;
...@@ -3687,7 +3687,7 @@ static struct inode *btrfs_iget_locked(struct super_block *s, ...@@ -3687,7 +3687,7 @@ static struct inode *btrfs_iget_locked(struct super_block *s,
* Returns in *is_new if the inode was read from disk * Returns in *is_new if the inode was read from disk
*/ */
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, 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; struct inode *inode;
...@@ -3702,6 +3702,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location, ...@@ -3702,6 +3702,8 @@ struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
inode_tree_add(inode); inode_tree_add(inode);
unlock_new_inode(inode); unlock_new_inode(inode);
if (new)
*new = 1;
} }
return inode; return inode;
...@@ -3754,7 +3756,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) ...@@ -3754,7 +3756,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
return NULL; return NULL;
if (location.type == BTRFS_INODE_ITEM_KEY) { 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; return inode;
} }
...@@ -3769,7 +3771,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry) ...@@ -3769,7 +3771,7 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
else else
inode = new_simple_dir(dir->i_sb, &location, sub_root); inode = new_simple_dir(dir->i_sb, &location, sub_root);
} else { } 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); srcu_read_unlock(&root->fs_info->subvol_srcu, index);
......
...@@ -3487,7 +3487,7 @@ static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info, ...@@ -3487,7 +3487,7 @@ static struct inode *create_reloc_inode(struct btrfs_fs_info *fs_info,
key.objectid = objectid; key.objectid = objectid;
key.type = BTRFS_INODE_ITEM_KEY; key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0; 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)); BUG_ON(IS_ERR(inode) || is_bad_inode(inode));
BTRFS_I(inode)->index_cnt = group->key.objectid; BTRFS_I(inode)->index_cnt = group->key.objectid;
......
...@@ -63,10 +63,10 @@ static void btrfs_put_super(struct super_block *sb) ...@@ -63,10 +63,10 @@ static void btrfs_put_super(struct super_block *sb)
} }
enum { enum {
Opt_degraded, Opt_subvol, Opt_device, Opt_nodatasum, Opt_nodatacow, Opt_degraded, Opt_subvol, Opt_subvolid, Opt_device, Opt_nodatasum,
Opt_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier, Opt_nodatacow, Opt_max_extent, Opt_max_inline, Opt_alloc_start,
Opt_ssd, Opt_nossd, Opt_ssd_spread, Opt_thread_pool, Opt_noacl, Opt_nobarrier, Opt_ssd, Opt_nossd, Opt_ssd_spread, Opt_thread_pool,
Opt_compress, Opt_compress_force, Opt_notreelog, Opt_ratio, Opt_noacl, Opt_compress, Opt_compress_force, Opt_notreelog, Opt_ratio,
Opt_flushoncommit, Opt_flushoncommit,
Opt_discard, Opt_err, Opt_discard, Opt_err,
}; };
...@@ -74,6 +74,7 @@ enum { ...@@ -74,6 +74,7 @@ enum {
static match_table_t tokens = { static match_table_t tokens = {
{Opt_degraded, "degraded"}, {Opt_degraded, "degraded"},
{Opt_subvol, "subvol=%s"}, {Opt_subvol, "subvol=%s"},
{Opt_subvolid, "subvolid=%d"},
{Opt_device, "device=%s"}, {Opt_device, "device=%s"},
{Opt_nodatasum, "nodatasum"}, {Opt_nodatasum, "nodatasum"},
{Opt_nodatacow, "nodatacow"}, {Opt_nodatacow, "nodatacow"},
...@@ -157,6 +158,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) ...@@ -157,6 +158,7 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
btrfs_set_opt(info->mount_opt, DEGRADED); btrfs_set_opt(info->mount_opt, DEGRADED);
break; break;
case Opt_subvol: case Opt_subvol:
case Opt_subvolid:
case Opt_device: case Opt_device:
/* /*
* These are parsed by btrfs_parse_early_options * These are parsed by btrfs_parse_early_options
...@@ -292,12 +294,13 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) ...@@ -292,12 +294,13 @@ int btrfs_parse_options(struct btrfs_root *root, char *options)
* only when we need to allocate a new super block. * only when we need to allocate a new super block.
*/ */
static int btrfs_parse_early_options(const char *options, fmode_t flags, static int btrfs_parse_early_options(const char *options, fmode_t flags,
void *holder, char **subvol_name, void *holder, char **subvol_name, u64 *subvol_objectid,
struct btrfs_fs_devices **fs_devices) struct btrfs_fs_devices **fs_devices)
{ {
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
char *opts, *p; char *opts, *p;
int error = 0; int error = 0;
int intarg;
if (!options) if (!options)
goto out; goto out;
...@@ -320,6 +323,12 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags, ...@@ -320,6 +323,12 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags,
case Opt_subvol: case Opt_subvol:
*subvol_name = match_strdup(&args[0]); *subvol_name = match_strdup(&args[0]);
break; break;
case Opt_subvolid:
intarg = 0;
match_int(&args[0], &intarg);
if (intarg)
*subvol_objectid = intarg;
break;
case Opt_device: case Opt_device:
error = btrfs_scan_one_device(match_strdup(&args[0]), error = btrfs_scan_one_device(match_strdup(&args[0]),
flags, holder, fs_devices); flags, holder, fs_devices);
...@@ -347,6 +356,110 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags, ...@@ -347,6 +356,110 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags,
return error; 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;
/*
* We have a specific subvol we want to mount, just setup location and
* go look up the root.
*/
if (subvol_objectid) {
location.objectid = subvol_objectid;
location.type = BTRFS_ROOT_ITEM_KEY;
location.offset = (u64)-1;
goto find_root;
}
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, static int btrfs_fill_super(struct super_block *sb,
struct btrfs_fs_devices *fs_devices, struct btrfs_fs_devices *fs_devices,
void *data, int silent) void *data, int silent)
...@@ -380,7 +493,7 @@ static int btrfs_fill_super(struct super_block *sb, ...@@ -380,7 +493,7 @@ static int btrfs_fill_super(struct super_block *sb,
key.objectid = BTRFS_FIRST_FREE_OBJECTID; key.objectid = BTRFS_FIRST_FREE_OBJECTID;
key.type = BTRFS_INODE_ITEM_KEY; key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0; 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)) { if (IS_ERR(inode)) {
err = PTR_ERR(inode); err = PTR_ERR(inode);
goto fail_close; goto fail_close;
...@@ -392,12 +505,6 @@ static int btrfs_fill_super(struct super_block *sb, ...@@ -392,12 +505,6 @@ static int btrfs_fill_super(struct super_block *sb,
err = -ENOMEM; err = -ENOMEM;
goto fail_close; 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; sb->s_root = root_dentry;
...@@ -489,19 +596,22 @@ static int btrfs_test_super(struct super_block *s, void *data) ...@@ -489,19 +596,22 @@ static int btrfs_test_super(struct super_block *s, void *data)
static int btrfs_get_sb(struct file_system_type *fs_type, int flags, static int btrfs_get_sb(struct file_system_type *fs_type, int flags,
const char *dev_name, void *data, struct vfsmount *mnt) const char *dev_name, void *data, struct vfsmount *mnt)
{ {
char *subvol_name = NULL;
struct block_device *bdev = NULL; struct block_device *bdev = NULL;
struct super_block *s; struct super_block *s;
struct dentry *root; struct dentry *root;
struct btrfs_fs_devices *fs_devices = NULL; struct btrfs_fs_devices *fs_devices = NULL;
fmode_t mode = FMODE_READ; fmode_t mode = FMODE_READ;
char *subvol_name = NULL;
u64 subvol_objectid = 0;
int error = 0; int error = 0;
int found = 0;
if (!(flags & MS_RDONLY)) if (!(flags & MS_RDONLY))
mode |= FMODE_WRITE; mode |= FMODE_WRITE;
error = btrfs_parse_early_options(data, mode, fs_type, error = btrfs_parse_early_options(data, mode, fs_type,
&subvol_name, &fs_devices); &subvol_name, &subvol_objectid,
&fs_devices);
if (error) if (error)
return error; return error;
...@@ -530,6 +640,7 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags, ...@@ -530,6 +640,7 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags,
goto error_close_devices; goto error_close_devices;
} }
found = 1;
btrfs_close_devices(fs_devices); btrfs_close_devices(fs_devices);
} else { } else {
char b[BDEVNAME_SIZE]; char b[BDEVNAME_SIZE];
...@@ -547,25 +658,35 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags, ...@@ -547,25 +658,35 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags,
s->s_flags |= MS_ACTIVE; s->s_flags |= MS_ACTIVE;
} }
if (!strcmp(subvol_name, ".")) root = get_default_root(s, subvol_objectid);
root = dget(s->s_root); if (IS_ERR(root)) {
else { error = PTR_ERR(root);
mutex_lock(&s->s_root->d_inode->i_mutex); deactivate_locked_super(s);
root = lookup_one_len(subvol_name, s->s_root, goto error;
}
/* if they gave us a subvolume name bind mount into that */
if (strcmp(subvol_name, ".")) {
struct dentry *new_root;
mutex_lock(&root->d_inode->i_mutex);
new_root = lookup_one_len(subvol_name, root,
strlen(subvol_name)); strlen(subvol_name));
mutex_unlock(&s->s_root->d_inode->i_mutex); mutex_unlock(&root->d_inode->i_mutex);
if (IS_ERR(root)) { if (IS_ERR(new_root)) {
deactivate_locked_super(s); deactivate_locked_super(s);
error = PTR_ERR(root); error = PTR_ERR(new_root);
goto error_free_subvol_name; dput(root);
goto error_close_devices;
} }
if (!root->d_inode) { if (!new_root->d_inode) {
dput(root); dput(root);
dput(new_root);
deactivate_locked_super(s); deactivate_locked_super(s);
error = -ENXIO; error = -ENXIO;
goto error_free_subvol_name; goto error_close_devices;
} }
dput(root);
root = new_root;
} }
mnt->mnt_sb = s; mnt->mnt_sb = s;
...@@ -580,6 +701,7 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags, ...@@ -580,6 +701,7 @@ static int btrfs_get_sb(struct file_system_type *fs_type, int flags,
btrfs_close_devices(fs_devices); btrfs_close_devices(fs_devices);
error_free_subvol_name: error_free_subvol_name:
kfree(subvol_name); kfree(subvol_name);
error:
return error; return error;
} }
......
...@@ -445,7 +445,7 @@ static noinline struct inode *read_one_inode(struct btrfs_root *root, ...@@ -445,7 +445,7 @@ static noinline struct inode *read_one_inode(struct btrfs_root *root,
key.objectid = objectid; key.objectid = objectid;
key.type = BTRFS_INODE_ITEM_KEY; key.type = BTRFS_INODE_ITEM_KEY;
key.offset = 0; 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)) { if (IS_ERR(inode)) {
inode = NULL; inode = NULL;
} else if (is_bad_inode(inode)) { } else if (is_bad_inode(inode)) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册