Skip to content

Commit 05dbe68

Browse files
osandovmasoncl
authored andcommitted
Btrfs: unify subvol= and subvolid= mounting
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. Reviewed-by: David Sterba <[email protected]> Signed-off-by: Omar Sandoval <[email protected]> Signed-off-by: Chris Mason <[email protected]>
1 parent bb289b7 commit 05dbe68

File tree

1 file changed

+171
-67
lines changed

1 file changed

+171
-67
lines changed

fs/btrfs/super.c

Lines changed: 171 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -836,33 +836,153 @@ static int btrfs_parse_early_options(const char *options, fmode_t flags,
836836
return error;
837837
}
838838

839-
static struct dentry *get_default_root(struct super_block *sb,
840-
u64 subvol_objectid)
839+
static char *get_subvol_name_from_objectid(struct btrfs_fs_info *fs_info,
840+
u64 subvol_objectid)
841841
{
842-
struct btrfs_fs_info *fs_info = btrfs_sb(sb);
843842
struct btrfs_root *root = fs_info->tree_root;
844-
struct btrfs_root *new_root;
845-
struct btrfs_dir_item *di;
846-
struct btrfs_path *path;
847-
struct btrfs_key location;
848-
struct inode *inode;
849-
u64 dir_id;
850-
int new = 0;
843+
struct btrfs_root *fs_root;
844+
struct btrfs_root_ref *root_ref;
845+
struct btrfs_inode_ref *inode_ref;
846+
struct btrfs_key key;
847+
struct btrfs_path *path = NULL;
848+
char *name = NULL, *ptr;
849+
u64 dirid;
850+
int len;
851+
int ret;
852+
853+
path = btrfs_alloc_path();
854+
if (!path) {
855+
ret = -ENOMEM;
856+
goto err;
857+
}
858+
path->leave_spinning = 1;
859+
860+
name = kmalloc(PATH_MAX, GFP_NOFS);
861+
if (!name) {
862+
ret = -ENOMEM;
863+
goto err;
864+
}
865+
ptr = name + PATH_MAX - 1;
866+
ptr[0] = '\0';
851867

852868
/*
853-
* We have a specific subvol we want to mount, just setup location and
854-
* go look up the root.
869+
* Walk up the subvolume trees in the tree of tree roots by root
870+
* backrefs until we hit the top-level subvolume.
855871
*/
856-
if (subvol_objectid) {
857-
location.objectid = subvol_objectid;
858-
location.type = BTRFS_ROOT_ITEM_KEY;
859-
location.offset = (u64)-1;
860-
goto find_root;
872+
while (subvol_objectid != BTRFS_FS_TREE_OBJECTID) {
873+
key.objectid = subvol_objectid;
874+
key.type = BTRFS_ROOT_BACKREF_KEY;
875+
key.offset = (u64)-1;
876+
877+
ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
878+
if (ret < 0) {
879+
goto err;
880+
} else if (ret > 0) {
881+
ret = btrfs_previous_item(root, path, subvol_objectid,
882+
BTRFS_ROOT_BACKREF_KEY);
883+
if (ret < 0) {
884+
goto err;
885+
} else if (ret > 0) {
886+
ret = -ENOENT;
887+
goto err;
888+
}
889+
}
890+
891+
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
892+
subvol_objectid = key.offset;
893+
894+
root_ref = btrfs_item_ptr(path->nodes[0], path->slots[0],
895+
struct btrfs_root_ref);
896+
len = btrfs_root_ref_name_len(path->nodes[0], root_ref);
897+
ptr -= len + 1;
898+
if (ptr < name) {
899+
ret = -ENAMETOOLONG;
900+
goto err;
901+
}
902+
read_extent_buffer(path->nodes[0], ptr + 1,
903+
(unsigned long)(root_ref + 1), len);
904+
ptr[0] = '/';
905+
dirid = btrfs_root_ref_dirid(path->nodes[0], root_ref);
906+
btrfs_release_path(path);
907+
908+
key.objectid = subvol_objectid;
909+
key.type = BTRFS_ROOT_ITEM_KEY;
910+
key.offset = (u64)-1;
911+
fs_root = btrfs_read_fs_root_no_name(fs_info, &key);
912+
if (IS_ERR(fs_root)) {
913+
ret = PTR_ERR(fs_root);
914+
goto err;
915+
}
916+
917+
/*
918+
* Walk up the filesystem tree by inode refs until we hit the
919+
* root directory.
920+
*/
921+
while (dirid != BTRFS_FIRST_FREE_OBJECTID) {
922+
key.objectid = dirid;
923+
key.type = BTRFS_INODE_REF_KEY;
924+
key.offset = (u64)-1;
925+
926+
ret = btrfs_search_slot(NULL, fs_root, &key, path, 0, 0);
927+
if (ret < 0) {
928+
goto err;
929+
} else if (ret > 0) {
930+
ret = btrfs_previous_item(fs_root, path, dirid,
931+
BTRFS_INODE_REF_KEY);
932+
if (ret < 0) {
933+
goto err;
934+
} else if (ret > 0) {
935+
ret = -ENOENT;
936+
goto err;
937+
}
938+
}
939+
940+
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
941+
dirid = key.offset;
942+
943+
inode_ref = btrfs_item_ptr(path->nodes[0],
944+
path->slots[0],
945+
struct btrfs_inode_ref);
946+
len = btrfs_inode_ref_name_len(path->nodes[0],
947+
inode_ref);
948+
ptr -= len + 1;
949+
if (ptr < name) {
950+
ret = -ENAMETOOLONG;
951+
goto err;
952+
}
953+
read_extent_buffer(path->nodes[0], ptr + 1,
954+
(unsigned long)(inode_ref + 1), len);
955+
ptr[0] = '/';
956+
btrfs_release_path(path);
957+
}
861958
}
862959

960+
btrfs_free_path(path);
961+
if (ptr == name + PATH_MAX - 1) {
962+
name[0] = '/';
963+
name[1] = '\0';
964+
} else {
965+
memmove(name, ptr, name + PATH_MAX - ptr);
966+
}
967+
return name;
968+
969+
err:
970+
btrfs_free_path(path);
971+
kfree(name);
972+
return ERR_PTR(ret);
973+
}
974+
975+
static int get_default_subvol_objectid(struct btrfs_fs_info *fs_info, u64 *objectid)
976+
{
977+
struct btrfs_root *root = fs_info->tree_root;
978+
struct btrfs_dir_item *di;
979+
struct btrfs_path *path;
980+
struct btrfs_key location;
981+
u64 dir_id;
982+
863983
path = btrfs_alloc_path();
864984
if (!path)
865-
return ERR_PTR(-ENOMEM);
985+
return -ENOMEM;
866986
path->leave_spinning = 1;
867987

868988
/*
@@ -874,58 +994,23 @@ static struct dentry *get_default_root(struct super_block *sb,
874994
di = btrfs_lookup_dir_item(NULL, root, path, dir_id, "default", 7, 0);
875995
if (IS_ERR(di)) {
876996
btrfs_free_path(path);
877-
return ERR_CAST(di);
997+
return PTR_ERR(di);
878998
}
879999
if (!di) {
8801000
/*
8811001
* Ok the default dir item isn't there. This is weird since
8821002
* it's always been there, but don't freak out, just try and
883-
* mount to root most subvolume.
1003+
* mount the top-level subvolume.
8841004
*/
8851005
btrfs_free_path(path);
886-
dir_id = BTRFS_FIRST_FREE_OBJECTID;
887-
new_root = fs_info->fs_root;
888-
goto setup_root;
1006+
*objectid = BTRFS_FS_TREE_OBJECTID;
1007+
return 0;
8891008
}
8901009

8911010
btrfs_dir_item_key_to_cpu(path->nodes[0], di, &location);
8921011
btrfs_free_path(path);
893-
894-
find_root:
895-
new_root = btrfs_read_fs_root_no_name(fs_info, &location);
896-
if (IS_ERR(new_root))
897-
return ERR_CAST(new_root);
898-
899-
if (!(sb->s_flags & MS_RDONLY)) {
900-
int ret;
901-
down_read(&fs_info->cleanup_work_sem);
902-
ret = btrfs_orphan_cleanup(new_root);
903-
up_read(&fs_info->cleanup_work_sem);
904-
if (ret)
905-
return ERR_PTR(ret);
906-
}
907-
908-
dir_id = btrfs_root_dirid(&new_root->root_item);
909-
setup_root:
910-
location.objectid = dir_id;
911-
location.type = BTRFS_INODE_ITEM_KEY;
912-
location.offset = 0;
913-
914-
inode = btrfs_iget(sb, &location, new_root, &new);
915-
if (IS_ERR(inode))
916-
return ERR_CAST(inode);
917-
918-
/*
919-
* If we're just mounting the root most subvol put the inode and return
920-
* a reference to the dentry. We will have already gotten a reference
921-
* to the inode in btrfs_fill_super so we're good to go.
922-
*/
923-
if (!new && d_inode(sb->s_root) == inode) {
924-
iput(inode);
925-
return dget(sb->s_root);
926-
}
927-
928-
return d_obtain_root(inode);
1012+
*objectid = location.objectid;
1013+
return 0;
9291014
}
9301015

