2012-02-03 20:00:17 +01:00
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public
|
|
|
|
* License v2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public
|
|
|
|
* License along with this program; if not, write to the
|
|
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 021110-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/stat.h>
|
2016-01-13 18:44:59 +01:00
|
|
|
#include <sys/vfs.h>
|
2012-02-03 20:00:17 +01:00
|
|
|
#include <libgen.h>
|
|
|
|
#include <limits.h>
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
#include <getopt.h>
|
2013-02-01 08:56:28 +01:00
|
|
|
#include <uuid/uuid.h>
|
2016-01-13 18:44:59 +01:00
|
|
|
#include <linux/magic.h>
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
#include "kerncompat.h"
|
|
|
|
#include "ioctl.h"
|
2012-08-07 12:37:54 +02:00
|
|
|
#include "qgroup.h"
|
2012-02-03 20:00:17 +01:00
|
|
|
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
#include "ctree.h"
|
2012-02-03 20:00:17 +01:00
|
|
|
#include "commands.h"
|
2013-01-25 20:27:47 +01:00
|
|
|
#include "utils.h"
|
2012-08-08 10:54:48 +02:00
|
|
|
#include "btrfs-list.h"
|
2013-01-28 06:22:30 +01:00
|
|
|
#include "utils.h"
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2015-04-08 18:15:57 +02:00
|
|
|
static int is_subvolume_cleaned(int fd, u64 subvolid)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct btrfs_ioctl_search_args args;
|
|
|
|
struct btrfs_ioctl_search_key *sk = &args.key;
|
|
|
|
|
|
|
|
sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
|
|
|
|
sk->min_objectid = subvolid;
|
|
|
|
sk->max_objectid = subvolid;
|
|
|
|
sk->min_type = BTRFS_ROOT_ITEM_KEY;
|
|
|
|
sk->max_type = BTRFS_ROOT_ITEM_KEY;
|
|
|
|
sk->min_offset = 0;
|
|
|
|
sk->max_offset = (u64)-1;
|
|
|
|
sk->min_transid = 0;
|
|
|
|
sk->max_transid = (u64)-1;
|
|
|
|
sk->nr_items = 1;
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
|
|
|
|
if (ret < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (sk->nr_items == 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int wait_for_subvolume_cleaning(int fd, int count, u64 *ids,
|
|
|
|
int sleep_interval)
|
|
|
|
{
|
2015-08-26 16:03:38 +02:00
|
|
|
int ret;
|
2015-04-08 18:15:57 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
while (1) {
|
2015-08-26 16:03:39 +02:00
|
|
|
int clean = 1;
|
|
|
|
|
2015-04-08 18:15:57 +02:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
if (!ids[i])
|
|
|
|
continue;
|
|
|
|
ret = is_subvolume_cleaned(fd, ids[i]);
|
|
|
|
if (ret < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error(
|
|
|
|
"cannot read status of dead subvolume %llu: %s",
|
|
|
|
(unsigned long long)ids[i], strerror(-ret));
|
2015-08-26 16:03:39 +02:00
|
|
|
return ret;
|
2015-04-08 18:15:57 +02:00
|
|
|
}
|
|
|
|
if (ret) {
|
|
|
|
printf("Subvolume id %llu is gone\n", ids[i]);
|
|
|
|
ids[i] = 0;
|
2015-08-26 16:03:39 +02:00
|
|
|
} else {
|
|
|
|
clean = 0;
|
2015-04-08 18:15:57 +02:00
|
|
|
}
|
|
|
|
}
|
2015-10-06 11:32:16 +02:00
|
|
|
if (clean)
|
2015-04-08 18:15:57 +02:00
|
|
|
break;
|
|
|
|
sleep(sleep_interval);
|
|
|
|
}
|
2015-08-26 16:03:38 +02:00
|
|
|
|
2015-08-26 16:03:39 +02:00
|
|
|
return 0;
|
2015-04-08 18:15:57 +02:00
|
|
|
}
|
|
|
|
|
2012-02-08 16:45:54 +01:00
|
|
|
static const char * const subvolume_cmd_group_usage[] = {
|
|
|
|
"btrfs subvolume <command> <args>",
|
|
|
|
NULL
|
|
|
|
};
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
static const char * const cmd_subvol_create_usage[] = {
|
2013-03-27 14:54:12 +01:00
|
|
|
"btrfs subvolume create [-i <qgroupid>] [<dest>/]<name>",
|
2012-02-03 20:00:17 +01:00
|
|
|
"Create a subvolume",
|
|
|
|
"Create a subvolume <name> in <dest>. If <dest> is not given",
|
|
|
|
"subvolume <name> will be created in the current directory.",
|
2013-03-27 14:54:12 +01:00
|
|
|
"",
|
|
|
|
"-i <qgroupid> add the newly created subvolume to a qgroup. This",
|
|
|
|
" option can be given multiple times.",
|
2012-02-03 20:00:17 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_subvol_create(int argc, char **argv)
|
2012-02-03 20:00:17 +01:00
|
|
|
{
|
2013-03-04 23:40:00 +01:00
|
|
|
int retval, res, len;
|
|
|
|
int fddst = -1;
|
2013-09-05 10:00:43 +02:00
|
|
|
char *dupname = NULL;
|
|
|
|
char *dupdir = NULL;
|
2012-02-03 20:00:17 +01:00
|
|
|
char *newname;
|
|
|
|
char *dstdir;
|
2012-02-03 20:00:17 +01:00
|
|
|
char *dst;
|
2012-08-07 12:37:54 +02:00
|
|
|
struct btrfs_qgroup_inherit *inherit = NULL;
|
2013-07-15 13:36:50 +02:00
|
|
|
DIR *dirstream = NULL;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2012-08-07 12:37:54 +02:00
|
|
|
optind = 1;
|
|
|
|
while (1) {
|
2014-11-27 19:26:35 +01:00
|
|
|
int c = getopt(argc, argv, "c:i:v");
|
2012-08-07 12:37:54 +02:00
|
|
|
if (c < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case 'c':
|
|
|
|
res = qgroup_inherit_add_copy(&inherit, optarg, 0);
|
2013-07-03 18:07:38 +02:00
|
|
|
if (res) {
|
|
|
|
retval = res;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-08-07 12:37:54 +02:00
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
res = qgroup_inherit_add_group(&inherit, optarg);
|
2013-07-03 18:07:38 +02:00
|
|
|
if (res) {
|
|
|
|
retval = res;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-08-07 12:37:54 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage(cmd_subvol_create_usage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_argc_exact(argc - optind, 1))
|
2012-02-03 20:00:17 +01:00
|
|
|
usage(cmd_subvol_create_usage);
|
|
|
|
|
2012-08-07 12:37:54 +02:00
|
|
|
dst = argv[optind];
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2013-03-04 23:40:00 +01:00
|
|
|
retval = 1; /* failure */
|
2012-02-03 20:00:17 +01:00
|
|
|
res = test_isdir(dst);
|
2016-01-13 18:04:22 +01:00
|
|
|
if (res < 0 && res != -ENOENT) {
|
|
|
|
error("cannot access %s: %s", dst, strerror(-res));
|
|
|
|
goto out;
|
|
|
|
}
|
2013-03-04 23:39:59 +01:00
|
|
|
if (res >= 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("target path already exists: %s", dst);
|
2013-03-04 23:40:00 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2013-09-05 10:00:43 +02:00
|
|
|
dupname = strdup(dst);
|
|
|
|
newname = basename(dupname);
|
|
|
|
dupdir = strdup(dst);
|
|
|
|
dstdir = dirname(dupdir);
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2014-08-01 04:44:21 +02:00
|
|
|
if (!test_issubvolname(newname)) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("invalid subvolume name: %s", newname);
|
2013-03-04 23:40:00 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
len = strlen(newname);
|
|
|
|
if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("subvolume name too long: %s", newname);
|
2013-03-04 23:40:00 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2015-10-12 15:22:54 +02:00
|
|
|
fddst = btrfs_open_dir(dstdir, &dirstream, 1);
|
|
|
|
if (fddst < 0)
|
2013-03-04 23:40:00 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
printf("Create subvolume '%s/%s'\n", dstdir, newname);
|
2012-08-07 12:37:54 +02:00
|
|
|
if (inherit) {
|
|
|
|
struct btrfs_ioctl_vol_args_v2 args;
|
|
|
|
|
|
|
|
memset(&args, 0, sizeof(args));
|
2013-01-25 20:27:47 +01:00
|
|
|
strncpy_null(args.name, newname);
|
2012-08-07 12:37:54 +02:00
|
|
|
args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
|
|
|
|
args.size = qgroup_inherit_size(inherit);
|
|
|
|
args.qgroup_inherit = inherit;
|
|
|
|
|
|
|
|
res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
|
|
|
|
} else {
|
|
|
|
struct btrfs_ioctl_vol_args args;
|
|
|
|
|
|
|
|
memset(&args, 0, sizeof(args));
|
2013-01-25 20:27:47 +01:00
|
|
|
strncpy_null(args.name, newname);
|
2012-08-07 12:37:54 +02:00
|
|
|
|
|
|
|
res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
|
|
|
|
}
|
|
|
|
|
2013-03-04 23:39:59 +01:00
|
|
|
if (res < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("cannot create subvolume: %s", strerror(errno));
|
2013-03-04 23:40:00 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2013-03-04 23:40:00 +01:00
|
|
|
retval = 0; /* success */
|
|
|
|
out:
|
2013-07-15 13:36:50 +02:00
|
|
|
close_file_or_dir(fddst, dirstream);
|
2013-03-04 23:40:00 +01:00
|
|
|
free(inherit);
|
2013-09-05 10:00:43 +02:00
|
|
|
free(dupname);
|
|
|
|
free(dupdir);
|
2013-03-04 23:40:00 +01:00
|
|
|
|
|
|
|
return retval;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2013-12-12 19:11:26 +01:00
|
|
|
static int wait_for_commit(int fd)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_START_SYNC, NULL);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
return ioctl(fd, BTRFS_IOC_WAIT_SYNC, NULL);
|
|
|
|
}
|
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
static const char * const cmd_subvol_delete_usage[] = {
|
2013-12-12 19:11:26 +01:00
|
|
|
"btrfs subvolume delete [options] <subvolume> [<subvolume>...]",
|
2012-09-21 08:54:08 +02:00
|
|
|
"Delete subvolume(s)",
|
2013-12-12 19:11:26 +01:00
|
|
|
"Delete subvolumes from the filesystem. The corresponding directory",
|
|
|
|
"is removed instantly but the data blocks are removed later.",
|
|
|
|
"The deletion does not involve full commit by default due to",
|
|
|
|
"performance reasons (as a consequence, the subvolume may appear again",
|
|
|
|
"after a crash). Use one of the --commit options to wait until the",
|
|
|
|
"operation is safely stored on the media.",
|
|
|
|
"",
|
|
|
|
"-c|--commit-after wait for transaction commit at the end of the operation",
|
|
|
|
"-C|--commit-each wait for transaction commit after deleting each subvolume",
|
2012-02-03 20:00:17 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_subvol_delete(int argc, char **argv)
|
2012-02-03 20:00:17 +01:00
|
|
|
{
|
2016-01-12 11:20:18 +01:00
|
|
|
int res, ret = 0;
|
2013-12-12 19:11:26 +01:00
|
|
|
int cnt;
|
|
|
|
int fd = -1;
|
2012-02-03 20:00:17 +01:00
|
|
|
struct btrfs_ioctl_vol_args args;
|
|
|
|
char *dname, *vname, *cpath;
|
2013-09-05 10:00:43 +02:00
|
|
|
char *dupdname = NULL;
|
|
|
|
char *dupvname = NULL;
|
2012-02-03 20:00:17 +01:00
|
|
|
char *path;
|
2013-07-15 13:36:50 +02:00
|
|
|
DIR *dirstream = NULL;
|
2014-11-27 19:26:35 +01:00
|
|
|
int verbose = 0;
|
2014-11-27 19:34:22 +01:00
|
|
|
int commit_mode = 0;
|
2013-12-12 19:11:26 +01:00
|
|
|
|
|
|
|
optind = 1;
|
|
|
|
while (1) {
|
|
|
|
int c;
|
2015-01-19 13:44:49 +01:00
|
|
|
static const struct option long_options[] = {
|
2015-01-19 13:30:06 +01:00
|
|
|
{"commit-after", no_argument, NULL, 'c'}, /* commit mode 1 */
|
|
|
|
{"commit-each", no_argument, NULL, 'C'}, /* commit mode 2 */
|
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
2013-12-12 19:11:26 +01:00
|
|
|
|
|
|
|
c = getopt_long(argc, argv, "cC", long_options, NULL);
|
|
|
|
if (c < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch(c) {
|
|
|
|
case 'c':
|
2014-11-27 19:34:22 +01:00
|
|
|
commit_mode = 1;
|
2013-12-12 19:11:26 +01:00
|
|
|
break;
|
|
|
|
case 'C':
|
2014-11-27 19:34:22 +01:00
|
|
|
commit_mode = 2;
|
2013-12-12 19:11:26 +01:00
|
|
|
break;
|
2014-11-27 19:26:35 +01:00
|
|
|
case 'v':
|
|
|
|
verbose++;
|
|
|
|
break;
|
2013-12-12 19:11:26 +01:00
|
|
|
default:
|
|
|
|
usage(cmd_subvol_delete_usage);
|
|
|
|
}
|
|
|
|
}
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2013-12-12 19:11:26 +01:00
|
|
|
if (check_argc_min(argc - optind, 1))
|
2012-02-03 20:00:17 +01:00
|
|
|
usage(cmd_subvol_delete_usage);
|
|
|
|
|
2014-11-27 19:26:35 +01:00
|
|
|
if (verbose > 0) {
|
|
|
|
printf("Transaction commit: %s\n",
|
2014-11-27 19:34:22 +01:00
|
|
|
!commit_mode ? "none (default)" :
|
|
|
|
commit_mode == 1 ? "at the end" : "after each");
|
2014-11-27 19:26:35 +01:00
|
|
|
}
|
2013-12-12 19:11:26 +01:00
|
|
|
|
|
|
|
cnt = optind;
|
|
|
|
|
2012-09-21 08:54:08 +02:00
|
|
|
again:
|
|
|
|
path = argv[cnt];
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
res = test_issubvolume(path);
|
2013-09-04 17:22:19 +02:00
|
|
|
if (res < 0) {
|
2016-01-13 18:14:32 +01:00
|
|
|
error("cannot access subvolume %s: %s", path, strerror(-res));
|
2013-09-04 17:22:19 +02:00
|
|
|
ret = 1;
|
2012-09-21 08:54:08 +02:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
2013-09-04 17:22:19 +02:00
|
|
|
if (!res) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("not a subvolume: %s", path);
|
2013-09-04 17:22:19 +02:00
|
|
|
ret = 1;
|
2012-09-21 08:54:08 +02:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2013-08-15 01:16:45 +02:00
|
|
|
cpath = realpath(path, NULL);
|
2013-10-12 17:47:52 +02:00
|
|
|
if (!cpath) {
|
|
|
|
ret = errno;
|
2016-01-11 11:01:47 +01:00
|
|
|
error("cannot find real path for '%s': %s",
|
2013-10-12 17:47:52 +02:00
|
|
|
path, strerror(errno));
|
|
|
|
goto out;
|
|
|
|
}
|
2013-09-05 10:00:43 +02:00
|
|
|
dupdname = strdup(cpath);
|
|
|
|
dname = dirname(dupdname);
|
|
|
|
dupvname = strdup(cpath);
|
|
|
|
vname = basename(dupvname);
|
2012-02-03 20:00:17 +01:00
|
|
|
free(cpath);
|
|
|
|
|
2015-10-12 15:22:54 +02:00
|
|
|
fd = btrfs_open_dir(dname, &dirstream, 1);
|
2012-02-03 20:00:17 +01:00
|
|
|
if (fd < 0) {
|
2013-09-04 17:22:19 +02:00
|
|
|
ret = 1;
|
2012-09-21 08:54:08 +02:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 19:15:06 +01:00
|
|
|
printf("Delete subvolume (%s): '%s/%s'\n",
|
2014-11-27 19:34:22 +01:00
|
|
|
commit_mode == 2 || (commit_mode == 1 && cnt + 1 == argc)
|
2014-11-27 19:15:06 +01:00
|
|
|
? "commit" : "no-commit", dname, vname);
|
2015-06-12 14:36:51 +02:00
|
|
|
memset(&args, 0, sizeof(args));
|
2013-01-25 20:27:47 +01:00
|
|
|
strncpy_null(args.name, vname);
|
2012-02-03 20:00:17 +01:00
|
|
|
res = ioctl(fd, BTRFS_IOC_SNAP_DESTROY, &args);
|
|
|
|
if(res < 0 ){
|
2016-01-12 11:20:18 +01:00
|
|
|
error("cannot delete '%s/%s': %s", dname, vname,
|
|
|
|
strerror(errno));
|
2013-09-04 17:22:19 +02:00
|
|
|
ret = 1;
|
2012-09-21 08:54:08 +02:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 19:34:22 +01:00
|
|
|
if (commit_mode == 1) {
|
2013-12-12 19:11:26 +01:00
|
|
|
res = wait_for_commit(fd);
|
|
|
|
if (res < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("unable to wait for commit after '%s': %s",
|
2013-12-12 19:11:26 +01:00
|
|
|
path, strerror(errno));
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-21 08:54:08 +02:00
|
|
|
out:
|
2013-09-05 10:00:43 +02:00
|
|
|
free(dupdname);
|
|
|
|
free(dupvname);
|
2013-11-07 00:15:40 +01:00
|
|
|
dupdname = NULL;
|
|
|
|
dupvname = NULL;
|
2012-09-21 08:54:08 +02:00
|
|
|
cnt++;
|
2013-12-12 19:11:26 +01:00
|
|
|
if (cnt < argc) {
|
|
|
|
close_file_or_dir(fd, dirstream);
|
2014-01-20 09:44:21 +01:00
|
|
|
/* avoid double free */
|
|
|
|
fd = -1;
|
|
|
|
dirstream = NULL;
|
2012-09-21 08:54:08 +02:00
|
|
|
goto again;
|
2013-12-12 19:11:26 +01:00
|
|
|
}
|
|
|
|
|
2014-11-27 19:34:22 +01:00
|
|
|
if (commit_mode == 2 && fd != -1) {
|
2013-12-12 19:11:26 +01:00
|
|
|
res = wait_for_commit(fd);
|
|
|
|
if (res < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("unable to do final sync after deletion: %s",
|
2013-12-12 19:11:26 +01:00
|
|
|
strerror(errno));
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
close_file_or_dir(fd, dirstream);
|
2012-09-21 08:54:08 +02:00
|
|
|
|
|
|
|
return ret;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2012-10-09 18:27:52 +02:00
|
|
|
/*
|
|
|
|
* Naming of options:
|
|
|
|
* - uppercase for filters and sort options
|
|
|
|
* - lowercase for enabling specific items in the output
|
|
|
|
*/
|
2012-02-03 20:00:17 +01:00
|
|
|
static const char * const cmd_subvol_list_usage[] = {
|
2013-07-23 04:43:21 +02:00
|
|
|
"btrfs subvolume list [options] [-G [+|-]value] [-C [+|-]value] "
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
"[--sort=gen,ogen,rootid,path] <path>",
|
2012-02-03 20:00:17 +01:00
|
|
|
"List subvolumes (and snapshots)",
|
|
|
|
"",
|
2012-08-03 11:48:56 +02:00
|
|
|
"-p print parent ID",
|
2012-12-11 15:24:58 +01:00
|
|
|
"-a print all the subvolumes in the filesystem and",
|
|
|
|
" distinguish absolute and relative path with respect",
|
|
|
|
" to the given <path>",
|
2013-02-01 17:19:49 +01:00
|
|
|
"-c print the ogeneration of the subvolume",
|
2012-10-09 18:27:53 +02:00
|
|
|
"-g print the generation of the subvolume",
|
2014-08-12 22:22:53 +02:00
|
|
|
"-o print only subvolumes below specified path",
|
2012-09-01 10:30:35 +02:00
|
|
|
"-u print the uuid of subvolumes (and snapshots)",
|
2013-02-01 08:56:21 +01:00
|
|
|
"-q print the parent uuid of the snapshots",
|
2014-08-13 23:18:17 +02:00
|
|
|
"-R print the uuid of the received snapshots",
|
2012-09-20 13:04:22 +02:00
|
|
|
"-t print the result as a table",
|
2012-09-27 19:33:01 +02:00
|
|
|
"-s list snapshots only in the filesystem",
|
2012-09-18 11:14:37 +02:00
|
|
|
"-r list readonly subvolumes (including snapshots)",
|
2013-10-23 19:00:09 +02:00
|
|
|
"-d list deleted subvolumes that are not yet cleaned",
|
2012-10-09 18:27:52 +02:00
|
|
|
"-G [+|-]value",
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
" filter the subvolumes by generation",
|
|
|
|
" (+value: >= value; -value: <= value; value: = value)",
|
2012-10-09 18:27:52 +02:00
|
|
|
"-C [+|-]value",
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
" filter the subvolumes by ogeneration",
|
|
|
|
" (+value: >= value; -value: <= value; value: = value)",
|
|
|
|
"--sort=gen,ogen,rootid,path",
|
|
|
|
" list the subvolume in order of gen, ogen, rootid or path",
|
|
|
|
" you also can add '+' or '-' in front of each items.",
|
|
|
|
" (+:ascending, -:descending, ascending default)",
|
|
|
|
NULL,
|
2012-02-03 20:00:17 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_subvol_list(int argc, char **argv)
|
2012-02-03 20:00:17 +01:00
|
|
|
{
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
struct btrfs_list_filter_set *filter_set;
|
|
|
|
struct btrfs_list_comparer_set *comparer_set;
|
2012-09-18 11:14:37 +02:00
|
|
|
u64 flags = 0;
|
2013-02-01 08:56:31 +01:00
|
|
|
int fd = -1;
|
2012-09-27 19:04:20 +02:00
|
|
|
u64 top_id;
|
2013-02-01 08:56:31 +01:00
|
|
|
int ret = -1, uerr = 0;
|
2012-02-03 20:00:17 +01:00
|
|
|
char *subvol;
|
2012-09-20 13:04:22 +02:00
|
|
|
int is_tab_result = 0;
|
2012-09-27 19:25:00 +02:00
|
|
|
int is_list_all = 0;
|
2012-12-11 15:25:00 +01:00
|
|
|
int is_only_in_path = 0;
|
2013-07-15 13:36:50 +02:00
|
|
|
DIR *dirstream = NULL;
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
|
|
|
|
filter_set = btrfs_list_alloc_filter_set();
|
|
|
|
comparer_set = btrfs_list_alloc_comparer_set();
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
optind = 1;
|
2012-02-03 20:00:17 +01:00
|
|
|
while(1) {
|
2015-01-19 13:30:06 +01:00
|
|
|
int c;
|
2015-01-19 13:44:49 +01:00
|
|
|
static const struct option long_options[] = {
|
2015-04-08 17:39:51 +02:00
|
|
|
{"sort", required_argument, NULL, 'S'},
|
2015-01-19 13:30:06 +01:00
|
|
|
{NULL, 0, NULL, 0}
|
|
|
|
};
|
|
|
|
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
c = getopt_long(argc, argv,
|
2014-08-13 23:18:17 +02:00
|
|
|
"acdgopqsurRG:C:t", long_options, NULL);
|
2012-02-03 20:00:17 +01:00
|
|
|
if (c < 0)
|
|
|
|
break;
|
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
switch(c) {
|
|
|
|
case 'p':
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_PARENT);
|
2012-02-03 20:00:17 +01:00
|
|
|
break;
|
2012-09-27 19:25:00 +02:00
|
|
|
case 'a':
|
|
|
|
is_list_all = 1;
|
|
|
|
break;
|
2013-02-01 17:19:49 +01:00
|
|
|
case 'c':
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
|
|
|
|
break;
|
2013-10-23 19:00:09 +02:00
|
|
|
case 'd':
|
|
|
|
btrfs_list_setup_filter(&filter_set,
|
|
|
|
BTRFS_LIST_FILTER_DELETED,
|
|
|
|
0);
|
|
|
|
break;
|
2012-10-09 18:27:53 +02:00
|
|
|
case 'g':
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
|
|
|
|
break;
|
2012-12-11 15:24:59 +01:00
|
|
|
case 'o':
|
|
|
|
is_only_in_path = 1;
|
|
|
|
break;
|
2012-09-20 13:04:22 +02:00
|
|
|
case 't':
|
|
|
|
is_tab_result = 1;
|
|
|
|
break;
|
2012-08-03 11:48:56 +02:00
|
|
|
case 's':
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
btrfs_list_setup_filter(&filter_set,
|
|
|
|
BTRFS_LIST_FILTER_SNAPSHOT_ONLY,
|
|
|
|
0);
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_OTIME);
|
2012-10-25 10:00:31 +02:00
|
|
|
break;
|
2012-08-14 08:04:06 +02:00
|
|
|
case 'u':
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_UUID);
|
2012-08-14 08:04:06 +02:00
|
|
|
break;
|
2013-02-01 08:56:21 +01:00
|
|
|
case 'q':
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_PUUID);
|
|
|
|
break;
|
2014-08-13 23:18:17 +02:00
|
|
|
case 'R':
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_RUUID);
|
|
|
|
break;
|
2012-09-18 11:14:37 +02:00
|
|
|
case 'r':
|
|
|
|
flags |= BTRFS_ROOT_SUBVOL_RDONLY;
|
|
|
|
break;
|
2012-10-09 18:27:52 +02:00
|
|
|
case 'G':
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
|
|
|
|
ret = btrfs_list_parse_filter_string(optarg,
|
|
|
|
&filter_set,
|
|
|
|
BTRFS_LIST_FILTER_GEN);
|
2013-02-01 08:56:31 +01:00
|
|
|
if (ret) {
|
|
|
|
uerr = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
break;
|
|
|
|
|
2012-10-09 18:27:52 +02:00
|
|
|
case 'C':
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_OGENERATION);
|
|
|
|
ret = btrfs_list_parse_filter_string(optarg,
|
|
|
|
&filter_set,
|
|
|
|
BTRFS_LIST_FILTER_CGEN);
|
2013-02-01 08:56:31 +01:00
|
|
|
if (ret) {
|
|
|
|
uerr = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
ret = btrfs_list_parse_sort_string(optarg,
|
|
|
|
&comparer_set);
|
2013-02-01 08:56:31 +01:00
|
|
|
if (ret) {
|
|
|
|
uerr = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
Btrfs-progs: introduce -g -c --sort options into btrfs subvol list command
This patch introduces '-g' '-c' '--sort' options
The option '-g' can help you filter the subvolumes by the generation, you may
use it just like:
btrfs subvol list -g +/-value <path>
'+' means the generation of the subvolumes should >= the value you specified.
'-' means the generation should <= the value
If you don't input either '+' nor '-', this command will list the subvolumes
that their generation equals to the value.
However if you want to find gengeration between value1 and value2
you may use the above like:
btrfs sub list -g -value1 -g +value2 <path>
The option '-c' can help you filter the subvolumes by the ogeneration, you may
use it just like:
btrfs subvol list -c +/-value <path>
The usage is the same to '-g'
You might want to list subvolumes in order of some items, such as root id, gen
and so on, you can use '--sort'. Now you can sort the subvolumes by root id,
gen, ogen and path.
For example:
If you want to list subvolumes in order of rootid, you can use the option like
that:
btrfs sub list --sort=+/-rooid <path>
Here, '+' means the result is sorted by ascending order. '-' is by descending
order. If you don't specify either '+' nor '-', the result is sorted by
default - ascending order.
If you want to combine sort items, you do it like that:
btrfs sub list --sort=-rootid,+path,ogen,gen <path>
Signed-off-by: Wang Shilong <wangsl-fnst@cn.fujitsu.com>
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-19 11:21:51 +02:00
|
|
|
break;
|
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
default:
|
2013-02-01 08:56:31 +01:00
|
|
|
uerr = 1;
|
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
}
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2012-09-18 11:14:37 +02:00
|
|
|
if (flags)
|
|
|
|
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_FLAGS,
|
|
|
|
flags);
|
|
|
|
|
2013-02-01 08:56:31 +01:00
|
|
|
if (check_argc_exact(argc - optind, 1)) {
|
|
|
|
uerr = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
subvol = argv[optind];
|
2015-10-12 15:22:54 +02:00
|
|
|
fd = btrfs_open_dir(subvol, &dirstream, 1);
|
2012-02-03 20:00:17 +01:00
|
|
|
if (fd < 0) {
|
2013-02-01 08:56:31 +01:00
|
|
|
ret = -1;
|
2016-01-11 11:01:47 +01:00
|
|
|
error("can't access '%s'", subvol);
|
2013-02-01 08:56:31 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
|
2013-02-25 23:54:37 +01:00
|
|
|
ret = btrfs_list_get_path_rootid(fd, &top_id);
|
|
|
|
if (ret) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("can't get rootid for '%s'", subvol);
|
2013-02-25 23:54:37 +01:00
|
|
|
goto out;
|
|
|
|
}
|
2012-12-11 15:24:58 +01:00
|
|
|
|
|
|
|
if (is_list_all)
|
|
|
|
btrfs_list_setup_filter(&filter_set,
|
|
|
|
BTRFS_LIST_FILTER_FULL_PATH,
|
|
|
|
top_id);
|
2012-12-11 15:24:59 +01:00
|
|
|
else if (is_only_in_path)
|
2012-09-27 19:25:00 +02:00
|
|
|
btrfs_list_setup_filter(&filter_set,
|
|
|
|
BTRFS_LIST_FILTER_TOPID_EQUAL,
|
|
|
|
top_id);
|
2012-09-27 19:04:20 +02:00
|
|
|
|
2013-02-01 08:56:26 +01:00
|
|
|
/* by default we shall print the following columns*/
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
|
|
|
|
|
|
|
|
if (is_tab_result)
|
|
|
|
ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
|
|
|
|
BTRFS_LIST_LAYOUT_TABLE,
|
2013-02-01 08:56:28 +01:00
|
|
|
!is_list_all && !is_only_in_path, NULL);
|
2013-02-01 08:56:26 +01:00
|
|
|
else
|
|
|
|
ret = btrfs_list_subvols_print(fd, filter_set, comparer_set,
|
|
|
|
BTRFS_LIST_LAYOUT_DEFAULT,
|
2013-02-01 08:56:28 +01:00
|
|
|
!is_list_all && !is_only_in_path, NULL);
|
2013-02-01 08:56:31 +01:00
|
|
|
|
|
|
|
out:
|
2013-07-15 13:36:50 +02:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2013-02-01 08:56:31 +01:00
|
|
|
if (filter_set)
|
|
|
|
btrfs_list_free_filter_set(filter_set);
|
|
|
|
if (comparer_set)
|
|
|
|
btrfs_list_free_comparer_set(comparer_set);
|
|
|
|
if (uerr)
|
|
|
|
usage(cmd_subvol_list_usage);
|
2013-09-04 17:22:19 +02:00
|
|
|
return !!ret;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2015-07-11 00:05:05 +02:00
|
|
|
static const char * const cmd_subvol_snapshot_usage[] = {
|
2013-07-23 04:43:21 +02:00
|
|
|
"btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
|
2012-02-03 20:00:17 +01:00
|
|
|
"Create a snapshot of the subvolume",
|
|
|
|
"Create a writable/readonly snapshot of the subvolume <source> with",
|
2013-07-23 04:43:21 +02:00
|
|
|
"the name <name> in the <dest> directory. If only <dest> is given,",
|
|
|
|
"the subvolume will be named the basename of <source>.",
|
2012-02-03 20:00:17 +01:00
|
|
|
"",
|
2013-03-27 14:54:12 +01:00
|
|
|
"-r create a readonly snapshot",
|
|
|
|
"-i <qgroupid> add the newly created snapshot to a qgroup. This",
|
|
|
|
" option can be given multiple times.",
|
2012-02-03 20:00:17 +01:00
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2015-07-11 00:05:05 +02:00
|
|
|
static int cmd_subvol_snapshot(int argc, char **argv)
|
2012-02-03 20:00:17 +01:00
|
|
|
{
|
|
|
|
char *subvol, *dst;
|
2013-03-04 23:39:58 +01:00
|
|
|
int res, retval;
|
|
|
|
int fd = -1, fddst = -1;
|
|
|
|
int len, readonly = 0;
|
2013-09-05 10:00:43 +02:00
|
|
|
char *dupname = NULL;
|
|
|
|
char *dupdir = NULL;
|
2012-02-03 20:00:17 +01:00
|
|
|
char *newname;
|
|
|
|
char *dstdir;
|
|
|
|
struct btrfs_ioctl_vol_args_v2 args;
|
2012-08-07 12:37:54 +02:00
|
|
|
struct btrfs_qgroup_inherit *inherit = NULL;
|
2013-07-15 13:36:50 +02:00
|
|
|
DIR *dirstream1 = NULL, *dirstream2 = NULL;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
optind = 1;
|
2012-08-07 12:37:54 +02:00
|
|
|
memset(&args, 0, sizeof(args));
|
2012-02-03 20:00:17 +01:00
|
|
|
while (1) {
|
2012-08-07 12:37:54 +02:00
|
|
|
int c = getopt(argc, argv, "c:i:r");
|
2012-02-03 20:00:17 +01:00
|
|
|
if (c < 0)
|
|
|
|
break;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
switch (c) {
|
2012-08-07 12:37:54 +02:00
|
|
|
case 'c':
|
|
|
|
res = qgroup_inherit_add_copy(&inherit, optarg, 0);
|
2013-07-03 18:07:38 +02:00
|
|
|
if (res) {
|
|
|
|
retval = res;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-08-07 12:37:54 +02:00
|
|
|
break;
|
|
|
|
case 'i':
|
|
|
|
res = qgroup_inherit_add_group(&inherit, optarg);
|
2013-07-03 18:07:38 +02:00
|
|
|
if (res) {
|
|
|
|
retval = res;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-08-07 12:37:54 +02:00
|
|
|
break;
|
2012-02-03 20:00:17 +01:00
|
|
|
case 'r':
|
|
|
|
readonly = 1;
|
|
|
|
break;
|
2012-08-07 12:37:54 +02:00
|
|
|
case 'x':
|
|
|
|
res = qgroup_inherit_add_copy(&inherit, optarg, 1);
|
2013-07-03 18:07:38 +02:00
|
|
|
if (res) {
|
|
|
|
retval = res;
|
|
|
|
goto out;
|
|
|
|
}
|
2012-08-07 12:37:54 +02:00
|
|
|
break;
|
2012-02-03 20:00:17 +01:00
|
|
|
default:
|
2015-07-11 00:05:05 +02:00
|
|
|
usage(cmd_subvol_snapshot_usage);
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
if (check_argc_exact(argc - optind, 2))
|
2015-07-11 00:05:05 +02:00
|
|
|
usage(cmd_subvol_snapshot_usage);
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
subvol = argv[optind];
|
|
|
|
dst = argv[optind + 1];
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2013-03-04 23:39:58 +01:00
|
|
|
retval = 1; /* failure */
|
2012-02-03 20:00:17 +01:00
|
|
|
res = test_issubvolume(subvol);
|
2013-03-04 23:39:57 +01:00
|
|
|
if (res < 0) {
|
2016-01-13 18:14:32 +01:00
|
|
|
error("cannot access subvolume %s: %s", subvol, strerror(-res));
|
2013-03-04 23:39:58 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
2013-03-04 23:39:57 +01:00
|
|
|
if (!res) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("not a subvolume: %s", subvol);
|
2013-03-04 23:39:58 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
res = test_isdir(dst);
|
2016-01-13 18:04:22 +01:00
|
|
|
if (res < 0 && res != -ENOENT) {
|
|
|
|
error("cannot access %s: %s", dst, strerror(-res));
|
|
|
|
goto out;
|
|
|
|
}
|
2013-03-04 23:39:57 +01:00
|
|
|
if (res == 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("'%s' exists and it is not a directory", dst);
|
2013-03-04 23:39:58 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2013-03-04 23:39:57 +01:00
|
|
|
if (res > 0) {
|
2013-09-05 10:00:43 +02:00
|
|
|
dupname = strdup(subvol);
|
|
|
|
newname = basename(dupname);
|
2012-02-03 20:00:17 +01:00
|
|
|
dstdir = dst;
|
2013-03-04 23:39:57 +01:00
|
|
|
} else {
|
2013-09-05 10:00:43 +02:00
|
|
|
dupname = strdup(dst);
|
|
|
|
newname = basename(dupname);
|
|
|
|
dupdir = strdup(dst);
|
|
|
|
dstdir = dirname(dupdir);
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2014-08-01 04:44:21 +02:00
|
|
|
if (!test_issubvolname(newname)) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("invalid snapshot name '%s'", newname);
|
2013-03-04 23:39:58 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
len = strlen(newname);
|
|
|
|
if (len == 0 || len >= BTRFS_VOL_NAME_MAX) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("snapshot name too long '%s'", newname);
|
2013-03-04 23:39:58 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2015-10-12 15:22:54 +02:00
|
|
|
fddst = btrfs_open_dir(dstdir, &dirstream1, 1);
|
|
|
|
if (fddst < 0)
|
2013-03-04 23:39:58 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2015-10-12 15:22:54 +02:00
|
|
|
fd = btrfs_open_dir(subvol, &dirstream2, 1);
|
|
|
|
if (fd < 0)
|
2013-03-04 23:39:58 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
if (readonly) {
|
|
|
|
args.flags |= BTRFS_SUBVOL_RDONLY;
|
|
|
|
printf("Create a readonly snapshot of '%s' in '%s/%s'\n",
|
|
|
|
subvol, dstdir, newname);
|
|
|
|
} else {
|
|
|
|
printf("Create a snapshot of '%s' in '%s/%s'\n",
|
|
|
|
subvol, dstdir, newname);
|
|
|
|
}
|
|
|
|
|
|
|
|
args.fd = fd;
|
2012-08-07 12:37:54 +02:00
|
|
|
if (inherit) {
|
|
|
|
args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
|
|
|
|
args.size = qgroup_inherit_size(inherit);
|
|
|
|
args.qgroup_inherit = inherit;
|
|
|
|
}
|
2013-01-25 20:27:47 +01:00
|
|
|
strncpy_null(args.name, newname);
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2013-03-04 23:39:58 +01:00
|
|
|
res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2013-03-04 23:39:57 +01:00
|
|
|
if (res < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("cannot snapshot '%s': %s", subvol, strerror(errno));
|
2013-03-04 23:39:58 +01:00
|
|
|
goto out;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2013-03-04 23:39:58 +01:00
|
|
|
retval = 0; /* success */
|
|
|
|
|
|
|
|
out:
|
2013-07-15 13:36:50 +02:00
|
|
|
close_file_or_dir(fddst, dirstream1);
|
|
|
|
close_file_or_dir(fd, dirstream2);
|
2013-03-04 23:39:58 +01:00
|
|
|
free(inherit);
|
2013-09-05 10:00:43 +02:00
|
|
|
free(dupname);
|
|
|
|
free(dupdir);
|
2013-03-04 23:39:58 +01:00
|
|
|
|
|
|
|
return retval;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
static const char * const cmd_subvol_get_default_usage[] = {
|
2012-05-11 14:55:17 +02:00
|
|
|
"btrfs subvolume get-default <path>",
|
2012-02-03 20:00:17 +01:00
|
|
|
"Get the default subvolume of a filesystem",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_subvol_get_default(int argc, char **argv)
|
2012-02-03 20:00:17 +01:00
|
|
|
{
|
2013-02-25 23:54:46 +01:00
|
|
|
int fd = -1;
|
2012-02-03 20:00:17 +01:00
|
|
|
int ret;
|
|
|
|
char *subvol;
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
struct btrfs_list_filter_set *filter_set;
|
|
|
|
u64 default_id;
|
2013-07-15 13:36:50 +02:00
|
|
|
DIR *dirstream = NULL;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2016-03-01 16:02:08 +01:00
|
|
|
clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
|
|
|
|
|
2016-03-18 02:26:15 +01:00
|
|
|
if (check_argc_exact(argc - optind, 1))
|
2012-02-03 20:00:17 +01:00
|
|
|
usage(cmd_subvol_get_default_usage);
|
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
subvol = argv[1];
|
2015-10-12 15:22:54 +02:00
|
|
|
fd = btrfs_open_dir(subvol, &dirstream, 1);
|
|
|
|
if (fd < 0)
|
2013-02-25 23:54:46 +01:00
|
|
|
return 1;
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
|
|
|
|
ret = btrfs_list_get_default_subvolume(fd, &default_id);
|
|
|
|
if (ret) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("failed to look up default subvolume: %s",
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
strerror(errno));
|
2013-02-25 23:54:46 +01:00
|
|
|
goto out;
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
}
|
|
|
|
|
2013-02-25 23:54:46 +01:00
|
|
|
ret = 1;
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
if (default_id == 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("'default' dir item not found");
|
2013-02-25 23:54:46 +01:00
|
|
|
goto out;
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* no need to resolve roots if FS_TREE is default */
|
|
|
|
if (default_id == BTRFS_FS_TREE_OBJECTID) {
|
|
|
|
printf("ID 5 (FS_TREE)\n");
|
2013-11-06 11:49:12 +01:00
|
|
|
ret = 0;
|
2013-02-25 23:54:46 +01:00
|
|
|
goto out;
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
filter_set = btrfs_list_alloc_filter_set();
|
|
|
|
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_ROOTID,
|
|
|
|
default_id);
|
|
|
|
|
2013-02-01 08:56:26 +01:00
|
|
|
/* by default we shall print the following columns*/
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_OBJECTID);
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_GENERATION);
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_TOP_LEVEL);
|
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
|
|
|
|
|
|
|
|
ret = btrfs_list_subvols_print(fd, filter_set, NULL,
|
2013-02-01 08:56:28 +01:00
|
|
|
BTRFS_LIST_LAYOUT_DEFAULT, 1, NULL);
|
2013-02-01 08:56:31 +01:00
|
|
|
|
|
|
|
if (filter_set)
|
|
|
|
btrfs_list_free_filter_set(filter_set);
|
2013-02-25 23:54:46 +01:00
|
|
|
out:
|
2013-07-15 13:36:50 +02:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2013-09-04 17:22:19 +02:00
|
|
|
return !!ret;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
static const char * const cmd_subvol_set_default_usage[] = {
|
2012-05-11 14:55:17 +02:00
|
|
|
"btrfs subvolume set-default <subvolid> <path>",
|
2012-02-03 20:00:17 +01:00
|
|
|
"Set the default subvolume of a filesystem",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_subvol_set_default(int argc, char **argv)
|
2012-02-03 20:00:17 +01:00
|
|
|
{
|
|
|
|
int ret=0, fd, e;
|
|
|
|
u64 objectid;
|
2012-02-03 20:00:17 +01:00
|
|
|
char *path;
|
|
|
|
char *subvolid;
|
2013-07-15 13:36:50 +02:00
|
|
|
DIR *dirstream = NULL;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2016-03-01 16:02:08 +01:00
|
|
|
clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
|
|
|
|
|
|
|
|
if (check_argc_exact(argc - optind, 2))
|
2012-02-03 20:00:17 +01:00
|
|
|
usage(cmd_subvol_set_default_usage);
|
|
|
|
|
2016-03-01 16:02:08 +01:00
|
|
|
subvolid = argv[optind];
|
|
|
|
path = argv[optind + 1];
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2014-02-20 02:30:52 +01:00
|
|
|
objectid = arg_strtou64(subvolid);
|
2013-02-25 23:54:49 +01:00
|
|
|
|
2015-10-12 15:22:54 +02:00
|
|
|
fd = btrfs_open_dir(path, &dirstream, 1);
|
|
|
|
if (fd < 0)
|
2013-02-25 23:54:49 +01:00
|
|
|
return 1;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_DEFAULT_SUBVOL, &objectid);
|
|
|
|
e = errno;
|
2013-07-15 13:36:50 +02:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2013-02-25 23:54:49 +01:00
|
|
|
if (ret < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("unable to set a new default subvolume: %s",
|
2012-02-03 20:00:17 +01:00
|
|
|
strerror(e));
|
2013-02-25 23:54:49 +01:00
|
|
|
return 1;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-11 00:05:05 +02:00
|
|
|
static const char * const cmd_subvol_find_new_usage[] = {
|
2012-02-03 20:00:17 +01:00
|
|
|
"btrfs subvolume find-new <path> <lastgen>",
|
|
|
|
"List the recently modified files in a filesystem",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2015-07-11 00:05:05 +02:00
|
|
|
static int cmd_subvol_find_new(int argc, char **argv)
|
2012-02-03 20:00:17 +01:00
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
int ret;
|
|
|
|
char *subvol;
|
|
|
|
u64 last_gen;
|
2013-07-15 13:36:50 +02:00
|
|
|
DIR *dirstream = NULL;
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2016-03-01 16:02:08 +01:00
|
|
|
clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
|
|
|
|
|
|
|
|
if (check_argc_exact(argc - optind, 2))
|
2015-07-11 00:05:05 +02:00
|
|
|
usage(cmd_subvol_find_new_usage);
|
2012-02-03 20:00:17 +01:00
|
|
|
|
2016-03-01 16:02:08 +01:00
|
|
|
subvol = argv[optind];
|
|
|
|
last_gen = arg_strtou64(argv[optind + 1]);
|
2012-02-03 20:00:17 +01:00
|
|
|
|
|
|
|
ret = test_issubvolume(subvol);
|
|
|
|
if (ret < 0) {
|
2016-01-13 18:14:32 +01:00
|
|
|
error("cannot access subvolume %s: %s", subvol, strerror(-ret));
|
2013-09-04 17:22:19 +02:00
|
|
|
return 1;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
if (!ret) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("not a subvolume: %s", subvol);
|
2013-09-04 17:22:19 +02:00
|
|
|
return 1;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2015-10-12 15:22:54 +02:00
|
|
|
fd = btrfs_open_dir(subvol, &dirstream, 1);
|
|
|
|
if (fd < 0)
|
2013-09-04 17:22:19 +02:00
|
|
|
return 1;
|
2013-09-23 20:17:11 +02:00
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_SYNC);
|
|
|
|
if (ret < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("sync ioctl failed on '%s': %s",
|
2013-09-23 20:17:11 +02:00
|
|
|
subvol, strerror(errno));
|
|
|
|
close_file_or_dir(fd, dirstream);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
Btrfs-progs: restructure list_subvolumes
The current code of list_subvols() has very bad scalability, if we want to
add new filter conditions or new sort methods, we have to modify lots of code.
Beside that, the most code of list_snapshots() is similar to list_subvols(),
So I restructure list_subvols(), and split the subvolume filter function,
the subvolume sort function and the output function from list_subvols().
In order to implement it, we defined some importtant structures:
struct btrfs_list_filter {
btrfs_list_filter_func filter_func;
void *data;
};
struct btrfs_list_comparer {
btrfs_list_comp_func comp_func;
int is_descending;
};
struct {
char *name;
char *column_name;
int need_print;
} btrfs_list_columns[];
If we want to add a new filter condition, we can choose a suitable filter
function, or implement a new filter function[1], and add it into a set of
the filters, and then pass the filter set into list_subvols(). We also can
mix several filters (just add those filters into the set, and pass the set
into list_subvols()) if the users specify two or more filter conditions.
The subvolume sort function is similar to the subvolume filter function. The
differentiation is the order of comparers in the array which is passed into
list_subvols() show us the priority of the sort methods.
The output function is different with the above two functions, we define a
array to manage all the columns that can be outputed, and use a member variant
(->need_print) to control the output of the relative column. Some columns are
outputed by default. But we can change it according to the requirement of the
users.
After appling this patch, we needn't implement a independent list_snapshots()
function, just pass a filter function which is used to identify the snapshot
into list_subvols().
[1]: If we implement new filter functions or compare functions, we must add
them into the array all_filter_funcs or the array all_comp_funcs, and modify
the relative enum variants(btrfs_list_filter_enum, btrfs_list_comp_enum).
Signed-off-by: Miao Xie <miaox@cn.fujitsu.com>
2012-09-18 08:51:49 +02:00
|
|
|
ret = btrfs_list_find_updated_files(fd, 0, last_gen);
|
2013-07-15 13:36:50 +02:00
|
|
|
close_file_or_dir(fd, dirstream);
|
2013-09-04 17:22:19 +02:00
|
|
|
return !!ret;
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
|
2013-02-01 08:56:28 +01:00
|
|
|
static const char * const cmd_subvol_show_usage[] = {
|
|
|
|
"btrfs subvolume show <subvol-path>",
|
|
|
|
"Show more information of the subvolume",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static int cmd_subvol_show(int argc, char **argv)
|
|
|
|
{
|
|
|
|
struct root_info get_ri;
|
2016-03-21 08:21:04 +01:00
|
|
|
struct btrfs_list_filter_set *filter_set = NULL;
|
2013-02-01 08:56:28 +01:00
|
|
|
char tstr[256];
|
2013-11-04 08:17:41 +01:00
|
|
|
char uuidparse[BTRFS_UUID_UNPARSED_SIZE];
|
2016-03-21 08:21:04 +01:00
|
|
|
char *fullpath = NULL;
|
2013-02-01 08:56:28 +01:00
|
|
|
char raw_prefix[] = "\t\t\t\t";
|
2016-03-21 08:21:04 +01:00
|
|
|
int fd = -1;
|
2013-09-04 17:22:19 +02:00
|
|
|
int ret = 1;
|
2016-03-21 08:21:04 +01:00
|
|
|
DIR *dirstream1 = NULL;
|
2013-02-01 08:56:28 +01:00
|
|
|
|
2016-01-14 10:37:02 +01:00
|
|
|
clean_args_no_options(argc, argv, cmd_subvol_show_usage);
|
2016-01-13 15:38:28 +01:00
|
|
|
|
|
|
|
if (check_argc_exact(argc - optind, 1))
|
2013-02-01 08:56:28 +01:00
|
|
|
usage(cmd_subvol_show_usage);
|
|
|
|
|
2016-03-21 08:21:04 +01:00
|
|
|
memset(&get_ri, 0, sizeof(get_ri));
|
2016-01-13 15:38:28 +01:00
|
|
|
fullpath = realpath(argv[optind], NULL);
|
2013-02-01 08:56:28 +01:00
|
|
|
if (!fullpath) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("cannot find real path for '%s': %s",
|
2016-01-13 15:38:28 +01:00
|
|
|
argv[optind], strerror(errno));
|
2013-02-01 08:56:28 +01:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-03-21 08:21:04 +01:00
|
|
|
ret = get_subvol_info(fullpath, &get_ri);
|
|
|
|
if (ret == 2) {
|
|
|
|
/*
|
|
|
|
* Since the top level btrfs was given don't
|
|
|
|
* take that as error
|
|
|
|
*/
|
2016-01-11 11:01:47 +01:00
|
|
|
printf("%s is toplevel subvolume\n", fullpath);
|
2016-03-21 08:21:04 +01:00
|
|
|
ret = 0;
|
2013-02-01 08:56:28 +01:00
|
|
|
goto out;
|
|
|
|
}
|
2014-11-27 03:01:34 +01:00
|
|
|
if (ret) {
|
2016-03-21 08:21:04 +01:00
|
|
|
ret < 0 ?
|
|
|
|
error("Failed to get subvol info %s: %s\n",
|
|
|
|
fullpath, strerror(-ret)):
|
|
|
|
error("Failed to get subvol info %s: %d\n",
|
|
|
|
fullpath, ret);
|
|
|
|
return ret;
|
2013-02-01 08:56:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* print the info */
|
|
|
|
printf("%s\n", fullpath);
|
|
|
|
printf("\tName: \t\t\t%s\n", get_ri.name);
|
|
|
|
|
|
|
|
if (uuid_is_null(get_ri.uuid))
|
|
|
|
strcpy(uuidparse, "-");
|
|
|
|
else
|
|
|
|
uuid_unparse(get_ri.uuid, uuidparse);
|
2015-06-02 17:57:56 +02:00
|
|
|
printf("\tUUID: \t\t\t%s\n", uuidparse);
|
2013-02-01 08:56:28 +01:00
|
|
|
|
|
|
|
if (uuid_is_null(get_ri.puuid))
|
|
|
|
strcpy(uuidparse, "-");
|
|
|
|
else
|
|
|
|
uuid_unparse(get_ri.puuid, uuidparse);
|
2015-06-02 17:57:56 +02:00
|
|
|
printf("\tParent UUID: \t\t%s\n", uuidparse);
|
2013-02-01 08:56:28 +01:00
|
|
|
|
2015-06-02 17:48:32 +02:00
|
|
|
if (uuid_is_null(get_ri.ruuid))
|
|
|
|
strcpy(uuidparse, "-");
|
|
|
|
else
|
|
|
|
uuid_unparse(get_ri.ruuid, uuidparse);
|
2015-06-02 17:57:56 +02:00
|
|
|
printf("\tReceived UUID: \t\t%s\n", uuidparse);
|
2015-06-02 17:48:32 +02:00
|
|
|
|
2013-07-09 18:39:24 +02:00
|
|
|
if (get_ri.otime) {
|
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
localtime_r(&get_ri.otime, &tm);
|
2015-06-02 17:57:56 +02:00
|
|
|
strftime(tstr, 256, "%Y-%m-%d %X %z", &tm);
|
2013-07-09 18:39:24 +02:00
|
|
|
} else
|
2013-02-01 08:56:28 +01:00
|
|
|
strcpy(tstr, "-");
|
|
|
|
printf("\tCreation time: \t\t%s\n", tstr);
|
|
|
|
|
2015-06-02 17:57:56 +02:00
|
|
|
printf("\tSubvolume ID: \t\t%llu\n", get_ri.root_id);
|
|
|
|
printf("\tGeneration: \t\t%llu\n", get_ri.gen);
|
2013-02-01 08:56:28 +01:00
|
|
|
printf("\tGen at creation: \t%llu\n", get_ri.ogen);
|
2015-06-02 17:57:56 +02:00
|
|
|
printf("\tParent ID: \t\t%llu\n", get_ri.ref_tree);
|
|
|
|
printf("\tTop level ID: \t\t%llu\n", get_ri.top_id);
|
2013-02-01 08:56:28 +01:00
|
|
|
|
2013-02-01 08:56:32 +01:00
|
|
|
if (get_ri.flags & BTRFS_ROOT_SUBVOL_RDONLY)
|
|
|
|
printf("\tFlags: \t\t\treadonly\n");
|
|
|
|
else
|
|
|
|
printf("\tFlags: \t\t\t-\n");
|
|
|
|
|
2013-02-01 08:56:28 +01:00
|
|
|
/* print the snapshots of the given subvol if any*/
|
|
|
|
printf("\tSnapshot(s):\n");
|
|
|
|
filter_set = btrfs_list_alloc_filter_set();
|
|
|
|
btrfs_list_setup_filter(&filter_set, BTRFS_LIST_FILTER_BY_PARENT,
|
2013-05-07 14:24:40 +02:00
|
|
|
(u64)(unsigned long)get_ri.uuid);
|
2013-02-01 08:56:28 +01:00
|
|
|
btrfs_list_setup_print_column(BTRFS_LIST_PATH);
|
2016-03-21 08:21:04 +01:00
|
|
|
|
|
|
|
fd = open_file_or_dir(fullpath, &dirstream1);
|
|
|
|
if (fd < 0) {
|
|
|
|
fprintf(stderr, "ERROR: can't access '%s'\n", fullpath);
|
|
|
|
goto out;
|
|
|
|
}
|
2013-02-01 08:56:28 +01:00
|
|
|
btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
|
|
|
|
1, raw_prefix);
|
|
|
|
|
2016-03-21 08:21:04 +01:00
|
|
|
out:
|
2013-02-01 08:56:28 +01:00
|
|
|
/* clean up */
|
2013-12-12 11:41:07 +01:00
|
|
|
free(get_ri.path);
|
|
|
|
free(get_ri.name);
|
|
|
|
free(get_ri.full_path);
|
|
|
|
btrfs_list_free_filter_set(filter_set);
|
2013-02-01 08:56:28 +01:00
|
|
|
|
2013-07-15 13:36:50 +02:00
|
|
|
close_file_or_dir(fd, dirstream1);
|
2013-12-12 11:41:07 +01:00
|
|
|
free(fullpath);
|
2013-09-04 17:22:19 +02:00
|
|
|
return !!ret;
|
2013-02-01 08:56:28 +01:00
|
|
|
}
|
|
|
|
|
2014-07-23 22:56:13 +02:00
|
|
|
static const char * const cmd_subvol_sync_usage[] = {
|
|
|
|
"btrfs subvolume sync <path> [<subvol-id>...]",
|
|
|
|
"Wait until given subvolume(s) are completely removed from the filesystem.",
|
|
|
|
"Wait until given subvolume(s) are completely removed from the filesystem",
|
|
|
|
"after deletion.",
|
2015-04-08 00:28:48 +02:00
|
|
|
"If no subvolume id is given, wait until all current deletion requests",
|
|
|
|
"are completed, but do not wait for subvolumes deleted meanwhile.",
|
|
|
|
"The status of subvolume ids is checked periodically.",
|
2014-07-23 22:56:13 +02:00
|
|
|
"",
|
|
|
|
"-s <N> sleep N seconds between checks (default: 1)",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2015-04-08 00:28:48 +02:00
|
|
|
#if 0
|
2014-07-23 22:56:13 +02:00
|
|
|
/*
|
|
|
|
* If we're looking for any dead subvolume, take a shortcut and look
|
|
|
|
* for any ORPHAN_ITEMs in the tree root
|
|
|
|
*/
|
|
|
|
static int fs_has_dead_subvolumes(int fd)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct btrfs_ioctl_search_args args;
|
|
|
|
struct btrfs_ioctl_search_key *sk = &args.key;
|
|
|
|
struct btrfs_ioctl_search_header sh;
|
|
|
|
u64 min_subvolid = 0;
|
|
|
|
|
|
|
|
again:
|
|
|
|
sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
|
|
|
|
sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
|
|
|
|
sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
|
|
|
|
sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
|
|
|
|
sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
|
|
|
|
sk->min_offset = min_subvolid;
|
|
|
|
sk->max_offset = (u64)-1;
|
|
|
|
sk->min_transid = 0;
|
|
|
|
sk->max_transid = (u64)-1;
|
|
|
|
sk->nr_items = 1;
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
|
|
|
|
if (ret < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (!sk->nr_items)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
memcpy(&sh, args.buf, sizeof(sh));
|
|
|
|
min_subvolid = sh.offset;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Verify that the root item is really there and we haven't hit
|
|
|
|
* a stale orphan
|
|
|
|
*/
|
|
|
|
sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
|
|
|
|
sk->min_objectid = min_subvolid;
|
|
|
|
sk->max_objectid = min_subvolid;
|
|
|
|
sk->min_type = BTRFS_ROOT_ITEM_KEY;
|
|
|
|
sk->max_type = BTRFS_ROOT_ITEM_KEY;
|
|
|
|
sk->min_offset = 0;
|
|
|
|
sk->max_offset = (u64)-1;
|
|
|
|
sk->min_transid = 0;
|
|
|
|
sk->max_transid = (u64)-1;
|
|
|
|
sk->nr_items = 1;
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
|
|
|
|
if (ret < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stale orphan, try the next one
|
|
|
|
*/
|
|
|
|
if (!sk->nr_items) {
|
|
|
|
min_subvolid++;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
2015-04-08 00:28:48 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#define SUBVOL_ID_BATCH 1024
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enumerate all dead subvolumes that exist in the filesystem.
|
|
|
|
* Fill @ids and reallocate to bigger size if needed.
|
|
|
|
*/
|
2015-08-26 16:03:37 +02:00
|
|
|
static int enumerate_dead_subvols(int fd, u64 **ids)
|
2015-04-08 00:28:48 +02:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct btrfs_ioctl_search_args args;
|
|
|
|
struct btrfs_ioctl_search_key *sk = &args.key;
|
|
|
|
int idx = 0;
|
2015-08-26 16:03:37 +02:00
|
|
|
int count = 0;
|
2015-04-08 00:28:48 +02:00
|
|
|
|
|
|
|
memset(&args, 0, sizeof(args));
|
|
|
|
|
|
|
|
sk->tree_id = BTRFS_ROOT_TREE_OBJECTID;
|
|
|
|
sk->min_objectid = BTRFS_ORPHAN_OBJECTID;
|
|
|
|
sk->max_objectid = BTRFS_ORPHAN_OBJECTID;
|
|
|
|
sk->min_type = BTRFS_ORPHAN_ITEM_KEY;
|
|
|
|
sk->max_type = BTRFS_ORPHAN_ITEM_KEY;
|
|
|
|
sk->min_offset = 0;
|
|
|
|
sk->max_offset = (u64)-1;
|
|
|
|
sk->min_transid = 0;
|
|
|
|
sk->max_transid = (u64)-1;
|
|
|
|
sk->nr_items = 4096;
|
|
|
|
|
2015-08-26 16:03:37 +02:00
|
|
|
*ids = NULL;
|
2015-04-08 00:28:48 +02:00
|
|
|
while (1) {
|
|
|
|
struct btrfs_ioctl_search_header *sh;
|
|
|
|
unsigned long off;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
|
|
|
|
if (ret < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (!sk->nr_items)
|
|
|
|
return idx;
|
|
|
|
|
|
|
|
off = 0;
|
|
|
|
for (i = 0; i < sk->nr_items; i++) {
|
|
|
|
sh = (struct btrfs_ioctl_search_header*)(args.buf + off);
|
|
|
|
off += sizeof(*sh);
|
|
|
|
|
2016-05-03 16:20:52 +02:00
|
|
|
if (btrfs_search_header_type(sh)
|
|
|
|
== BTRFS_ORPHAN_ITEM_KEY) {
|
2015-04-08 00:28:48 +02:00
|
|
|
if (idx >= count) {
|
|
|
|
u64 *newids;
|
|
|
|
|
|
|
|
count += SUBVOL_ID_BATCH;
|
2016-03-17 11:41:18 +01:00
|
|
|
newids = (u64*)realloc(*ids,
|
|
|
|
count * sizeof(u64));
|
2015-04-08 00:28:48 +02:00
|
|
|
if (!newids)
|
|
|
|
return -ENOMEM;
|
|
|
|
*ids = newids;
|
|
|
|
}
|
2016-05-03 16:20:52 +02:00
|
|
|
(*ids)[idx] = btrfs_search_header_offset(sh);
|
2015-08-26 16:03:37 +02:00
|
|
|
idx++;
|
2015-04-08 00:28:48 +02:00
|
|
|
}
|
2016-05-03 16:20:52 +02:00
|
|
|
off += btrfs_search_header_len(sh);
|
2015-04-08 00:28:48 +02:00
|
|
|
|
2016-05-03 16:20:52 +02:00
|
|
|
sk->min_objectid = btrfs_search_header_objectid(sh);
|
|
|
|
sk->min_type = btrfs_search_header_type(sh);
|
|
|
|
sk->min_offset = btrfs_search_header_offset(sh);
|
2015-04-08 00:28:48 +02:00
|
|
|
}
|
|
|
|
if (sk->min_offset < (u64)-1)
|
|
|
|
sk->min_offset++;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
if (sk->min_type != BTRFS_ORPHAN_ITEM_KEY)
|
|
|
|
break;
|
|
|
|
if (sk->min_objectid != BTRFS_ORPHAN_OBJECTID)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return idx;
|
|
|
|
}
|
2014-07-23 22:56:13 +02:00
|
|
|
|
|
|
|
static int cmd_subvol_sync(int argc, char **argv)
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
int i;
|
|
|
|
int ret = 1;
|
|
|
|
DIR *dirstream = NULL;
|
|
|
|
u64 *ids = NULL;
|
|
|
|
int id_count;
|
|
|
|
int sleep_interval = 1;
|
|
|
|
|
|
|
|
optind = 1;
|
|
|
|
while (1) {
|
|
|
|
int c = getopt(argc, argv, "s:");
|
|
|
|
|
|
|
|
if (c < 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (c) {
|
|
|
|
case 's':
|
2016-05-04 15:36:35 +02:00
|
|
|
sleep_interval = atoi(optarg);
|
2014-07-23 22:56:13 +02:00
|
|
|
if (sleep_interval < 1) {
|
2016-05-04 15:36:35 +02:00
|
|
|
error("invalid sleep interval %s", optarg);
|
2014-07-23 22:56:13 +02:00
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage(cmd_subvol_sync_usage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (check_argc_min(argc - optind, 1))
|
|
|
|
usage(cmd_subvol_sync_usage);
|
|
|
|
|
2015-10-12 15:22:54 +02:00
|
|
|
fd = btrfs_open_dir(argv[optind], &dirstream, 1);
|
2014-07-23 22:56:13 +02:00
|
|
|
if (fd < 0) {
|
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
optind++;
|
|
|
|
|
|
|
|
id_count = argc - optind;
|
|
|
|
if (!id_count) {
|
2015-08-26 16:03:37 +02:00
|
|
|
id_count = enumerate_dead_subvols(fd, &ids);
|
2015-04-08 00:28:48 +02:00
|
|
|
if (id_count < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("can't enumerate dead subvolumes: %s",
|
2015-04-08 00:28:48 +02:00
|
|
|
strerror(-id_count));
|
2014-07-23 22:56:13 +02:00
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-04-08 00:28:48 +02:00
|
|
|
if (id_count == 0) {
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ids = (u64*)malloc(id_count * sizeof(u64));
|
|
|
|
if (!ids) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("not enough memory");
|
2014-07-23 22:56:13 +02:00
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-04-08 00:28:48 +02:00
|
|
|
|
|
|
|
for (i = 0; i < id_count; i++) {
|
|
|
|
u64 id;
|
|
|
|
const char *arg;
|
|
|
|
|
|
|
|
arg = argv[optind + i];
|
|
|
|
errno = 0;
|
|
|
|
id = strtoull(arg, NULL, 10);
|
|
|
|
if (errno < 0) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("unrecognized subvolume id %s", arg);
|
2015-04-08 00:28:48 +02:00
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (id < BTRFS_FIRST_FREE_OBJECTID
|
|
|
|
|| id > BTRFS_LAST_FREE_OBJECTID) {
|
2016-01-11 11:01:47 +01:00
|
|
|
error("subvolume id %s out of range\n", arg);
|
2015-04-08 00:28:48 +02:00
|
|
|
ret = 1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
ids[i] = id;
|
|
|
|
}
|
2014-07-23 22:56:13 +02:00
|
|
|
}
|
|
|
|
|
2015-04-08 18:15:57 +02:00
|
|
|
ret = wait_for_subvolume_cleaning(fd, id_count, ids, sleep_interval);
|
2014-07-23 22:56:13 +02:00
|
|
|
|
|
|
|
out:
|
|
|
|
free(ids);
|
|
|
|
close_file_or_dir(fd, dirstream);
|
|
|
|
|
|
|
|
return !!ret;
|
|
|
|
}
|
|
|
|
|
2015-06-09 14:22:05 +02:00
|
|
|
static const char subvolume_cmd_group_info[] =
|
|
|
|
"manage subvolumes: create, delete, list, etc";
|
|
|
|
|
2012-02-03 20:00:17 +01:00
|
|
|
const struct cmd_group subvolume_cmd_group = {
|
2015-06-09 14:22:05 +02:00
|
|
|
subvolume_cmd_group_usage, subvolume_cmd_group_info, {
|
2012-02-03 20:00:17 +01:00
|
|
|
{ "create", cmd_subvol_create, cmd_subvol_create_usage, NULL, 0 },
|
|
|
|
{ "delete", cmd_subvol_delete, cmd_subvol_delete_usage, NULL, 0 },
|
|
|
|
{ "list", cmd_subvol_list, cmd_subvol_list_usage, NULL, 0 },
|
2015-07-11 00:05:05 +02:00
|
|
|
{ "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
|
|
|
|
NULL, 0 },
|
2012-02-03 20:00:17 +01:00
|
|
|
{ "get-default", cmd_subvol_get_default,
|
|
|
|
cmd_subvol_get_default_usage, NULL, 0 },
|
|
|
|
{ "set-default", cmd_subvol_set_default,
|
|
|
|
cmd_subvol_set_default_usage, NULL, 0 },
|
2015-07-11 00:05:05 +02:00
|
|
|
{ "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
|
|
|
|
NULL, 0 },
|
2013-02-01 08:56:28 +01:00
|
|
|
{ "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
|
2014-07-23 22:56:13 +02:00
|
|
|
{ "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
|
2013-08-15 01:16:45 +02:00
|
|
|
NULL_CMD_STRUCT
|
2012-02-03 20:00:17 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
int cmd_subvolume(int argc, char **argv)
|
|
|
|
{
|
|
|
|
return handle_command_group(&subvolume_cmd_group, argc, argv);
|
|
|
|
}
|