return is_descending ? -ret : ret;
}
+static int comp_entry_with_path(struct root_info *entry1,
+ struct root_info *entry2,
+ int is_descending)
+{
+ int ret;
+
+ if (strcmp(entry1->full_path, entry2->full_path) > 0)
+ ret = 1;
+ else if (strcmp(entry1->full_path, entry2->full_path) < 0)
+ ret = -1;
+ else
+ ret = 0;
+
+ return is_descending ? -ret : ret;
+}
+
static btrfs_list_comp_func all_comp_funcs[] = {
[BTRFS_LIST_COMP_ROOTID] = comp_entry_with_rootid,
[BTRFS_LIST_COMP_OGEN] = comp_entry_with_ogen,
[BTRFS_LIST_COMP_GEN] = comp_entry_with_gen,
+ [BTRFS_LIST_COMP_PATH] = comp_entry_with_path,
};
+static char *all_sort_items[] = {
+ [BTRFS_LIST_COMP_ROOTID] = "rootid",
+ [BTRFS_LIST_COMP_OGEN] = "ogen",
+ [BTRFS_LIST_COMP_GEN] = "gen",
+ [BTRFS_LIST_COMP_PATH] = "path",
+ [BTRFS_LIST_COMP_MAX] = NULL,
+};
+
+static int btrfs_list_get_sort_item(char *sort_name)
+{
+ int i;
+
+ for (i = 0; i < BTRFS_LIST_COMP_MAX; i++) {
+ if (strcmp(sort_name, all_sort_items[i]) == 0)
+ return i;
+ }
+ return -1;
+}
+
struct btrfs_list_comparer_set *btrfs_list_alloc_comparer_set(void)
{
struct btrfs_list_comparer_set *set;
return ri->flags & flags;
}
+static int filter_gen_more(struct root_info *ri, u64 data)
+{
+ return ri->gen >= data;
+}
+
+static int filter_gen_less(struct root_info *ri, u64 data)
+{
+ return ri->gen <= data;
+}
+
+static int filter_gen_equal(struct root_info *ri, u64 data)
+{
+ return ri->gen == data;
+}
+
+static int filter_cgen_more(struct root_info *ri, u64 data)
+{
+ return ri->ogen >= data;
+}
+
+static int filter_cgen_less(struct root_info *ri, u64 data)
+{
+ return ri->ogen <= data;
+}
+
+static int filter_cgen_equal(struct root_info *ri, u64 data)
+{
+ return ri->ogen == data;
+}
+
static btrfs_list_filter_func all_filter_funcs[] = {
[BTRFS_LIST_FILTER_ROOTID] = filter_by_rootid,
[BTRFS_LIST_FILTER_SNAPSHOT_ONLY] = filter_snapshot,
[BTRFS_LIST_FILTER_FLAGS] = filter_flags,
+ [BTRFS_LIST_FILTER_GEN_MORE] = filter_gen_more,
+ [BTRFS_LIST_FILTER_GEN_LESS] = filter_gen_less,
+ [BTRFS_LIST_FILTER_GEN_EQUAL] = filter_gen_equal,
+ [BTRFS_LIST_FILTER_CGEN_MORE] = filter_cgen_more,
+ [BTRFS_LIST_FILTER_CGEN_LESS] = filter_cgen_less,
+ [BTRFS_LIST_FILTER_CGEN_EQUAL] = filter_cgen_equal,
};
struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void)
return ret_path;
}
+
+int btrfs_list_parse_sort_string(char *optarg,
+ struct btrfs_list_comparer_set **comps)
+{
+ int order;
+ int flag;
+ char *p;
+ char **ptr_argv;
+ int what_to_sort;
+
+ while ((p = strtok(optarg, ",")) != NULL) {
+ flag = 0;
+ ptr_argv = all_sort_items;
+
+ while (*ptr_argv) {
+ if (strcmp(*ptr_argv, p) == 0) {
+ flag = 1;
+ break;
+ } else {
+ p++;
+ if (strcmp(*ptr_argv, p) == 0) {
+ flag = 1;
+ p--;
+ break;
+ }
+ p--;
+ }
+ ptr_argv++;
+ }
+
+ if (flag == 0)
+ return -1;
+
+ else {
+ if (*p == '+') {
+ order = 0;
+ p++;
+ } else if (*p == '-') {
+ order = 1;
+ p++;
+ } else
+ order = 0;
+
+ what_to_sort = btrfs_list_get_sort_item(p);
+ btrfs_list_setup_comparer(comps, what_to_sort, order);
+ }
+ optarg = NULL;
+ }
+
+ return 0;
+}
+
+/*
+ * This function is used to parse the argument of filter condition.
+ *
+ * type is the filter object.
+ */
+int btrfs_list_parse_filter_string(char *optarg,
+ struct btrfs_list_filter_set **filters,
+ enum btrfs_list_filter_enum type)
+{
+
+ u64 arg;
+ char *ptr_parse_end = NULL;
+ char *ptr_optarg_end = optarg + strlen(optarg);
+
+ switch (*(optarg++)) {
+ case '+':
+ arg = (u64)strtol(optarg, &ptr_parse_end, 10);
+ type += 2;
+ if (ptr_parse_end != ptr_optarg_end)
+ return -1;
+
+ btrfs_list_setup_filter(filters, type, arg);
+ break;
+ case '-':
+ arg = (u64)strtoll(optarg, &ptr_parse_end, 10);
+ type += 1;
+ if (ptr_parse_end != ptr_optarg_end)
+ return -1;
+
+ btrfs_list_setup_filter(filters, type, arg);
+ break;
+ default:
+ optarg--;
+ arg = (u64)strtoll(optarg, &ptr_parse_end, 10);
+
+ if (ptr_parse_end != ptr_optarg_end)
+ return -1;
+ btrfs_list_setup_filter(filters, type, arg);
+ break;
+ }
+
+ return 0;
+}
+
BTRFS_LIST_FILTER_ROOTID,
BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
BTRFS_LIST_FILTER_FLAGS,
+ BTRFS_LIST_FILTER_GEN,
+ BTRFS_LIST_FILTER_GEN_EQUAL = BTRFS_LIST_FILTER_GEN,
+ BTRFS_LIST_FILTER_GEN_LESS,
+ BTRFS_LIST_FILTER_GEN_MORE,
+ BTRFS_LIST_FILTER_CGEN,
+ BTRFS_LIST_FILTER_CGEN_EQUAL = BTRFS_LIST_FILTER_CGEN,
+ BTRFS_LIST_FILTER_CGEN_LESS,
+ BTRFS_LIST_FILTER_CGEN_MORE,
BTRFS_LIST_FILTER_MAX,
};
BTRFS_LIST_COMP_ROOTID,
BTRFS_LIST_COMP_OGEN,
BTRFS_LIST_COMP_GEN,
+ BTRFS_LIST_COMP_PATH,
BTRFS_LIST_COMP_MAX,
};
+int btrfs_list_parse_sort_string(char *optarg,
+ struct btrfs_list_comparer_set **comps);
+int btrfs_list_parse_filter_string(char *optarg,
+ struct btrfs_list_filter_set **filters,
+ enum btrfs_list_filter_enum type);
void btrfs_list_setup_print_column(enum btrfs_list_column_enum column);
struct btrfs_list_filter_set *btrfs_list_alloc_filter_set(void);
void btrfs_list_free_filter_set(struct btrfs_list_filter_set *filter_set);
#include <sys/stat.h>
#include <libgen.h>
#include <limits.h>
+#include <getopt.h>
#include "kerncompat.h"
#include "ioctl.h"
}
static const char * const cmd_subvol_list_usage[] = {
- "btrfs subvolume list [-pur] [-s 0|1] <path>",
+ "btrfs subvolume list [-pur] [-s 0|1] [-g [+|-]value] [-c [+|-]value] "
+ "[--sort=gen,ogen,rootid,path] <path>",
"List subvolumes (and snapshots)",
"",
"-p print parent ID",
"-s value list snapshots with generation in ascending/descending order",
" (1: ascending, 0: descending)",
"-r list readonly subvolumes (including snapshots)",
- NULL
+ "-g [+|-]value",
+ " filter the subvolumes by generation",
+ " (+value: >= value; -value: <= value; value: = value)",
+ "-c [+|-]value",
+ " filter the subvolumes by ogeneration",
+ " (+value: >= value; -value: <= value; value: = value)",
+ "--sort=gen,ogen,rootid,path",
+ " 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)",
+ NULL,
};
static int cmd_subvol_list(int argc, char **argv)
int fd;
int ret;
int order;
+ int c;
char *subvol;
+ struct option long_options[] = {
+ {"sort", 1, NULL, 'S'},
+ {0, 0, 0, 0}
+ };
filter_set = btrfs_list_alloc_filter_set();
comparer_set = btrfs_list_alloc_comparer_set();
optind = 1;
while(1) {
- int c = getopt(argc, argv, "ps:ur");
+ c = getopt_long(argc, argv,
+ "ps:urg:c:", long_options, NULL);
if (c < 0)
break;
!order);
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
- break;
+
case 'u':
btrfs_list_setup_print_column(BTRFS_LIST_UUID);
break;
case 'r':
flags |= BTRFS_ROOT_SUBVOL_RDONLY;
break;
+ case 'g':
+ btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
+ ret = btrfs_list_parse_filter_string(optarg,
+ &filter_set,
+ BTRFS_LIST_FILTER_GEN);
+ if (ret)
+ usage(cmd_subvol_list_usage);
+ break;
+
+ case 'c':
+ btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
+ ret = btrfs_list_parse_filter_string(optarg,
+ &filter_set,
+ BTRFS_LIST_FILTER_CGEN);
+ if (ret)
+ usage(cmd_subvol_list_usage);
+ break;
+ case 'S':
+ ret = btrfs_list_parse_sort_string(optarg,
+ &comparer_set);
+ if (ret)
+ usage(cmd_subvol_list_usage);
+ break;
+
default:
usage(cmd_subvol_list_usage);
}
.PP
\fBbtrfs\fP \fBsubvolume create\fP\fI [<dest>/]<name>\fP
.PP
-\fBbtrfs\fP \fBsubvolume list\fP\fI [-pr] <path>\fP
+\fBbtrfs\fP \fBsubvolume list\fP\fI [-pr] [-s 0|1] [-g [+|-]value] [-c [+|-]value] [--rootid=rootid,gen,ogen,path] <path>\fP
.PP
\fBbtrfs\fP \fBsubvolume set-default\fP\fI <id> <path>\fP
.PP
\fI<dest>\fR is omitted).
.TP
-\fBsubvolume list\fR\fI [-pr] <path>\fR
+\fBsubvolume list\fR\fI [-pr][-s 0|1] [-g [+|-]value] [-c [+|-]value] [--sort=gen,ogen,rootid,path] <path>\fR
+.RS
List the subvolumes present in the filesystem \fI<path>\fR. For every
subvolume the following information is shown by default.
ID <ID> top level <ID> path <path>
where path is the relative path of the subvolume to the \fItop level\fR
subvolume.
+
The subvolume's ID may be used by the \fBsubvolume set-default\fR command, or
at mount time via the \fIsubvol=\fR option.
If \fI-p\fR is given, then \fIparent <ID>\fR is added to the output between ID
and top level. The parent's ID may be used at mount time via the
\fIsubvolrootid=\fR option.
-If \fI-r\fR is given, only readonly subvolumes in the filesystem will be listed.
+
+\fB-r\fP only readonly subvolumes in the filesystem wille be listed.
+
+\fB-s\fP only snapshot subvolumes in the filesystem will be listed.
+
+\fB-g [+|-]value\fP
+list subvolumes in the filesystem that its generation is
+>=, <= or = value. '+' means >= value, '-' means <= value, If there is
+neither '+' nor '-', it means = value.
+
+\fB-c [+|-]value\fP
+list subvolumes in the filesystem that its ogeneration is
+>=, <= or = value. The usage is the same to '-g' option.
+
+\fB--sort=gen,ogen,path,rootid\fP
+list subvolumes in order by specified items.
+you can add '+' or '-' in front of each items, '+' means ascending,'-'
+means descending. The default is ascending.
+
+for \fB--sort\fP you can combine some items together by ',', just like
+\f--sort=+ogen,-gen,path,rootid\fR.
+.RE
.TP
\fBsubvolume set-default\fR\fI <id> <path>\fR