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:
parent
9b7859bacc
commit
397715149d
|
@ -155,11 +155,15 @@ 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 <subdir>
|
||||
-u|--subvol <subdir>:<flags>
|
||||
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:
|
||||
|
||||
* *default*: create as default subvolume (this can only be specified once)
|
||||
|
||||
--shrink
|
||||
Shrink the filesystem to its minimal size, only works with *--rootdir* option.
|
||||
|
||||
|
|
82
mkfs/main.c
82
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", "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("-K|--nodiscard", "do not perform whole device TRIM"),
|
||||
OPTLINE("-f|--force", "force overwrite of existing filesystem"),
|
||||
|
@ -1015,6 +1015,46 @@ 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 (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;
|
||||
|
@ -1058,6 +1098,7 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
|
|||
char *source_dir = NULL;
|
||||
size_t source_dir_len = 0;
|
||||
struct rootdir_subvol *rds;
|
||||
bool has_default_subvol = false;
|
||||
LIST_HEAD(subvols);
|
||||
|
||||
cpu_detect_flags();
|
||||
|
@ -1215,16 +1256,49 @@ int BOX_MAIN(mkfs)(int argc, char **argv)
|
|||
break;
|
||||
case 'u': {
|
||||
struct rootdir_subvol *subvol;
|
||||
char *colon;
|
||||
|
||||
subvol = malloc(sizeof(struct rootdir_subvol));
|
||||
subvol = calloc(1, sizeof(struct rootdir_subvol));
|
||||
if (!subvol) {
|
||||
error_msg(ERROR_MSG_MEMORY, NULL);
|
||||
ret = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
subvol->dir = strdup(optarg);
|
||||
subvol->full_path = NULL;
|
||||
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;
|
||||
}
|
||||
} 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);
|
||||
break;
|
||||
|
|
|
@ -99,6 +99,7 @@ static u64 g_hardlink_count;
|
|||
static struct btrfs_trans_handle *g_trans = NULL;
|
||||
static struct list_head *g_subvols;
|
||||
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)
|
||||
{
|
||||
|
@ -436,6 +437,9 @@ static int ftw_add_subvol(const char *full_path, const struct stat *st,
|
|||
return ret;
|
||||
}
|
||||
|
||||
if (subvol->is_default)
|
||||
default_subvol_id = subvol_id;
|
||||
|
||||
key.objectid = subvol_id;
|
||||
key.type = BTRFS_ROOT_ITEM_KEY;
|
||||
key.offset = (u64)-1;
|
||||
|
@ -701,6 +705,47 @@ static int ftw_add_inode(const char *full_path, const struct stat *st,
|
|||
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,
|
||||
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)
|
||||
rootdir_path_pop(¤t_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;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ struct rootdir_subvol {
|
|||
struct list_head list;
|
||||
char *dir;
|
||||
char *full_path;
|
||||
bool is_default;
|
||||
};
|
||||
|
||||
int btrfs_mkfs_fill_dir(struct btrfs_trans_handle *trans, const char *source_dir,
|
||||
|
|
Loading…
Reference in New Issue