btrfs-progs: mkfs: add default flag to --subvol

Change --subvol that it can accept flags, and add a "default" flag that
allows you to mark a subvolume as the default.

Signed-off-by: Mark Harmstone <maharmstone@fb.com>
This commit is contained in:
Mark Harmstone 2024-08-15 15:27:01 +01:00 committed by David Sterba
parent 9b7859bacc
commit 397715149d
4 changed files with 137 additions and 5 deletions

View File

@ -155,11 +155,15 @@ OPTIONS
contain the files from *rootdir*. Since version 4.14.1 the filesystem size is 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. not minimized. Please see option *--shrink* if you need that functionality.
-u|--subvol <subdir> -u|--subvol <subdir>:<flags>
Specify that *subdir* is to be created as a subvolume rather than a regular 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 directory. The option *--rootdir* must also be specified, and *subdir* must be an
existing subdirectory within it. This option can be specified multiple times. existing subdirectory within it. This option can be specified multiple times.
*flags* is an optional comma-separated list of modifiers. Valid choices are:
* *default*: create as default subvolume (this can only be specified once)
--shrink --shrink
Shrink the filesystem to its minimal size, only works with *--rootdir* option. Shrink the filesystem to its minimal size, only works with *--rootdir* option.

View File

@ -440,7 +440,7 @@ static const char * const mkfs_usage[] = {
"Creation:", "Creation:",
OPTLINE("-b|--byte-count SIZE", "set size of each device to SIZE (filesystem size is sum of all device sizes)"), 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("-r|--rootdir DIR", "copy files from DIR to the image root directory"),
OPTLINE("-u|--subvol SUBDIR", "create SUBDIR as subvolume rather than normal directory, can be specified multiple times"), OPTLINE("-u|--subvol SUBDIR:FLAGS", "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("--shrink", "(with --rootdir) shrink the filled filesystem to minimal size"),
OPTLINE("-K|--nodiscard", "do not perform whole device TRIM"), OPTLINE("-K|--nodiscard", "do not perform whole device TRIM"),
OPTLINE("-f|--force", "force overwrite of existing filesystem"), OPTLINE("-f|--force", "force overwrite of existing filesystem"),
@ -1015,6 +1015,46 @@ static void *prepare_one_device(void *ctx)
return NULL; 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 (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) int BOX_MAIN(mkfs)(int argc, char **argv)
{ {
char *file; char *file;
@ -1058,6 +1098,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
char *source_dir = NULL; char *source_dir = NULL;
size_t source_dir_len = 0; size_t source_dir_len = 0;
struct rootdir_subvol *rds; struct rootdir_subvol *rds;
bool has_default_subvol = false;
LIST_HEAD(subvols); LIST_HEAD(subvols);
cpu_detect_flags(); cpu_detect_flags();
@ -1215,16 +1256,49 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
break; break;
case 'u': { case 'u': {
struct rootdir_subvol *subvol; struct rootdir_subvol *subvol;
char *colon;
subvol = malloc(sizeof(struct rootdir_subvol)); subvol = calloc(1, sizeof(struct rootdir_subvol));
if (!subvol) { if (!subvol) {
error_msg(ERROR_MSG_MEMORY, NULL); error_msg(ERROR_MSG_MEMORY, NULL);
ret = 1; ret = 1;
goto error; goto error;
} }
subvol->dir = strdup(optarg); colon = strstr(optarg, ":");
subvol->full_path = NULL;
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;
}
} else {
subvol->dir = strdup(optarg);
}
if (subvol->is_default) {
if (has_default_subvol) {
error("subvol default flag can only be specified once");
ret = 1;
goto error;
}
has_default_subvol = true;
}
list_add_tail(&subvol->list, &subvols); list_add_tail(&subvol->list, &subvols);
break; break;

View File

@ -99,6 +99,7 @@ static u64 g_hardlink_count;
static struct btrfs_trans_handle *g_trans = NULL; static struct btrfs_trans_handle *g_trans = NULL;
static struct list_head *g_subvols; static struct list_head *g_subvols;
static u64 next_subvol_id = BTRFS_FIRST_FREE_OBJECTID; static u64 next_subvol_id = BTRFS_FIRST_FREE_OBJECTID;
static u64 default_subvol_id;
static inline struct inode_entry *rootdir_path_last(struct rootdir_path *path) static inline struct inode_entry *rootdir_path_last(struct rootdir_path *path)
{ {
@ -436,6 +437,9 @@ static int ftw_add_subvol(const char *full_path, const struct stat *st,
return ret; return ret;
} }
if (subvol->is_default)
default_subvol_id = subvol_id;
key.objectid = subvol_id; key.objectid = subvol_id;
key.type = BTRFS_ROOT_ITEM_KEY; key.type = BTRFS_ROOT_ITEM_KEY;
key.offset = (u64)-1; key.offset = (u64)-1;
@ -701,6 +705,47 @@ static int ftw_add_inode(const char *full_path, const struct stat *st,
return 0; return 0;
}; };
static int set_default_subvolume(struct btrfs_trans_handle *trans)
{
struct btrfs_path path = { 0 };
struct btrfs_dir_item *di;
struct btrfs_key location;
struct extent_buffer *leaf;
struct btrfs_disk_key disk_key;
u64 features;
di = btrfs_lookup_dir_item(trans, trans->fs_info->tree_root, &path,
btrfs_super_root_dir(trans->fs_info->super_copy),
"default", 7, 1);
if (IS_ERR_OR_NULL(di)) {
btrfs_release_path(&path);
if (di)
return PTR_ERR(di);
else
return -ENOENT;
}
leaf = path.nodes[0];
location.objectid = default_subvol_id;
location.type = BTRFS_ROOT_ITEM_KEY;
location.offset = 0;
btrfs_cpu_key_to_disk(&disk_key, &location);
btrfs_set_dir_item_key(leaf, di, &disk_key);
btrfs_mark_buffer_dirty(leaf);
btrfs_release_path(&path);
features = btrfs_super_incompat_flags(trans->fs_info->super_copy);
features |= BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL;
btrfs_set_super_incompat_flags(trans->fs_info->super_copy, features);
return 0;
}
int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir, int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir,
struct btrfs_root *root, struct list_head *subvols) struct btrfs_root *root, struct list_head *subvols)
{ {
@ -732,6 +777,14 @@ int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir
while (current_path.level > 0) while (current_path.level > 0)
rootdir_path_pop(&current_path); rootdir_path_pop(&current_path);
if (default_subvol_id != 0) {
ret = set_default_subvolume(trans);
if (ret < 0) {
error("error setting default subvolume: %d", ret);
return ret;
}
}
return 0; return 0;
} }

View File

@ -32,6 +32,7 @@ struct rootdir_subvol {
struct list_head list; struct list_head list;
char *dir; char *dir;
char *full_path; char *full_path;
bool is_default;
}; };
int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir, int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir,