@@ -440,6 +440,7 @@ static const char * const mkfs_usage[] = {
440440 "Creation:" ,
441441 OPTLINE ("-b|--byte-count SIZE" , "set size of each device to SIZE (filesystem size is sum of all device sizes)" ),
442442 OPTLINE ("-r|--rootdir DIR" , "copy files from DIR to the image root directory" ),
443+ OPTLINE ("-u|--subvol SUBDIR" , "create SUBDIR as subvolume rather than normal directory" ),
443444 OPTLINE ("--shrink" , "(with --rootdir) shrink the filled filesystem to minimal size" ),
444445 OPTLINE ("-K|--nodiscard" , "do not perform whole device TRIM" ),
445446 OPTLINE ("-f|--force" , "force overwrite of existing filesystem" ),
@@ -1055,6 +1056,9 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
10551056 char * label = NULL ;
10561057 int nr_global_roots = sysconf (_SC_NPROCESSORS_ONLN );
10571058 char * source_dir = NULL ;
1059+ size_t source_dir_len = 0 ;
1060+ struct rootdir_subvol * rds ;
1061+ LIST_HEAD (subvols );
10581062
10591063 cpu_detect_flags ();
10601064 hash_init_accel ();
@@ -1085,6 +1089,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
10851089 { "data" , required_argument , NULL , 'd' },
10861090 { "version" , no_argument , NULL , 'V' },
10871091 { "rootdir" , required_argument , NULL , 'r' },
1092+ { "subvol" , required_argument , NULL , 'u' },
10881093 { "nodiscard" , no_argument , NULL , 'K' },
10891094 { "features" , required_argument , NULL , 'O' },
10901095 { "runtime-features" , required_argument , NULL , 'R' },
@@ -1102,7 +1107,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
11021107 { NULL , 0 , NULL , 0 }
11031108 };
11041109
1105- c = getopt_long (argc , argv , "A:b:fl:n:s:m:d:L:R:O:r:U:VvMKq " ,
1110+ c = getopt_long (argc , argv , "A:b:fl:n:s:m:d:L:R:O:r:U:VvMKqu: " ,
11061111 long_options , NULL );
11071112 if (c < 0 )
11081113 break ;
@@ -1208,6 +1213,22 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
12081213 free (source_dir );
12091214 source_dir = strdup (optarg );
12101215 break ;
1216+ case 'u' : {
1217+ struct rootdir_subvol * s ;
1218+
1219+ s = malloc (sizeof (struct rootdir_subvol ));
1220+ if (!s ) {
1221+ error ("out of memory" );
1222+ ret = 1 ;
1223+ goto error ;
1224+ }
1225+
1226+ s -> dir = strdup (optarg );
1227+ s -> full_path = NULL ;
1228+
1229+ list_add_tail (& s -> list , & subvols );
1230+ break ;
1231+ }
12111232 case 'U' :
12121233 strncpy_null (fs_uuid , optarg , BTRFS_UUID_UNPARSED_SIZE );
12131234 break ;
@@ -1272,6 +1293,90 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
12721293 ret = 1 ;
12731294 goto error ;
12741295 }
1296+ if (!list_empty (& subvols ) && source_dir == NULL ) {
1297+ error ("the option --subvol must be used with --rootdir" );
1298+ ret = 1 ;
1299+ goto error ;
1300+ }
1301+
1302+ if (source_dir ) {
1303+ char * canonical = realpath (source_dir , NULL );
1304+
1305+ if (!canonical ) {
1306+ error ("could not get canonical path to %s" , source_dir );
1307+ ret = 1 ;
1308+ goto error ;
1309+ }
1310+
1311+ free (source_dir );
1312+ source_dir = canonical ;
1313+ source_dir_len = strlen (source_dir );
1314+ }
1315+
1316+ list_for_each_entry (rds , & subvols , list ) {
1317+ char * path , * canonical ;
1318+ struct rootdir_subvol * rds2 ;
1319+ size_t dir_len ;
1320+
1321+ dir_len = strlen (rds -> dir );
1322+
1323+ path = malloc (source_dir_len + 1 + dir_len + 1 );
1324+ if (!path ) {
1325+ error ("out of memory" );
1326+ ret = 1 ;
1327+ goto error ;
1328+ }
1329+
1330+ memcpy (path , source_dir , source_dir_len );
1331+ path [source_dir_len ] = '/' ;
1332+ memcpy (path + source_dir_len + 1 , rds -> dir , dir_len + 1 );
1333+
1334+ canonical = realpath (path , NULL );
1335+ if (!canonical ) {
1336+ error ("could not get canonical path to %s" , rds -> dir );
1337+ free (path );
1338+ ret = 1 ;
1339+ goto error ;
1340+ }
1341+
1342+ free (path );
1343+ path = canonical ;
1344+
1345+ if (!path_exists (path )) {
1346+ error ("subvol %s does not exist" , rds -> dir );
1347+ free (path );
1348+ ret = 1 ;
1349+ goto error ;
1350+ }
1351+
1352+ if (!path_is_dir (path )) {
1353+ error ("subvol %s is not a directory" , rds -> dir );
1354+ free (path );
1355+ ret = 1 ;
1356+ goto error ;
1357+ }
1358+
1359+ rds -> full_path = path ;
1360+
1361+ if (strlen (path ) < source_dir_len + 1 ||
1362+ memcmp (path , source_dir , source_dir_len ) ||
1363+ path [source_dir_len ] != '/' ) {
1364+ error ("subvol %s is not a child of %s" , rds -> dir ,
1365+ source_dir );
1366+ ret = 1 ;
1367+ goto error ;
1368+ }
1369+
1370+ for (rds2 = list_first_entry (& subvols , struct rootdir_subvol , list );
1371+ rds2 != rds ; rds2 = list_next_entry (rds2 , list )) {
1372+ if (!strcmp (rds2 -> full_path , path )) {
1373+ error ("subvol %s specified more than once" ,
1374+ rds -> dir );
1375+ ret = 1 ;
1376+ goto error ;
1377+ }
1378+ }
1379+ }
12751380
12761381 if (* fs_uuid ) {
12771382 uuid_t dummy_uuid ;
@@ -1821,24 +1926,37 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
18211926 error_msg (ERROR_MSG_START_TRANS , "%m" );
18221927 goto out ;
18231928 }
1824- ret = btrfs_rebuild_uuid_tree (fs_info );
1825- if (ret < 0 )
1826- goto out ;
1827-
1828- ret = cleanup_temp_chunks (fs_info , & allocation , data_profile ,
1829- metadata_profile , metadata_profile );
1830- if (ret < 0 ) {
1831- error ("failed to cleanup temporary chunks: %d" , ret );
1832- goto out ;
1833- }
18341929
18351930 if (source_dir ) {
18361931 pr_verbose (LOG_DEFAULT , "Rootdir from: %s\n" , source_dir );
1837- ret = btrfs_mkfs_fill_dir (source_dir , root );
1932+
1933+ trans = btrfs_start_transaction (root , 1 );
1934+ if (IS_ERR (trans )) {
1935+ errno = - PTR_ERR (trans );
1936+ error_msg (ERROR_MSG_START_TRANS , "%m" );
1937+ goto out ;
1938+ }
1939+
1940+ ret = btrfs_mkfs_fill_dir (trans , source_dir , root ,
1941+ & subvols );
18381942 if (ret ) {
18391943 error ("error while filling filesystem: %d" , ret );
1944+ btrfs_abort_transaction (trans , ret );
1945+ goto out ;
1946+ }
1947+
1948+ ret = btrfs_commit_transaction (trans , root );
1949+ if (ret ) {
1950+ errno = - ret ;
1951+ error_msg (ERROR_MSG_COMMIT_TRANS , "%m" );
18401952 goto out ;
18411953 }
1954+
1955+ list_for_each_entry (rds , & subvols , list ) {
1956+ pr_verbose (LOG_DEFAULT , " Subvol from: %s\n" ,
1957+ rds -> full_path );
1958+ }
1959+
18421960 if (shrink_rootdir ) {
18431961 pr_verbose (LOG_DEFAULT , " Shrink: yes\n" );
18441962 ret = btrfs_mkfs_shrink_fs (fs_info , & shrink_size ,
@@ -1853,6 +1971,17 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
18531971 }
18541972 }
18551973
1974+ ret = btrfs_rebuild_uuid_tree (fs_info );
1975+ if (ret < 0 )
1976+ goto out ;
1977+
1978+ ret = cleanup_temp_chunks (fs_info , & allocation , data_profile ,
1979+ metadata_profile , metadata_profile );
1980+ if (ret < 0 ) {
1981+ error ("failed to cleanup temporary chunks: %d" , ret );
1982+ goto out ;
1983+ }
1984+
18561985 if (features .runtime_flags & BTRFS_FEATURE_RUNTIME_QUOTA ||
18571986 features .incompat_flags & BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA ) {
18581987 ret = setup_quota_root (fs_info );
@@ -1946,6 +2075,18 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
19462075 free (label );
19472076 free (source_dir );
19482077
2078+ while (!list_empty (& subvols )) {
2079+ struct rootdir_subvol * head = list_entry (subvols .next ,
2080+ struct rootdir_subvol ,
2081+ list );
2082+
2083+ free (head -> dir );
2084+ free (head -> full_path );
2085+
2086+ list_del (& head -> list );
2087+ free (head );
2088+ }
2089+
19492090 return !!ret ;
19502091
19512092success :
0 commit comments