@@ -59,6 +59,13 @@ static const char * const cmd_subvolume_list_usage[] = {
5959 "List subvolumes and snapshots in the filesystem." ,
6060 "" ,
6161 "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+ "" ,
6269 OPTLINE ("-o" , "print only the immediate children subvolumes of the "
6370 "subvolume containing <path>" ),
6471 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,
866873 return subvols ;
867874}
868875
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 ,
870878 struct btrfs_list_filter_set * filter_set )
871879{
880+ u64 top_id = below ? 0 : BTRFS_FS_TREE_OBJECTID ;
872881 struct subvol_list * subvols ;
873882 size_t capacity = 4 ;
874883 struct btrfs_util_subvolume_iterator * iter ;
@@ -883,15 +892,28 @@ static struct subvol_list *btrfs_list_subvols(int fd,
883892 }
884893 subvols -> num = 0 ;
885894
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 );
889896 if (err ) {
890897 iter = NULL ;
891898 error_btrfs_util (err );
892899 goto out ;
893900 }
894901
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+
895917 for (;;) {
896918 struct root_info subvol ;
897919
@@ -945,14 +967,15 @@ static struct subvol_list *btrfs_list_subvols(int fd,
945967
946968static int btrfs_list_subvols_print (int fd , struct btrfs_list_filter_set * filter_set ,
947969 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 )
949972{
950973 struct subvol_list * subvols ;
951974
952975 if (filter_set -> only_deleted )
953976 subvols = btrfs_list_deleted_subvols (fd , filter_set );
954977 else
955- subvols = btrfs_list_subvols (fd , filter_set );
978+ subvols = btrfs_list_subvols (fd , include_top , below , filter_set );
956979 if (!subvols )
957980 return -1 ;
958981
@@ -1097,8 +1120,10 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
10971120 char * top_path = NULL ;
10981121 int ret = -1 , uerr = 0 ;
10991122 char * subvol ;
1123+ bool is_list_below = false;
11001124 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;
11021127 enum btrfs_list_layout layout = BTRFS_LIST_LAYOUT_DEFAULT ;
11031128
11041129 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
11131138 };
11141139
11151140 c = getopt_long (argc , argv ,
1116- "acdgopqsurRG:C:t " , long_options , NULL );
1141+ "acdgopqsurRG:C:tOA " , long_options , NULL );
11171142 if (c < 0 )
11181143 break ;
11191144
@@ -1122,7 +1147,7 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
11221147 btrfs_list_setup_print_column (BTRFS_LIST_PARENT );
11231148 break ;
11241149 case 'a' :
1125- is_list_all = true;
1150+ is_old_a_option = true;
11261151 break ;
11271152 case 'c' :
11281153 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
11341159 btrfs_list_setup_print_column (BTRFS_LIST_GENERATION );
11351160 break ;
11361161 case 'o' :
1137- is_only_in_path = true;
1162+ is_old_o_option = true;
11381163 break ;
11391164 case 't' :
11401165 layout = BTRFS_LIST_LAYOUT_TABLE ;
@@ -1187,6 +1212,12 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
11871212 goto out ;
11881213 }
11891214 break ;
1215+ case 'O' :
1216+ is_list_below = true;
1217+ break ;
1218+ case 'A' :
1219+ is_list_all = true;
1220+ break ;
11901221
11911222 default :
11921223 uerr = 1 ;
@@ -1197,6 +1228,19 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
11971228 if (check_argc_exact (argc - optind , 1 ))
11981229 goto out ;
11991230
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+
12001244 subvol = argv [optind ];
12011245 fd = btrfs_open_dir (subvol );
12021246 if (fd < 0 ) {
@@ -1216,15 +1260,15 @@ static int cmd_subvolume_list(const struct cmd_struct *cmd, int argc, char **arg
12161260 goto out ;
12171261 }
12181262
1219- if (is_list_all )
1263+ if (is_old_a_option )
12201264 btrfs_list_setup_filter (& filter_set ,
12211265 BTRFS_LIST_FILTER_FULL_PATH ,
12221266 top_id );
1223- else if (is_only_in_path )
1267+ else if (is_old_o_option )
12241268 btrfs_list_setup_filter (& filter_set ,
12251269 BTRFS_LIST_FILTER_TOPID_EQUAL ,
12261270 top_id );
1227- else if (!filter_set -> only_deleted ) {
1271+ else if (!is_list_below && ! is_list_all && ! filter_set -> only_deleted ) {
12281272 enum btrfs_util_error err ;
12291273
12301274 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
12411285 /* by default we shall print the following columns*/
12421286 btrfs_list_setup_print_column (BTRFS_LIST_OBJECTID );
12431287 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 );
12451290 btrfs_list_setup_print_column (BTRFS_LIST_PATH );
12461291
12471292 if (bconf .output_format == CMD_FORMAT_JSON )
12481293 layout = BTRFS_LIST_LAYOUT_JSON ;
12491294
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 );
12511298
12521299out :
12531300 free (top_path );
0 commit comments