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
|
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.
|
||||||
|
|
||||||
|
|
82
mkfs/main.c
82
mkfs/main.c
|
@ -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;
|
||||||
|
|
|
@ -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(¤t_path);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue