Skip to content

Commit 46f8278

Browse files
committed
btrfs-progs: add --subvol option to mkfs.btrfs
This patch adds a --subvol option, which tells mkfs.btrfs to create the specified directories as subvolumes. Given a populated directory img, the command $ mkfs.btrfs --rootdir img --subvol usr --subvol home --subvol home/username /dev/loop0 will create subvolumes usr and home within the FS root, and subvolume username within the home subvolume. It will fail if any of the directories do not yet exist. Signed-off-by: Mark Harmstone <[email protected]>
1 parent 29da0fe commit 46f8278

File tree

4 files changed

+304
-42
lines changed

4 files changed

+304
-42
lines changed

mkfs/main.c

Lines changed: 153 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

19512092
success:

0 commit comments

Comments
 (0)