Skip to content

Commit 082ce75

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 0e11b21 commit 082ce75

File tree

5 files changed

+310
-42
lines changed

5 files changed

+310
-42
lines changed

Documentation/mkfs.btrfs.rst

+5
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

+150-12
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;
@@ -1821,24 +1925,37 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
18211925
error_msg(ERROR_MSG_START_TRANS, "%m");
18221926
goto out;
18231927
}
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-
}
18341928

18351929
if (source_dir) {
18361930
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);
18381941
if (ret) {
18391942
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");
18401951
goto out;
18411952
}
1953+
1954+
list_for_each_entry(rds, &subvols, list) {
1955+
pr_verbose(LOG_DEFAULT, " Subvolume: %s\n",
1956+
rds->full_path);
1957+
}
1958+
18421959
if (shrink_rootdir) {
18431960
pr_verbose(LOG_DEFAULT, " Shrink: yes\n");
18441961
ret = btrfs_mkfs_shrink_fs(fs_info, &shrink_size,
@@ -1853,6 +1970,17 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
18531970
}
18541971
}
18551972

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+
18561984
if (features.runtime_flags & BTRFS_FEATURE_RUNTIME_QUOTA ||
18571985
features.incompat_flags & BTRFS_FEATURE_INCOMPAT_SIMPLE_QUOTA) {
18581986
ret = setup_quota_root(fs_info);
@@ -1946,6 +2074,16 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
19462074
free(label);
19472075
free(source_dir);
19482076

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+
19492087
return !!ret;
19502088

19512089
success:

0 commit comments

Comments
 (0)