@@ -59,6 +59,13 @@ static const char * const cmd_subvolume_list_usage[] = {
59
59
"List subvolumes and snapshots in the filesystem." ,
60
60
"" ,
61
61
"Path filtering:" ,
62
+ OPTLINE ("-O" , "print all subvolumes below <path> relative to <path>" ),
63
+ OPTLINE ("-A" , "print all subvolumes in the filesystem relative to the "
64
+ "root of the filesystem" ),
65
+ "" ,
66
+ "You likely always want either -O or -A. The -o and -a options and the" ,
67
+ "default are confusing and only kept for backwards compatibility." ,
68
+ "" ,
62
69
OPTLINE ("-o" , "print only the immediate children subvolumes of the "
63
70
"subvolume containing <path>" ),
64
71
OPTLINE ("-a" , "print all subvolumes in the filesystem other than the "
@@ -866,9 +873,11 @@ static struct subvol_list *btrfs_list_deleted_subvols(int fd,
866
873
return subvols ;
867
874
}
868
875
869
- static struct subvol_list * btrfs_list_subvols (int fd ,
876
+ static struct subvol_list * btrfs_list_subvols (int fd , bool include_top ,
877
+ bool below ,
870
878
struct btrfs_list_filter_set * filter_set )
871
879
{
880
+ u64 top_id = below ? 0 : BTRFS_FS_TREE_OBJECTID ;
872
881
struct subvol_list * subvols ;
873
882
size_t capacity = 4 ;
874
883
struct btrfs_util_subvolume_iterator * iter ;
@@ -883,15 +892,28 @@ static struct subvol_list *btrfs_list_subvols(int fd,
883
892
}
884
893
subvols -> num = 0 ;
885
894
886
- err = btrfs_util_create_subvolume_iterator_fd (fd ,
887
- BTRFS_FS_TREE_OBJECTID , 0 ,
888
- & iter );
895
+ err = btrfs_util_create_subvolume_iterator_fd (fd , top_id , 0 , & iter );
889
896
if (err ) {
890
897
iter = NULL ;
891
898
error_btrfs_util (err );
892
899
goto out ;
893
900
}
894
901
902
+ if (include_top ) {
903
+ err = btrfs_util_subvolume_info_fd (fd , top_id ,
904
+ & subvols -> subvols [0 ].info );
905
+ if (err ) {
906
+ error_btrfs_util (err );
907
+ goto out ;
908
+ }
909
+ subvols -> subvols [0 ].path = strdup ("" );
910
+ if (!subvols -> subvols [0 ].path ) {
911
+ error ("out of memory" );
912
+ goto out ;
913
+ }
914
+ subvols -> num ++ ;
915
+ }
916
+
895
917
for (;;) {
896
918
struct root_info subvol ;
897
919
@@ -945,14 +967,15 @@ static struct subvol_list *btrfs_list_subvols(int fd,
945
967
946
968
static int btrfs_list_subvols_print (int fd , struct btrfs_list_filter_set * filter_set ,
947
969
struct btrfs_list_comparer_set * comp_set ,
948
- enum btrfs_list_layout layout )
970
+ enum btrfs_list_layout layout , bool include_top ,
971
+ bool below )
949
972
{
950
973
struct subvol_list * subvols ;
951
974
952
975
if (filter_set -> only_deleted )
953
976
subvols = btrfs_list_deleted_subvols (fd , filter_set );
954
977
else
955
- subvols = btrfs_list_subvols (fd , filter_set );
978
+ subvols = btrfs_list_subvols (fd , include_top , below , filter_set );
956
979
if (!subvols )
957
980
return -1 ;
958
981
@@ -1097,8 +1120,10 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
1097
1120
char * top_path = NULL ;
1098
1121
int ret = -1 , uerr = 0 ;
1099
1122
char * subvol ;
1123
+ bool is_list_below = false;
1100
1124
bool is_list_all = false;
1101
- bool is_only_in_path = false;
1125
+ bool is_old_a_option = false;
1126
+ bool is_old_o_option = false;
1102
1127
enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT ;
1103
1128
1104
1129
filter_set = btrfs_list_alloc_filter_set ();
@@ -1113,7 +1138,7 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
1113
1138
};
1114
1139
1115
1140
c = getopt_long (argc , argv ,
1116
- "acdgopqsurRG:C:t " , long_options , NULL );
1141
+ "acdgopqsurRG:C:tOA " , long_options , NULL );
1117
1142
if (c < 0 )
1118
1143
break ;
1119
1144
@@ -1122,7 +1147,7 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
1122
1147
btrfs_list_setup_print_column (BTRFS_LIST_PARENT );
1123
1148
break ;
1124
1149
case 'a' :
1125
- is_list_all = true;
1150
+ is_old_a_option = true;
1126
1151
break ;
1127
1152
case 'c' :
1128
1153
btrfs_list_setup_print_column (BTRFS_LIST_OGENERATION );
@@ -1134,7 +1159,7 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
1134
1159
btrfs_list_setup_print_column (BTRFS_LIST_GENERATION );
1135
1160
break ;
1136
1161
case 'o' :
1137
- is_only_in_path = true;
1162
+ is_old_o_option = true;
1138
1163
break ;
1139
1164
case 't' :
1140
1165
layout = BTRFS_LIST_LAYOUT_TABLE ;
@@ -1187,6 +1212,12 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
1187
1212
goto out ;
1188
1213
}
1189
1214
break ;
1215
+ case 'O' :
1216
+ is_list_below = true;
1217
+ break ;
1218
+ case 'A' :
1219
+ is_list_all = true;
1220
+ break ;
1190
1221
1191
1222
default :
1192
1223
uerr = 1 ;
@@ -1197,6 +1228,19 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
1197
1228
if (check_argc_exact (argc - optind , 1 ))
1198
1229
goto out ;
1199
1230
1231
+ /*
1232
+ * The path filtering options and -d don't make sense together. For -O
1233
+ * and -A, we're strict about not combining them with each other or with
1234
+ * -o, -a, or -d. The -o, -a, and -d options are older and have never
1235
+ * been restricted, so although they produce dubious results when
1236
+ * combined, we allow it for backwards compatibility.
1237
+ */
1238
+ if (is_list_below + is_list_all +
1239
+ (is_old_a_option || is_old_o_option || filter_set -> only_deleted ) > 1 ) {
1240
+ error ("-O, -A, -o, -a, and -d are mutually exclusive" );
1241
+ goto out ;
1242
+ }
1243
+
1200
1244
subvol = argv [optind ];
1201
1245
fd = btrfs_open_dir (subvol );
1202
1246
if (fd < 0 ) {
@@ -1216,15 +1260,15 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
1216
1260
goto out ;
1217
1261
}
1218
1262
1219
- if (is_list_all )
1263
+ if (is_old_a_option )
1220
1264
btrfs_list_setup_filter (& filter_set ,
1221
1265
BTRFS_LIST_FILTER_FULL_PATH ,
1222
1266
top_id );
1223
- else if (is_only_in_path )
1267
+ else if (is_old_o_option )
1224
1268
btrfs_list_setup_filter (& filter_set ,
1225
1269
BTRFS_LIST_FILTER_TOPID_EQUAL ,
1226
1270
top_id );
1227
- else if (!filter_set -> only_deleted ) {
1271
+ else if (!is_list_below && ! is_list_all && ! filter_set -> only_deleted ) {
1228
1272
enum btrfs_util_error err ;
1229
1273
1230
1274
err = btrfs_util_subvolume_get_path_fd (fd , top_id , & top_path );
@@ -1241,13 +1285,16 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
1241
1285
/* by default we shall print the following columns*/
1242
1286
btrfs_list_setup_print_column (BTRFS_LIST_OBJECTID );
1243
1287
btrfs_list_setup_print_column (BTRFS_LIST_GENERATION );
1244
- btrfs_list_setup_print_column (BTRFS_LIST_TOP_LEVEL );
1288
+ btrfs_list_setup_print_column (is_list_below || is_list_all ?
1289
+ BTRFS_LIST_PARENT : BTRFS_LIST_TOP_LEVEL );
1245
1290
btrfs_list_setup_print_column (BTRFS_LIST_PATH );
1246
1291
1247
1292
if (bconf .output_format == CMD_FORMAT_JSON )
1248
1293
layout = BTRFS_LIST_LAYOUT_JSON ;
1249
1294
1250
- ret = btrfs_list_subvols_print (fd , filter_set , comparer_set , layout );
1295
+ ret = btrfs_list_subvols_print (fd , filter_set , comparer_set , layout ,
1296
+ is_list_below || is_list_all ,
1297
+ is_list_below );
1251
1298
1252
1299
out :
1253
1300
free (top_path );
0 commit comments