Skip to content

Commit e844ffc

Browse files
maharmstonekdave
authored andcommitted
btrfs-progs: mkfs: add new option --subvol
Add a new option --subvol, which tells mkfs.btrfs to create the specified directories as subvolumes when used with --rootdir. Given a populated directory dir, the command $ mkfs.btrfs --rootdir dir --subvol usr --subvol home --subvol home/username img will create subvolumes 'usr' and 'home' within the toplevel subvolume, and subvolume 'username' within the 'home' subvolume. It will fail if any of the directories do not yet exist. Pull-request: #868 Reviewed-by: Qu Wenruo <[email protected]> Signed-off-by: Mark Harmstone <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent a4d2d1b commit e844ffc

File tree

5 files changed

+310
-42
lines changed

5 files changed

+310
-42
lines changed

Documentation/mkfs.btrfs.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ OPTIONS
155155
contain the files from *rootdir*. Since version 4.14.1 the filesystem size is
156156
not minimized. Please see option *--shrink* if you need that functionality.
157157

158+
-u|--subvol <subdir>
159+
Specify that *subdir* is to be created as a subvolume rather than a regular
160+
directory. The option *--rootdir* must also be specified, and *subdir* must be an
161+
existing subdirectory within it. This option can be specified multiple times.
162+
158163
--shrink
159164
Shrink the filesystem to its minimal size, only works with *--rootdir* option.
160165

mkfs/main.c

Lines changed: 150 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, 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

19522090
success:

0 commit comments

Comments
 (0)