@@ -440,6 +440,7 @@ static const char * const mkfs_usage[] = {
440
440
"Creation:" ,
441
441
OPTLINE ("-b|--byte-count SIZE" , "set size of each device to SIZE (filesystem size is sum of all device sizes)" ),
442
442
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" ),
443
444
OPTLINE ("--shrink" , "(with --rootdir) shrink the filled filesystem to minimal size" ),
444
445
OPTLINE ("-K|--nodiscard" , "do not perform whole device TRIM" ),
445
446
OPTLINE ("-f|--force" , "force overwrite of existing filesystem" ),
@@ -1055,6 +1056,9 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
1055
1056
char * label = NULL ;
1056
1057
int nr_global_roots = sysconf (_SC_NPROCESSORS_ONLN );
1057
1058
char * source_dir = NULL ;
1059
+ size_t source_dir_len = 0 ;
1060
+ struct rootdir_subvol * rds ;
1061
+ LIST_HEAD (subvols );
1058
1062
1059
1063
cpu_detect_flags ();
1060
1064
hash_init_accel ();
@@ -1085,6 +1089,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
1085
1089
{ "data" , required_argument , NULL , 'd' },
1086
1090
{ "version" , no_argument , NULL , 'V' },
1087
1091
{ "rootdir" , required_argument , NULL , 'r' },
1092
+ { "subvol" , required_argument , NULL , 'u' },
1088
1093
{ "nodiscard" , no_argument , NULL , 'K' },
1089
1094
{ "features" , required_argument , NULL , 'O' },
1090
1095
{ "runtime-features" , required_argument , NULL , 'R' },
@@ -1102,7 +1107,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
1102
1107
{ NULL , 0 , NULL , 0 }
1103
1108
};
1104
1109
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: " ,
1106
1111
long_options , NULL );
1107
1112
if (c < 0 )
1108
1113
break ;
@@ -1208,6 +1213,22 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
1208
1213
free (source_dir );
1209
1214
source_dir = strdup (optarg );
1210
1215
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
+ }
1211
1232
case 'U' :
1212
1233
strncpy_null (fs_uuid , optarg , BTRFS_UUID_UNPARSED_SIZE );
1213
1234
break ;
@@ -1272,6 +1293,89 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
1272
1293
ret = 1 ;
1273
1294
goto error ;
1274
1295
}
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
+ }
1275
1379
1276
1380
if (* fs_uuid ) {
1277
1381
uuid_t dummy_uuid ;
@@ -1821,24 +1925,37 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
1821
1925
error_msg (ERROR_MSG_START_TRANS , "%m" );
1822
1926
goto out ;
1823
1927
}
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
- }
1834
1928
1835
1929
if (source_dir ) {
1836
1930
pr_verbose (LOG_DEFAULT , "Rootdir from: %s\n" , source_dir );
1837
- ret = btrfs_mkfs_fill_dir (source_dir , root );
1931
+
1932
+ trans = btrfs_start_transaction (root , 1 );
1933
+ if (IS_ERR (trans )) {
1934
+ errno = - PTR_ERR (trans );
1935
+ error_msg (ERROR_MSG_START_TRANS , "%m" );
1936
+ goto out ;
1937
+ }
1938
+
1939
+ ret = btrfs_mkfs_fill_dir (trans , source_dir , root ,
1940
+ & subvols );
1838
1941
if (ret ) {
1839
1942
error ("error while filling filesystem: %d" , ret );
1943
+ btrfs_abort_transaction (trans , ret );
1944
+ goto out ;
1945
+ }
1946
+
1947
+ ret = btrfs_commit_transaction (trans , root );
1948
+ if (ret ) {
1949
+ errno = - ret ;
1950
+ error_msg (ERROR_MSG_COMMIT_TRANS , "%m" );
1840
1951
goto out ;
1841
1952
}
1953
+
1954
+ list_for_each_entry (rds , & subvols , list ) {
1955
+ pr_verbose (LOG_DEFAULT , " Subvolume: %s\n" ,
1956
+ rds -> full_path );
1957
+ }
1958
+
1842
1959
if (shrink_rootdir ) {
1843
1960
pr_verbose (LOG_DEFAULT , " Shrink: yes\n" );
1844
1961
ret = btrfs_mkfs_shrink_fs (fs_info , & shrink_size ,
@@ -1853,6 +1970,17 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
1853
1970
}
1854
1971
}
1855
1972
1973
+ ret = btrfs_rebuild_uuid_tree (fs_info );
1974
+ if (ret < 0 )
1975
+ goto out ;
1976
+
1977
+ ret = cleanup_temp_chunks (fs_info , & allocation , data_profile ,
1978
+ metadata_profile , metadata_profile );
1979
+ if (ret < 0 ) {
1980
+ error ("failed to cleanup temporary chunks: %d" , ret );
1981
+ goto out ;
1982
+ }
1983
+
1856
1984
if (features .runtime_flags & BTRFS_FEATURE_RUNTIME_QUOTA ||
1857
1985
features .incompat_flags & BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA ) {
1858
1986
ret = setup_quota_root (fs_info );
@@ -1946,6 +2074,16 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
1946
2074
free (label );
1947
2075
free (source_dir );
1948
2076
2077
+ while (!list_empty (& subvols )) {
2078
+ struct rootdir_subvol * head ;
2079
+
2080
+ head = list_entry (subvols .next , struct rootdir_subvol , list );
2081
+ free (head -> dir );
2082
+ free (head -> full_path );
2083
+ list_del (& head -> list );
2084
+ free (head );
2085
+ }
2086
+
1949
2087
return !!ret ;
1950
2088
1951
2089
success :
0 commit comments