btrfs-progs: subvol show: print more details about toplevel subvolume
The toplevel subvolume is special and the other listing code leaves it out so we have to add several special cases to handle it. There's no backreference so the path is built artificially. New helper btrfs_get_toplevel_subvol is a reduced version of btrfs_get_subvol. There's some information usually missing for the toplevel subvolume, eg. the uuid or creation info. This has to be fixed on the mkfs side, the other subvolumes are created by kernel. Example: /mnt Name: <FS_TREE> UUID: - Parent UUID: - Received UUID: - Creation time: - Subvolume ID: 5 Generation: 233 Gen at creation: 0 Parent ID: 0 Top level ID: 0 Flags: - Snapshot(s): subv1 Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
eb28e2fcae
commit
d4aa2bc07e
37
btrfs-list.c
37
btrfs-list.c
|
@ -983,7 +983,7 @@ static int list_subvol_search(int fd, struct root_lookup *root_lookup)
|
||||||
/* Search both live and deleted subvolumes */
|
/* Search both live and deleted subvolumes */
|
||||||
sk->min_type = BTRFS_ROOT_ITEM_KEY;
|
sk->min_type = BTRFS_ROOT_ITEM_KEY;
|
||||||
sk->max_type = BTRFS_ROOT_BACKREF_KEY;
|
sk->max_type = BTRFS_ROOT_BACKREF_KEY;
|
||||||
sk->min_objectid = BTRFS_FIRST_FREE_OBJECTID;
|
sk->min_objectid = BTRFS_FS_TREE_OBJECTID;
|
||||||
sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
|
sk->max_objectid = BTRFS_LAST_FREE_OBJECTID;
|
||||||
sk->max_offset = (u64)-1;
|
sk->max_offset = (u64)-1;
|
||||||
sk->max_transid = (u64)-1;
|
sk->max_transid = (u64)-1;
|
||||||
|
@ -1014,7 +1014,9 @@ static int list_subvol_search(int fd, struct root_lookup *root_lookup)
|
||||||
add_root_backref(root_lookup, sh.objectid,
|
add_root_backref(root_lookup, sh.objectid,
|
||||||
sh.offset, dir_id, name,
|
sh.offset, dir_id, name,
|
||||||
name_len);
|
name_len);
|
||||||
} else if (sh.type == BTRFS_ROOT_ITEM_KEY) {
|
} else if (sh.type == BTRFS_ROOT_ITEM_KEY &&
|
||||||
|
(sh.objectid >= BTRFS_FIRST_FREE_OBJECTID ||
|
||||||
|
sh.objectid == BTRFS_FS_TREE_OBJECTID)) {
|
||||||
time_t otime;
|
time_t otime;
|
||||||
u8 uuid[BTRFS_UUID_SIZE];
|
u8 uuid[BTRFS_UUID_SIZE];
|
||||||
u8 puuid[BTRFS_UUID_SIZE];
|
u8 puuid[BTRFS_UUID_SIZE];
|
||||||
|
@ -1524,6 +1526,37 @@ static char *strdup_or_null(const char *s)
|
||||||
return strdup(s);
|
return strdup(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct root_lookup rl;
|
||||||
|
struct rb_node *rbn;
|
||||||
|
struct root_info *ri;
|
||||||
|
u64 root_id;
|
||||||
|
|
||||||
|
ret = btrfs_list_get_path_rootid(fd, &root_id);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = btrfs_list_subvols(fd, &rl);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
rbn = rb_first(&rl.root);
|
||||||
|
ri = rb_entry(rbn, struct root_info, rb_node);
|
||||||
|
|
||||||
|
if (ri->root_id != BTRFS_FS_TREE_OBJECTID)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
memcpy(the_ri, ri, offsetof(struct root_info, path));
|
||||||
|
the_ri->path = strdup_or_null("/");
|
||||||
|
the_ri->name = strdup_or_null("<FS_TREE>");
|
||||||
|
the_ri->full_path = strdup_or_null("/");
|
||||||
|
rb_free_nodes(&rl.root, free_root_info);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int btrfs_get_subvol(int fd, struct root_info *the_ri)
|
int btrfs_get_subvol(int fd, struct root_info *the_ri)
|
||||||
{
|
{
|
||||||
int ret, rr;
|
int ret, rr;
|
||||||
|
|
|
@ -175,5 +175,6 @@ int btrfs_list_get_default_subvolume(int fd, u64 *default_id);
|
||||||
char *btrfs_list_path_for_root(int fd, u64 root);
|
char *btrfs_list_path_for_root(int fd, u64 root);
|
||||||
int btrfs_list_get_path_rootid(int fd, u64 *treeid);
|
int btrfs_list_get_path_rootid(int fd, u64 *treeid);
|
||||||
int btrfs_get_subvol(int fd, struct root_info *the_ri);
|
int btrfs_get_subvol(int fd, struct root_info *the_ri);
|
||||||
|
int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -921,15 +921,6 @@ static int cmd_subvol_show(int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = get_subvol_info(fullpath, &get_ri);
|
ret = get_subvol_info(fullpath, &get_ri);
|
||||||
if (ret == 2) {
|
|
||||||
/*
|
|
||||||
* Since the top level btrfs was given don't
|
|
||||||
* take that as error
|
|
||||||
*/
|
|
||||||
printf("%s is toplevel subvolume\n", fullpath);
|
|
||||||
ret = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
error("Failed to get subvol info %s: %s\n",
|
error("Failed to get subvol info %s: %s\n",
|
||||||
|
|
13
utils.c
13
utils.c
|
@ -4168,18 +4168,13 @@ int get_subvol_info(const char *fullpath, struct root_info *get_ri)
|
||||||
if (mntfd < 0)
|
if (mntfd < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (sv_id == BTRFS_FS_TREE_OBJECTID) {
|
|
||||||
ret = 2;
|
|
||||||
/*
|
|
||||||
* So that caller may decide if thats an error or just fine.
|
|
||||||
*/
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(get_ri, 0, sizeof(*get_ri));
|
memset(get_ri, 0, sizeof(*get_ri));
|
||||||
get_ri->root_id = sv_id;
|
get_ri->root_id = sv_id;
|
||||||
|
|
||||||
ret = btrfs_get_subvol(mntfd, get_ri);
|
if (sv_id == BTRFS_FS_TREE_OBJECTID)
|
||||||
|
ret = btrfs_get_toplevel_subvol(mntfd, get_ri);
|
||||||
|
else
|
||||||
|
ret = btrfs_get_subvol(mntfd, get_ri);
|
||||||
if (ret)
|
if (ret)
|
||||||
error("can't find '%s': %d", svpath, ret);
|
error("can't find '%s': %d", svpath, ret);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue