@@ -170,6 +170,11 @@ you can add \'\+' or \'-' in front of each items, \'+' means ascending,
+
for --sort you can combine some items together by \',', just like
--sort=+ogen,-gen,path,rootid.
++
+--nosort::::
+Output the results incrementally without sort. This avoids loading all
+subvolume information to memory and can be useful when there is a lot
+of subvolumes.
*set-default* [<subvolume>|<id> <path>]::
Set the default subvolume for the (mounted) filesystem.
@@ -1036,6 +1036,23 @@ static void print_all_subvol_info_tab_head(void)
}
}
+static void print_one_subvol_info(struct listed_subvol *subvol,
+ enum btrfs_list_layout layout,
+ const char *raw_prefix)
+{
+ switch (layout) {
+ case BTRFS_LIST_LAYOUT_DEFAULT:
+ print_one_subvol_info_default(subvol);
+ break;
+ case BTRFS_LIST_LAYOUT_TABLE:
+ print_one_subvol_info_table(subvol);
+ break;
+ case BTRFS_LIST_LAYOUT_RAW:
+ print_one_subvol_info_raw(subvol, raw_prefix);
+ break;
+ }
+}
+
static void print_all_subvol_info(struct subvol_list *subvols,
enum btrfs_list_layout layout,
const char *raw_prefix)
@@ -1048,17 +1065,7 @@ static void print_all_subvol_info(struct subvol_list *subvols,
for (i = 0; i < subvols->num; i++) {
struct listed_subvol *subvol = &subvols->subvols[i];
- switch (layout) {
- case BTRFS_LIST_LAYOUT_DEFAULT:
- print_one_subvol_info_default(subvol);
- break;
- case BTRFS_LIST_LAYOUT_TABLE:
- print_one_subvol_info_table(subvol);
- break;
- case BTRFS_LIST_LAYOUT_RAW:
- print_one_subvol_info_raw(subvol, raw_prefix);
- break;
- }
+ print_one_subvol_info(subvol, layout, raw_prefix);
}
}
@@ -1159,7 +1166,9 @@ static void get_subvols_info(struct subvol_list **subvols,
int tree_id,
size_t *capacity,
const char *prefix,
- int show_top)
+ int show_top,
+ enum btrfs_list_layout layout,
+ const char *raw_prefix)
{
struct btrfs_util_subvolume_iterator *iter;
enum btrfs_util_error err;
@@ -1216,9 +1225,14 @@ static void get_subvols_info(struct subvol_list **subvols,
if (!filters_match(&subvol, filter_set)) {
free(subvol.path);
} else {
- ret = add_subvol(subvols, &subvol, capacity);
- if (ret)
- goto out;
+ if (*subvols == NULL) {
+ print_one_subvol_info(&subvol,
+ layout, raw_prefix);
+ } else {
+ ret = add_subvol(subvols, &subvol, capacity);
+ if (ret)
+ goto out;
+ }
}
}
@@ -1263,9 +1277,14 @@ skip:
if (!filters_match(&subvol, filter_set)) {
free(subvol.path);
} else {
- ret = add_subvol(subvols, &subvol, capacity);
- if (ret)
- goto out;
+ if (*subvols == NULL) {
+ print_one_subvol_info(&subvol,
+ layout, raw_prefix);
+ } else {
+ ret = add_subvol(subvols, &subvol, capacity);
+ if (ret)
+ goto out;
+ }
}
}
@@ -1275,7 +1294,7 @@ out:
btrfs_util_destroy_subvolume_iterator(iter);
if (ret) {
free_subvol_list(*subvols);
- *subvols = NULL;
+ *subvols = ERR_PTR(ret);
}
}
@@ -1283,23 +1302,31 @@ static struct subvol_list *btrfs_list_subvols(int fd,
int is_list_all,
int absolute_path,
int follow_mount,
+ int no_sort,
+ enum btrfs_list_layout layout,
+ const char *raw_prefix,
const char *path,
struct btrfs_list_filter_set_v2 *filter_set)
{
- struct subvol_list *subvols;
+ struct subvol_list *subvols = NULL;
size_t capacity = 0;
- subvols = malloc(sizeof(*subvols));
- if (!subvols) {
- error("out of memory");
- return NULL;
+ if (!no_sort) {
+ subvols = malloc(sizeof(*subvols));
+ if (!subvols) {
+ error("out of memory");
+ return ERR_PTR(-1);
+ }
+ subvols->num = 0;
}
- subvols->num = 0;
+
+ if (no_sort && layout == BTRFS_LIST_LAYOUT_TABLE)
+ print_all_subvol_info_tab_head();
if (is_list_all) {
get_subvols_info(&subvols, filter_set, fd,
BTRFS_FS_TREE_OBJECTID, &capacity, NULL,
- false);
+ false, layout, raw_prefix);
} else {
char *fullpath;
@@ -1307,16 +1334,17 @@ static struct subvol_list *btrfs_list_subvols(int fd,
if (!fullpath) {
error("cannot find real path for '%s': %m", path);
free_subvol_list(subvols);
- return NULL;
+ return ERR_PTR(-1);
}
get_subvols_info(&subvols, filter_set, fd, 0, &capacity,
- (absolute_path ? fullpath : NULL), false);
+ (absolute_path ? fullpath : NULL), false,
+ layout, raw_prefix);
- if (subvols == NULL) {
- free(fullpath);
- return NULL;
- }
+ if (IS_ERR(subvols)) {
+ free(fullpath);
+ return ERR_PTR(-1);
+ }
/* Follow mounted subvolumes below @path */
if (follow_mount) {
@@ -1334,7 +1362,7 @@ static struct subvol_list *btrfs_list_subvols(int fd,
error("failed to get fsid: %m");
free(fullpath);
free_subvol_list(subvols);
- return NULL;
+ return ERR_PTR(-1);
}
f = setmntent("/proc/self/mounts", "r");
@@ -1342,7 +1370,7 @@ static struct subvol_list *btrfs_list_subvols(int fd,
error("failed to read mount entry: %m");
free(fullpath);
free_subvol_list(subvols);
- return NULL;
+ return ERR_PTR(-1);
}
/* Iterate for each mount entry */
@@ -1369,7 +1397,7 @@ static struct subvol_list *btrfs_list_subvols(int fd,
error("failed to get fsid: %m");
free(fullpath);
free_subvol_list(subvols);
- return NULL;
+ return ERR_PTR(-1);
}
if (uuid_compare(fsid, fsid2))
continue;
@@ -1381,7 +1409,7 @@ static struct subvol_list *btrfs_list_subvols(int fd,
mnt->mnt_dir);
free(fullpath);
free_subvol_list(subvols);
- return NULL;
+ return ERR_PTR(-1);
}
get_subvols_info(&subvols, filter_set,
fd2, 0, &capacity,
@@ -1389,11 +1417,11 @@ static struct subvol_list *btrfs_list_subvols(int fd,
(strlen(fullpath) == 1 ?
mnt->mnt_dir + 1 :
mnt->mnt_dir + strlen(fullpath) + 1)),
- true);
+ true, layout, raw_prefix);
close_file_or_dir(fd2, dirstream);
- if (subvols == NULL) {
+ if (IS_ERR(subvols)) {
free(fullpath);
- return NULL;
+ return ERR_PTR(-1);
}
}
}
@@ -1410,6 +1438,7 @@ static int btrfs_list_subvols_print_v2(int fd,
int is_list_all,
int absolute_path,
int follow_mount,
+ int no_sort,
const char *path,
const char *raw_prefix)
{
@@ -1419,15 +1448,20 @@ static int btrfs_list_subvols_print_v2(int fd,
subvols = btrfs_list_deleted_subvols(fd, filter_set);
else
subvols = btrfs_list_subvols(fd, is_list_all, absolute_path,
- follow_mount, path, filter_set);
- if (!subvols)
+ follow_mount, no_sort,
+ layout, raw_prefix,
+ path, filter_set);
+
+ if (IS_ERR(subvols))
return -1;
- sort_subvols(comp_set, subvols);
+ if (!no_sort) {
+ sort_subvols(comp_set, subvols);
- print_all_subvol_info(subvols, layout, raw_prefix);
+ print_all_subvol_info(subvols, layout, raw_prefix);
- free_subvol_list(subvols);
+ free_subvol_list(subvols);
+ }
return 0;
}
@@ -1575,6 +1609,9 @@ static const char * const cmd_subvol_list_usage[] = {
" list the subvolume in order of gen, ogen, rootid or path",
" you also can add '+' or '-' in front of each items.",
" (+:ascending, -:descending, ascending default)",
+ "--nosort Output the results incrementally without sort.",
+ " This avoids loading all subvolume information to memory",
+ " and can be useful when there is a lot of subvolumes",
NULL,
};
@@ -1589,6 +1626,8 @@ static int cmd_subvol_list(int argc, char **argv)
char *subvol;
int is_list_all = 0;
int follow_mount = 0;
+ int sort = 0;
+ int no_sort = 0;
int is_only_in_path = 0;
int absolute_path = 0;
DIR *dirstream = NULL;
@@ -1601,6 +1640,7 @@ static int cmd_subvol_list(int argc, char **argv)
int c;
static const struct option long_options[] = {
{"sort", required_argument, NULL, 'S'},
+ {"nosort", no_argument, NULL, 'N'},
{NULL, 0, NULL, 0}
};
@@ -1680,6 +1720,7 @@ static int cmd_subvol_list(int argc, char **argv)
}
break;
case 'S':
+ sort = 1;
ret = btrfs_list_parse_sort_string_v2(optarg,
&comparer_set);
if (ret) {
@@ -1687,6 +1728,9 @@ static int cmd_subvol_list(int argc, char **argv)
goto out;
}
break;
+ case 'N':
+ no_sort = 1;
+ break;
default:
uerr = 1;
@@ -1717,6 +1761,12 @@ static int cmd_subvol_list(int argc, char **argv)
goto out;
}
+ if (sort && no_sort) {
+ ret = -1;
+ error("cannot use --sort with --nosort option");
+ goto out;
+ }
+
subvol = argv[optind];
fd = btrfs_open_dir(subvol, &dirstream, 1);
if (fd < 0) {
@@ -1750,7 +1800,7 @@ static int cmd_subvol_list(int argc, char **argv)
ret = btrfs_list_subvols_print_v2(fd, filter_set, comparer_set,
layout, is_list_all, absolute_path, follow_mount,
- subvol, NULL);
+ no_sort, subvol, NULL);
out:
close_file_or_dir(fd, dirstream);
Currently, "subvolume list" loads all the subvolume information into memory first in order to sort them. This may cause a performance problem if there are a lot of subvolumes. This commit adds --nosort option to output subvolume information incrementally without sort to avoid consuming memory. Signed-off-by: Misono Tomohiro <misono.tomohiro@jp.fujitsu.com> --- Documentation/btrfs-subvolume.asciidoc | 5 ++ cmds-subvolume.c | 140 ++++++++++++++++++++++----------- 2 files changed, 100 insertions(+), 45 deletions(-)