|
@@ -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_max_extent, Opt_max_inline, Opt_alloc_start, Opt_nobarrier,
|
|
|
|
- Opt_ssd, Opt_nossd, Opt_ssd_spread, Opt_thread_pool, Opt_noacl,
|
|
|
|
- Opt_compress, Opt_compress_force, Opt_notreelog, Opt_ratio,
|
|
|
|
|
|
+ Opt_degraded, Opt_subvol, Opt_subvolid, Opt_device, Opt_nodatasum,
|
|
|
|
+ Opt_nodatacow, Opt_max_extent, Opt_max_inline, Opt_alloc_start,
|
|
|
|
+ Opt_nobarrier, Opt_ssd, Opt_nossd, Opt_ssd_spread, Opt_thread_pool,
|
|
|
|
+ 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 {
|
|
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)
|
|
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 @@ out:
|
|
* 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,
|
|
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,
|
|
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,
|
|
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,
|
|
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)
|
|
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,
|
|
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,
|
|
s->s_flags |= MS_ACTIVE;
|
|
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,
|
|
|
|
|
|
+ root = get_default_root(s, subvol_objectid);
|
|
|
|
+ if (IS_ERR(root)) {
|
|
|
|
+ error = PTR_ERR(root);
|
|
|
|
+ deactivate_locked_super(s);
|
|
|
|
+ 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);
|
|
|
|
- goto error_free_subvol_name;
|
|
|
|
|
|
+ error = PTR_ERR(new_root);
|
|
|
|
+ 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 @@ error_close_devices:
|
|
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;
|
|
}
|
|
}
|
|
|
|
|