9311016
static int btrfs_fill_super(struct super_block *sb,
@@ -1211,6 +1296,25 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
12111296
goto out;
12121297
}
12131298

1299+
if (!subvol_name) {
1300+
if (!subvol_objectid) {
1301+
ret = get_default_subvol_objectid(btrfs_sb(mnt->mnt_sb),
1302+
&subvol_objectid);
1303+
if (ret) {
1304+
root = ERR_PTR(ret);
1305+
goto out;
1306+
}
1307+
}
1308+
subvol_name = get_subvol_name_from_objectid(btrfs_sb(mnt->mnt_sb),
1309+
subvol_objectid);
1310+
if (IS_ERR(subvol_name)) {
1311+
root = ERR_CAST(subvol_name);
1312+
subvol_name = NULL;
1313+
goto out;
1314+
}
1315+
1316+
}
1317+
12141318
root = mount_subtree(mnt, subvol_name);
12151319
/* mount_subtree() drops our reference on the vfsmount. */
12161320
mnt = NULL;
@@ -1227,6 +1331,11 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
12271331
ret = -EINVAL;
12281332
}
12291333
if (subvol_objectid && root_objectid != subvol_objectid) {
1334+
/*
1335+
* This will also catch a race condition where a
1336+
* subvolume which was passed by ID is renamed and
1337+
* another subvolume is renamed over the old location.
1338+
*/
12301339
pr_err("BTRFS: subvol '%s' does not match subvolid %llu\n",
12311340
subvol_name, subvol_objectid);
12321341
ret = -EINVAL;
@@ -1306,7 +1415,6 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
13061415
{
13071416
struct block_device *bdev = NULL;
13081417
struct super_block *s;
1309-
struct dentry *root;
13101418
struct btrfs_fs_devices *fs_devices = NULL;
13111419
struct btrfs_fs_info *fs_info = NULL;
13121420
struct security_mnt_opts new_sec_opts;
@@ -1326,7 +1434,7 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
13261434
return ERR_PTR(error);
13271435
}
13281436

1329-
if (subvol_name) {
1437+
if (subvol_name || subvol_objectid != BTRFS_FS_TREE_OBJECTID) {
13301438
/* mount_subvol() will free subvol_name. */
13311439
return mount_subvol(subvol_name, subvol_objectid, flags,
13321440
device_name, data);
@@ -1395,23 +1503,19 @@ static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
13951503
error = btrfs_fill_super(s, fs_devices, data,
13961504
flags & MS_SILENT ? 1 : 0);
13971505
}
1398-
1399-
root = !error ? get_default_root(s, subvol_objectid) : ERR_PTR(error);
1400-
if (IS_ERR(root)) {
1506+
if (error) {
14011507
deactivate_locked_super(s);
1402-
error = PTR_ERR(root);
14031508
goto error_sec_opts;
14041509
}
14051510

14061511
fs_info = btrfs_sb(s);
14071512
error = setup_security_options(fs_info, s, &new_sec_opts);
14081513
if (error) {
1409-
dput(root);
14101514
deactivate_locked_super(s);
14111515
goto error_sec_opts;
14121516
}
14131517

1414-
return root;
1518+
return dget(s->s_root);
14151519

14161520
error_close_devices:
14171521
btrfs_close_devices(fs_devices);

0 commit comments

Comments
 (0)