@@ -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, can be specified multiple times" ),
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 * subvol ;
1218+
1219+ subvol = malloc (sizeof (struct rootdir_subvol ));
1220+ if (!subvol ) {
1221+ error_msg (ERROR_MSG_MEMORY , NULL );
1222+ ret = 1 ;
1223+ goto error ;
1224+ }
1225+
1226+ subvol -> dir = strdup (optarg );
1227+ subvol -> full_path = NULL ;
1228+
1229+ list_add_tail (& subvol -> list , & subvols );
1230+ break ;
1231+ }
12111232 case 'U' :
12121233 strncpy_null (fs_uuid , optarg , BTRFS_UUID_UNPARSED_SIZE );
12131234 break ;
@@ -1272,6 +1293,89 @@ 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 ("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_msg (ERROR_MSG_MEMORY , NULL );
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 ("subvolume %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 ("subvolume %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 ) != 0 ||
1363+ path [source_dir_len ] != '/' ) {
1364+ error ("subvolume %s is not a child of %s" , rds -> dir , source_dir );
1365+ ret = 1 ;
1366+ goto error ;
1367+ }
1368+
1369+ for (rds2 = list_first_entry (& subvols , struct rootdir_subvol , list );
1370+ rds2 != rds ;
1371+ rds2 = list_next_entry (rds2 , list )) {
1372+ if (strcmp (rds2 -> full_path , path ) == 0 ) {
1373+ error ("subvolume %s specified more than once" , rds -> dir );
1374+ ret = 1 ;
1375+ goto error ;
1376+ }
1377+ }
1378+ }
12751379
12761380 if (* fs_uuid ) {
12771381 uuid_t dummy_uuid ;
@@ -1822,24 +1926,37 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
18221926 error_msg (ERROR_MSG_START_TRANS , "%m" );
18231927 goto out ;
18241928 }
1825- ret = btrfs_rebuild_uuid_tree (fs_info );
1826- if (ret < 0 )
1827- goto out ;
1828-
1829- ret = cleanup_temp_chunks (fs_info , & allocation , data_profile ,
1830- metadata_profile , metadata_profile );
1831- if (ret < 0 ) {
1832- error ("failed to cleanup temporary chunks: %d" , ret );
1833- goto out ;
1834- }
18351929
18361930 if (source_dir ) {
18371931 pr_verbose (LOG_DEFAULT , "Rootdir from: %s\n" , source_dir );
1838- 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 );
18391942 if (ret ) {
18401943 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" );
18411952 goto out ;
18421953 }
1954+
1955+ list_for_each_entry (rds , & subvols , list ) {
1956+ pr_verbose (LOG_DEFAULT , " Subvolume: %s\n" ,
1957+ rds -> full_path );
1958+ }
1959+
18431960 if (shrink_rootdir ) {
18441961 pr_verbose (LOG_DEFAULT , " Shrink: yes\n" );
18451962 ret = btrfs_mkfs_shrink_fs (fs_info , & shrink_size ,
@@ -1854,6 +1971,17 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
18541971 }
18551972 }
18561973
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+
18571985 if (features .runtime_flags & BTRFS_FEATURE_RUNTIME_QUOTA ||
18581986 features .incompat_flags & BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA ) {
18591987 ret = setup_quota_root (fs_info );
@@ -1947,6 +2075,16 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
19472075 free (label );
19482076 free (source_dir );
19492077
2078+ while (!list_empty (& subvols )) {
2079+ struct rootdir_subvol * head ;
2080+
2081+ head = list_entry (subvols .next , struct rootdir_subvol , list );
2082+ free (head -> dir );
2083+ free (head -> full_path );
2084+ list_del (& head -> list );
2085+ free (head );
2086+ }
2087+
19502088 return !!ret ;
19512089
19522090success :
0 commit comments