From 8ba72e45c8a6da1275b448ad4912d96e0a2c54b5 Mon Sep 17 00:00:00 2001 From: Mark Harmstone Date: Mon, 30 Sep 2024 15:55:10 +0100 Subject: [PATCH] btrfs-progs: mkfs: rework format of --subvol option value Change mkfs.btrfs --subvol so that instead of being of the form --subvol DIR:FLAGS, it's instead --subvol MODIFIER:DIR, with MODIFIER being ro, rw, default, or ro-default. Signed-off-by: Mark Harmstone --- Documentation/mkfs.btrfs.rst | 19 ++++++-- mkfs/main.c | 85 +++++++++--------------------------- 2 files changed, 36 insertions(+), 68 deletions(-) diff --git a/Documentation/mkfs.btrfs.rst b/Documentation/mkfs.btrfs.rst index a6251afd..58336e82 100644 --- a/Documentation/mkfs.btrfs.rst +++ b/Documentation/mkfs.btrfs.rst @@ -155,15 +155,26 @@ OPTIONS contain the files from *rootdir*. Since version 4.14.1 the filesystem size is not minimized. Please see option *--shrink* if you need that functionality. --u|--subvol : +-u|--subvol : Specify that *subdir* is to be created as a subvolume rather than a regular directory. The option *--rootdir* must also be specified, and *subdir* must be an existing subdirectory within it. This option can be specified multiple times. - *flags* is an optional comma-separated list of modifiers. Valid choices are: + *type* is an optional additional modifier. Valid choices are: - * *default*: create as default subvolume (this can only be specified once) - * *ro*: create as readonly subvolume + * *default*: create as default subvolume + * *ro*: create as read-only subvolume + * *rw*: create as read-write subvolume (the default) + * *default-ro*: create as read-only default subvolume + + Only one of *default* and *default-ro* may be specified. + + If you wish to create a subvolume with a name containing a colon and you don't + want this to be parsed as containing a modifier, you can prefix the path with `./`: + + .. code-block:: bash + + $ mkfs.btrfs --rootdir dir --subvol ./ro:subdir /dev/loop0 If there are hard links inside *rootdir* and *subdir* will split the subvolumes, like the following case:: diff --git a/mkfs/main.c b/mkfs/main.c index 06cc2484..c6ef4fc2 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -440,7 +440,7 @@ static const char * const mkfs_usage[] = { "Creation:", OPTLINE("-b|--byte-count SIZE", "set size of each device to SIZE (filesystem size is sum of all device sizes)"), OPTLINE("-r|--rootdir DIR", "copy files from DIR to the image root directory"), - OPTLINE("-u|--subvol SUBDIR:FLAGS", "create SUBDIR as subvolume rather than normal directory, can be specified multiple times"), + OPTLINE("-u|--subvol TYPE:SUBDIR", "create SUBDIR as subvolume rather than normal directory, can be specified multiple times"), OPTLINE("--shrink", "(with --rootdir) shrink the filled filesystem to minimal size"), OPTLINE("-K|--nodiscard", "do not perform whole device TRIM"), OPTLINE("-f|--force", "force overwrite of existing filesystem"), @@ -1015,48 +1015,6 @@ static void *prepare_one_device(void *ctx) return NULL; } -static int parse_subvol_flags(struct rootdir_subvol *subvol, const char *flags) -{ - char *buf, *orig_buf; - int ret; - - buf = orig_buf = strdup(flags); - - if (!buf) { - error_msg(ERROR_MSG_MEMORY, NULL); - ret = -ENOMEM; - goto out; - } - - while (true) { - char *comma = strstr(buf, ","); - - if (comma) - *comma = 0; - - if (!strcmp(buf, "default")) { - subvol->is_default = true; - } else if (!strcmp(buf, "ro")) { - subvol->readonly = true; - } else if (buf[0] != 0) { - error("unrecognized subvol flag \"%s\"", buf); - ret = 1; - goto out; - } - - if (comma) - buf = comma + 1; - else - break; - } - - ret = 0; - -out: - free(orig_buf); - return ret; -} - int BOX_MAIN(mkfs)(int argc, char **argv) { char *file; @@ -1259,6 +1217,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv) case 'u': { struct rootdir_subvol *subvol; char *colon; + bool valid_prefix = false; subvol = calloc(1, sizeof(struct rootdir_subvol)); if (!subvol) { @@ -1270,32 +1229,30 @@ int BOX_MAIN(mkfs)(int argc, char **argv) colon = strstr(optarg, ":"); if (colon) { - /* Make sure we choose the last colon in - * optarg, in case the subvol name - * itself contains a colon. */ - do { - char *colon2; - - colon2 = strstr(colon + 1, ":"); - - if (colon2) - colon = colon2; - else - break; - } while (true); - - subvol->dir = strndup(optarg, colon - optarg); - if (parse_subvol_flags(subvol, colon + 1)) { - ret = 1; - goto error; + if (!string_has_prefix(optarg, "default:")) { + subvol->is_default = true; + valid_prefix = true; + } else if (!string_has_prefix(optarg, "ro:")) { + subvol->readonly = true; + valid_prefix = true; + } else if (!string_has_prefix(optarg, "rw:")) { + subvol->readonly = false; + valid_prefix = true; + } else if (!string_has_prefix(optarg, "default-ro:")) { + subvol->is_default = true; + subvol->readonly = true; + valid_prefix = true; } - } else { - subvol->dir = strdup(optarg); } + if (valid_prefix) + subvol->dir = strndup(colon + 1, strlen(colon + 1)); + else + subvol->dir = strdup(optarg); + if (subvol->is_default) { if (has_default_subvol) { - error("subvol default flag can only be specified once"); + error("default subvol can only be specified once"); ret = 1; goto error; }