btrfs-progs: subvolume create: accept multiple arguments
This patch would make "btrfs subvolume create" to accept multiple arguments, just like "mkdir". The existing options like "-i <qgroupid>" and "-p" would all be applied to all subvolume(s). If one destination failed, the command would return 1, while still retry the remaining destinations. Issue: #695 Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
943a69ef2d
commit
5aa959fb34
|
@ -49,11 +49,18 @@ do not affect the files in the original subvolume.
|
||||||
SUBCOMMAND
|
SUBCOMMAND
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
create [options] [<dest>/]<name>
|
create [options] [<dest>/]<name> [[<dest2>/]<name2> ...]
|
||||||
Create a subvolume *name* in *dest*.
|
Create subvolume(s) at the destination(s).
|
||||||
|
|
||||||
If *dest* is not given, subvolume *name* will be created in the current
|
If *dest* part of the path is not given, subvolume *name* will be
|
||||||
directory.
|
created in the current directory.
|
||||||
|
|
||||||
|
If multiple desinations are given, then given options are applied to all
|
||||||
|
subvolumes.
|
||||||
|
|
||||||
|
If failure happens for any of the destinations, the command would
|
||||||
|
still retry the remaining destinations, but would return 1 to indicate
|
||||||
|
the failure (similar to what :command:`mkdir` would do.
|
||||||
|
|
||||||
``Options``
|
``Options``
|
||||||
|
|
||||||
|
|
152
cmds/subvolume.c
152
cmds/subvolume.c
|
@ -114,81 +114,38 @@ static const char * const subvolume_cmd_group_usage[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char * const cmd_subvolume_create_usage[] = {
|
static const char * const cmd_subvolume_create_usage[] = {
|
||||||
"btrfs subvolume create [options] [<dest>/]<name>",
|
"btrfs subvolume create [options] [<dest>/]<name> [[<dest2>/]<name2> ...]",
|
||||||
"Create a subvolume",
|
"Create subvolume(s)",
|
||||||
"Create a subvolume <name> in <dest>. If <dest> is not given",
|
"Create subvolume(s) at specified destination. If <dest> is not given",
|
||||||
"subvolume <name> will be created in the current directory.",
|
"subvolume <name> will be created in the current directory. Options apply",
|
||||||
|
"to all created subvolumes.",
|
||||||
"",
|
"",
|
||||||
OPTLINE("-i <qgroupid>", "add the newly created subvolume to a qgroup. This option can be given multiple times."),
|
OPTLINE("-i <qgroupid>", "add the newly created subvolume(s) to a qgroup. This option can be given multiple times."),
|
||||||
OPTLINE("-p|--parents", "create any missing parent directories for each argument (like mkdir -p)"),
|
OPTLINE("-p|--parents", "create any missing parent directories for each argument (like mkdir -p)"),
|
||||||
HELPINFO_INSERT_GLOBALS,
|
HELPINFO_INSERT_GLOBALS,
|
||||||
HELPINFO_INSERT_QUIET,
|
HELPINFO_INSERT_QUIET,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static int cmd_subvolume_create(const struct cmd_struct *cmd, int argc, char **argv)
|
static int create_one_subvolume(const char *dst, struct btrfs_qgroup_inherit *inherit,
|
||||||
|
bool create_parents)
|
||||||
{
|
{
|
||||||
int retval, res, len;
|
int ret;
|
||||||
|
int len;
|
||||||
int fddst = -1;
|
int fddst = -1;
|
||||||
char *dupname = NULL;
|
char *dupname = NULL;
|
||||||
char *dupdir = NULL;
|
char *dupdir = NULL;
|
||||||
char *newname;
|
char *newname;
|
||||||
char *dstdir;
|
char *dstdir;
|
||||||
char *dst;
|
|
||||||
struct btrfs_qgroup_inherit *inherit = NULL;
|
|
||||||
DIR *dirstream = NULL;
|
DIR *dirstream = NULL;
|
||||||
bool create_parents = false;
|
|
||||||
|
|
||||||
optind = 0;
|
ret = path_is_dir(dst);
|
||||||
while (1) {
|
if (ret < 0 && ret != -ENOENT) {
|
||||||
int c;
|
errno = -ret;
|
||||||
static const struct option long_options[] = {
|
|
||||||
{ "parents", no_argument, NULL, 'p' },
|
|
||||||
{ NULL, 0, NULL, 0 }
|
|
||||||
};
|
|
||||||
|
|
||||||
c = getopt_long(argc, argv, "i:p", long_options, NULL);
|
|
||||||
if (c < 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
switch (c) {
|
|
||||||
case 'c':
|
|
||||||
res = btrfs_qgroup_inherit_add_copy(&inherit, optarg, 0);
|
|
||||||
if (res) {
|
|
||||||
retval = res;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
res = btrfs_qgroup_inherit_add_group(&inherit, optarg);
|
|
||||||
if (res) {
|
|
||||||
retval = res;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
create_parents = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage_unknown_option(cmd, argv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_argc_exact(argc - optind, 1)) {
|
|
||||||
retval = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
dst = argv[optind];
|
|
||||||
|
|
||||||
retval = 1; /* failure */
|
|
||||||
res = path_is_dir(dst);
|
|
||||||
if (res < 0 && res != -ENOENT) {
|
|
||||||
errno = -res;
|
|
||||||
error("cannot access %s: %m", dst);
|
error("cannot access %s: %m", dst);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (res >= 0) {
|
if (ret >= 0) {
|
||||||
error("target path already exists: %s", dst);
|
error("target path already exists: %s", dst);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -230,15 +187,15 @@ static int cmd_subvolume_create(const struct cmd_struct *cmd, int argc, char **a
|
||||||
token = strtok(dstdir_dup, "/");
|
token = strtok(dstdir_dup, "/");
|
||||||
while (token) {
|
while (token) {
|
||||||
strcat(p, token);
|
strcat(p, token);
|
||||||
res = path_is_dir(p);
|
ret = path_is_dir(p);
|
||||||
if (res == -ENOENT) {
|
if (ret == -ENOENT) {
|
||||||
res = mkdir(p, 0777);
|
ret = mkdir(p, 0777);
|
||||||
if (res < 0) {
|
if (ret < 0) {
|
||||||
error("failed to create directory %s: %m", p);
|
error("failed to create directory %s: %m", p);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
} else if (res <= 0) {
|
} else if (ret <= 0) {
|
||||||
errno = res;
|
errno = ret ;
|
||||||
error("failed to check directory %s before creation: %m", p);
|
error("failed to check directory %s before creation: %m", p);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -261,28 +218,87 @@ static int cmd_subvolume_create(const struct cmd_struct *cmd, int argc, char **a
|
||||||
args.size = btrfs_qgroup_inherit_size(inherit);
|
args.size = btrfs_qgroup_inherit_size(inherit);
|
||||||
args.qgroup_inherit = inherit;
|
args.qgroup_inherit = inherit;
|
||||||
|
|
||||||
res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
|
ret = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
|
||||||
} else {
|
} else {
|
||||||
struct btrfs_ioctl_vol_args args;
|
struct btrfs_ioctl_vol_args args;
|
||||||
|
|
||||||
memset(&args, 0, sizeof(args));
|
memset(&args, 0, sizeof(args));
|
||||||
strncpy_null(args.name, newname);
|
strncpy_null(args.name, newname);
|
||||||
|
|
||||||
res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
|
ret = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res < 0) {
|
if (ret < 0) {
|
||||||
error("cannot create subvolume: %m");
|
error("cannot create subvolume: %m");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = 0; /* success */
|
|
||||||
out:
|
out:
|
||||||
close_file_or_dir(fddst, dirstream);
|
close_file_or_dir(fddst, dirstream);
|
||||||
free(inherit);
|
|
||||||
free(dupname);
|
free(dupname);
|
||||||
free(dupdir);
|
free(dupdir);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
static int cmd_subvolume_create(const struct cmd_struct *cmd, int argc, char **argv)
|
||||||
|
{
|
||||||
|
int retval, ret;
|
||||||
|
struct btrfs_qgroup_inherit *inherit = NULL;
|
||||||
|
bool has_error = false;
|
||||||
|
bool create_parents = false;
|
||||||
|
|
||||||
|
optind = 0;
|
||||||
|
while (1) {
|
||||||
|
int c;
|
||||||
|
static const struct option long_options[] = {
|
||||||
|
{ "parents", no_argument, NULL, 'p' },
|
||||||
|
{ NULL, 0, NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
c = getopt_long(argc, argv, "i:p", long_options, NULL);
|
||||||
|
if (c < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
ret = btrfs_qgroup_inherit_add_copy(&inherit, optarg, 0);
|
||||||
|
if (ret) {
|
||||||
|
retval = ret;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
ret = btrfs_qgroup_inherit_add_group(&inherit, optarg);
|
||||||
|
if (ret) {
|
||||||
|
retval = ret;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
create_parents = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
usage_unknown_option(cmd, argv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_argc_min(argc - optind, 1)) {
|
||||||
|
retval = 1;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = 1;
|
||||||
|
|
||||||
|
for (int i = optind; i < argc; i++) {
|
||||||
|
ret = create_one_subvolume(argv[i], inherit, create_parents);
|
||||||
|
if (ret < 0)
|
||||||
|
has_error = true;
|
||||||
|
}
|
||||||
|
if (!has_error)
|
||||||
|
retval = 0;
|
||||||
|
out:
|
||||||
|
free(inherit);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
static DEFINE_SIMPLE_COMMAND(subvolume_create, "create");
|
static DEFINE_SIMPLE_COMMAND(subvolume_create, "create");
|
||||||
|
|
Loading…
Reference in New Issue