All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/20] btrfs-progs: qgroups usability
@ 2018-03-08  2:40 jeffm
  2018-03-08  2:40 ` [PATCH 01/20] btrfs-progs: quota: Add -W option to rescan to wait without starting rescan jeffm
                   ` (19 more replies)
  0 siblings, 20 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

Thanks to Qu Wenruo, Nikolay Borisov, and Tomohiro Misono for taking
the time to review my previous patchset.  I've incorporated your
suggestions into this version.

Obviously this one is quite a bit longer than the first version.  After
I posted it, Dave and I talked offline about whether it would make sense
to add the ability to output in JSON to other commands.  If it does,
and we agreed that it did, it would make sense for the choice of format
to be a global option.  In order to do that, I've had to rework some of
how we handle command definition and execution.  Mostly that is around
how to pass format flags and how to inform the user about which output
formats are available for each command.

So, here's the updated series:

* btrfs-progs: quota: Add -W option to rescan to wait without starting
  rescan
  - unchanged

* btrfs-progs: qgroups: fix misleading index check
  - unchanged

* btrfs-progs: constify pathnames passed as arguments
  - removed stray formatting change in send-utils.c

* btrfs-progs: btrfs-list: add rb_entry helpers for root_info
  - new patch, accessors for root_info rb_nodes

* btrfs-progs: btrfs-list: add btrfs_cleanup_root_info
  - new patch, adds a helper to clean up strings attached to root_info

* btrfs-progs: qgroups: add pathname to show output
  - Fixed help text to be more accurate
  - Fixed coding style issues
  - Added checks for NULL pathname
  - Free root_info strings after looking up pathname
  - Free pathname during teardown

* btrfs-progs: qgroups: introduce and use info and limit structures
  - edited to 80 columns

* btrfs-progs: qgroups: introduce btrfs_qgroup_query
  - Fixed issue with ENOENT vs ENOTTY
  - Added filter for search results
  - Use temporary key for search header
  - Cache passed search key for comparison since the loop modifies it

* btrfs-progs: subvolume: add quota info to btrfs sub show
  - Fixed/improved error reporting

* btrfs-progs: help: convert ints used as bools to bool
  - new patch

* btrfs-progs: reorder placement of help declarations for send/receive
  - new patch, required to remove usage declarations from commands.h

* btrfs-progs: filesystem balance: split out special handling
  - new patch, stop directly aliasing 'filesystem balance' to
    'balance' -- it still does the right thing for normal execution
    but help says "go read the balance help instead"

* btrfs-progs: use cmd_struct as command entry point
  - new patch, removes most command callback and usage declarations
  - replaces with cmd_struct declarations that are used in command
    group arrays directly, similar to how sysfs attributes are
    defined

* btrfs-progs: pass cmd_struct to command callback function
  - new patch, required to pass flags and have access to usage array

* btrfs-progs: pass cmd_struct to clean_args_no_options{,_relaxed}
  - new patch, required to pass cmd_struct to usage()

* btrfs-progs: pass cmd_struct to usage()
  - new patch, required to dynamically print what output formats
    are available based on flags defined in command

* btrfs-progs: add support for output formats
  - new patch, adds infrastructure for output formats, including passing
    caller context to commands

* btrfs-progs: add generic support for json output
  - new patch, split out JSON library detection from qgroups patch

* btrfs-progs: handle command groups directly for common case
  - new patch, remove most simple command group callbacks

* btrfs-progs: qgroups: add json output for usage command
  - remove -j and --compat-json options in favor of global --format json
    or --format json:compat
  - added macro for qgroupid format buffer length
  - handle NULL pathnames better

-Jeff

Jeff Mahoney (20):
  btrfs-progs: quota: Add -W option to rescan to wait without starting
    rescan
  btrfs-progs: qgroups: fix misleading index check
  btrfs-progs: constify pathnames passed as arguments
  btrfs-progs: btrfs-list: add rb_entry helpers for root_info
  btrfs-progs: btrfs-list: add btrfs_cleanup_root_info
  btrfs-progs: qgroups: add pathname to show output
  btrfs-progs: qgroups: introduce and use info and limit structures
  btrfs-progs: qgroups: introduce btrfs_qgroup_query
  btrfs-progs: subvolume: add quota info to btrfs sub show
  btrfs-progs: help: convert ints used as bools to bool
  btrfs-progs: reorder placement of help declarations for send/receive
  btrfs-progs: filesystem balance: split out special handling
  btrfs-progs: use cmd_struct as command entry point
  btrfs-progs: pass cmd_struct to command callback function
  btrfs-progs: pass cmd_struct to clean_args_no_options{,_relaxed}
  btrfs-progs: pass cmd_struct to usage()
  btrfs-progs: add support for output formats
  btrfs-progs: add generic support for json output
  btrfs-progs: qgroups: add json output for usage command
  btrfs-progs: handle command groups directly for common case

 Documentation/btrfs-qgroup.asciidoc |   7 +
 Documentation/btrfs-quota.asciidoc  |  10 +-
 Makefile.inc.in                     |   4 +-
 btrfs-calc-size.c                   |   5 +-
 btrfs-debug-tree.c                  |   6 +-
 btrfs-list.c                        |  48 ++-
 btrfs-list.h                        |   1 +
 btrfs-show-super.c                  |   6 +-
 btrfs.c                             | 174 ++++++++---
 check/main.c                        |  10 +-
 chunk-recover.c                     |   4 +-
 cmds-balance.c                      |  74 +++--
 cmds-device.c                       |  96 +++---
 cmds-fi-du.c                        |  11 +-
 cmds-fi-usage.c                     |  17 +-
 cmds-filesystem.c                   | 113 ++++---
 cmds-inspect-dump-super.c           |  11 +-
 cmds-inspect-dump-tree.c            |  11 +-
 cmds-inspect-tree-stats.c           |  11 +-
 cmds-inspect.c                      |  78 ++---
 cmds-property.c                     |  55 ++--
 cmds-qgroup.c                       | 125 +++++---
 cmds-quota.c                        |  63 ++--
 cmds-receive.c                      |  70 +++--
 cmds-replace.c                      |  45 +--
 cmds-rescue.c                       |  60 ++--
 cmds-restore.c                      |  12 +-
 cmds-scrub.c                        |  64 ++--
 cmds-send.c                         |  74 ++---
 cmds-subvolume.c                    | 167 +++++++---
 commands.h                          | 165 ++++++----
 configure.ac                        |   6 +
 help.c                              | 121 +++++---
 help.h                              |  15 +-
 kerncompat.h                        |   1 +
 qgroup.c                            | 601 ++++++++++++++++++++++++++++++------
 qgroup.h                            |  25 +-
 utils.c                             |  23 +-
 utils.h                             |   2 +
 39 files changed, 1647 insertions(+), 744 deletions(-)

-- 
2.12.3


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH 01/20] btrfs-progs: quota: Add -W option to rescan to wait without starting rescan
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-05-03  5:17   ` Qu Wenruo
  2018-03-08  2:40 ` [PATCH 02/20] btrfs-progs: qgroups: fix misleading index check jeffm
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

This patch adds a new -W option to wait for a rescan without starting a
new operation.  This is useful for things like xfstests where we want
do to do a "btrfs quota enable" and not continue until the subsequent
rescan has finished.

In addition to documenting the new option in the man page, I've cleaned
up the rescan entry to document the -w option a bit better.

Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 Documentation/btrfs-quota.asciidoc | 10 +++++++---
 cmds-quota.c                       | 20 ++++++++++++++------
 2 files changed, 21 insertions(+), 9 deletions(-)

diff --git a/Documentation/btrfs-quota.asciidoc b/Documentation/btrfs-quota.asciidoc
index 85ebf729..0b64a69b 100644
--- a/Documentation/btrfs-quota.asciidoc
+++ b/Documentation/btrfs-quota.asciidoc
@@ -238,15 +238,19 @@ Disable subvolume quota support for a filesystem.
 *enable* <path>::
 Enable subvolume quota support for a filesystem.
 
-*rescan* [-s] <path>::
+*rescan* [-s|-w|-W] <path>::
 Trash all qgroup numbers and scan the metadata again with the current config.
 +
 `Options`
 +
 -s::::
-show status of a running rescan operation.
+Show status of a running rescan operation.
+
 -w::::
-wait for rescan operation to finish(can be already in progress).
+Start rescan operation and wait until it has finished before exiting.  If a rescan is already running, wait until it finishes and then exit without starting a new one.
+
+-W::::
+Wait for rescan operation to finish and then exit.  If a rescan is not already running, exit silently.
 
 EXIT STATUS
 -----------
diff --git a/cmds-quota.c b/cmds-quota.c
index 745889d1..7f933495 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -120,14 +120,20 @@ static int cmd_quota_rescan(int argc, char **argv)
 	int wait_for_completion = 0;
 
 	while (1) {
-		int c = getopt(argc, argv, "sw");
+		int c = getopt(argc, argv, "swW");
 		if (c < 0)
 			break;
 		switch (c) {
 		case 's':
 			ioctlnum = BTRFS_IOC_QUOTA_RESCAN_STATUS;
 			break;
+		case 'W':
+			ioctlnum = 0;
+			wait_for_completion = 1;
+			break;
 		case 'w':
+			/* Reset it in case the user did both -W and -w */
+			ioctlnum = BTRFS_IOC_QUOTA_RESCAN;
 			wait_for_completion = 1;
 			break;
 		default:
@@ -135,8 +141,8 @@ static int cmd_quota_rescan(int argc, char **argv)
 		}
 	}
 
-	if (ioctlnum != BTRFS_IOC_QUOTA_RESCAN && wait_for_completion) {
-		error("switch -w cannot be used with -s");
+	if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN_STATUS && wait_for_completion) {
+		error("switches -w/-W cannot be used with -s");
 		return 1;
 	}
 
@@ -150,8 +156,10 @@ static int cmd_quota_rescan(int argc, char **argv)
 	if (fd < 0)
 		return 1;
 
-	ret = ioctl(fd, ioctlnum, &args);
-	e = errno;
+	if (ioctlnum) {
+		ret = ioctl(fd, ioctlnum, &args);
+		e = errno;
+	}
 
 	if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN_STATUS) {
 		close_file_or_dir(fd, dirstream);
@@ -167,7 +175,7 @@ static int cmd_quota_rescan(int argc, char **argv)
 		return 0;
 	}
 
-	if (ret == 0) {
+	if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN && ret == 0) {
 		printf("quota rescan started\n");
 		fflush(stdout);
 	} else if (ret < 0 && (!wait_for_completion || e != EINPROGRESS)) {
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 02/20] btrfs-progs: qgroups: fix misleading index check
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
  2018-03-08  2:40 ` [PATCH 01/20] btrfs-progs: quota: Add -W option to rescan to wait without starting rescan jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 03/20] btrfs-progs: constify pathnames passed as arguments jeffm
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

In print_single_qgroup_table we check the loop index against
BTRFS_QGROUP_CHILD, but what we really mean is "last column."  Since
we have an enum value to indicate the last value, use that instead
of assuming that BTRFS_QGROUP_CHILD is always last.

Reviewed-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: Nikolay Borisov <nborisov@suse.com>
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 qgroup.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/qgroup.c b/qgroup.c
index 11659e83..67bc0738 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -267,7 +267,7 @@ static void print_single_qgroup_table(struct btrfs_qgroup *qgroup)
 			continue;
 		print_qgroup_column(qgroup, i);
 
-		if (i != BTRFS_QGROUP_CHILD)
+		if (i != BTRFS_QGROUP_ALL - 1)
 			printf(" ");
 	}
 	printf("\n");
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 03/20] btrfs-progs: constify pathnames passed as arguments
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
  2018-03-08  2:40 ` [PATCH 01/20] btrfs-progs: quota: Add -W option to rescan to wait without starting rescan jeffm
  2018-03-08  2:40 ` [PATCH 02/20] btrfs-progs: qgroups: fix misleading index check jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 04/20] btrfs-progs: btrfs-list: add rb_entry helpers for root_info jeffm
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

It's unlikely we're going to modify a pathname argument, so codify that
and use const.

Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 chunk-recover.c | 4 ++--
 cmds-device.c   | 2 +-
 cmds-fi-usage.c | 6 +++---
 cmds-rescue.c   | 4 ++--
 4 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/chunk-recover.c b/chunk-recover.c
index 705bcf52..1d30db51 100644
--- a/chunk-recover.c
+++ b/chunk-recover.c
@@ -1492,7 +1492,7 @@ out:
 	return ERR_PTR(ret);
 }
 
-static int recover_prepare(struct recover_control *rc, char *path)
+static int recover_prepare(struct recover_control *rc, const char *path)
 {
 	int ret;
 	int fd;
@@ -2296,7 +2296,7 @@ static void validate_rebuild_chunks(struct recover_control *rc)
 /*
  * Return 0 when successful, < 0 on error and > 0 if aborted by user
  */
-int btrfs_recover_chunk_tree(char *path, int verbose, int yes)
+int btrfs_recover_chunk_tree(const char *path, int verbose, int yes)
 {
 	int ret = 0;
 	struct btrfs_root *root = NULL;
diff --git a/cmds-device.c b/cmds-device.c
index 86459d1b..a49c9d9d 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -526,7 +526,7 @@ static const char * const cmd_device_usage_usage[] = {
 	NULL
 };
 
-static int _cmd_device_usage(int fd, char *path, unsigned unit_mode)
+static int _cmd_device_usage(int fd, const char *path, unsigned unit_mode)
 {
 	int i;
 	int ret = 0;
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index de7ad668..9a1c76ab 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -227,7 +227,7 @@ static int cmp_btrfs_ioctl_space_info(const void *a, const void *b)
 /*
  * This function load all the information about the space usage
  */
-static struct btrfs_ioctl_space_args *load_space_info(int fd, char *path)
+static struct btrfs_ioctl_space_args *load_space_info(int fd, const char *path)
 {
 	struct btrfs_ioctl_space_args *sargs = NULL, *sargs_orig = NULL;
 	int ret, count;
@@ -305,7 +305,7 @@ static void get_raid56_used(struct chunk_info *chunks, int chunkcount,
 #define	MIN_UNALOCATED_THRESH	SZ_16M
 static int print_filesystem_usage_overall(int fd, struct chunk_info *chunkinfo,
 		int chunkcount, struct device_info *devinfo, int devcount,
-		char *path, unsigned unit_mode)
+		const char *path, unsigned unit_mode)
 {
 	struct btrfs_ioctl_space_args *sargs = NULL;
 	int i;
@@ -931,7 +931,7 @@ static void _cmd_filesystem_usage_linear(unsigned unit_mode,
 static int print_filesystem_usage_by_chunk(int fd,
 		struct chunk_info *chunkinfo, int chunkcount,
 		struct device_info *devinfo, int devcount,
-		char *path, unsigned unit_mode, int tabular)
+		const char *path, unsigned unit_mode, int tabular)
 {
 	struct btrfs_ioctl_space_args *sargs;
 	int ret = 0;
diff --git a/cmds-rescue.c b/cmds-rescue.c
index c40088ad..c61145bc 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -32,8 +32,8 @@ static const char * const rescue_cmd_group_usage[] = {
 	NULL
 };
 
-int btrfs_recover_chunk_tree(char *path, int verbose, int yes);
-int btrfs_recover_superblocks(char *path, int verbose, int yes);
+int btrfs_recover_chunk_tree(const char *path, int verbose, int yes);
+int btrfs_recover_superblocks(const char *path, int verbose, int yes);
 
 static const char * const cmd_rescue_chunk_recover_usage[] = {
 	"btrfs rescue chunk-recover [options] <device>",
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 04/20] btrfs-progs: btrfs-list: add rb_entry helpers for root_info
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (2 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 03/20] btrfs-progs: constify pathnames passed as arguments jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 05/20] btrfs-progs: btrfs-list: add btrfs_cleanup_root_info jeffm
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

We use rb_entry all over the place for the root_info pointers.  Add
a helper to make the code more readable.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs-list.c | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index e01c5899..90c98be1 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -44,6 +44,16 @@ struct root_lookup {
 	struct rb_root root;
 };
 
+static inline struct root_info *to_root_info(struct rb_node *node)
+{
+	return rb_entry(node, struct root_info, rb_node);
+}
+
+static inline struct root_info *to_root_info_sorted(struct rb_node *node)
+{
+	return rb_entry(node, struct root_info, sort_node);
+}
+
 static struct {
 	char	*name;
 	char	*column_name;
@@ -309,7 +319,7 @@ static int sort_tree_insert(struct root_lookup *sort_tree,
 
 	while (*p) {
 		parent = *p;
-		curr = rb_entry(parent, struct root_info, sort_node);
+		curr = to_root_info_sorted(parent);
 
 		ret = sort_comp(ins, curr, comp_set);
 		if (ret < 0)
@@ -340,7 +350,7 @@ static int root_tree_insert(struct root_lookup *root_tree,
 
 	while(*p) {
 		parent = *p;
-		curr = rb_entry(parent, struct root_info, rb_node);
+		curr = to_root_info(parent);
 
 		ret = comp_entry_with_rootid(ins, curr, 0);
 		if (ret < 0)
@@ -371,7 +381,7 @@ static struct root_info *root_tree_search(struct root_lookup *root_tree,
 	tmp.root_id = root_id;
 
 	while(n) {
-		entry = rb_entry(n, struct root_info, rb_node);
+		entry = to_root_info(n);
 
 		ret = comp_entry_with_rootid(&tmp, entry, 0);
 		if (ret < 0)
@@ -528,7 +538,7 @@ static void free_root_info(struct rb_node *node)
 {
 	struct root_info *ri;
 
-	ri = rb_entry(node, struct root_info, rb_node);
+	ri = to_root_info(node);
 	free(ri->name);
 	free(ri->path);
 	free(ri->full_path);
@@ -1268,7 +1278,7 @@ static void filter_and_sort_subvol(struct root_lookup *all_subvols,
 
 	n = rb_last(&all_subvols->root);
 	while (n) {
-		entry = rb_entry(n, struct root_info, rb_node);
+		entry = to_root_info(n);
 
 		ret = resolve_root(all_subvols, entry, top_id);
 		if (ret == -ENOENT) {
@@ -1300,7 +1310,7 @@ static int list_subvol_fill_paths(int fd, struct root_lookup *root_lookup)
 	while (n) {
 		struct root_info *entry;
 		int ret;
-		entry = rb_entry(n, struct root_info, rb_node);
+		entry = to_root_info(n);
 		ret = lookup_ino_path(fd, entry);
 		if (ret && ret != -ENOENT)
 			return ret;
@@ -1467,7 +1477,7 @@ static void print_all_subvol_info(struct root_lookup *sorted_tree,
 
 	n = rb_first(&sorted_tree->root);
 	while (n) {
-		entry = rb_entry(n, struct root_info, sort_node);
+		entry = to_root_info_sorted(n);
 
 		/* The toplevel subvolume is not listed by default */
 		if (entry->root_id == BTRFS_FS_TREE_OBJECTID)
@@ -1558,7 +1568,7 @@ int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri)
 		return ret;
 
 	rbn = rb_first(&rl.root);
-	ri = rb_entry(rbn, struct root_info, rb_node);
+	ri = to_root_info(rbn);
 
 	if (ri->root_id != BTRFS_FS_TREE_OBJECTID)
 		return -ENOENT;
@@ -1590,7 +1600,7 @@ int btrfs_get_subvol(int fd, struct root_info *the_ri)
 
 	rbn = rb_first(&rl.root);
 	while(rbn) {
-		ri = rb_entry(rbn, struct root_info, rb_node);
+		ri = to_root_info(rbn);
 		rr = resolve_root(&rl, ri, root_id);
 		if (rr == -ENOENT) {
 			ret = -ENOENT;
@@ -1814,7 +1824,7 @@ char *btrfs_list_path_for_root(int fd, u64 root)
 	while (n) {
 		struct root_info *entry;
 
-		entry = rb_entry(n, struct root_info, rb_node);
+		entry = to_root_info(n);
 		ret = resolve_root(&root_lookup, entry, top_id);
 		if (ret == -ENOENT && entry->root_id == root) {
 			ret_path = NULL;
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 05/20] btrfs-progs: btrfs-list: add btrfs_cleanup_root_info
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (3 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 04/20] btrfs-progs: btrfs-list: add rb_entry helpers for root_info jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 06/20] btrfs-progs: qgroups: add pathname to show output jeffm
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

Currently we can pass back root_info structures to callers but
have to free the strings manually.  This adds a helper to do it
and uses it in cmd_subvol_show.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs-list.c     | 18 +++++++++++++++---
 btrfs-list.h     |  1 +
 cmds-subvolume.c |  5 +----
 3 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/btrfs-list.c b/btrfs-list.c
index 90c98be1..2fe31e9c 100644
--- a/btrfs-list.c
+++ b/btrfs-list.c
@@ -533,15 +533,27 @@ static int add_root_backref(struct root_lookup *root_lookup, u64 root_id,
 			name_len, 0, 0, 0, NULL, NULL, NULL);
 }
 
+static void __btrfs_free_root_info_strings(struct root_info *ri)
+{
+	free(ri->name);
+	free(ri->path);
+	free(ri->full_path);
+}
+
+void btrfs_cleanup_root_info(struct root_info *ri)
+{
+	__btrfs_free_root_info_strings(ri);
+	ri->name = NULL;
+	ri->path = NULL;
+	ri->full_path = NULL;
+}
 
 static void free_root_info(struct rb_node *node)
 {
 	struct root_info *ri;
 
 	ri = to_root_info(node);
-	free(ri->name);
-	free(ri->path);
-	free(ri->full_path);
+	__btrfs_free_root_info_strings(ri);
 	free(ri);
 }
 
diff --git a/btrfs-list.h b/btrfs-list.h
index 6e5fc778..9d0478b8 100644
--- a/btrfs-list.h
+++ b/btrfs-list.h
@@ -176,5 +176,6 @@ char *btrfs_list_path_for_root(int fd, u64 root);
 int btrfs_list_get_path_rootid(int fd, u64 *treeid);
 int btrfs_get_subvol(int fd, struct root_info *the_ri);
 int btrfs_get_toplevel_subvol(int fd, struct root_info *the_ri);
+void btrfs_cleanup_root_info(struct root_info *ri);
 
 #endif
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 8a473f7a..769d2a76 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1113,10 +1113,7 @@ static int cmd_subvol_show(int argc, char **argv)
 			1, raw_prefix);
 
 out:
-	/* clean up */
-	free(get_ri.path);
-	free(get_ri.name);
-	free(get_ri.full_path);
+	btrfs_cleanup_root_info(&get_ri);
 	free(filter_set);
 
 	close_file_or_dir(fd, dirstream1);
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 06/20] btrfs-progs: qgroups: add pathname to show output
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (4 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 05/20] btrfs-progs: btrfs-list: add btrfs_cleanup_root_info jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  5:33   ` Qu Wenruo
  2018-03-08  2:40 ` [PATCH 07/20] btrfs-progs: qgroups: introduce and use info and limit structures jeffm
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

The btrfs qgroup show command currently only exports qgroup IDs,
forcing the user to resolve which subvolume each corresponds to.

This patch adds pathname resolution to qgroup show so that when
the -P option is used, the last column contains the pathname of
the root of the subvolume it describes.  In the case of nested
qgroups, it will show the number of member qgroups or the paths
of the members if the -v option is used.

Pathname can also be used as a sort parameter.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 Documentation/btrfs-qgroup.asciidoc |   4 +
 cmds-qgroup.c                       |  18 ++++-
 kerncompat.h                        |   1 +
 qgroup.c                            | 152 ++++++++++++++++++++++++++++++++----
 qgroup.h                            |   4 +-
 utils.c                             |  23 ++++--
 utils.h                             |   2 +
 7 files changed, 178 insertions(+), 26 deletions(-)

diff --git a/Documentation/btrfs-qgroup.asciidoc b/Documentation/btrfs-qgroup.asciidoc
index 3108457c..360b3269 100644
--- a/Documentation/btrfs-qgroup.asciidoc
+++ b/Documentation/btrfs-qgroup.asciidoc
@@ -97,10 +97,14 @@ print child qgroup id.
 print limit of referenced size of qgroup.
 -e::::
 print limit of exclusive size of qgroup.
+-P::::
+print pathname to the root of the subvolume managed by qgroup.  For nested qgroups, the number of members will be printed unless -v is specified.
 -F::::
 list all qgroups which impact the given path(include ancestral qgroups)
 -f::::
 list all qgroups which impact the given path(exclude ancestral qgroups)
+-v::::
+Be more verbose.  Print pathnames of member qgroups when nested.
 --raw::::
 raw numbers in bytes, without the 'B' suffix.
 --human-readable::::
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 48686436..d704aeaf 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -280,8 +280,11 @@ static const char * const cmd_qgroup_show_usage[] = {
 	"               (including ancestral qgroups)",
 	"-f             list all qgroups which impact the given path",
 	"               (excluding ancestral qgroups)",
+	"-P             print first-level qgroups using pathname",
+	"               - nested qgroups will be reported as a count",
+	"-v             verbose, prints pathnames for all nested qgroups",
 	HELPINFO_UNITS_LONG,
-	"--sort=qgroupid,rfer,excl,max_rfer,max_excl",
+	"--sort=qgroupid,rfer,excl,max_rfer,max_excl,pathname",
 	"               list qgroups sorted by specified items",
 	"               you can use '+' or '-' in front of each item.",
 	"               (+:ascending, -:descending, ascending default)",
@@ -299,6 +302,7 @@ static int cmd_qgroup_show(int argc, char **argv)
 	int filter_flag = 0;
 	unsigned unit_mode;
 	int sync = 0;
+	bool verbose = false;
 
 	struct btrfs_qgroup_comparer_set *comparer_set;
 	struct btrfs_qgroup_filter_set *filter_set;
@@ -316,10 +320,11 @@ static int cmd_qgroup_show(int argc, char **argv)
 		static const struct option long_options[] = {
 			{"sort", required_argument, NULL, GETOPT_VAL_SORT},
 			{"sync", no_argument, NULL, GETOPT_VAL_SYNC},
+			{"verbose", no_argument, NULL, 'v'},
 			{ NULL, 0, NULL, 0 }
 		};
 
-		c = getopt_long(argc, argv, "pcreFf", long_options, NULL);
+		c = getopt_long(argc, argv, "pPcreFfv", long_options, NULL);
 		if (c < 0)
 			break;
 		switch (c) {
@@ -327,6 +332,10 @@ static int cmd_qgroup_show(int argc, char **argv)
 			btrfs_qgroup_setup_print_column(
 				BTRFS_QGROUP_PARENT);
 			break;
+		case 'P':
+			btrfs_qgroup_setup_print_column(
+				BTRFS_QGROUP_PATHNAME);
+			break;
 		case 'c':
 			btrfs_qgroup_setup_print_column(
 				BTRFS_QGROUP_CHILD);
@@ -354,6 +363,9 @@ static int cmd_qgroup_show(int argc, char **argv)
 		case GETOPT_VAL_SYNC:
 			sync = 1;
 			break;
+		case 'v':
+			verbose = true;
+			break;
 		default:
 			usage(cmd_qgroup_show_usage);
 		}
@@ -394,7 +406,7 @@ static int cmd_qgroup_show(int argc, char **argv)
 					BTRFS_QGROUP_FILTER_PARENT,
 					qgroupid);
 	}
-	ret = btrfs_show_qgroups(fd, filter_set, comparer_set);
+	ret = btrfs_show_qgroups(fd, filter_set, comparer_set, verbose);
 	close_file_or_dir(fd, dirstream);
 	free(filter_set);
 	free(comparer_set);
diff --git a/kerncompat.h b/kerncompat.h
index fa96715f..f97495ee 100644
--- a/kerncompat.h
+++ b/kerncompat.h
@@ -29,6 +29,7 @@
 #include <stddef.h>
 #include <linux/types.h>
 #include <stdint.h>
+#include <stdbool.h>
 
 #include <features.h>
 
diff --git a/qgroup.c b/qgroup.c
index 67bc0738..5600da99 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -40,6 +40,9 @@ struct btrfs_qgroup {
 	struct rb_node all_parent_node;
 	u64 qgroupid;
 
+	/* NULL for qgroups with level > 0 */
+	const char *pathname;
+
 	/*
 	 * info_item
 	 */
@@ -134,6 +137,13 @@ static struct {
 		.max_len	= 5,
 	},
 	{
+		.name		= "pathname",
+		.column_name	= "pathname",
+		.need_print	= 0,
+		.unit_mode	= 0,
+		.max_len	= 10,
+	},
+	{
 		.name		= NULL,
 		.column_name	= NULL,
 		.need_print	= 0,
@@ -210,8 +220,49 @@ static void print_qgroup_column_add_blank(enum btrfs_qgroup_column_enum column,
 		printf(" ");
 }
 
+void print_pathname_column(struct btrfs_qgroup *qgroup, bool verbose)
+{
+	struct btrfs_qgroup_list *list = NULL;
+
+	fputs("  ", stdout);
+	if (btrfs_qgroup_level(qgroup->qgroupid) > 0) {
+		int count = 0;
+
+		list_for_each_entry(list, &qgroup->qgroups,
+				    next_qgroup) {
+			if (verbose) {
+				struct btrfs_qgroup *member = list->qgroup;
+				u64 qgroupid = member->qgroupid;
+				u64 level = btrfs_qgroup_level(qgroupid);
+				u64 sid = btrfs_qgroup_subvid(qgroupid);
+
+				if (count)
+					fputs(" ", stdout);
+				if (level == 0) {
+					const char *path = member->pathname;
+
+					if (!path)
+						path = "<missing>";
+					fputs(path, stdout);
+				} else
+					printf("%llu/%llu", level, sid);
+			}
+			count++;
+		}
+		if (!count)
+			fputs("<empty>", stdout);
+		else if (!verbose)
+			printf("<%u member qgroup%c>", count,
+			       count != 1 ? 's' : '\0');
+	} else if (qgroup->pathname)
+		fputs(qgroup->pathname, stdout);
+	else
+		fputs("<missing>", stdout);
+}
+
 static void print_qgroup_column(struct btrfs_qgroup *qgroup,
-				enum btrfs_qgroup_column_enum column)
+				enum btrfs_qgroup_column_enum column,
+				bool verbose)
 {
 	int len;
 	int unit_mode = btrfs_qgroup_columns[column].unit_mode;
@@ -253,19 +304,22 @@ static void print_qgroup_column(struct btrfs_qgroup *qgroup,
 		len = print_child_column(qgroup);
 		print_qgroup_column_add_blank(BTRFS_QGROUP_CHILD, len);
 		break;
+	case BTRFS_QGROUP_PATHNAME:
+		print_pathname_column(qgroup, verbose);
+		break;
 	default:
 		break;
 	}
 }
 
-static void print_single_qgroup_table(struct btrfs_qgroup *qgroup)
+static void print_single_qgroup_table(struct btrfs_qgroup *qgroup, bool verbose)
 {
 	int i;
 
 	for (i = 0; i < BTRFS_QGROUP_ALL; i++) {
 		if (!btrfs_qgroup_columns[i].need_print)
 			continue;
-		print_qgroup_column(qgroup, i);
+		print_qgroup_column(qgroup, i, verbose);
 
 		if (i != BTRFS_QGROUP_ALL - 1)
 			printf(" ");
@@ -338,6 +392,47 @@ static int comp_entry_with_qgroupid(struct btrfs_qgroup *entry1,
 	return is_descending ? -ret : ret;
 }
 
+/* Sorts first-level qgroups by pathname and nested qgroups by qgroupid */
+static int comp_entry_with_pathname(struct btrfs_qgroup *entry1,
+				    struct btrfs_qgroup *entry2,
+				    int is_descending)
+{
+	int ret = 0;
+	const char *p1 = entry1->pathname;
+	const char *p2 = entry2->pathname;
+
+	u64 level1 = btrfs_qgroup_level(entry1->qgroupid);
+	u64 level2 = btrfs_qgroup_level(entry2->qgroupid);
+
+	if (level1 != level2) {
+		if (entry1->qgroupid > entry2->qgroupid)
+			ret = 1;
+		else if (entry1->qgroupid < entry2->qgroupid)
+			ret = -1;
+	}
+
+	if (ret)
+		goto out;
+
+	while (*p1 && *p2) {
+		if (*p1 != *p2)
+			break;
+		p1++;
+		p2++;
+	}
+
+	if (*p1 == '/')
+		ret = 1;
+	else if (*p2 == '/')
+		ret = -1;
+	else if (*p1 > *p2)
+		ret = 1;
+	else if (*p1 < *p2)
+		ret = -1;
+out:
+	return is_descending ? -ret : ret;
+}
+
 static int comp_entry_with_rfer(struct btrfs_qgroup *entry1,
 				struct btrfs_qgroup *entry2,
 				int is_descending)
@@ -404,6 +499,7 @@ static int comp_entry_with_max_excl(struct btrfs_qgroup *entry1,
 
 static btrfs_qgroup_comp_func all_comp_funcs[] = {
 	[BTRFS_QGROUP_COMP_QGROUPID]	= comp_entry_with_qgroupid,
+	[BTRFS_QGROUP_COMP_PATHNAME]	= comp_entry_with_pathname,
 	[BTRFS_QGROUP_COMP_RFER]	= comp_entry_with_rfer,
 	[BTRFS_QGROUP_COMP_EXCL]	= comp_entry_with_excl,
 	[BTRFS_QGROUP_COMP_MAX_RFER]	= comp_entry_with_max_rfer,
@@ -412,6 +508,7 @@ static btrfs_qgroup_comp_func all_comp_funcs[] = {
 
 static char *all_sort_items[] = {
 	[BTRFS_QGROUP_COMP_QGROUPID]	= "qgroupid",
+	[BTRFS_QGROUP_COMP_PATHNAME]	= "pathname",
 	[BTRFS_QGROUP_COMP_RFER]	= "rfer",
 	[BTRFS_QGROUP_COMP_EXCL]	= "excl",
 	[BTRFS_QGROUP_COMP_MAX_RFER]	= "max_rfer",
@@ -578,6 +675,26 @@ static struct btrfs_qgroup *qgroup_tree_search(struct qgroup_lookup *root_tree,
 	return NULL;
 }
 
+static const char *qgroup_pathname(int fd, u64 qgroupid)
+{
+	struct root_info root_info;
+	int ret;
+	char *pathname;
+
+	ret = get_subvol_info_by_rootid_fd(fd, &root_info, qgroupid);
+	if (ret)
+		return NULL;
+
+	ret = asprintf(&pathname, "%s%s",
+		       root_info.full_path[0] == '/' ? "" : "/",
+		       root_info.full_path);
+	btrfs_cleanup_root_info(&root_info);
+	if (ret < 0)
+		return NULL;
+
+	return pathname;
+}
+
 /*
  * Lookup or insert btrfs_qgroup into qgroup_lookup.
  *
@@ -588,7 +705,7 @@ static struct btrfs_qgroup *qgroup_tree_search(struct qgroup_lookup *root_tree,
  * Return the pointer to the btrfs_qgroup if found or if inserted successfully.
  * Return ERR_PTR if any error occurred.
  */
-static struct btrfs_qgroup *get_or_add_qgroup(
+static struct btrfs_qgroup *get_or_add_qgroup(int fd,
 		struct qgroup_lookup *qgroup_lookup, u64 qgroupid)
 {
 	struct btrfs_qgroup *bq;
@@ -608,6 +725,8 @@ static struct btrfs_qgroup *get_or_add_qgroup(
 	INIT_LIST_HEAD(&bq->qgroups);
 	INIT_LIST_HEAD(&bq->members);
 
+	bq->pathname = qgroup_pathname(fd, qgroupid);
+
 	ret = qgroup_tree_insert(qgroup_lookup, bq);
 	if (ret) {
 		error("failed to insert %llu into tree: %s",
@@ -619,12 +738,12 @@ static struct btrfs_qgroup *get_or_add_qgroup(
 	return bq;
 }
 
-static int update_qgroup_info(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
-			      struct btrfs_qgroup_info_item *info)
+static int update_qgroup_info(int fd, struct qgroup_lookup *qgroup_lookup,
+			      u64 qgroupid, struct btrfs_qgroup_info_item *info)
 {
 	struct btrfs_qgroup *bq;
 
-	bq = get_or_add_qgroup(qgroup_lookup, qgroupid);
+	bq = get_or_add_qgroup(fd, qgroup_lookup, qgroupid);
 	if (IS_ERR_OR_NULL(bq))
 		return PTR_ERR(bq);
 
@@ -637,13 +756,13 @@ static int update_qgroup_info(struct qgroup_lookup *qgroup_lookup, u64 qgroupid,
 	return 0;
 }
 
-static int update_qgroup_limit(struct qgroup_lookup *qgroup_lookup,
+static int update_qgroup_limit(int fd, struct qgroup_lookup *qgroup_lookup,
 			       u64 qgroupid,
 			       struct btrfs_qgroup_limit_item *limit)
 {
 	struct btrfs_qgroup *bq;
 
-	bq = get_or_add_qgroup(qgroup_lookup, qgroupid);
+	bq = get_or_add_qgroup(fd, qgroup_lookup, qgroupid);
 	if (IS_ERR_OR_NULL(bq))
 		return PTR_ERR(bq);
 
@@ -712,6 +831,8 @@ static void __free_btrfs_qgroup(struct btrfs_qgroup *bq)
 		list_del(&list->next_member);
 		free(list);
 	}
+	if (bq->pathname)
+		free((void *)bq->pathname);
 	free(bq);
 }
 
@@ -1107,7 +1228,7 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
 				info = (struct btrfs_qgroup_info_item *)
 				       (args.buf + off);
 
-				ret = update_qgroup_info(qgroup_lookup,
+				ret = update_qgroup_info(fd, qgroup_lookup,
 							 qgroupid, info);
 				break;
 			case BTRFS_QGROUP_LIMIT_KEY:
@@ -1115,7 +1236,7 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
 				limit = (struct btrfs_qgroup_limit_item *)
 					(args.buf + off);
 
-				ret = update_qgroup_limit(qgroup_lookup,
+				ret = update_qgroup_limit(fd, qgroup_lookup,
 							  qgroupid, limit);
 				break;
 			case BTRFS_QGROUP_RELATION_KEY:
@@ -1159,7 +1280,7 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
 	return ret;
 }
 
-static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
+static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup, bool verbose)
 {
 
 	struct rb_node *n;
@@ -1170,14 +1291,15 @@ static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
 	n = rb_first(&qgroup_lookup->root);
 	while (n) {
 		entry = rb_entry(n, struct btrfs_qgroup, sort_node);
-		print_single_qgroup_table(entry);
+		print_single_qgroup_table(entry, verbose);
 		n = rb_next(n);
 	}
 }
 
 int btrfs_show_qgroups(int fd,
 		       struct btrfs_qgroup_filter_set *filter_set,
-		       struct btrfs_qgroup_comparer_set *comp_set)
+		       struct btrfs_qgroup_comparer_set *comp_set,
+		       bool verbose)
 {
 
 	struct qgroup_lookup qgroup_lookup;
@@ -1189,7 +1311,7 @@ int btrfs_show_qgroups(int fd,
 		return ret;
 	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
 				  filter_set, comp_set);
-	print_all_qgroups(&sort_tree);
+	print_all_qgroups(&sort_tree, verbose);
 
 	__free_all_qgroups(&qgroup_lookup);
 	return ret;
diff --git a/qgroup.h b/qgroup.h
index 875fbdf3..f7ab7de5 100644
--- a/qgroup.h
+++ b/qgroup.h
@@ -59,11 +59,13 @@ enum btrfs_qgroup_column_enum {
 	BTRFS_QGROUP_MAX_EXCL,
 	BTRFS_QGROUP_PARENT,
 	BTRFS_QGROUP_CHILD,
+	BTRFS_QGROUP_PATHNAME,
 	BTRFS_QGROUP_ALL,
 };
 
 enum btrfs_qgroup_comp_enum {
 	BTRFS_QGROUP_COMP_QGROUPID,
+	BTRFS_QGROUP_COMP_PATHNAME,
 	BTRFS_QGROUP_COMP_RFER,
 	BTRFS_QGROUP_COMP_EXCL,
 	BTRFS_QGROUP_COMP_MAX_RFER,
@@ -80,7 +82,7 @@ enum btrfs_qgroup_filter_enum {
 int btrfs_qgroup_parse_sort_string(const char *opt_arg,
 				struct btrfs_qgroup_comparer_set **comps);
 int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
-		       struct btrfs_qgroup_comparer_set *);
+		       struct btrfs_qgroup_comparer_set *, bool verbose);
 void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column);
 void btrfs_qgroup_setup_units(unsigned unit_mode);
 struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void);
diff --git a/utils.c b/utils.c
index e9cb3a82..7b7f87f1 100644
--- a/utils.c
+++ b/utils.c
@@ -2556,15 +2556,9 @@ out:
 	return ret;
 }
 
-int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri, u64 r_id)
+int get_subvol_info_by_rootid_fd(int fd, struct root_info *get_ri, u64 r_id)
 {
-	int fd;
 	int ret;
-	DIR *dirstream = NULL;
-
-	fd = btrfs_open_dir(mnt, &dirstream, 1);
-	if (fd < 0)
-		return -EINVAL;
 
 	memset(get_ri, 0, sizeof(*get_ri));
 	get_ri->root_id = r_id;
@@ -2574,6 +2568,21 @@ int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri, u64 r_i
 	else
 		ret = btrfs_get_subvol(fd, get_ri);
 
+	return ret;
+}
+
+int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri,
+			      u64 r_id)
+{
+	int fd;
+	int ret;
+	DIR *dirstream = NULL;
+
+	fd = btrfs_open_dir(mnt, &dirstream, 1);
+	if (fd < 0)
+		return -EINVAL;
+
+	ret = get_subvol_info_by_rootid_fd(fd, get_ri, r_id);
 	if (ret)
 		error("can't find rootid '%llu' on '%s': %d", r_id, mnt, ret);
 
diff --git a/utils.h b/utils.h
index b871c9ff..722d3c48 100644
--- a/utils.h
+++ b/utils.h
@@ -154,6 +154,8 @@ int test_isdir(const char *path);
 
 const char *subvol_strip_mountpoint(const char *mnt, const char *full_path);
 int get_subvol_info(const char *fullpath, struct root_info *get_ri);
+int get_subvol_info_by_rootid_fd(int fd, struct root_info *get_ri,
+				 u64 rootid_arg);
 int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri,
 							u64 rootid_arg);
 int get_subvol_info_by_uuid(const char *mnt, struct root_info *get_ri,
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 07/20] btrfs-progs: qgroups: introduce and use info and limit structures
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (5 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 06/20] btrfs-progs: qgroups: add pathname to show output jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  5:34   ` Qu Wenruo
  2018-03-08  2:40 ` [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query jeffm
                   ` (12 subsequent siblings)
  19 siblings, 1 reply; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

We use structures to pass the info and limit from the kernel as items
but store the individual values separately in btrfs_qgroup.  We already
have a btrfs_qgroup_limit structure that's used for setting the limit.

This patch introduces a btrfs_qgroup_info structure and uses that and
btrfs_qgroup_limit in btrfs_qgroup.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 qgroup.c | 82 ++++++++++++++++++++++++++++++++++------------------------------
 qgroup.h |  8 +++++++
 2 files changed, 52 insertions(+), 38 deletions(-)

diff --git a/qgroup.c b/qgroup.c
index 5600da99..57815718 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -46,20 +46,12 @@ struct btrfs_qgroup {
 	/*
 	 * info_item
 	 */
-	u64 generation;
-	u64 rfer;	/*referenced*/
-	u64 rfer_cmpr;	/*referenced compressed*/
-	u64 excl;	/*exclusive*/
-	u64 excl_cmpr;	/*exclusive compressed*/
+	struct btrfs_qgroup_info info;
 
 	/*
 	 *limit_item
 	 */
-	u64 flags;	/*which limits are set*/
-	u64 max_rfer;
-	u64 max_excl;
-	u64 rsv_rfer;
-	u64 rsv_excl;
+	struct btrfs_qgroup_limit limit;
 
 	/*qgroups this group is member of*/
 	struct list_head qgroups;
@@ -260,6 +252,11 @@ void print_pathname_column(struct btrfs_qgroup *qgroup, bool verbose)
 		fputs("<missing>", stdout);
 }
 
+static int print_u64(u64 value, int unit_mode, int max_len)
+{
+	return printf("%*s", max_len, pretty_size_mode(value, unit_mode));
+}
+
 static void print_qgroup_column(struct btrfs_qgroup *qgroup,
 				enum btrfs_qgroup_column_enum column,
 				bool verbose)
@@ -279,24 +276,26 @@ static void print_qgroup_column(struct btrfs_qgroup *qgroup,
 		print_qgroup_column_add_blank(BTRFS_QGROUP_QGROUPID, len);
 		break;
 	case BTRFS_QGROUP_RFER:
-		len = printf("%*s", max_len, pretty_size_mode(qgroup->rfer, unit_mode));
+		len = print_u64(qgroup->info.referenced, unit_mode, max_len);
 		break;
 	case BTRFS_QGROUP_EXCL:
-		len = printf("%*s", max_len, pretty_size_mode(qgroup->excl, unit_mode));
+		len = print_u64(qgroup->info.exclusive, unit_mode, max_len);
 		break;
 	case BTRFS_QGROUP_PARENT:
 		len = print_parent_column(qgroup);
 		print_qgroup_column_add_blank(BTRFS_QGROUP_PARENT, len);
 		break;
 	case BTRFS_QGROUP_MAX_RFER:
-		if (qgroup->flags & BTRFS_QGROUP_LIMIT_MAX_RFER)
-			len = printf("%*s", max_len, pretty_size_mode(qgroup->max_rfer, unit_mode));
+		if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_RFER)
+			len = print_u64(qgroup->limit.max_referenced,
+					unit_mode, max_len);
 		else
 			len = printf("%*s", max_len, "none");
 		break;
 	case BTRFS_QGROUP_MAX_EXCL:
-		if (qgroup->flags & BTRFS_QGROUP_LIMIT_MAX_EXCL)
-			len = printf("%*s", max_len, pretty_size_mode(qgroup->max_excl, unit_mode));
+		if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_EXCL)
+			len = print_u64(qgroup->limit.max_exclusive,
+					unit_mode, max_len);
 		else
 			len = printf("%*s", max_len, "none");
 		break;
@@ -439,9 +438,9 @@ static int comp_entry_with_rfer(struct btrfs_qgroup *entry1,
 {
 	int ret;
 
-	if (entry1->rfer > entry2->rfer)
+	if (entry1->info.referenced > entry2->info.referenced)
 		ret = 1;
-	else if (entry1->rfer < entry2->rfer)
+	else if (entry1->info.referenced < entry2->info.referenced)
 		ret = -1;
 	else
 		ret = 0;
@@ -455,9 +454,9 @@ static int comp_entry_with_excl(struct btrfs_qgroup *entry1,
 {
 	int ret;
 
-	if (entry1->excl > entry2->excl)
+	if (entry1->info.exclusive > entry2->info.exclusive)
 		ret = 1;
-	else if (entry1->excl < entry2->excl)
+	else if (entry1->info.exclusive < entry2->info.exclusive)
 		ret = -1;
 	else
 		ret = 0;
@@ -471,9 +470,9 @@ static int comp_entry_with_max_rfer(struct btrfs_qgroup *entry1,
 {
 	int ret;
 
-	if (entry1->max_rfer > entry2->max_rfer)
+	if (entry1->limit.max_referenced > entry2->limit.max_referenced)
 		ret = 1;
-	else if (entry1->max_rfer < entry2->max_rfer)
+	else if (entry1->limit.max_referenced < entry2->limit.max_referenced)
 		ret = -1;
 	else
 		ret = 0;
@@ -487,9 +486,9 @@ static int comp_entry_with_max_excl(struct btrfs_qgroup *entry1,
 {
 	int ret;
 
-	if (entry1->max_excl > entry2->max_excl)
+	if (entry1->limit.max_exclusive > entry2->limit.max_exclusive)
 		ret = 1;
-	else if (entry1->max_excl < entry2->max_excl)
+	else if (entry1->limit.max_exclusive < entry2->limit.max_exclusive)
 		ret = -1;
 	else
 		ret = 0;
@@ -747,11 +746,13 @@ static int update_qgroup_info(int fd, struct qgroup_lookup *qgroup_lookup,
 	if (IS_ERR_OR_NULL(bq))
 		return PTR_ERR(bq);
 
-	bq->generation = btrfs_stack_qgroup_info_generation(info);
-	bq->rfer = btrfs_stack_qgroup_info_referenced(info);
-	bq->rfer_cmpr = btrfs_stack_qgroup_info_referenced_compressed(info);
-	bq->excl = btrfs_stack_qgroup_info_exclusive(info);
-	bq->excl_cmpr = btrfs_stack_qgroup_info_exclusive_compressed(info);
+	bq->info.generation = btrfs_stack_qgroup_info_generation(info);
+	bq->info.referenced = btrfs_stack_qgroup_info_referenced(info);
+	bq->info.referenced_compressed =
+			btrfs_stack_qgroup_info_referenced_compressed(info);
+	bq->info.exclusive = btrfs_stack_qgroup_info_exclusive(info);
+	bq->info.exclusive_compressed =
+			btrfs_stack_qgroup_info_exclusive_compressed(info);
 
 	return 0;
 }
@@ -766,11 +767,14 @@ static int update_qgroup_limit(int fd, struct qgroup_lookup *qgroup_lookup,
 	if (IS_ERR_OR_NULL(bq))
 		return PTR_ERR(bq);
 
-	bq->flags = btrfs_stack_qgroup_limit_flags(limit);
-	bq->max_rfer = btrfs_stack_qgroup_limit_max_referenced(limit);
-	bq->max_excl = btrfs_stack_qgroup_limit_max_exclusive(limit);
-	bq->rsv_rfer = btrfs_stack_qgroup_limit_rsv_referenced(limit);
-	bq->rsv_excl = btrfs_stack_qgroup_limit_rsv_exclusive(limit);
+	bq->limit.flags = btrfs_stack_qgroup_limit_flags(limit);
+	bq->limit.max_referenced =
+			btrfs_stack_qgroup_limit_max_referenced(limit);
+	bq->limit.max_exclusive =
+			btrfs_stack_qgroup_limit_max_exclusive(limit);
+	bq->limit.rsv_referenced =
+			btrfs_stack_qgroup_limit_rsv_referenced(limit);
+	bq->limit.rsv_exclusive = btrfs_stack_qgroup_limit_rsv_exclusive(limit);
 
 	return 0;
 }
@@ -1063,22 +1067,24 @@ static void __update_columns_max_len(struct btrfs_qgroup *bq,
 			btrfs_qgroup_columns[column].max_len = len;
 		break;
 	case BTRFS_QGROUP_RFER:
-		len = strlen(pretty_size_mode(bq->rfer, unit_mode));
+		len = strlen(pretty_size_mode(bq->info.referenced, unit_mode));
 		if (btrfs_qgroup_columns[column].max_len < len)
 			btrfs_qgroup_columns[column].max_len = len;
 		break;
 	case BTRFS_QGROUP_EXCL:
-		len = strlen(pretty_size_mode(bq->excl, unit_mode));
+		len = strlen(pretty_size_mode(bq->info.exclusive, unit_mode));
 		if (btrfs_qgroup_columns[column].max_len < len)
 			btrfs_qgroup_columns[column].max_len = len;
 		break;
 	case BTRFS_QGROUP_MAX_RFER:
-		len = strlen(pretty_size_mode(bq->max_rfer, unit_mode));
+		len = strlen(pretty_size_mode(bq->limit.max_referenced,
+			     unit_mode));
 		if (btrfs_qgroup_columns[column].max_len < len)
 			btrfs_qgroup_columns[column].max_len = len;
 		break;
 	case BTRFS_QGROUP_MAX_EXCL:
-		len = strlen(pretty_size_mode(bq->max_excl, unit_mode));
+		len = strlen(pretty_size_mode(bq->limit.max_exclusive,
+			     unit_mode));
 		if (btrfs_qgroup_columns[column].max_len < len)
 			btrfs_qgroup_columns[column].max_len = len;
 		break;
diff --git a/qgroup.h b/qgroup.h
index f7ab7de5..5e71349c 100644
--- a/qgroup.h
+++ b/qgroup.h
@@ -79,6 +79,14 @@ enum btrfs_qgroup_filter_enum {
 	BTRFS_QGROUP_FILTER_MAX,
 };
 
+struct btrfs_qgroup_info {
+	u64 generation;
+	u64 referenced;
+	u64 referenced_compressed;
+	u64 exclusive;
+	u64 exclusive_compressed;
+};
+
 int btrfs_qgroup_parse_sort_string(const char *opt_arg,
 				struct btrfs_qgroup_comparer_set **comps);
 int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (6 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 07/20] btrfs-progs: qgroups: introduce and use info and limit structures jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  5:54   ` Qu Wenruo
  2018-03-08  2:40 ` [PATCH 09/20] btrfs-progs: subvolume: add quota info to btrfs sub show jeffm
                   ` (11 subsequent siblings)
  19 siblings, 1 reply; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

The only mechanism we have in the progs for searching qgroups is to load
all of them and filter the results.  This works for qgroup show but
to add quota information to 'btrfs subvoluem show' it's pretty wasteful.

This patch splits out setting up the search and performing the search so
we can search for a single qgroupid more easily.  Since TREE_SEARCH
will give results that don't strictly match the search terms, we add
a filter to match only the results we care about.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 qgroup.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++---------------
 qgroup.h |   7 ++++
 2 files changed, 116 insertions(+), 34 deletions(-)

diff --git a/qgroup.c b/qgroup.c
index 57815718..d076b1de 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -1165,11 +1165,30 @@ static inline void print_status_flag_warning(u64 flags)
 		warning("qgroup data inconsistent, rescan recommended");
 }
 
-static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
+static bool key_in_range(const struct btrfs_key *key,
+			 const struct btrfs_ioctl_search_key *sk)
+{
+	if (key->objectid < sk->min_objectid ||
+	    key->objectid > sk->max_objectid)
+		return false;
+
+	if (key->type < sk->min_type ||
+	    key->type > sk->max_type)
+		return false;
+
+	if (key->offset < sk->min_offset ||
+	    key->offset > sk->max_offset)
+		return false;
+
+	return true;
+}
+
+static int __qgroups_search(int fd, struct btrfs_ioctl_search_args *args,
+			    struct qgroup_lookup *qgroup_lookup)
 {
 	int ret;
-	struct btrfs_ioctl_search_args args;
-	struct btrfs_ioctl_search_key *sk = &args.key;
+	struct btrfs_ioctl_search_key *sk = &args->key;
+	struct btrfs_ioctl_search_key filter_key = args->key;
 	struct btrfs_ioctl_search_header *sh;
 	unsigned long off = 0;
 	unsigned int i;
@@ -1180,30 +1199,15 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
 	u64 qgroupid;
 	u64 qgroupid1;
 
-	memset(&args, 0, sizeof(args));
-
-	sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
-	sk->max_type = BTRFS_QGROUP_RELATION_KEY;
-	sk->min_type = BTRFS_QGROUP_STATUS_KEY;
-	sk->max_objectid = (u64)-1;
-	sk->max_offset = (u64)-1;
-	sk->max_transid = (u64)-1;
-	sk->nr_items = 4096;
-
 	qgroup_lookup_init(qgroup_lookup);
 
 	while (1) {
-		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, args);
 		if (ret < 0) {
-			if (errno == ENOENT) {
-				error("can't list qgroups: quotas not enabled");
+			if (errno == ENOENT)
 				ret = -ENOTTY;
-			} else {
-				error("can't list qgroups: %s",
-				       strerror(errno));
+			else
 				ret = -errno;
-			}
-
 			break;
 		}
 
@@ -1217,37 +1221,46 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
 		 * read the root_ref item it contains
 		 */
 		for (i = 0; i < sk->nr_items; i++) {
-			sh = (struct btrfs_ioctl_search_header *)(args.buf +
+			struct btrfs_key key;
+
+			sh = (struct btrfs_ioctl_search_header *)(args->buf +
 								  off);
 			off += sizeof(*sh);
 
-			switch (btrfs_search_header_type(sh)) {
+			key.objectid = btrfs_search_header_objectid(sh);
+			key.type = btrfs_search_header_type(sh);
+			key.offset = btrfs_search_header_offset(sh);
+
+			if (!key_in_range(&key, &filter_key))
+				goto next;
+
+			switch (key.type) {
 			case BTRFS_QGROUP_STATUS_KEY:
 				si = (struct btrfs_qgroup_status_item *)
-				     (args.buf + off);
+				     (args->buf + off);
 				flags = btrfs_stack_qgroup_status_flags(si);
 
 				print_status_flag_warning(flags);
 				break;
 			case BTRFS_QGROUP_INFO_KEY:
-				qgroupid = btrfs_search_header_offset(sh);
+				qgroupid = key.offset;
 				info = (struct btrfs_qgroup_info_item *)
-				       (args.buf + off);
+				       (args->buf + off);
 
 				ret = update_qgroup_info(fd, qgroup_lookup,
 							 qgroupid, info);
 				break;
 			case BTRFS_QGROUP_LIMIT_KEY:
-				qgroupid = btrfs_search_header_offset(sh);
+				qgroupid = key.offset;
 				limit = (struct btrfs_qgroup_limit_item *)
-					(args.buf + off);
+					(args->buf + off);
 
 				ret = update_qgroup_limit(fd, qgroup_lookup,
 							  qgroupid, limit);
 				break;
 			case BTRFS_QGROUP_RELATION_KEY:
-				qgroupid = btrfs_search_header_offset(sh);
-				qgroupid1 = btrfs_search_header_objectid(sh);
+				qgroupid = key.offset;
+				qgroupid1 = key.objectid;
 
 				if (qgroupid < qgroupid1)
 					break;
@@ -1262,15 +1275,16 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
 			if (ret)
 				return ret;
 
+next:
 			off += btrfs_search_header_len(sh);
 
 			/*
 			 * record the mins in sk so we can make sure the
 			 * next search doesn't repeat this root
 			 */
-			sk->min_type = btrfs_search_header_type(sh);
-			sk->min_offset = btrfs_search_header_offset(sh);
-			sk->min_objectid = btrfs_search_header_objectid(sh);
+			sk->min_type = key.type;
+			sk->min_offset = key.offset;
+			sk->min_objectid = key.objectid;
 		}
 		sk->nr_items = 4096;
 		/*
@@ -1286,6 +1300,67 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
 	return ret;
 }
 
+static int qgroups_search_all(int fd, struct qgroup_lookup *qgroup_lookup)
+{
+	struct btrfs_ioctl_search_args args = {
+		.key = {
+			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
+			.max_type = BTRFS_QGROUP_RELATION_KEY,
+			.min_type = BTRFS_QGROUP_STATUS_KEY,
+			.max_objectid = (u64)-1,
+			.max_offset = (u64)-1,
+			.max_transid = (u64)-1,
+			.nr_items = 4096,
+		},
+	};
+	int ret;
+
+	ret = __qgroups_search(fd, &args, qgroup_lookup);
+	if (ret == -ENOTTY)
+		error("can't list qgroups: quotas not enabled");
+	else if (ret < 0)
+		error("can't list qgroups: %s", strerror(-ret));
+	return ret;
+}
+
+int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats)
+{
+	struct btrfs_ioctl_search_args args = {
+		.key = {
+			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
+			.min_type = BTRFS_QGROUP_INFO_KEY,
+			.max_type = BTRFS_QGROUP_LIMIT_KEY,
+			.max_objectid = 0,
+			.min_offset = qgroupid,
+			.max_offset = qgroupid,
+			.max_transid = (u64)-1,
+			.nr_items = 4096,
+		},
+	};
+	struct qgroup_lookup qgroup_lookup;
+	struct btrfs_qgroup *qgroup;
+	struct rb_node *n;
+	int ret;
+
+	ret = __qgroups_search(fd, &args, &qgroup_lookup);
+	if (ret < 0)
+		return ret;
+
+	ret = -ENODATA;
+	n = rb_first(&qgroup_lookup.root);
+	if (n) {
+		qgroup = rb_entry(n, struct btrfs_qgroup, rb_node);
+		stats->qgroupid = qgroup->qgroupid;
+		stats->info = qgroup->info;
+		stats->limit = qgroup->limit;
+
+		ret = 0;
+	}
+
+	__free_all_qgroups(&qgroup_lookup);
+	return ret;
+}
+
 static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup, bool verbose)
 {
 
@@ -1312,7 +1387,7 @@ int btrfs_show_qgroups(int fd,
 	struct qgroup_lookup sort_tree;
 	int ret;
 
-	ret = __qgroups_search(fd, &qgroup_lookup);
+	ret = qgroups_search_all(fd, &qgroup_lookup);
 	if (ret)
 		return ret;
 	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
diff --git a/qgroup.h b/qgroup.h
index 5e71349c..688f92b2 100644
--- a/qgroup.h
+++ b/qgroup.h
@@ -87,6 +87,12 @@ struct btrfs_qgroup_info {
 	u64 exclusive_compressed;
 };
 
+struct btrfs_qgroup_stats {
+	u64 qgroupid;
+	struct btrfs_qgroup_info info;
+	struct btrfs_qgroup_limit limit;
+};
+
 int btrfs_qgroup_parse_sort_string(const char *opt_arg,
 				struct btrfs_qgroup_comparer_set **comps);
 int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
@@ -105,4 +111,5 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
 int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
 			    int type);
 
+int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats);
 #endif
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 09/20] btrfs-progs: subvolume: add quota info to btrfs sub show
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (7 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 10/20] btrfs-progs: help: convert ints used as bools to bool jeffm
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

This patch reports on the first-level qgroup, if any, associated with
a particular subvolume.  It displays the usage and limit, subject
to the usual unit parameters.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 cmds-subvolume.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)

diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 769d2a76..96fb7b06 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -972,6 +972,7 @@ static const char * const cmd_subvol_show_usage[] = {
 	"Show more information about the subvolume",
 	"-r|--rootid   rootid of the subvolume",
 	"-u|--uuid     uuid of the subvolume",
+	HELPINFO_UNITS_SHORT_LONG,
 	"",
 	"If no option is specified, <subvol-path> will be shown, otherwise",
 	"the rootid or uuid are resolved relative to the <mnt> path.",
@@ -993,6 +994,13 @@ static int cmd_subvol_show(int argc, char **argv)
 	int by_uuid = 0;
 	u64 rootid_arg;
 	u8 uuid_arg[BTRFS_UUID_SIZE];
+	struct btrfs_qgroup_stats stats;
+	unsigned int unit_mode;
+	const char *referenced_size;
+	const char *referenced_limit_size = "-";
+	unsigned int field_width = 0;
+
+	unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
 
 	while (1) {
 		int c;
@@ -1112,6 +1120,48 @@ static int cmd_subvol_show(int argc, char **argv)
 	btrfs_list_subvols_print(fd, filter_set, NULL, BTRFS_LIST_LAYOUT_RAW,
 			1, raw_prefix);
 
+	ret = btrfs_qgroup_query(fd, get_ri.root_id, &stats);
+	if (ret && ret != -ENOTTY && ret != -ENODATA) {
+		fprintf(stderr,
+			"\nERROR: BTRFS_IOC_QUOTA_QUERY failed: %s\n",
+			strerror(-ret));
+		goto out;
+	}
+
+	printf("\tQuota Usage:\t\t");
+	fflush(stdout);
+	if (ret) {
+		if (ret == -ENOTTY)
+			printf("quotas not enabled\n");
+		else
+			printf("quotas not available\n");
+		goto out;
+	}
+
+	referenced_size = pretty_size_mode(stats.info.referenced, unit_mode);
+	if (stats.limit.max_referenced)
+		referenced_limit_size = pretty_size_mode(
+						stats.limit.max_referenced,
+						unit_mode);
+	field_width = max(strlen(referenced_size),
+			  strlen(referenced_limit_size));
+
+	printf("%-*s referenced, %s exclusive\n ", field_width,
+	       referenced_size,
+	       pretty_size_mode(stats.info.exclusive, unit_mode));
+
+	printf("\tQuota Limits:\t\t");
+	if (stats.limit.max_referenced || stats.limit.max_exclusive) {
+		const char *excl = "-";
+
+		if (stats.limit.max_exclusive)
+			excl = pretty_size_mode(stats.limit.max_exclusive,
+						unit_mode);
+		printf("%-*s referenced, %s exclusive\n", field_width,
+		       referenced_limit_size, excl);
+	} else
+		printf("None\n");
+
 out:
 	btrfs_cleanup_root_info(&get_ri);
 	free(filter_set);
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 10/20] btrfs-progs: help: convert ints used as bools to bool
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (8 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 09/20] btrfs-progs: subvolume: add quota info to btrfs sub show jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  5:55   ` Qu Wenruo
  2018-03-08  2:40 ` [PATCH 11/20] btrfs-progs: reorder placement of help declarations for send/receive jeffm
                   ` (9 subsequent siblings)
  19 siblings, 1 reply; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

We use an int for 'full', 'all', and 'err' when we really mean a boolean.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs.c | 14 +++++++-------
 help.c  | 25 +++++++++++++------------
 help.h  |  4 ++--
 3 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/btrfs.c b/btrfs.c
index 2d39f2ce..fec1a135 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -109,7 +109,7 @@ static void handle_help_options_next_level(const struct cmd_struct *cmd,
 			argv++;
 			help_command_group(cmd->next, argc, argv);
 		} else {
-			usage_command(cmd, 1, 0);
+			usage_command(cmd, true, false);
 		}
 
 		exit(0);
@@ -125,7 +125,7 @@ int handle_command_group(const struct cmd_group *grp, int argc,
 	argc--;
 	argv++;
 	if (argc < 1) {
-		usage_command_group(grp, 0, 0);
+		usage_command_group(grp, false, false);
 		exit(1);
 	}
 
@@ -212,20 +212,20 @@ static int handle_global_options(int argc, char **argv)
 
 void handle_special_globals(int shift, int argc, char **argv)
 {
-	int has_help = 0;
-	int has_full = 0;
+	bool has_help = false;
+	bool has_full = false;
 	int i;
 
 	for (i = 0; i < shift; i++) {
 		if (strcmp(argv[i], "--help") == 0)
-			has_help = 1;
+			has_help = true;
 		else if (strcmp(argv[i], "--full") == 0)
-			has_full = 1;
+			has_full = true;
 	}
 
 	if (has_help) {
 		if (has_full)
-			usage_command_group(&btrfs_cmd_group, 1, 0);
+			usage_command_group(&btrfs_cmd_group, true, false);
 		else
 			cmd_help(argc, argv);
 		exit(0);
diff --git a/help.c b/help.c
index 311a4320..ef7986b4 100644
--- a/help.c
+++ b/help.c
@@ -196,8 +196,8 @@ static int do_usage_one_command(const char * const *usagestr,
 }
 
 static int usage_command_internal(const char * const *usagestr,
-				  const char *token, int full, int lst,
-				  int alias, FILE *outf)
+				  const char *token, bool full, bool lst,
+				  bool alias, FILE *outf)
 {
 	unsigned int flags = 0;
 	int ret;
@@ -223,17 +223,17 @@ static int usage_command_internal(const char * const *usagestr,
 }
 
 static void usage_command_usagestr(const char * const *usagestr,
-				   const char *token, int full, int err)
+				   const char *token, bool full, bool err)
 {
 	FILE *outf = err ? stderr : stdout;
 	int ret;
 
-	ret = usage_command_internal(usagestr, token, full, 0, 0, outf);
+	ret = usage_command_internal(usagestr, token, full, false, false, outf);
 	if (!ret)
 		fputc('\n', outf);
 }
 
-void usage_command(const struct cmd_struct *cmd, int full, int err)
+void usage_command(const struct cmd_struct *cmd, bool full, bool err)
 {
 	usage_command_usagestr(cmd->usagestr, cmd->token, full, err);
 }
@@ -241,11 +241,11 @@ void usage_command(const struct cmd_struct *cmd, int full, int err)
 __attribute__((noreturn))
 void usage(const char * const *usagestr)
 {
-	usage_command_usagestr(usagestr, NULL, 1, 1);
+	usage_command_usagestr(usagestr, NULL, true, true);
 	exit(1);
 }
 
-static void usage_command_group_internal(const struct cmd_group *grp, int full,
+static void usage_command_group_internal(const struct cmd_group *grp, bool full,
 					 FILE *outf)
 {
 	const struct cmd_struct *cmd = grp->commands;
@@ -265,7 +265,8 @@ static void usage_command_group_internal(const struct cmd_group *grp, int full,
 			}
 
 			usage_command_internal(cmd->usagestr, cmd->token, full,
-					       1, cmd->flags & CMD_ALIAS, outf);
+					       true, cmd->flags & CMD_ALIAS,
+					       outf);
 			if (cmd->flags & CMD_ALIAS)
 				putchar('\n');
 			continue;
@@ -327,7 +328,7 @@ void usage_command_group_short(const struct cmd_group *grp)
 	fprintf(stderr, "All command groups have their manual page named 'btrfs-<group>'.\n");
 }
 
-void usage_command_group(const struct cmd_group *grp, int full, int err)
+void usage_command_group(const struct cmd_group *grp, bool full, bool err)
 {
 	const char * const *usagestr = grp->usagestr;
 	FILE *outf = err ? stderr : stdout;
@@ -350,7 +351,7 @@ __attribute__((noreturn))
 void help_unknown_token(const char *arg, const struct cmd_group *grp)
 {
 	fprintf(stderr, "%s: unknown token '%s'\n", get_argv0_buf(), arg);
-	usage_command_group(grp, 0, 1);
+	usage_command_group(grp, false, true);
 	exit(1);
 }
 
@@ -372,13 +373,13 @@ void help_ambiguous_token(const char *arg, const struct cmd_group *grp)
 
 void help_command_group(const struct cmd_group *grp, int argc, char **argv)
 {
-	int full = 0;
+	bool full = false;
 
 	if (argc > 1) {
 		if (!strcmp(argv[1], "--full"))
 			full = 1;
 	}
 
-	usage_command_group(grp, full, 0);
+	usage_command_group(grp, full, false);
 }
 
diff --git a/help.h b/help.h
index efeded30..a69ea6b2 100644
--- a/help.h
+++ b/help.h
@@ -57,8 +57,8 @@ struct cmd_group;
 
 __attribute__((noreturn))
 void usage(const char * const *usagestr);
-void usage_command(const struct cmd_struct *cmd, int full, int err);
-void usage_command_group(const struct cmd_group *grp, int all, int err);
+void usage_command(const struct cmd_struct *cmd, bool full, bool err);
+void usage_command_group(const struct cmd_group *grp, bool all, bool err);
 void usage_command_group_short(const struct cmd_group *grp);
 
 __attribute__((noreturn))
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 11/20] btrfs-progs: reorder placement of help declarations for send/receive
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (9 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 10/20] btrfs-progs: help: convert ints used as bools to bool jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 12/20] btrfs-progs: filesystem balance: split out special handling jeffm
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

The usage definitions for send and receive follow the command
definitions, which use them.  This works because we declare them
in commands.h.  When we move to using cmd_struct as the entry point,
these declarations will be removed, breaking the commands.  Since
that would be an otherwise unrelated change, this patch reorders
them separately.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 cmds-receive.c | 62 ++++++++++++++++++++++++++--------------------------
 cmds-send.c    | 69 +++++++++++++++++++++++++++++-----------------------------
 2 files changed, 66 insertions(+), 65 deletions(-)

diff --git a/cmds-receive.c b/cmds-receive.c
index 68123a31..b3709f36 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -1248,6 +1248,37 @@ out:
 	return ret;
 }
 
+const char * const cmd_receive_usage[] = {
+	"btrfs receive [options] <mount>\n"
+	"btrfs receive --dump [options]",
+	"Receive subvolumes from a stream",
+	"Receives one or more subvolumes that were previously",
+	"sent with btrfs send. The received subvolumes are stored",
+	"into MOUNT.",
+	"The receive will fail in case the receiving subvolume",
+	"already exists. It will also fail in case a previously",
+	"received subvolume has been changed after it was received.",
+	"After receiving a subvolume, it is immediately set to",
+	"read-only.",
+	"",
+	"-v               increase verbosity about performed actions",
+	"-f FILE          read the stream from FILE instead of stdin",
+	"-e               terminate after receiving an <end cmd> marker in the stream.",
+	"                 Without this option the receiver side terminates only in case",
+	"                 of an error on end of file.",
+	"-C|--chroot      confine the process to <mount> using chroot",
+	"-E|--max-errors NERR",
+	"                 terminate as soon as NERR errors occur while",
+	"                 stream processing commands from the stream.",
+	"                 Default value is 1. A value of 0 means no limit.",
+	"-m ROOTMOUNT     the root mount point of the destination filesystem.",
+	"                 If /proc is not accessible, use this to tell us where",
+	"                 this file system is mounted.",
+	"--dump           dump stream metadata, one line per operation,",
+	"                 does not require the MOUNT parameter",
+	NULL
+};
+
 int cmd_receive(int argc, char **argv)
 {
 	char *tomnt = NULL;
@@ -1357,34 +1388,3 @@ out:
 
 	return !!ret;
 }
-
-const char * const cmd_receive_usage[] = {
-	"btrfs receive [options] <mount>\n"
-	"btrfs receive --dump [options]",
-	"Receive subvolumes from a stream",
-	"Receives one or more subvolumes that were previously",
-	"sent with btrfs send. The received subvolumes are stored",
-	"into MOUNT.",
-	"The receive will fail in case the receiving subvolume",
-	"already exists. It will also fail in case a previously",
-	"received subvolume has been changed after it was received.",
-	"After receiving a subvolume, it is immediately set to",
-	"read-only.",
-	"",
-	"-v               increase verbosity about performed actions",
-	"-f FILE          read the stream from FILE instead of stdin",
-	"-e               terminate after receiving an <end cmd> marker in the stream.",
-	"                 Without this option the receiver side terminates only in case",
-	"                 of an error on end of file.",
-	"-C|--chroot      confine the process to <mount> using chroot",
-	"-E|--max-errors NERR",
-	"                 terminate as soon as NERR errors occur while",
-	"                 stream processing commands from the stream.",
-	"                 Default value is 1. A value of 0 means no limit.",
-	"-m ROOTMOUNT     the root mount point of the destination filesystem.",
-	"                 If /proc is not accessible, use this to tell us where",
-	"                 this file system is mounted.",
-	"--dump           dump stream metadata, one line per operation,",
-	"                 does not require the MOUNT parameter",
-	NULL
-};
diff --git a/cmds-send.c b/cmds-send.c
index c5ecdaa1..8365e9c9 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -489,6 +489,41 @@ static void free_send_info(struct btrfs_send *sctx)
 	subvol_uuid_search_finit(&sctx->sus);
 }
 
+
+const char * const cmd_send_usage[] = {
+	"btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
+	"Send the subvolume(s) to stdout.",
+	"Sends the subvolume(s) specified by <subvol> to stdout.",
+	"<subvol> should be read-only here.",
+	"By default, this will send the whole subvolume. To do an incremental",
+	"send, use '-p <parent>'. If you want to allow btrfs to clone from",
+	"any additional local snapshots, use '-c <clone-src>' (multiple times",
+	"where applicable). You must not specify clone sources unless you",
+	"guarantee that these snapshots are exactly in the same state on both",
+	"sides, the sender and the receiver. It is allowed to omit the",
+	"'-p <parent>' option when '-c <clone-src>' options are given, in",
+	"which case 'btrfs send' will determine a suitable parent among the",
+	"clone sources itself.",
+	"\n",
+	"-e               If sending multiple subvols at once, use the new",
+	"                 format and omit the end-cmd between the subvols.",
+	"-p <parent>      Send an incremental stream from <parent> to",
+	"                 <subvol>.",
+	"-c <clone-src>   Use this snapshot as a clone source for an ",
+	"                 incremental send (multiple allowed)",
+	"-f <outfile>     Output is normally written to stdout. To write to",
+	"                 a file, use this option. An alternative would be to",
+	"                 use pipes.",
+	"--no-data        send in NO_FILE_DATA mode, Note: the output stream",
+	"                 does not contain any file data and thus cannot be used",
+	"                 to transfer changes. This mode is faster and useful to",
+	"                 show the differences in metadata.",
+	"-v|--verbose     enable verbose output to stderr, each occurrence of",
+	"                 this option increases verbosity",
+	"-q|--quiet       suppress all messages, except errors",
+	NULL
+};
+
 int cmd_send(int argc, char **argv)
 {
 	char *subvol = NULL;
@@ -774,37 +809,3 @@ out:
 	free_send_info(&send);
 	return !!ret;
 }
-
-const char * const cmd_send_usage[] = {
-	"btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
-	"Send the subvolume(s) to stdout.",
-	"Sends the subvolume(s) specified by <subvol> to stdout.",
-	"<subvol> should be read-only here.",
-	"By default, this will send the whole subvolume. To do an incremental",
-	"send, use '-p <parent>'. If you want to allow btrfs to clone from",
-	"any additional local snapshots, use '-c <clone-src>' (multiple times",
-	"where applicable). You must not specify clone sources unless you",
-	"guarantee that these snapshots are exactly in the same state on both",
-	"sides, the sender and the receiver. It is allowed to omit the",
-	"'-p <parent>' option when '-c <clone-src>' options are given, in",
-	"which case 'btrfs send' will determine a suitable parent among the",
-	"clone sources itself.",
-	"\n",
-	"-e               If sending multiple subvols at once, use the new",
-	"                 format and omit the end-cmd between the subvols.",
-	"-p <parent>      Send an incremental stream from <parent> to",
-	"                 <subvol>.",
-	"-c <clone-src>   Use this snapshot as a clone source for an ",
-	"                 incremental send (multiple allowed)",
-	"-f <outfile>     Output is normally written to stdout. To write to",
-	"                 a file, use this option. An alternative would be to",
-	"                 use pipes.",
-	"--no-data        send in NO_FILE_DATA mode, Note: the output stream",
-	"                 does not contain any file data and thus cannot be used",
-	"                 to transfer changes. This mode is faster and useful to",
-	"                 show the differences in metadata.",
-	"-v|--verbose     enable verbose output to stderr, each occurrence of",
-	"                 this option increases verbosity",
-	"-q|--quiet       suppress all messages, except errors",
-	NULL
-};
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 12/20] btrfs-progs: filesystem balance: split out special handling
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (10 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 11/20] btrfs-progs: reorder placement of help declarations for send/receive jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 13/20] btrfs-progs: use cmd_struct as command entry point jeffm
                   ` (7 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

In preparation to use cmd_struct as the command entry point, we need
to split out the 'filesystem balance' handling to not call cmd_balance
directly.  The reason is that the flags that indicate a command is
hidden are a part of cmd_struct and so we can use a cmd_struct as a
direct alias in another command group and ALSO have it be hidden
without declaring another cmd_struct.

This change has no immediate impact since cmd_balance will still
use its usage information directly from cmds-balance.c.  It will
take effect once we start passing cmd_structs around for usage
information.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 cmds-filesystem.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 467aff11..62112705 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -1184,6 +1184,18 @@ static int cmd_filesystem_label(int argc, char **argv)
 	}
 }
 
+static const char * const cmd_filesystem_balance_usage[] = {
+	"btrfs filesystem balance [args...] (alias of \"btrfs balance\")",
+	"Please see \"btrfs balance --help\" for more information.",
+	NULL
+};
+
+/* Compatible old "btrfs filesystem balance" command */
+static int cmd_filesystem_balance(int argc, char **argv)
+{
+	return cmd_balance(argc, argv);
+}
+
 static const char filesystem_cmd_group_info[] =
 "overall filesystem tasks and information";
 
@@ -1197,8 +1209,9 @@ const struct cmd_group filesystem_cmd_group = {
 			0 },
 		{ "defragment", cmd_filesystem_defrag,
 			cmd_filesystem_defrag_usage, NULL, 0 },
-		{ "balance", cmd_balance, NULL, &balance_cmd_group,
-			CMD_HIDDEN },
+		{ "balance", cmd_filesystem_balance,
+		   cmd_filesystem_balance_usage, &balance_cmd_group,
+		   CMD_HIDDEN },
 		{ "resize", cmd_filesystem_resize, cmd_filesystem_resize_usage,
 			NULL, 0 },
 		{ "label", cmd_filesystem_label, cmd_filesystem_label_usage,
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 13/20] btrfs-progs: use cmd_struct as command entry point
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (11 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 12/20] btrfs-progs: filesystem balance: split out special handling jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-12  3:11   ` Jeff Mahoney
  2018-03-12  3:24   ` Jeff Mahoney
  2018-03-08  2:40 ` [PATCH 14/20] btrfs-progs: pass cmd_struct to command callback function jeffm
                   ` (6 subsequent siblings)
  19 siblings, 2 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

Rather than having global command usage and callbacks used to create
cmd_structs in the command array, establish the cmd_struct structures
separately and use those.  The next commit in the series passes the
cmd_struct to the command callbacks such that we can access flags
and determine which of several potential command we were called as.

This establishes several macros to more easily define the commands
within each command's source.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs-calc-size.c         |   5 +-
 btrfs-debug-tree.c        |   5 +-
 btrfs-show-super.c        |   6 +--
 btrfs.c                   |  48 ++++++++++--------
 check/main.c              |   5 +-
 cmds-balance.c            |  27 ++++++----
 cmds-device.c             |  31 ++++++-----
 cmds-fi-du.c              |   5 +-
 cmds-fi-usage.c           |   5 +-
 cmds-filesystem.c         |  52 ++++++++++---------
 cmds-inspect-dump-super.c |   5 +-
 cmds-inspect-dump-tree.c  |   5 +-
 cmds-inspect-tree-stats.c |   5 +-
 cmds-inspect.c            |  36 +++++++------
 cmds-property.c           |  19 +++----
 cmds-qgroup.c             |  31 +++++------
 cmds-quota.c              |  17 ++++---
 cmds-receive.c            |   5 +-
 cmds-replace.c            |  19 +++----
 cmds-rescue.c             |  22 ++++----
 cmds-restore.c            |   5 +-
 cmds-scrub.c              |  20 +++++---
 cmds-send.c               |   6 +--
 cmds-subvolume.c          |  38 ++++++++------
 commands.h                | 127 +++++++++++++++++++++++++++-------------------
 help.c                    |  21 +++++---
 26 files changed, 326 insertions(+), 244 deletions(-)

diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
index d2d68ab2..908e830f 100644
--- a/btrfs-calc-size.c
+++ b/btrfs-calc-size.c
@@ -23,15 +23,16 @@
 
 int main(int argc, char **argv)
 {
+	const struct cmd_struct *cmd = &cmd_struct_inspect_tree_stats;
 	int ret;
 
 	warning(
 "\nthe tool has been deprecated, please use 'btrfs inspect-internal tree-stats' instead\n");
 
 	if (argc > 1 && !strcmp(argv[1], "--help"))
-		usage(cmd_inspect_tree_stats_usage);
+		usage(cmd->usagestr);
 
-	ret = cmd_inspect_tree_stats(argc, argv);
+	ret = cmd_execute(cmd, argc, argv);
 
 	btrfs_close_all_devices();
 
diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
index 7bee018f..7f254519 100644
--- a/btrfs-debug-tree.c
+++ b/btrfs-debug-tree.c
@@ -24,16 +24,17 @@
 
 int main(int argc, char **argv)
 {
+	const struct cmd_struct *cmd = &cmd_struct_inspect_dump_tree;
 	int ret;
 
 	set_argv0(argv);
 
 	if (argc > 1 && !strcmp(argv[1], "--help"))
-		usage(cmd_inspect_dump_tree_usage);
+		usage(cmd->usagestr);
 
 	radix_tree_init();
 
-	ret = cmd_inspect_dump_tree(argc, argv);
+	ret = cmd_execute(cmd, argc, argv);
 
 	btrfs_close_all_devices();
 
diff --git a/btrfs-show-super.c b/btrfs-show-super.c
index 4273e42d..ee717c33 100644
--- a/btrfs-show-super.c
+++ b/btrfs-show-super.c
@@ -22,7 +22,7 @@
 
 int main(int argc, char **argv)
 {
-
+	const struct cmd_struct *cmd = &cmd_struct_inspect_dump_super;
 	int ret;
 
 	set_argv0(argv);
@@ -31,9 +31,9 @@ int main(int argc, char **argv)
 "\nthe tool has been deprecated, please use 'btrfs inspect-internal dump-super' instead\n");
 
 	if (argc > 1 && !strcmp(argv[1], "--help"))
-		usage(cmd_inspect_dump_super_usage);
+		usage(cmd->usagestr);
 
-	ret = cmd_inspect_dump_super(argc, argv);
+	ret = cmd_execute(cmd, argc, argv);
 
 	return ret;
 }
diff --git a/btrfs.c b/btrfs.c
index fec1a135..1e68b0c0 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -42,10 +42,11 @@ static inline const char *skip_prefix(const char *str, const char *prefix)
 static int parse_one_token(const char *arg, const struct cmd_group *grp,
 			   const struct cmd_struct **cmd_ret)
 {
-	const struct cmd_struct *cmd = grp->commands;
 	const struct cmd_struct *abbrev_cmd = NULL, *ambiguous_cmd = NULL;
+	int i = 0;
 
-	for (; cmd->token; cmd++) {
+	for (i = 0; grp->commands[i]; i++) {
+		const struct cmd_struct *cmd = grp->commands[i];
 		const char *rest;
 
 		rest = skip_prefix(arg, cmd->token);
@@ -134,7 +135,7 @@ int handle_command_group(const struct cmd_group *grp, int argc,
 	handle_help_options_next_level(cmd, argc, argv);
 
 	fixup_argv0(argv, cmd->token);
-	return cmd->fn(argc, argv);
+	return cmd_execute(cmd, argc, argv);
 }
 
 static const struct cmd_group btrfs_cmd_group;
@@ -153,6 +154,8 @@ static int cmd_help(int argc, char **argv)
 	return 0;
 }
 
+static DEFINE_SIMPLE_COMMAND(help, "help");
+
 static const char * const cmd_version_usage[] = {
 	"btrfs version",
 	"Display btrfs-progs version",
@@ -164,6 +167,7 @@ static int cmd_version(int argc, char **argv)
 	printf("%s\n", PACKAGE_STRING);
 	return 0;
 }
+static DEFINE_SIMPLE_COMMAND(version, "version");
 
 /*
  * Parse global options, between binary name and first non-option argument
@@ -240,24 +244,24 @@ void handle_special_globals(int shift, int argc, char **argv)
 
 static const struct cmd_group btrfs_cmd_group = {
 	btrfs_cmd_group_usage, btrfs_cmd_group_info, {
-		{ "subvolume", cmd_subvolume, NULL, &subvolume_cmd_group, 0 },
-		{ "filesystem", cmd_filesystem, NULL, &filesystem_cmd_group, 0 },
-		{ "balance", cmd_balance, NULL, &balance_cmd_group, 0 },
-		{ "device", cmd_device, NULL, &device_cmd_group, 0 },
-		{ "scrub", cmd_scrub, NULL, &scrub_cmd_group, 0 },
-		{ "check", cmd_check, cmd_check_usage, NULL, 0 },
-		{ "rescue", cmd_rescue, NULL, &rescue_cmd_group, 0 },
-		{ "restore", cmd_restore, cmd_restore_usage, NULL, 0 },
-		{ "inspect-internal", cmd_inspect, NULL, &inspect_cmd_group, 0 },
-		{ "property", cmd_property, NULL, &property_cmd_group, 0 },
-		{ "send", cmd_send, cmd_send_usage, NULL, 0 },
-		{ "receive", cmd_receive, cmd_receive_usage, NULL, 0 },
-		{ "quota", cmd_quota, NULL, &quota_cmd_group, 0 },
-		{ "qgroup", cmd_qgroup, NULL, &qgroup_cmd_group, 0 },
-		{ "replace", cmd_replace, NULL, &replace_cmd_group, 0 },
-		{ "help", cmd_help, cmd_help_usage, NULL, 0 },
-		{ "version", cmd_version, cmd_version_usage, NULL, 0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_subvolume,
+		&cmd_struct_filesystem,
+		&cmd_struct_balance,
+		&cmd_struct_device,
+		&cmd_struct_scrub,
+		&cmd_struct_check,
+		&cmd_struct_rescue,
+		&cmd_struct_restore,
+		&cmd_struct_inspect,
+		&cmd_struct_property,
+		&cmd_struct_send,
+		&cmd_struct_receive,
+		&cmd_struct_quota,
+		&cmd_struct_qgroup,
+		&cmd_struct_replace,
+		&cmd_struct_help,
+		&cmd_struct_version,
+		NULL
 	},
 };
 
@@ -299,7 +303,7 @@ int main(int argc, char **argv)
 
 	fixup_argv0(argv, cmd->token);
 
-	ret = cmd->fn(argc, argv);
+	ret = cmd_execute(cmd, argc, argv);
 
 	btrfs_close_all_devices();
 
diff --git a/check/main.c b/check/main.c
index 97baae58..4b8f7678 100644
--- a/check/main.c
+++ b/check/main.c
@@ -9408,7 +9408,7 @@ close_out:
 	return ret;
 }
 
-const char * const cmd_check_usage[] = {
+static const char * const cmd_check_usage[] = {
 	"btrfs check [options] <device>",
 	"Check structural integrity of a filesystem (unmounted).",
 	"Check structural integrity of an unmounted filesystem. Verify internal",
@@ -9440,7 +9440,7 @@ const char * const cmd_check_usage[] = {
 	NULL
 };
 
-int cmd_check(int argc, char **argv)
+static int cmd_check(int argc, char **argv)
 {
 	struct cache_tree root_cache;
 	struct btrfs_root *root;
@@ -9930,3 +9930,4 @@ err_out:
 
 	return err;
 }
+DEFINE_SIMPLE_COMMAND(check, "check");
diff --git a/cmds-balance.c b/cmds-balance.c
index 0c91bdf1..7a60be61 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -672,6 +672,7 @@ static int cmd_balance_start(int argc, char **argv)
 
 	return do_balance(argv[optind], &args, start_flags);
 }
+static DEFINE_SIMPLE_COMMAND(balance_start, "start");
 
 static const char * const cmd_balance_pause_usage[] = {
 	"btrfs balance pause <path>",
@@ -710,6 +711,7 @@ static int cmd_balance_pause(int argc, char **argv)
 	close_file_or_dir(fd, dirstream);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(balance_pause, "pause");
 
 static const char * const cmd_balance_cancel_usage[] = {
 	"btrfs balance cancel <path>",
@@ -748,6 +750,7 @@ static int cmd_balance_cancel(int argc, char **argv)
 	close_file_or_dir(fd, dirstream);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(balance_cancel, "cancel");
 
 static const char * const cmd_balance_resume_usage[] = {
 	"btrfs balance resume <path>",
@@ -807,6 +810,7 @@ static int cmd_balance_resume(int argc, char **argv)
 	close_file_or_dir(fd, dirstream);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(balance_resume, "resume");
 
 static const char * const cmd_balance_status_usage[] = {
 	"btrfs balance status [-v] <path>",
@@ -898,6 +902,7 @@ out:
 	close_file_or_dir(fd, dirstream);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(balance_status, "status");
 
 static int cmd_balance_full(int argc, char **argv)
 {
@@ -908,23 +913,25 @@ static int cmd_balance_full(int argc, char **argv)
 
 	return do_balance(argv[1], &args, BALANCE_START_NOWARN);
 }
+static DEFINE_COMMAND(balance_full, "--full-balance", cmd_balance_full,
+		      NULL, NULL, CMD_HIDDEN);
 
 static const char balance_cmd_group_info[] =
 "balance data across devices, or change block groups using filters";
 
-const struct cmd_group balance_cmd_group = {
+static const struct cmd_group balance_cmd_group = {
 	balance_cmd_group_usage, balance_cmd_group_info, {
-		{ "start", cmd_balance_start, cmd_balance_start_usage, NULL, 0 },
-		{ "pause", cmd_balance_pause, cmd_balance_pause_usage, NULL, 0 },
-		{ "cancel", cmd_balance_cancel, cmd_balance_cancel_usage, NULL, 0 },
-		{ "resume", cmd_balance_resume, cmd_balance_resume_usage, NULL, 0 },
-		{ "status", cmd_balance_status, cmd_balance_status_usage, NULL, 0 },
-		{ "--full-balance", cmd_balance_full, NULL, NULL, 1 },
-		NULL_CMD_STRUCT
+		&cmd_struct_balance_start,
+		&cmd_struct_balance_pause,
+		&cmd_struct_balance_cancel,
+		&cmd_struct_balance_resume,
+		&cmd_struct_balance_status,
+		&cmd_struct_balance_full,
+		NULL
 	}
 };
 
-int cmd_balance(int argc, char **argv)
+static int cmd_balance(int argc, char **argv)
 {
 	if (argc == 2 && strcmp("start", argv[1]) != 0) {
 		/* old 'btrfs filesystem balance <path>' syntax */
@@ -938,3 +945,5 @@ int cmd_balance(int argc, char **argv)
 
 	return handle_command_group(&balance_cmd_group, argc, argv);
 }
+
+DEFINE_GROUP_COMMAND(balance, "balance");
diff --git a/cmds-device.c b/cmds-device.c
index a49c9d9d..96764d6c 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -140,6 +140,7 @@ error_out:
 	close_file_or_dir(fdmnt, dirstream);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(device_add, "add");
 
 static int _cmd_device_remove(int argc, char **argv,
 		const char * const *usagestr)
@@ -239,6 +240,7 @@ static int cmd_device_remove(int argc, char **argv)
 {
 	return _cmd_device_remove(argc, argv, cmd_device_remove_usage);
 }
+static DEFINE_SIMPLE_COMMAND(device_remove, "remove");
 
 static const char * const cmd_device_delete_usage[] = {
 	"btrfs device delete <device>|<devid> [<device>|<devid>...] <path>",
@@ -252,6 +254,8 @@ static int cmd_device_delete(int argc, char **argv)
 {
 	return _cmd_device_remove(argc, argv, cmd_device_delete_usage);
 }
+static DEFINE_COMMAND(device_delete, "delete", cmd_device_delete,
+		      cmd_device_delete_usage, NULL, CMD_ALIAS);
 
 static const char * const cmd_device_scan_usage[] = {
 	"btrfs device scan [(-d|--all-devices)|<device> [<device>...]]",
@@ -325,6 +329,7 @@ static int cmd_device_scan(int argc, char **argv)
 out:
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(device_scan, "scan");
 
 static const char * const cmd_device_ready_usage[] = {
 	"btrfs device ready <device>",
@@ -378,6 +383,7 @@ out:
 	close(fd);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(device_ready, "ready");
 
 static const char * const cmd_device_stats_usage[] = {
 	"btrfs device stats [options] <path>|<device>",
@@ -518,6 +524,7 @@ out:
 
 	return err;
 }
+static DEFINE_SIMPLE_COMMAND(device_stats, "stats");
 
 static const char * const cmd_device_usage_usage[] = {
 	"btrfs device usage [options] <path> [<path>..]",
@@ -590,26 +597,26 @@ static int cmd_device_usage(int argc, char **argv)
 
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(device_usage, "usage");
 
 static const char device_cmd_group_info[] =
 "manage and query devices in the filesystem";
 
-const struct cmd_group device_cmd_group = {
+static const struct cmd_group device_cmd_group = {
 	device_cmd_group_usage, device_cmd_group_info, {
-		{ "add", cmd_device_add, cmd_device_add_usage, NULL, 0 },
-		{ "delete", cmd_device_delete, cmd_device_delete_usage, NULL,
-			CMD_ALIAS },
-		{ "remove", cmd_device_remove, cmd_device_remove_usage, NULL, 0 },
-		{ "scan", cmd_device_scan, cmd_device_scan_usage, NULL, 0 },
-		{ "ready", cmd_device_ready, cmd_device_ready_usage, NULL, 0 },
-		{ "stats", cmd_device_stats, cmd_device_stats_usage, NULL, 0 },
-		{ "usage", cmd_device_usage,
-			cmd_device_usage_usage, NULL, 0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_device_add,
+		&cmd_struct_device_delete,
+		&cmd_struct_device_remove,
+		&cmd_struct_device_scan,
+		&cmd_struct_device_ready,
+		&cmd_struct_device_stats,
+		&cmd_struct_device_usage,
+		NULL
 	}
 };
 
-int cmd_device(int argc, char **argv)
+static int cmd_device(int argc, char **argv)
 {
 	return handle_command_group(&device_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND_TOKEN(device);
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
index 8a44665c..cb2f09c7 100644
--- a/cmds-fi-du.c
+++ b/cmds-fi-du.c
@@ -549,7 +549,7 @@ out:
 	return ret;
 }
 
-const char * const cmd_filesystem_du_usage[] = {
+static const char * const cmd_filesystem_du_usage[] = {
 	"btrfs filesystem du [options] <path> [<path>..]",
 	"Summarize disk usage of each file.",
 	"-s|--summarize     display only a total for each argument",
@@ -557,7 +557,7 @@ const char * const cmd_filesystem_du_usage[] = {
 	NULL
 };
 
-int cmd_filesystem_du(int argc, char **argv)
+static int cmd_filesystem_du(int argc, char **argv)
 {
 	int ret = 0, err = 0;
 	int i;
@@ -611,3 +611,4 @@ int cmd_filesystem_du(int argc, char **argv)
 
 	return err;
 }
+DEFINE_SIMPLE_COMMAND(filesystem_du, "du");
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 9a1c76ab..af882400 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -957,7 +957,7 @@ out:
 	return ret;
 }
 
-const char * const cmd_filesystem_usage_usage[] = {
+static const char * const cmd_filesystem_usage_usage[] = {
 	"btrfs filesystem usage [options] <path> [<path>..]",
 	"Show detailed information about internal filesystem usage .",
 	HELPINFO_UNITS_SHORT_LONG,
@@ -965,7 +965,7 @@ const char * const cmd_filesystem_usage_usage[] = {
 	NULL
 };
 
-int cmd_filesystem_usage(int argc, char **argv)
+static int cmd_filesystem_usage(int argc, char **argv)
 {
 	int ret = 0;
 	unsigned unit_mode;
@@ -1035,6 +1035,7 @@ cleanup:
 out:
 	return !!ret;
 }
+DEFINE_SIMPLE_COMMAND(filesystem_usage, "usage");
 
 void print_device_chunks(struct device_info *devinfo,
 		struct chunk_info *chunks_info_ptr,
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 62112705..ec038f2f 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -149,6 +149,7 @@ static int cmd_filesystem_df(int argc, char **argv)
 	close_file_or_dir(fd, dirstream);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(filesystem_df, "df");
 
 static int match_search_item_kernel(u8 *fsid, char *mnt, char *label,
 					char *search)
@@ -804,6 +805,7 @@ out:
 	free_seen_fsid(seen_fsid_hash);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(filesystem_show, "show");
 
 static const char * const cmd_filesystem_sync_usage[] = {
 	"btrfs filesystem sync <path>",
@@ -837,6 +839,7 @@ static int cmd_filesystem_sync(int argc, char **argv)
 
 	return 0;
 }
+static DEFINE_SIMPLE_COMMAND(filesystem_sync, "sync");
 
 static int parse_compress_type(char *s)
 {
@@ -1075,6 +1078,7 @@ next:
 
 	return !!defrag_global_errors;
 }
+static DEFINE_SIMPLE_COMMAND(filesystem_defrag, "defrag");
 
 static const char * const cmd_filesystem_resize_usage[] = {
 	"btrfs filesystem resize [devid:][+/-]<newsize>[kKmMgGtTpPeE]|[devid:]max <path>",
@@ -1153,6 +1157,7 @@ static int cmd_filesystem_resize(int argc, char **argv)
 	}
 	return 0;
 }
+static DEFINE_SIMPLE_COMMAND(filesystem_resize, "resize");
 
 static const char * const cmd_filesystem_label_usage[] = {
 	"btrfs filesystem label [<device>|<mount_point>] [<newlabel>]",
@@ -1183,6 +1188,7 @@ static int cmd_filesystem_label(int argc, char **argv)
 		return ret;
 	}
 }
+static DEFINE_SIMPLE_COMMAND(filesystem_label, "label");
 
 static const char * const cmd_filesystem_balance_usage[] = {
 	"btrfs filesystem balance [args...] (alias of \"btrfs balance\")",
@@ -1190,40 +1196,40 @@ static const char * const cmd_filesystem_balance_usage[] = {
 	NULL
 };
 
-/* Compatible old "btrfs filesystem balance" command */
 static int cmd_filesystem_balance(int argc, char **argv)
 {
-	return cmd_balance(argc, argv);
+	return cmd_execute(&cmd_struct_balance, argc, argv);
 }
 
+/*
+ * Compatible old "btrfs filesystem balance" command
+ *
+ * We can't use cmd_struct_balance directly here since this alias is
+ * for historical compatibility and is hidden.
+ */
+static DEFINE_COMMAND(filesystem_balance, "balance", cmd_filesystem_balance,
+		      cmd_filesystem_balance_usage, NULL, CMD_HIDDEN);
+
 static const char filesystem_cmd_group_info[] =
 "overall filesystem tasks and information";
 
-const struct cmd_group filesystem_cmd_group = {
+static const struct cmd_group filesystem_cmd_group = {
 	filesystem_cmd_group_usage, filesystem_cmd_group_info, {
-		{ "df", cmd_filesystem_df, cmd_filesystem_df_usage, NULL, 0 },
-		{ "du", cmd_filesystem_du, cmd_filesystem_du_usage, NULL, 0 },
-		{ "show", cmd_filesystem_show, cmd_filesystem_show_usage, NULL,
-			0 },
-		{ "sync", cmd_filesystem_sync, cmd_filesystem_sync_usage, NULL,
-			0 },
-		{ "defragment", cmd_filesystem_defrag,
-			cmd_filesystem_defrag_usage, NULL, 0 },
-		{ "balance", cmd_filesystem_balance,
-		   cmd_filesystem_balance_usage, &balance_cmd_group,
-		   CMD_HIDDEN },
-		{ "resize", cmd_filesystem_resize, cmd_filesystem_resize_usage,
-			NULL, 0 },
-		{ "label", cmd_filesystem_label, cmd_filesystem_label_usage,
-			NULL, 0 },
-		{ "usage", cmd_filesystem_usage,
-			cmd_filesystem_usage_usage, NULL, 0 },
-
-		NULL_CMD_STRUCT
+		&cmd_struct_filesystem_df,
+		&cmd_struct_filesystem_du,
+		&cmd_struct_filesystem_show,
+		&cmd_struct_filesystem_sync,
+		&cmd_struct_filesystem_defrag,
+		&cmd_struct_filesystem_balance,
+		&cmd_struct_filesystem_resize,
+		&cmd_struct_filesystem_label,
+		&cmd_struct_filesystem_usage,
+		NULL
 	}
 };
 
-int cmd_filesystem(int argc, char **argv)
+static int cmd_filesystem(int argc, char **argv)
 {
 	return handle_command_group(&filesystem_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND_TOKEN(filesystem);
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
index 150c2e5a..34542d71 100644
--- a/cmds-inspect-dump-super.c
+++ b/cmds-inspect-dump-super.c
@@ -486,7 +486,7 @@ static int load_and_dump_sb(char *filename, int fd, u64 sb_bytenr, int full,
 	return 0;
 }
 
-const char * const cmd_inspect_dump_super_usage[] = {
+static const char * const cmd_inspect_dump_super_usage[] = {
 	"btrfs inspect-internal dump-super [options] device [device...]",
 	"Dump superblock from a device in a textual form",
 	"-f|--full             print full superblock information, backup roots etc.",
@@ -504,7 +504,7 @@ const char * const cmd_inspect_dump_super_usage[] = {
 	NULL
 };
 
-int cmd_inspect_dump_super(int argc, char **argv)
+static int cmd_inspect_dump_super(int argc, char **argv)
 {
 	int all = 0;
 	int full = 0;
@@ -612,3 +612,4 @@ int cmd_inspect_dump_super(int argc, char **argv)
 out:
 	return ret;
 }
+DEFINE_SIMPLE_COMMAND(inspect_dump_super, "dump-super");
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
index df44bb63..6417eab3 100644
--- a/cmds-inspect-dump-tree.c
+++ b/cmds-inspect-dump-tree.c
@@ -183,7 +183,7 @@ static u64 treeid_from_string(const char *str, const char **end)
 	return id;
 }
 
-const char * const cmd_inspect_dump_tree_usage[] = {
+static const char * const cmd_inspect_dump_tree_usage[] = {
 	"btrfs inspect-internal dump-tree [options] device",
 	"Dump tree structures from a given device",
 	"Dump tree structures from a given device in textual form, expand keys to human",
@@ -201,7 +201,7 @@ const char * const cmd_inspect_dump_tree_usage[] = {
 	NULL
 };
 
-int cmd_inspect_dump_tree(int argc, char **argv)
+static int cmd_inspect_dump_tree(int argc, char **argv)
 {
 	struct btrfs_root *root;
 	struct btrfs_fs_info *info;
@@ -585,3 +585,4 @@ close_root:
 out:
 	return !!ret;
 }
+DEFINE_SIMPLE_COMMAND(inspect_dump_tree, "dump-tree");
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
index eced0db9..14d79ccc 100644
--- a/cmds-inspect-tree-stats.c
+++ b/cmds-inspect-tree-stats.c
@@ -420,14 +420,14 @@ out:
 	return ret;
 }
 
-const char * const cmd_inspect_tree_stats_usage[] = {
+static const char * const cmd_inspect_tree_stats_usage[] = {
 	"btrfs inspect-internal tree-stats [options] <device>",
 	"Print various stats for trees",
 	"-b		raw numbers in bytes",
 	NULL
 };
 
-int cmd_inspect_tree_stats(int argc, char **argv)
+static int cmd_inspect_tree_stats(int argc, char **argv)
 {
 	struct btrfs_key key;
 	struct btrfs_root *root;
@@ -494,3 +494,4 @@ out:
 	close_ctree(root);
 	return ret;
 }
+DEFINE_SIMPLE_COMMAND(inspect_tree_stats, "tree-stats");
diff --git a/cmds-inspect.c b/cmds-inspect.c
index afd7fe48..12f200b3 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -121,6 +121,7 @@ static int cmd_inspect_inode_resolve(int argc, char **argv)
 	return !!ret;
 
 }
+static DEFINE_SIMPLE_COMMAND(inspect_inode_resolve, "inode-resolve");
 
 static const char * const cmd_inspect_logical_resolve_usage[] = {
 	"btrfs inspect-internal logical-resolve [-Pv] [-s bufsize] <logical> <path>",
@@ -257,6 +258,7 @@ out:
 	free(inodes);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(inspect_logical_resolve, "logical-resolve");
 
 static const char * const cmd_inspect_subvolid_resolve_usage[] = {
 	"btrfs inspect-internal subvolid-resolve <subvolid> <path>",
@@ -299,6 +301,7 @@ out:
 	close_file_or_dir(fd, dirstream);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(inspect_subvolid_resolve, "subvolid-resolve");
 
 static const char* const cmd_inspect_rootid_usage[] = {
 	"btrfs inspect-internal rootid <path>",
@@ -336,6 +339,7 @@ out:
 
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(inspect_rootid, "rootid");
 
 static const char* const cmd_inspect_min_dev_size_usage[] = {
 	"btrfs inspect-internal min-dev-size [options] <path>",
@@ -625,33 +629,27 @@ static int cmd_inspect_min_dev_size(int argc, char **argv)
 out:
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(inspect_min_dev_size, "min-dev-size");
 
 static const char inspect_cmd_group_info[] =
 "query various internal information";
 
-const struct cmd_group inspect_cmd_group = {
+static const struct cmd_group inspect_cmd_group = {
 	inspect_cmd_group_usage, inspect_cmd_group_info, {
-		{ "inode-resolve", cmd_inspect_inode_resolve,
-			cmd_inspect_inode_resolve_usage, NULL, 0 },
-		{ "logical-resolve", cmd_inspect_logical_resolve,
-			cmd_inspect_logical_resolve_usage, NULL, 0 },
-		{ "subvolid-resolve", cmd_inspect_subvolid_resolve,
-			cmd_inspect_subvolid_resolve_usage, NULL, 0 },
-		{ "rootid", cmd_inspect_rootid, cmd_inspect_rootid_usage, NULL,
-			0 },
-		{ "min-dev-size", cmd_inspect_min_dev_size,
-			cmd_inspect_min_dev_size_usage, NULL, 0 },
-		{ "dump-tree", cmd_inspect_dump_tree,
-				cmd_inspect_dump_tree_usage, NULL, 0 },
-		{ "dump-super", cmd_inspect_dump_super,
-				cmd_inspect_dump_super_usage, NULL, 0 },
-		{ "tree-stats", cmd_inspect_tree_stats,
-				cmd_inspect_tree_stats_usage, NULL, 0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_inspect_inode_resolve,
+		&cmd_struct_inspect_logical_resolve,
+		&cmd_struct_inspect_subvolid_resolve,
+		&cmd_struct_inspect_rootid,
+		&cmd_struct_inspect_min_dev_size,
+		&cmd_struct_inspect_dump_tree,
+		&cmd_struct_inspect_dump_super,
+		&cmd_struct_inspect_tree_stats,
+		NULL
 	}
 };
 
-int cmd_inspect(int argc, char **argv)
+static int cmd_inspect(int argc, char **argv)
 {
 	return handle_command_group(&inspect_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND(inspect, "inspect");
diff --git a/cmds-property.c b/cmds-property.c
index 03bafa05..dce1f2a9 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -358,6 +358,7 @@ static int cmd_property_get(int argc, char **argv)
 
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(property_get, "get");
 
 static const char * const cmd_property_set_usage[] = {
 	"btrfs property set [-t <type>] <object> <name> <value>",
@@ -382,6 +383,7 @@ static int cmd_property_set(int argc, char **argv)
 
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(property_set, "set");
 
 static const char * const cmd_property_list_usage[] = {
 	"btrfs property list [-t <type>] <object>",
@@ -404,23 +406,22 @@ static int cmd_property_list(int argc, char **argv)
 
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(property_list, "list");
 
 static const char property_cmd_group_info[] =
 "modify properties of filesystem objects";
 
-const struct cmd_group property_cmd_group = {
+static const struct cmd_group property_cmd_group = {
 	property_cmd_group_usage, property_cmd_group_info, {
-		{ "get", cmd_property_get,
-			cmd_property_get_usage, NULL, 0 },
-		{ "set", cmd_property_set,
-			cmd_property_set_usage, NULL, 0 },
-		{ "list", cmd_property_list,
-			cmd_property_list_usage, NULL, 0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_property_get,
+		&cmd_struct_property_set,
+		&cmd_struct_property_list,
+		NULL
 	}
 };
 
-int cmd_property(int argc, char **argv)
+static int cmd_property(int argc, char **argv)
 {
 	return handle_command_group(&property_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND_TOKEN(property);
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index d704aeaf..6945d160 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -219,6 +219,7 @@ static int cmd_qgroup_assign(int argc, char **argv)
 {
 	return _cmd_qgroup_assign(1, argc, argv, cmd_qgroup_assign_usage);
 }
+static DEFINE_SIMPLE_COMMAND(qgroup_assign, "assign");
 
 static const char * const cmd_qgroup_remove_usage[] = {
 	"btrfs qgroup remove <src> <dst> <path>",
@@ -230,6 +231,7 @@ static int cmd_qgroup_remove(int argc, char **argv)
 {
 	return _cmd_qgroup_assign(0, argc, argv, cmd_qgroup_remove_usage);
 }
+static DEFINE_SIMPLE_COMMAND(qgroup_remove, "remove");
 
 static const char * const cmd_qgroup_create_usage[] = {
 	"btrfs qgroup create <qgroupid> <path>",
@@ -249,6 +251,7 @@ static int cmd_qgroup_create(int argc, char **argv)
 		usage(cmd_qgroup_create_usage);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(qgroup_create, "create");
 
 static const char * const cmd_qgroup_destroy_usage[] = {
 	"btrfs qgroup destroy <qgroupid> <path>",
@@ -268,6 +271,7 @@ static int cmd_qgroup_destroy(int argc, char **argv)
 		usage(cmd_qgroup_destroy_usage);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(qgroup_destroy, "destroy");
 
 static const char * const cmd_qgroup_show_usage[] = {
 	"btrfs qgroup show [options] <path>",
@@ -414,6 +418,7 @@ static int cmd_qgroup_show(int argc, char **argv)
 out:
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(qgroup_show, "show");
 
 static const char * const cmd_qgroup_limit_usage[] = {
 	"btrfs qgroup limit [options] <size>|none [<qgroupid>] <path>",
@@ -506,29 +511,25 @@ static int cmd_qgroup_limit(int argc, char **argv)
 	}
 	return 0;
 }
+static DEFINE_SIMPLE_COMMAND(qgroup_limit, "limit");
 
 static const char qgroup_cmd_group_info[] =
 "manage quota groups";
 
-const struct cmd_group qgroup_cmd_group = {
+static const struct cmd_group qgroup_cmd_group = {
 	qgroup_cmd_group_usage, qgroup_cmd_group_info, {
-		{ "assign", cmd_qgroup_assign, cmd_qgroup_assign_usage,
-		   NULL, 0 },
-		{ "remove", cmd_qgroup_remove, cmd_qgroup_remove_usage,
-		   NULL, 0 },
-		{ "create", cmd_qgroup_create, cmd_qgroup_create_usage,
-		   NULL, 0 },
-		{ "destroy", cmd_qgroup_destroy, cmd_qgroup_destroy_usage,
-		   NULL, 0 },
-		{ "show", cmd_qgroup_show, cmd_qgroup_show_usage,
-		   NULL, 0 },
-		{ "limit", cmd_qgroup_limit, cmd_qgroup_limit_usage,
-		   NULL, 0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_qgroup_assign,
+		&cmd_struct_qgroup_remove,
+		&cmd_struct_qgroup_create,
+		&cmd_struct_qgroup_destroy,
+		&cmd_struct_qgroup_show,
+		&cmd_struct_qgroup_limit,
+		NULL
 	}
 };
 
-int cmd_qgroup(int argc, char **argv)
+static int cmd_qgroup(int argc, char **argv)
 {
 	return handle_command_group(&qgroup_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND_TOKEN(qgroup);
diff --git a/cmds-quota.c b/cmds-quota.c
index 7f933495..5cd5607f 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -79,6 +79,7 @@ static int cmd_quota_enable(int argc, char **argv)
 		usage(cmd_quota_enable_usage);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(quota_enable, "enable");
 
 static const char * const cmd_quota_disable_usage[] = {
 	"btrfs quota disable <path>",
@@ -98,6 +99,7 @@ static int cmd_quota_disable(int argc, char **argv)
 		usage(cmd_quota_disable_usage);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(quota_disable, "disable");
 
 static const char * const cmd_quota_rescan_usage[] = {
 	"btrfs quota rescan [-sw] <path>",
@@ -197,21 +199,22 @@ static int cmd_quota_rescan(int argc, char **argv)
 	close_file_or_dir(fd, dirstream);
 	return 0;
 }
+static DEFINE_SIMPLE_COMMAND(quota_rescan, "rescan");
 
 static const char quota_cmd_group_info[] =
 "manage filesystem quota settings";
 
-const struct cmd_group quota_cmd_group = {
+static const struct cmd_group quota_cmd_group = {
 	quota_cmd_group_usage, quota_cmd_group_info, {
-		{ "enable", cmd_quota_enable, cmd_quota_enable_usage, NULL, 0 },
-		{ "disable", cmd_quota_disable, cmd_quota_disable_usage,
-		   NULL, 0 },
-		{ "rescan", cmd_quota_rescan, cmd_quota_rescan_usage, NULL, 0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_quota_enable,
+		&cmd_struct_quota_disable,
+		&cmd_struct_quota_rescan,
+		NULL
 	}
 };
 
-int cmd_quota(int argc, char **argv)
+static int cmd_quota(int argc, char **argv)
 {
 	return handle_command_group(&quota_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND_TOKEN(quota);
diff --git a/cmds-receive.c b/cmds-receive.c
index b3709f36..d88b8793 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -1248,7 +1248,7 @@ out:
 	return ret;
 }
 
-const char * const cmd_receive_usage[] = {
+static const char * const cmd_receive_usage[] = {
 	"btrfs receive [options] <mount>\n"
 	"btrfs receive --dump [options]",
 	"Receive subvolumes from a stream",
@@ -1279,7 +1279,7 @@ const char * const cmd_receive_usage[] = {
 	NULL
 };
 
-int cmd_receive(int argc, char **argv)
+static int cmd_receive(int argc, char **argv)
 {
 	char *tomnt = NULL;
 	char fromfile[PATH_MAX];
@@ -1388,3 +1388,4 @@ out:
 
 	return !!ret;
 }
+DEFINE_SIMPLE_COMMAND(receive, "receive");
diff --git a/cmds-replace.c b/cmds-replace.c
index 032a44fc..a7379c71 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -313,6 +313,7 @@ leave_with_error:
 		close(fddstdev);
 	return 1;
 }
+static DEFINE_SIMPLE_COMMAND(replace_start, "start");
 
 static const char *const cmd_replace_status_usage[] = {
 	"btrfs replace status [-1] <mount_point>",
@@ -356,6 +357,7 @@ static int cmd_replace_status(int argc, char **argv)
 	close_file_or_dir(fd, dirstream);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(replace_status, "status");
 
 static int print_replace_status(int fd, const char *path, int once)
 {
@@ -538,23 +540,22 @@ static int cmd_replace_cancel(int argc, char **argv)
 	}
 	return 0;
 }
+static DEFINE_SIMPLE_COMMAND(replace_cancel, "cancel");
 
 static const char replace_cmd_group_info[] =
 "replace a device in the filesystem";
 
-const struct cmd_group replace_cmd_group = {
+static const struct cmd_group replace_cmd_group = {
 	replace_cmd_group_usage, replace_cmd_group_info, {
-		{ "start", cmd_replace_start, cmd_replace_start_usage, NULL,
-		  0 },
-		{ "status", cmd_replace_status, cmd_replace_status_usage, NULL,
-		  0 },
-		{ "cancel", cmd_replace_cancel, cmd_replace_cancel_usage, NULL,
-		  0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_replace_start,
+		&cmd_struct_replace_status,
+		&cmd_struct_replace_cancel,
+		NULL
 	}
 };
 
-int cmd_replace(int argc, char **argv)
+static int cmd_replace(int argc, char **argv)
 {
 	return handle_command_group(&replace_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND_TOKEN(replace);
diff --git a/cmds-rescue.c b/cmds-rescue.c
index c61145bc..e3611f2f 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -94,6 +94,7 @@ static int cmd_rescue_chunk_recover(int argc, char *argv[])
 	}
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(rescue_chunk_recover, "chunk-recover");
 
 static const char * const cmd_rescue_super_recover_usage[] = {
 	"btrfs rescue super-recover [options] <device>",
@@ -149,6 +150,7 @@ static int cmd_rescue_super_recover(int argc, char **argv)
 	ret = btrfs_recover_superblocks(dname, verbose, yes);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(rescue_super_recover, "super-recover");
 
 static const char * const cmd_rescue_zero_log_usage[] = {
 	"btrfs rescue zero-log <device>",
@@ -202,6 +204,7 @@ static int cmd_rescue_zero_log(int argc, char **argv)
 out:
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(rescue_zero_log, "zero-log");
 
 static const char * const cmd_rescue_fix_device_size_usage[] = {
 	"btrfs rescue fix-device-size <device>",
@@ -247,24 +250,23 @@ static int cmd_rescue_fix_device_size(int argc, char **argv)
 out:
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(rescue_fix_device_size, "fix-device-size");
 
 static const char rescue_cmd_group_info[] =
 "toolbox for specific rescue operations";
 
-const struct cmd_group rescue_cmd_group = {
+static const struct cmd_group rescue_cmd_group = {
 	rescue_cmd_group_usage, rescue_cmd_group_info, {
-		{ "chunk-recover", cmd_rescue_chunk_recover,
-			cmd_rescue_chunk_recover_usage, NULL, 0},
-		{ "super-recover", cmd_rescue_super_recover,
-			cmd_rescue_super_recover_usage, NULL, 0},
-		{ "zero-log", cmd_rescue_zero_log, cmd_rescue_zero_log_usage, NULL, 0},
-		{ "fix-device-size", cmd_rescue_fix_device_size,
-			cmd_rescue_fix_device_size_usage, NULL, 0},
-		NULL_CMD_STRUCT
+		&cmd_struct_rescue_chunk_recover,
+		&cmd_struct_rescue_super_recover,
+		&cmd_struct_rescue_zero_log,
+		&cmd_struct_rescue_fix_device_size,
+		NULL
 	}
 };
 
-int cmd_rescue(int argc, char **argv)
+static int cmd_rescue(int argc, char **argv)
 {
 	return handle_command_group(&rescue_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND_TOKEN(rescue);
diff --git a/cmds-restore.c b/cmds-restore.c
index ade35f0f..05fd1c80 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -1389,7 +1389,7 @@ out:
 	return ret;
 }
 
-const char * const cmd_restore_usage[] = {
+static const char * const cmd_restore_usage[] = {
 	"btrfs restore [options] <device> <path> | -l <device>",
 	"Try to restore files from a damaged filesystem (unmounted)",
 	"",
@@ -1415,7 +1415,7 @@ const char * const cmd_restore_usage[] = {
 	NULL
 };
 
-int cmd_restore(int argc, char **argv)
+static int cmd_restore(int argc, char **argv)
 {
 	struct btrfs_root *root;
 	struct btrfs_key key;
@@ -1621,3 +1621,4 @@ out:
 	close_ctree(root);
 	return !!ret;
 }
+DEFINE_SIMPLE_COMMAND(restore, "restore");
diff --git a/cmds-scrub.c b/cmds-scrub.c
index dabe7d9a..b0f447c7 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -1580,6 +1580,7 @@ static int cmd_scrub_start(int argc, char **argv)
 {
 	return scrub_start(argc, argv, 0);
 }
+static DEFINE_SIMPLE_COMMAND(scrub_start, "start");
 
 static const char * const cmd_scrub_cancel_usage[] = {
 	"btrfs scrub cancel <path>|<device>",
@@ -1626,6 +1627,7 @@ out:
 	close_file_or_dir(fdmnt, dirstream);
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(scrub_cancel, "cancel");
 
 static const char * const cmd_scrub_resume_usage[] = {
 	"btrfs scrub resume [-BdqrR] [-c ioprio_class -n ioprio_classdata] <path>|<device>",
@@ -1645,6 +1647,7 @@ static int cmd_scrub_resume(int argc, char **argv)
 {
 	return scrub_start(argc, argv, 1);
 }
+static DEFINE_SIMPLE_COMMAND(scrub_resume, "resume");
 
 static const char * const cmd_scrub_status_usage[] = {
 	"btrfs scrub status [-dR] <path>|<device>",
@@ -1786,21 +1789,24 @@ out:
 
 	return !!err;
 }
+static DEFINE_SIMPLE_COMMAND(scrub_status, "status");
 
 static const char scrub_cmd_group_info[] =
 "verify checksums of data and metadata";
 
-const struct cmd_group scrub_cmd_group = {
+static const struct cmd_group scrub_cmd_group = {
 	scrub_cmd_group_usage, scrub_cmd_group_info, {
-		{ "start", cmd_scrub_start, cmd_scrub_start_usage, NULL, 0 },
-		{ "cancel", cmd_scrub_cancel, cmd_scrub_cancel_usage, NULL, 0 },
-		{ "resume", cmd_scrub_resume, cmd_scrub_resume_usage, NULL, 0 },
-		{ "status", cmd_scrub_status, cmd_scrub_status_usage, NULL, 0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_scrub_start,
+		&cmd_struct_scrub_cancel,
+		&cmd_struct_scrub_resume,
+		&cmd_struct_scrub_status,
+		NULL
 	}
 };
 
-int cmd_scrub(int argc, char **argv)
+static int cmd_scrub(int argc, char **argv)
 {
 	return handle_command_group(&scrub_cmd_group, argc, argv);
 }
+
+DEFINE_GROUP_COMMAND_TOKEN(scrub);
diff --git a/cmds-send.c b/cmds-send.c
index 8365e9c9..f1e5124d 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -489,8 +489,7 @@ static void free_send_info(struct btrfs_send *sctx)
 	subvol_uuid_search_finit(&sctx->sus);
 }
 
-
-const char * const cmd_send_usage[] = {
+static const char * const cmd_send_usage[] = {
 	"btrfs send [-ve] [-p <parent>] [-c <clone-src>] [-f <outfile>] <subvol> [<subvol>...]",
 	"Send the subvolume(s) to stdout.",
 	"Sends the subvolume(s) specified by <subvol> to stdout.",
@@ -524,7 +523,7 @@ const char * const cmd_send_usage[] = {
 	NULL
 };
 
-int cmd_send(int argc, char **argv)
+static int cmd_send(int argc, char **argv)
 {
 	char *subvol = NULL;
 	int ret;
@@ -809,3 +808,4 @@ out:
 	free_send_info(&send);
 	return !!ret;
 }
+DEFINE_SIMPLE_COMMAND(send, "send");
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 96fb7b06..6fc4b2e7 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -223,6 +223,7 @@ out:
 
 	return retval;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_create, "create");
 
 static int wait_for_commit(int fd)
 {
@@ -429,6 +430,7 @@ keep_fd:
 
 	return ret;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_delete, "delete");
 
 /*
  * Naming of options:
@@ -632,6 +634,7 @@ out:
 		usage(cmd_subvol_list_usage);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_list, "list");
 
 static const char * const cmd_subvol_snapshot_usage[] = {
 	"btrfs subvolume snapshot [-r] [-i <qgroupid>] <source> <dest>|[<dest>/]<name>",
@@ -788,6 +791,7 @@ out:
 
 	return retval;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_snapshot, "snapshot");
 
 static const char * const cmd_subvol_get_default_usage[] = {
 	"btrfs subvolume get-default <path>",
@@ -852,6 +856,7 @@ out:
 	close_file_or_dir(fd, dirstream);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_get_default, "get-default");
 
 static const char * const cmd_subvol_set_default_usage[] = {
 	"btrfs subvolume set-default <subvolume>\n"
@@ -918,6 +923,7 @@ static int cmd_subvol_set_default(int argc, char **argv)
 	}
 	return 0;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_set_default, "set-default");
 
 static const char * const cmd_subvol_find_new_usage[] = {
 	"btrfs subvolume find-new <path> <lastgen>",
@@ -966,6 +972,7 @@ static int cmd_subvol_find_new(int argc, char **argv)
 	close_file_or_dir(fd, dirstream);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_find_new, "find-new");
 
 static const char * const cmd_subvol_show_usage[] = {
 	"btrfs subvolume show [options] <subvol-path>|<mnt>",
@@ -1170,6 +1177,7 @@ out:
 	free(fullpath);
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_show, "show");
 
 static const char * const cmd_subvol_sync_usage[] = {
 	"btrfs subvolume sync <path> [<subvol-id>...]",
@@ -1420,30 +1428,28 @@ out:
 
 	return !!ret;
 }
+static DEFINE_SIMPLE_COMMAND(subvol_sync, "sync");
 
 static const char subvolume_cmd_group_info[] =
 "manage subvolumes: create, delete, list, etc";
 
-const struct cmd_group subvolume_cmd_group = {
+static const struct cmd_group subvolume_cmd_group = {
 	subvolume_cmd_group_usage, subvolume_cmd_group_info, {
-		{ "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 },
-		{ "snapshot", cmd_subvol_snapshot, cmd_subvol_snapshot_usage,
-			NULL, 0 },
-		{ "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 },
-		{ "find-new", cmd_subvol_find_new, cmd_subvol_find_new_usage,
-			NULL, 0 },
-		{ "show", cmd_subvol_show, cmd_subvol_show_usage, NULL, 0 },
-		{ "sync", cmd_subvol_sync, cmd_subvol_sync_usage, NULL, 0 },
-		NULL_CMD_STRUCT
+		&cmd_struct_subvol_create,
+		&cmd_struct_subvol_delete,
+		&cmd_struct_subvol_list,
+		&cmd_struct_subvol_snapshot,
+		&cmd_struct_subvol_get_default,
+		&cmd_struct_subvol_set_default,
+		&cmd_struct_subvol_find_new,
+		&cmd_struct_subvol_show,
+		&cmd_struct_subvol_sync,
+		NULL
 	}
 };
 
-int cmd_subvolume(int argc, char **argv)
+static int cmd_subvolume(int argc, char **argv)
 {
 	return handle_command_group(&subvolume_cmd_group, argc, argv);
 }
+DEFINE_GROUP_COMMAND_TOKEN(subvolume);
diff --git a/commands.h b/commands.h
index 76991f2b..9a65204b 100644
--- a/commands.h
+++ b/commands.h
@@ -56,69 +56,92 @@ struct cmd_struct {
 	int flags;
 };
 
-#define NULL_CMD_STRUCT {NULL, NULL, NULL, NULL, 0}
+/*
+ * These macros will create cmd_struct structures with a standard name:
+ * cmd_struct_<name>.
+ */
+#define __CMD_NAME(name)	cmd_struct_ ##name
+#define DECLARE_COMMAND(name)						\
+	extern const struct cmd_struct __CMD_NAME(name)
+
+/* Define a command with all members specified */
+#define DEFINE_COMMAND(name, _token, _fn, _usagestr, _group, _flags)	\
+	const struct cmd_struct __CMD_NAME(name) =			\
+		{							\
+			.token = (_token),				\
+			.fn = (_fn),					\
+			.usagestr = (_usagestr),			\
+			.next = (_group),				\
+			.flags = (_flags),				\
+		}
+
+/*
+ * Define a command for the common case - just a name and string.
+ * It's assumed that the callback is called cmd_<name> and the usage
+ * array is named cmd_<name>_usage.
+ */
+#define DEFINE_SIMPLE_COMMAND(name, token)				\
+	DEFINE_COMMAND(name, token, cmd_ ##name,			\
+		       cmd_ ##name ##_usage, NULL, 0)
+
+/*
+ * Define a command group callback.
+ * It's assumed that the callback is called cmd_<name> and the
+ * struct cmd_group is called <name>_cmd_group.
+ */
+#define DEFINE_GROUP_COMMAND(name, token)				\
+	DEFINE_COMMAND(name, token, cmd_ ##name,			\
+		       NULL, &(name ## _cmd_group), 0)
+
+/*
+ * Define a command group callback when the name and the string are
+ * the same.
+ */
+#define DEFINE_GROUP_COMMAND_TOKEN(name)				\
+	DEFINE_GROUP_COMMAND(name, #name)
 
 struct cmd_group {
 	const char * const *usagestr;
 	const char *infostr;
 
-	const struct cmd_struct commands[];
+	const struct cmd_struct * const commands[];
 };
 
+static inline int cmd_execute(const struct cmd_struct *cmd,
+			      int argc, char **argv)
+{
+	return cmd->fn(argc, argv);
+}
+
 int handle_command_group(const struct cmd_group *grp, int argc,
 			 char **argv);
 
 extern const char * const generic_cmd_help_usage[];
 
-extern const struct cmd_group subvolume_cmd_group;
-extern const struct cmd_group filesystem_cmd_group;
-extern const struct cmd_group balance_cmd_group;
-extern const struct cmd_group device_cmd_group;
-extern const struct cmd_group scrub_cmd_group;
-extern const struct cmd_group inspect_cmd_group;
-extern const struct cmd_group property_cmd_group;
-extern const struct cmd_group quota_cmd_group;
-extern const struct cmd_group qgroup_cmd_group;
-extern const struct cmd_group replace_cmd_group;
-extern const struct cmd_group rescue_cmd_group;
-
-extern const char * const cmd_send_usage[];
-extern const char * const cmd_receive_usage[];
-extern const char * const cmd_check_usage[];
-extern const char * const cmd_chunk_recover_usage[];
-extern const char * const cmd_super_recover_usage[];
-extern const char * const cmd_restore_usage[];
-extern const char * const cmd_rescue_usage[];
-extern const char * const cmd_inspect_dump_super_usage[];
-extern const char * const cmd_inspect_dump_tree_usage[];
-extern const char * const cmd_inspect_tree_stats_usage[];
-extern const char * const cmd_filesystem_du_usage[];
-extern const char * const cmd_filesystem_usage_usage[];
-
-int cmd_subvolume(int argc, char **argv);
-int cmd_filesystem(int argc, char **argv);
-int cmd_filesystem_du(int argc, char **argv);
-int cmd_filesystem_usage(int argc, char **argv);
-int cmd_balance(int argc, char **argv);
-int cmd_device(int argc, char **argv);
-int cmd_scrub(int argc, char **argv);
-int cmd_check(int argc, char **argv);
-int cmd_chunk_recover(int argc, char **argv);
-int cmd_super_recover(int argc, char **argv);
-int cmd_inspect(int argc, char **argv);
-int cmd_inspect_dump_super(int argc, char **argv);
-int cmd_inspect_dump_tree(int argc, char **argv);
-int cmd_inspect_tree_stats(int argc, char **argv);
-int cmd_property(int argc, char **argv);
-int cmd_send(int argc, char **argv);
-int cmd_receive(int argc, char **argv);
-int cmd_quota(int argc, char **argv);
-int cmd_qgroup(int argc, char **argv);
-int cmd_replace(int argc, char **argv);
-int cmd_restore(int argc, char **argv);
-int cmd_select_super(int argc, char **argv);
-int cmd_dump_super(int argc, char **argv);
-int cmd_debug_tree(int argc, char **argv);
-int cmd_rescue(int argc, char **argv);
+DECLARE_COMMAND(subvolume);
+DECLARE_COMMAND(filesystem);
+DECLARE_COMMAND(filesystem_du);
+DECLARE_COMMAND(filesystem_usage);
+DECLARE_COMMAND(balance);
+DECLARE_COMMAND(device);
+DECLARE_COMMAND(scrub);
+DECLARE_COMMAND(check);
+DECLARE_COMMAND(chunk_recover);
+DECLARE_COMMAND(super_recover);
+DECLARE_COMMAND(inspect);
+DECLARE_COMMAND(inspect_dump_super);
+DECLARE_COMMAND(inspect_dump_tree);
+DECLARE_COMMAND(inspect_tree_stats);
+DECLARE_COMMAND(property);
+DECLARE_COMMAND(send);
+DECLARE_COMMAND(receive);
+DECLARE_COMMAND(quota);
+DECLARE_COMMAND(qgroup);
+DECLARE_COMMAND(replace);
+DECLARE_COMMAND(restore);
+DECLARE_COMMAND(select_super);
+DECLARE_COMMAND(dump_super);
+DECLARE_COMMAND(debug_tree);
+DECLARE_COMMAND(rescue);
 
 #endif
diff --git a/help.c b/help.c
index ef7986b4..b7f06aae 100644
--- a/help.c
+++ b/help.c
@@ -248,14 +248,15 @@ void usage(const char * const *usagestr)
 static void usage_command_group_internal(const struct cmd_group *grp, bool full,
 					 FILE *outf)
 {
-	const struct cmd_struct *cmd = grp->commands;
+	int i;
 	int do_sep = 0;
 
-	for (; cmd->token; cmd++) {
+	for (i = 0; grp->commands[i]; i++) {
+		const struct cmd_struct *cmd = grp->commands[i];
 		if (cmd->flags & CMD_HIDDEN)
 			continue;
 
-		if (full && cmd != grp->commands)
+		if (full && i)
 			fputc('\n', outf);
 
 		if (!cmd->next) {
@@ -274,7 +275,7 @@ static void usage_command_group_internal(const struct cmd_group *grp, bool full,
 
 		/* this is an entry point to a nested command group */
 
-		if (!full && cmd != grp->commands)
+		if (!full && i)
 			fputc('\n', outf);
 
 		usage_command_group_internal(cmd->next, full, outf);
@@ -289,6 +290,7 @@ void usage_command_group_short(const struct cmd_group *grp)
 	const char * const *usagestr = grp->usagestr;
 	FILE *outf = stdout;
 	const struct cmd_struct *cmd;
+	int i;
 
 	if (usagestr && *usagestr) {
 		fprintf(outf, "usage: %s\n", *usagestr++);
@@ -299,7 +301,8 @@ void usage_command_group_short(const struct cmd_group *grp)
 	fputc('\n', outf);
 
 	fprintf(outf, "Command groups:\n");
-	for (cmd = grp->commands; cmd->token; cmd++) {
+	for (i = 0; grp->commands[i]; i++) {
+		cmd = grp->commands[i];
 		if (cmd->flags & CMD_HIDDEN)
 			continue;
 
@@ -310,7 +313,8 @@ void usage_command_group_short(const struct cmd_group *grp)
 	}
 
 	fprintf(outf, "\nCommands:\n");
-	for (cmd = grp->commands; cmd->token; cmd++) {
+	for (i = 0; grp->commands[i]; i++) {
+		cmd = grp->commands[i];
 		if (cmd->flags & CMD_HIDDEN)
 			continue;
 
@@ -358,12 +362,13 @@ void help_unknown_token(const char *arg, const struct cmd_group *grp)
 __attribute__((noreturn))
 void help_ambiguous_token(const char *arg, const struct cmd_group *grp)
 {
-	const struct cmd_struct *cmd = grp->commands;
+	int i;
 
 	fprintf(stderr, "%s: ambiguous token '%s'\n", get_argv0_buf(), arg);
 	fprintf(stderr, "\nDid you mean one of these ?\n");
 
-	for (; cmd->token; cmd++) {
+	for (i = 0; grp->commands[i]; i++) {
+		const struct cmd_struct *cmd = grp->commands[i];
 		if (!prefixcmp(cmd->token, arg))
 			fprintf(stderr, "\t%s\n", cmd->token);
 	}
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 14/20] btrfs-progs: pass cmd_struct to command callback function
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (12 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 13/20] btrfs-progs: use cmd_struct as command entry point jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 15/20] btrfs-progs: pass cmd_struct to clean_args_no_options{,_relaxed} jeffm
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

This patch passes the cmd_struct to the command callback function.  This
has several purposes: It allows the command callback to identify which
command was used to call it.  It also gives us direct access to the
usage associated with that command.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs.c                   |  8 ++++----
 check/main.c              |  2 +-
 cmds-balance.c            | 19 ++++++++++++-------
 cmds-device.c             | 35 +++++++++++++++++++----------------
 cmds-fi-du.c              |  3 ++-
 cmds-fi-usage.c           |  3 ++-
 cmds-filesystem.c         | 24 ++++++++++++++++--------
 cmds-inspect-dump-super.c |  3 ++-
 cmds-inspect-dump-tree.c  |  3 ++-
 cmds-inspect-tree-stats.c |  3 ++-
 cmds-inspect.c            | 17 +++++++++++------
 cmds-property.c           | 12 ++++++++----
 cmds-qgroup.c             | 18 +++++++++++-------
 cmds-quota.c              |  9 +++++----
 cmds-receive.c            |  2 +-
 cmds-replace.c            | 11 +++++++----
 cmds-rescue.c             | 14 +++++++++-----
 cmds-restore.c            |  2 +-
 cmds-scrub.c              | 23 +++++++++++------------
 cmds-send.c               |  2 +-
 cmds-subvolume.c          | 27 +++++++++++++++++----------
 commands.h                |  4 ++--
 22 files changed, 146 insertions(+), 98 deletions(-)

diff --git a/btrfs.c b/btrfs.c
index 1e68b0c0..49128182 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -148,7 +148,7 @@ static const char * const cmd_help_usage[] = {
 	NULL
 };
 
-static int cmd_help(int argc, char **argv)
+static int cmd_help(const struct cmd_struct *unused, int argc, char **argv)
 {
 	help_command_group(&btrfs_cmd_group, argc, argv);
 	return 0;
@@ -162,7 +162,7 @@ static const char * const cmd_version_usage[] = {
 	NULL
 };
 
-static int cmd_version(int argc, char **argv)
+static int cmd_version(const struct cmd_struct *unused, int argc, char **argv)
 {
 	printf("%s\n", PACKAGE_STRING);
 	return 0;
@@ -231,13 +231,13 @@ void handle_special_globals(int shift, int argc, char **argv)
 		if (has_full)
 			usage_command_group(&btrfs_cmd_group, true, false);
 		else
-			cmd_help(argc, argv);
+			cmd_execute(&cmd_struct_help, argc, argv);
 		exit(0);
 	}
 
 	for (i = 0; i < shift; i++)
 		if (strcmp(argv[i], "--version") == 0) {
-			cmd_version(argc, argv);
+			cmd_execute(&cmd_struct_version, argc, argv);
 			exit(0);
 		}
 }
diff --git a/check/main.c b/check/main.c
index 4b8f7678..bd31fb9f 100644
--- a/check/main.c
+++ b/check/main.c
@@ -9440,7 +9440,7 @@ static const char * const cmd_check_usage[] = {
 	NULL
 };
 
-static int cmd_check(int argc, char **argv)
+static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	struct cache_tree root_cache;
 	struct btrfs_root *root;
diff --git a/cmds-balance.c b/cmds-balance.c
index 7a60be61..1bd7b3ce 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -515,7 +515,8 @@ static const char * const cmd_balance_start_usage[] = {
 	NULL
 };
 
-static int cmd_balance_start(int argc, char **argv)
+static int cmd_balance_start(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	struct btrfs_ioctl_balance_args args;
 	struct btrfs_balance_args *ptrs[] = { &args.data, &args.sys,
@@ -680,7 +681,8 @@ static const char * const cmd_balance_pause_usage[] = {
 	NULL
 };
 
-static int cmd_balance_pause(int argc, char **argv)
+static int cmd_balance_pause(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	const char *path;
 	int fd;
@@ -719,7 +721,8 @@ static const char * const cmd_balance_cancel_usage[] = {
 	NULL
 };
 
-static int cmd_balance_cancel(int argc, char **argv)
+static int cmd_balance_cancel(const struct cmd_struct *cmd,
+			      int argc, char **argv)
 {
 	const char *path;
 	int fd;
@@ -758,7 +761,8 @@ static const char * const cmd_balance_resume_usage[] = {
 	NULL
 };
 
-static int cmd_balance_resume(int argc, char **argv)
+static int cmd_balance_resume(const struct cmd_struct *cmd,
+			      int argc, char **argv)
 {
 	struct btrfs_ioctl_balance_args args;
 	const char *path;
@@ -826,7 +830,8 @@ static const char * const cmd_balance_status_usage[] = {
  *   1 : Successful to know status of a pending balance
  *   0 : When there is no pending balance or completed
  */
-static int cmd_balance_status(int argc, char **argv)
+static int cmd_balance_status(const struct cmd_struct *cmd,
+			      int argc, char **argv)
 {
 	struct btrfs_ioctl_balance_args args;
 	const char *path;
@@ -904,7 +909,7 @@ out:
 }
 static DEFINE_SIMPLE_COMMAND(balance_status, "status");
 
-static int cmd_balance_full(int argc, char **argv)
+static int cmd_balance_full(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	struct btrfs_ioctl_balance_args args;
 
@@ -931,7 +936,7 @@ static const struct cmd_group balance_cmd_group = {
 	}
 };
 
-static int cmd_balance(int argc, char **argv)
+static int cmd_balance(const struct cmd_struct *unused, int argc, char **argv)
 {
 	if (argc == 2 && strcmp("start", argv[1]) != 0) {
 		/* old 'btrfs filesystem balance <path>' syntax */
diff --git a/cmds-device.c b/cmds-device.c
index 96764d6c..feb53f68 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -48,7 +48,8 @@ static const char * const cmd_device_add_usage[] = {
 	NULL
 };
 
-static int cmd_device_add(int argc, char **argv)
+static int cmd_device_add(const struct cmd_struct *cmd,
+			  int argc, char **argv)
 {
 	char	*mntpnt;
 	int i, fdmnt, ret = 0;
@@ -142,17 +143,17 @@ error_out:
 }
 static DEFINE_SIMPLE_COMMAND(device_add, "add");
 
-static int _cmd_device_remove(int argc, char **argv,
-		const char * const *usagestr)
+static int _cmd_device_remove(const struct cmd_struct *cmd,
+			      int argc, char **argv)
 {
 	char	*mntpnt;
 	int i, fdmnt, ret = 0;
 	DIR	*dirstream = NULL;
 
-	clean_args_no_options(argc, argv, usagestr);
+	clean_args_no_options(argc, argv, cmd->usagestr);
 
 	if (check_argc_min(argc - optind, 2))
-		usage(usagestr);
+		usage(cmd->usagestr);
 
 	mntpnt = argv[argc - 1];
 
@@ -236,9 +237,10 @@ static const char * const cmd_device_remove_usage[] = {
 	NULL
 };
 
-static int cmd_device_remove(int argc, char **argv)
+static int cmd_device_remove(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
-	return _cmd_device_remove(argc, argv, cmd_device_remove_usage);
+	return _cmd_device_remove(cmd, argc, argv);
 }
 static DEFINE_SIMPLE_COMMAND(device_remove, "remove");
 
@@ -250,9 +252,10 @@ static const char * const cmd_device_delete_usage[] = {
 	NULL
 };
 
-static int cmd_device_delete(int argc, char **argv)
+static int cmd_device_delete(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
-	return _cmd_device_remove(argc, argv, cmd_device_delete_usage);
+	return _cmd_device_remove(cmd, argc, argv);
 }
 static DEFINE_COMMAND(device_delete, "delete", cmd_device_delete,
 		      cmd_device_delete_usage, NULL, CMD_ALIAS);
@@ -264,7 +267,7 @@ static const char * const cmd_device_scan_usage[] = {
 	NULL
 };
 
-static int cmd_device_scan(int argc, char **argv)
+static int cmd_device_scan(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	int i;
 	int devstart;
@@ -337,14 +340,14 @@ static const char * const cmd_device_ready_usage[] = {
 	NULL
 };
 
-static int cmd_device_ready(int argc, char **argv)
+static int cmd_device_ready(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	struct	btrfs_ioctl_vol_args args;
 	int	fd;
 	int	ret;
 	char	*path;
 
-	clean_args_no_options(argc, argv, cmd_device_ready_usage);
+	clean_args_no_options(argc, argv, cmd->usagestr);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_device_ready_usage);
@@ -396,7 +399,7 @@ static const char * const cmd_device_stats_usage[] = {
 	NULL
 };
 
-static int cmd_device_stats(int argc, char **argv)
+static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	char *dev_path;
 	struct btrfs_ioctl_fs_info_args fi_args;
@@ -562,7 +565,7 @@ out:
 	return ret;
 }
 
-static int cmd_device_usage(int argc, char **argv)
+static int cmd_device_usage(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	unsigned unit_mode;
 	int ret = 0;
@@ -570,7 +573,7 @@ static int cmd_device_usage(int argc, char **argv)
 
 	unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
 
-	clean_args_no_options(argc, argv, cmd_device_usage_usage);
+	clean_args_no_options(argc, argv, cmd->usagestr);
 
 	if (check_argc_min(argc - optind, 1))
 		usage(cmd_device_usage_usage);
@@ -615,7 +618,7 @@ static const struct cmd_group device_cmd_group = {
 	}
 };
 
-static int cmd_device(int argc, char **argv)
+static int cmd_device(const struct cmd_struct *unused, int argc, char **argv)
 {
 	return handle_command_group(&device_cmd_group, argc, argv);
 }
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
index cb2f09c7..a86f4ad6 100644
--- a/cmds-fi-du.c
+++ b/cmds-fi-du.c
@@ -557,7 +557,8 @@ static const char * const cmd_filesystem_du_usage[] = {
 	NULL
 };
 
-static int cmd_filesystem_du(int argc, char **argv)
+static int cmd_filesystem_du(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	int ret = 0, err = 0;
 	int i;
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index af882400..36684762 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -965,7 +965,8 @@ static const char * const cmd_filesystem_usage_usage[] = {
 	NULL
 };
 
-static int cmd_filesystem_usage(int argc, char **argv)
+static int cmd_filesystem_usage(const struct cmd_struct *cmd,
+				int argc, char **argv)
 {
 	int ret = 0;
 	unsigned unit_mode;
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index ec038f2f..c2ee8595 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -115,7 +115,8 @@ static void print_df(struct btrfs_ioctl_space_args *sargs, unsigned unit_mode)
 	}
 }
 
-static int cmd_filesystem_df(int argc, char **argv)
+static int cmd_filesystem_df(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	struct btrfs_ioctl_space_args *sargs = NULL;
 	int ret;
@@ -666,7 +667,8 @@ static const char * const cmd_filesystem_show_usage[] = {
 	NULL
 };
 
-static int cmd_filesystem_show(int argc, char **argv)
+static int cmd_filesystem_show(const struct cmd_struct *cmd,
+			       int argc, char **argv)
 {
 	LIST_HEAD(all_uuids);
 	struct btrfs_fs_devices *fs_devices;
@@ -813,7 +815,8 @@ static const char * const cmd_filesystem_sync_usage[] = {
 	NULL
 };
 
-static int cmd_filesystem_sync(int argc, char **argv)
+static int cmd_filesystem_sync(const struct cmd_struct *cmd,
+			       int argc, char **argv)
 {
 	int 	fd, res;
 	char	*path;
@@ -910,7 +913,8 @@ error:
 	return 0;
 }
 
-static int cmd_filesystem_defrag(int argc, char **argv)
+static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
+				 int argc, char **argv)
 {
 	int fd;
 	int flush = 0;
@@ -1089,7 +1093,8 @@ static const char * const cmd_filesystem_resize_usage[] = {
 	NULL
 };
 
-static int cmd_filesystem_resize(int argc, char **argv)
+static int cmd_filesystem_resize(const struct cmd_struct *cmd,
+				 int argc, char **argv)
 {
 	struct btrfs_ioctl_vol_args	args;
 	int	fd, res, len, e;
@@ -1167,7 +1172,8 @@ static const char * const cmd_filesystem_label_usage[] = {
 	NULL
 };
 
-static int cmd_filesystem_label(int argc, char **argv)
+static int cmd_filesystem_label(const struct cmd_struct *cmd,
+				int argc, char **argv)
 {
 	clean_args_no_options(argc, argv, cmd_filesystem_label_usage);
 
@@ -1196,7 +1202,8 @@ static const char * const cmd_filesystem_balance_usage[] = {
 	NULL
 };
 
-static int cmd_filesystem_balance(int argc, char **argv)
+static int cmd_filesystem_balance(const struct cmd_struct *unused,
+				  int argc, char **argv)
 {
 	return cmd_execute(&cmd_struct_balance, argc, argv);
 }
@@ -1228,7 +1235,8 @@ static const struct cmd_group filesystem_cmd_group = {
 	}
 };
 
-static int cmd_filesystem(int argc, char **argv)
+static int cmd_filesystem(const struct cmd_struct *unused,
+			  int argc, char **argv)
 {
 	return handle_command_group(&filesystem_cmd_group, argc, argv);
 }
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
index 34542d71..ca503a2e 100644
--- a/cmds-inspect-dump-super.c
+++ b/cmds-inspect-dump-super.c
@@ -504,7 +504,8 @@ static const char * const cmd_inspect_dump_super_usage[] = {
 	NULL
 };
 
-static int cmd_inspect_dump_super(int argc, char **argv)
+static int cmd_inspect_dump_super(const struct cmd_struct *cmd,
+				  int argc, char **argv)
 {
 	int all = 0;
 	int full = 0;
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
index 6417eab3..5cc39a88 100644
--- a/cmds-inspect-dump-tree.c
+++ b/cmds-inspect-dump-tree.c
@@ -201,7 +201,8 @@ static const char * const cmd_inspect_dump_tree_usage[] = {
 	NULL
 };
 
-static int cmd_inspect_dump_tree(int argc, char **argv)
+static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
+				 int argc, char **argv)
 {
 	struct btrfs_root *root;
 	struct btrfs_fs_info *info;
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
index 14d79ccc..64ca3d0b 100644
--- a/cmds-inspect-tree-stats.c
+++ b/cmds-inspect-tree-stats.c
@@ -427,7 +427,8 @@ static const char * const cmd_inspect_tree_stats_usage[] = {
 	NULL
 };
 
-static int cmd_inspect_tree_stats(int argc, char **argv)
+static int cmd_inspect_tree_stats(const struct cmd_struct *cmd,
+				  int argc, char **argv)
 {
 	struct btrfs_key key;
 	struct btrfs_root *root;
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 12f200b3..1bdc8bd9 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -87,7 +87,8 @@ static const char * const cmd_inspect_inode_resolve_usage[] = {
 	NULL
 };
 
-static int cmd_inspect_inode_resolve(int argc, char **argv)
+static int cmd_inspect_inode_resolve(const struct cmd_struct *cmd,
+				     int argc, char **argv)
 {
 	int fd;
 	int verbose = 0;
@@ -134,7 +135,8 @@ static const char * const cmd_inspect_logical_resolve_usage[] = {
 	NULL
 };
 
-static int cmd_inspect_logical_resolve(int argc, char **argv)
+static int cmd_inspect_logical_resolve(const struct cmd_struct *cmd,
+				       int argc, char **argv)
 {
 	int ret;
 	int fd;
@@ -266,7 +268,8 @@ static const char * const cmd_inspect_subvolid_resolve_usage[] = {
 	NULL
 };
 
-static int cmd_inspect_subvolid_resolve(int argc, char **argv)
+static int cmd_inspect_subvolid_resolve(const struct cmd_struct *cmd,
+					int argc, char **argv)
 {
 	int ret;
 	int fd = -1;
@@ -309,7 +312,8 @@ static const char* const cmd_inspect_rootid_usage[] = {
 	NULL
 };
 
-static int cmd_inspect_rootid(int argc, char **argv)
+static int cmd_inspect_rootid(const struct cmd_struct *cmd,
+			      int argc, char **argv)
 {
 	int ret;
 	int fd = -1;
@@ -588,7 +592,8 @@ out:
 	return ret;
 }
 
-static int cmd_inspect_min_dev_size(int argc, char **argv)
+static int cmd_inspect_min_dev_size(const struct cmd_struct *cmd,
+				    int argc, char **argv)
 {
 	int ret;
 	int fd = -1;
@@ -648,7 +653,7 @@ static const struct cmd_group inspect_cmd_group = {
 	}
 };
 
-static int cmd_inspect(int argc, char **argv)
+static int cmd_inspect(const struct cmd_struct *unused, int argc, char **argv)
 {
 	return handle_command_group(&inspect_cmd_group, argc, argv);
 }
diff --git a/cmds-property.c b/cmds-property.c
index dce1f2a9..5684443c 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -341,7 +341,8 @@ static const char * const cmd_property_get_usage[] = {
 	NULL
 };
 
-static int cmd_property_get(int argc, char **argv)
+static int cmd_property_get(const struct cmd_struct *cmd,
+			    int argc, char **argv)
 {
 	int ret;
 	char *object = NULL;
@@ -368,7 +369,8 @@ static const char * const cmd_property_set_usage[] = {
 	NULL
 };
 
-static int cmd_property_set(int argc, char **argv)
+static int cmd_property_set(const struct cmd_struct *cmd,
+			    int argc, char **argv)
 {
 	int ret;
 	char *object = NULL;
@@ -393,7 +395,8 @@ static const char * const cmd_property_list_usage[] = {
 	NULL
 };
 
-static int cmd_property_list(int argc, char **argv)
+static int cmd_property_list(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	int ret;
 	char *object = NULL;
@@ -420,7 +423,8 @@ static const struct cmd_group property_cmd_group = {
 	}
 };
 
-static int cmd_property(int argc, char **argv)
+static int cmd_property(const struct cmd_struct *unused,
+			int argc, char **argv)
 {
 	return handle_command_group(&property_cmd_group, argc, argv);
 }
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 6945d160..2972baee 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -215,7 +215,8 @@ static const char * const cmd_qgroup_assign_usage[] = {
 	NULL
 };
 
-static int cmd_qgroup_assign(int argc, char **argv)
+static int cmd_qgroup_assign(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	return _cmd_qgroup_assign(1, argc, argv, cmd_qgroup_assign_usage);
 }
@@ -227,7 +228,8 @@ static const char * const cmd_qgroup_remove_usage[] = {
 	NULL
 };
 
-static int cmd_qgroup_remove(int argc, char **argv)
+static int cmd_qgroup_remove(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	return _cmd_qgroup_assign(0, argc, argv, cmd_qgroup_remove_usage);
 }
@@ -239,7 +241,8 @@ static const char * const cmd_qgroup_create_usage[] = {
 	NULL
 };
 
-static int cmd_qgroup_create(int argc, char **argv)
+static int cmd_qgroup_create(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	int ret;
 
@@ -259,7 +262,8 @@ static const char * const cmd_qgroup_destroy_usage[] = {
 	NULL
 };
 
-static int cmd_qgroup_destroy(int argc, char **argv)
+static int cmd_qgroup_destroy(const struct cmd_struct *cmd,
+			      int argc, char **argv)
 {
 	int ret;
 
@@ -296,7 +300,7 @@ static const char * const cmd_qgroup_show_usage[] = {
 	NULL
 };
 
-static int cmd_qgroup_show(int argc, char **argv)
+static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	char *path;
 	int ret = 0;
@@ -430,7 +434,7 @@ static const char * const cmd_qgroup_limit_usage[] = {
 	NULL
 };
 
-static int cmd_qgroup_limit(int argc, char **argv)
+static int cmd_qgroup_limit(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	int ret = 0;
 	int fd;
@@ -528,7 +532,7 @@ static const struct cmd_group qgroup_cmd_group = {
 	}
 };
 
-static int cmd_qgroup(int argc, char **argv)
+static int cmd_qgroup(const struct cmd_struct *unused, int argc, char **argv)
 {
 	return handle_command_group(&qgroup_cmd_group, argc, argv);
 }
diff --git a/cmds-quota.c b/cmds-quota.c
index 5cd5607f..a6e7a6f6 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -67,7 +67,7 @@ static const char * const cmd_quota_enable_usage[] = {
 	NULL
 };
 
-static int cmd_quota_enable(int argc, char **argv)
+static int cmd_quota_enable(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	int ret;
 
@@ -87,7 +87,8 @@ static const char * const cmd_quota_disable_usage[] = {
 	NULL
 };
 
-static int cmd_quota_disable(int argc, char **argv)
+static int cmd_quota_disable(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	int ret;
 
@@ -110,7 +111,7 @@ static const char * const cmd_quota_rescan_usage[] = {
 	NULL
 };
 
-static int cmd_quota_rescan(int argc, char **argv)
+static int cmd_quota_rescan(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	int ret = 0;
 	int fd;
@@ -213,7 +214,7 @@ static const struct cmd_group quota_cmd_group = {
 	}
 };
 
-static int cmd_quota(int argc, char **argv)
+static int cmd_quota(const struct cmd_struct *unused, int argc, char **argv)
 {
 	return handle_command_group(&quota_cmd_group, argc, argv);
 }
diff --git a/cmds-receive.c b/cmds-receive.c
index d88b8793..93c1838d 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -1279,7 +1279,7 @@ static const char * const cmd_receive_usage[] = {
 	NULL
 };
 
-static int cmd_receive(int argc, char **argv)
+static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	char *tomnt = NULL;
 	char fromfile[PATH_MAX];
diff --git a/cmds-replace.c b/cmds-replace.c
index a7379c71..f1e76bdf 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -114,7 +114,8 @@ static const char *const cmd_replace_start_usage[] = {
 	NULL
 };
 
-static int cmd_replace_start(int argc, char **argv)
+static int cmd_replace_start(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	struct btrfs_ioctl_dev_replace_args start_args = {0};
 	struct btrfs_ioctl_dev_replace_args status_args = {0};
@@ -325,7 +326,8 @@ static const char *const cmd_replace_status_usage[] = {
 	NULL
 };
 
-static int cmd_replace_status(int argc, char **argv)
+static int cmd_replace_status(const struct cmd_struct *cmd,
+			      int argc, char **argv)
 {
 	int fd;
 	int c;
@@ -494,7 +496,8 @@ static const char *const cmd_replace_cancel_usage[] = {
 	NULL
 };
 
-static int cmd_replace_cancel(int argc, char **argv)
+static int cmd_replace_cancel(const struct cmd_struct *cmd,
+			      int argc, char **argv)
 {
 	struct btrfs_ioctl_dev_replace_args args = {0};
 	int ret;
@@ -554,7 +557,7 @@ static const struct cmd_group replace_cmd_group = {
 	}
 };
 
-static int cmd_replace(int argc, char **argv)
+static int cmd_replace(const struct cmd_struct *unused, int argc, char **argv)
 {
 	return handle_command_group(&replace_cmd_group, argc, argv);
 }
diff --git a/cmds-rescue.c b/cmds-rescue.c
index e3611f2f..f5a618e1 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -45,7 +45,8 @@ static const char * const cmd_rescue_chunk_recover_usage[] = {
 	NULL
 };
 
-static int cmd_rescue_chunk_recover(int argc, char *argv[])
+static int cmd_rescue_chunk_recover(const struct cmd_struct *cmd,
+				    int argc, char *argv[])
 {
 	int ret = 0;
 	char *file;
@@ -113,7 +114,8 @@ static const char * const cmd_rescue_super_recover_usage[] = {
  *   3 : Fail to Recover bad supeblocks
  *   4 : Abort to recover bad superblocks
  */
-static int cmd_rescue_super_recover(int argc, char **argv)
+static int cmd_rescue_super_recover(const struct cmd_struct *cmd,
+				    int argc, char **argv)
 {
 	int ret;
 	int verbose = 0;
@@ -159,7 +161,8 @@ static const char * const cmd_rescue_zero_log_usage[] = {
 	NULL
 };
 
-static int cmd_rescue_zero_log(int argc, char **argv)
+static int cmd_rescue_zero_log(const struct cmd_struct *cmd,
+			       int argc, char **argv)
 {
 	struct btrfs_root *root;
 	struct btrfs_trans_handle *trans;
@@ -213,7 +216,8 @@ static const char * const cmd_rescue_fix_device_size_usage[] = {
 	NULL
 };
 
-static int cmd_rescue_fix_device_size(int argc, char **argv)
+static int cmd_rescue_fix_device_size(const struct cmd_struct *cmd,
+				      int argc, char **argv)
 {
 	struct btrfs_fs_info *fs_info;
 	char *devname;
@@ -265,7 +269,7 @@ static const struct cmd_group rescue_cmd_group = {
 	}
 };
 
-static int cmd_rescue(int argc, char **argv)
+static int cmd_rescue(const struct cmd_struct *unused, int argc, char **argv)
 {
 	return handle_command_group(&rescue_cmd_group, argc, argv);
 }
diff --git a/cmds-restore.c b/cmds-restore.c
index 05fd1c80..e850ec97 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -1415,7 +1415,7 @@ static const char * const cmd_restore_usage[] = {
 	NULL
 };
 
-static int cmd_restore(int argc, char **argv)
+static int cmd_restore(const struct cmd_struct *unused, int argc, char **argv)
 {
 	struct btrfs_root *root;
 	struct btrfs_key key;
diff --git a/cmds-scrub.c b/cmds-scrub.c
index b0f447c7..a762f035 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -1097,7 +1097,8 @@ static int is_scrub_running_in_kernel(int fd,
 static const char * const cmd_scrub_start_usage[];
 static const char * const cmd_scrub_resume_usage[];
 
-static int scrub_start(int argc, char **argv, int resume)
+static int scrub_start(const struct cmd_struct *cmd, int argc, char **argv,
+		       bool resume)
 {
 	int fdmnt;
 	int prg_fd = -1;
@@ -1176,16 +1177,14 @@ static int scrub_start(int argc, char **argv, int resume)
 			break;
 		case '?':
 		default:
-			usage(resume ? cmd_scrub_resume_usage :
-						cmd_scrub_start_usage);
+			usage(cmd->usagestr);
 		}
 	}
 
 	/* try to catch most error cases before forking */
 
 	if (check_argc_exact(argc - optind, 1)) {
-		usage(resume ? cmd_scrub_resume_usage :
-					cmd_scrub_start_usage);
+		usage(cmd->usagestr);
 	}
 
 	spc.progress = NULL;
@@ -1576,9 +1575,9 @@ static const char * const cmd_scrub_start_usage[] = {
 	NULL
 };
 
-static int cmd_scrub_start(int argc, char **argv)
+static int cmd_scrub_start(const struct cmd_struct *cmd, int argc, char **argv)
 {
-	return scrub_start(argc, argv, 0);
+	return scrub_start(cmd, argc, argv, false);
 }
 static DEFINE_SIMPLE_COMMAND(scrub_start, "start");
 
@@ -1588,7 +1587,7 @@ static const char * const cmd_scrub_cancel_usage[] = {
 	NULL
 };
 
-static int cmd_scrub_cancel(int argc, char **argv)
+static int cmd_scrub_cancel(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	char *path;
 	int ret;
@@ -1643,9 +1642,9 @@ static const char * const cmd_scrub_resume_usage[] = {
 	NULL
 };
 
-static int cmd_scrub_resume(int argc, char **argv)
+static int cmd_scrub_resume(const struct cmd_struct *cmd, int argc, char **argv)
 {
-	return scrub_start(argc, argv, 1);
+	return scrub_start(cmd, argc, argv, true);
 }
 static DEFINE_SIMPLE_COMMAND(scrub_resume, "resume");
 
@@ -1658,7 +1657,7 @@ static const char * const cmd_scrub_status_usage[] = {
 	NULL
 };
 
-static int cmd_scrub_status(int argc, char **argv)
+static int cmd_scrub_status(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	char *path;
 	struct btrfs_ioctl_fs_info_args fi_args;
@@ -1804,7 +1803,7 @@ static const struct cmd_group scrub_cmd_group = {
 	}
 };
 
-static int cmd_scrub(int argc, char **argv)
+static int cmd_scrub(const struct cmd_struct *unused, int argc, char **argv)
 {
 	return handle_command_group(&scrub_cmd_group, argc, argv);
 }
diff --git a/cmds-send.c b/cmds-send.c
index f1e5124d..bd501576 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -523,7 +523,7 @@ static const char * const cmd_send_usage[] = {
 	NULL
 };
 
-static int cmd_send(int argc, char **argv)
+static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	char *subvol = NULL;
 	int ret;
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 6fc4b2e7..13303db8 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -116,7 +116,8 @@ static const char * const cmd_subvol_create_usage[] = {
 	NULL
 };
 
-static int cmd_subvol_create(int argc, char **argv)
+static int cmd_subvol_create(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	int	retval, res, len;
 	int	fddst = -1;
@@ -251,7 +252,8 @@ static const char * const cmd_subvol_delete_usage[] = {
 	NULL
 };
 
-static int cmd_subvol_delete(int argc, char **argv)
+static int cmd_subvol_delete(const struct cmd_struct *cmd,
+			     int argc, char **argv)
 {
 	int res, ret = 0;
 	int cnt;
@@ -477,7 +479,7 @@ static const char * const cmd_subvol_list_usage[] = {
 	NULL,
 };
 
-static int cmd_subvol_list(int argc, char **argv)
+static int cmd_subvol_list(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	struct btrfs_list_filter_set *filter_set;
 	struct btrfs_list_comparer_set *comparer_set;
@@ -649,7 +651,8 @@ static const char * const cmd_subvol_snapshot_usage[] = {
 	NULL
 };
 
-static int cmd_subvol_snapshot(int argc, char **argv)
+static int cmd_subvol_snapshot(const struct cmd_struct *cmd,
+			       int argc, char **argv)
 {
 	char	*subvol, *dst;
 	int	res, retval;
@@ -799,7 +802,8 @@ static const char * const cmd_subvol_get_default_usage[] = {
 	NULL
 };
 
-static int cmd_subvol_get_default(int argc, char **argv)
+static int cmd_subvol_get_default(const struct cmd_struct *cmd,
+				  int argc, char **argv)
 {
 	int fd = -1;
 	int ret;
@@ -867,7 +871,8 @@ static const char * const cmd_subvol_set_default_usage[] = {
 	NULL
 };
 
-static int cmd_subvol_set_default(int argc, char **argv)
+static int cmd_subvol_set_default(const struct cmd_struct *cmd,
+				  int argc, char **argv)
 {
 	int	ret=0, fd;
 	u64	objectid;
@@ -931,7 +936,8 @@ static const char * const cmd_subvol_find_new_usage[] = {
 	NULL
 };
 
-static int cmd_subvol_find_new(int argc, char **argv)
+static int cmd_subvol_find_new(const struct cmd_struct *cmd,
+			       int argc, char **argv)
 {
 	int fd;
 	int ret;
@@ -986,7 +992,7 @@ static const char * const cmd_subvol_show_usage[] = {
 	NULL
 };
 
-static int cmd_subvol_show(int argc, char **argv)
+static int cmd_subvol_show(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	struct root_info get_ri;
 	struct btrfs_list_filter_set *filter_set = NULL;
@@ -1337,7 +1343,7 @@ static int enumerate_dead_subvols(int fd, u64 **ids)
 	return idx;
 }
 
-static int cmd_subvol_sync(int argc, char **argv)
+static int cmd_subvol_sync(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	int fd = -1;
 	int i;
@@ -1448,7 +1454,8 @@ static const struct cmd_group subvolume_cmd_group = {
 	}
 };
 
-static int cmd_subvolume(int argc, char **argv)
+static int cmd_subvolume(const struct cmd_struct *unused,
+			 int argc, char **argv)
 {
 	return handle_command_group(&subvolume_cmd_group, argc, argv);
 }
diff --git a/commands.h b/commands.h
index 9a65204b..4c5469ac 100644
--- a/commands.h
+++ b/commands.h
@@ -24,7 +24,7 @@ enum {
 
 struct cmd_struct {
 	const char *token;
-	int (*fn)(int, char **);
+	int (*fn)(const struct cmd_struct *cmd, int argc, char **argv);
 
 	/*
 	 * Usage strings
@@ -110,7 +110,7 @@ struct cmd_group {
 static inline int cmd_execute(const struct cmd_struct *cmd,
 			      int argc, char **argv)
 {
-	return cmd->fn(argc, argv);
+	return cmd->fn(cmd, argc, argv);
 }
 
 int handle_command_group(const struct cmd_group *grp, int argc,
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 15/20] btrfs-progs: pass cmd_struct to clean_args_no_options{,_relaxed}
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (13 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 14/20] btrfs-progs: pass cmd_struct to command callback function jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 16/20] btrfs-progs: pass cmd_struct to usage() jeffm
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

Now that we have a cmd_struct everywhere, we can pass it to
clean_args_no_options and have it resolve the usage string from
it there.  This is necessary for it to pass the cmd_struct to
usage() in the next patch.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 cmds-balance.c    |  6 +++---
 cmds-device.c     |  9 +++++----
 cmds-filesystem.c |  8 ++++----
 cmds-inspect.c    |  4 ++--
 cmds-qgroup.c     | 16 ++++++++--------
 cmds-quota.c      |  4 ++--
 cmds-rescue.c     |  4 ++--
 cmds-scrub.c      | 15 ++++++++-------
 cmds-subvolume.c  |  6 +++---
 help.c            |  9 +++++----
 help.h            |  7 ++++---
 11 files changed, 46 insertions(+), 42 deletions(-)

diff --git a/cmds-balance.c b/cmds-balance.c
index 1bd7b3ce..488fffcc 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -689,7 +689,7 @@ static int cmd_balance_pause(const struct cmd_struct *cmd,
 	int ret;
 	DIR *dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_balance_pause_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_balance_pause_usage);
@@ -729,7 +729,7 @@ static int cmd_balance_cancel(const struct cmd_struct *cmd,
 	int ret;
 	DIR *dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_balance_cancel_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_balance_cancel_usage);
@@ -770,7 +770,7 @@ static int cmd_balance_resume(const struct cmd_struct *cmd,
 	int fd;
 	int ret;
 
-	clean_args_no_options(argc, argv, cmd_balance_resume_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_balance_resume_usage);
diff --git a/cmds-device.c b/cmds-device.c
index feb53f68..5be748f7 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -149,11 +149,12 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
 	char	*mntpnt;
 	int i, fdmnt, ret = 0;
 	DIR	*dirstream = NULL;
+	const char * const *usagestr = cmd->usagestr;
 
-	clean_args_no_options(argc, argv, cmd->usagestr);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_min(argc - optind, 2))
-		usage(cmd->usagestr);
+		usage(usagestr);
 
 	mntpnt = argv[argc - 1];
 
@@ -347,7 +348,7 @@ static int cmd_device_ready(const struct cmd_struct *cmd, int argc, char **argv)
 	int	ret;
 	char	*path;
 
-	clean_args_no_options(argc, argv, cmd->usagestr);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_device_ready_usage);
@@ -573,7 +574,7 @@ static int cmd_device_usage(const struct cmd_struct *cmd, int argc, char **argv)
 
 	unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
 
-	clean_args_no_options(argc, argv, cmd->usagestr);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_min(argc - optind, 1))
 		usage(cmd_device_usage_usage);
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index c2ee8595..b793532b 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -127,7 +127,7 @@ static int cmd_filesystem_df(const struct cmd_struct *cmd,
 
 	unit_mode = get_unit_mode_from_arg(&argc, argv, 1);
 
-	clean_args_no_options(argc, argv, cmd_filesystem_df_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_filesystem_df_usage);
@@ -822,7 +822,7 @@ static int cmd_filesystem_sync(const struct cmd_struct *cmd,
 	char	*path;
 	DIR	*dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_filesystem_sync_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_filesystem_sync_usage);
@@ -1102,7 +1102,7 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
 	DIR	*dirstream = NULL;
 	struct stat st;
 
-	clean_args_no_options_relaxed(argc, argv, cmd_filesystem_resize_usage);
+	clean_args_no_options_relaxed(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 2))
 		usage(cmd_filesystem_resize_usage);
@@ -1175,7 +1175,7 @@ static const char * const cmd_filesystem_label_usage[] = {
 static int cmd_filesystem_label(const struct cmd_struct *cmd,
 				int argc, char **argv)
 {
-	clean_args_no_options(argc, argv, cmd_filesystem_label_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_min(argc - optind, 1) ||
 			check_argc_max(argc - optind, 2))
diff --git a/cmds-inspect.c b/cmds-inspect.c
index 1bdc8bd9..ece8c8d4 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -277,7 +277,7 @@ static int cmd_inspect_subvolid_resolve(const struct cmd_struct *cmd,
 	char path[PATH_MAX];
 	DIR *dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_inspect_subvolid_resolve_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 2))
 		usage(cmd_inspect_subvolid_resolve_usage);
@@ -320,7 +320,7 @@ static int cmd_inspect_rootid(const struct cmd_struct *cmd,
 	u64 rootid;
 	DIR *dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_inspect_rootid_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_inspect_rootid_usage);
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 2972baee..6e59c559 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -33,8 +33,8 @@ static const char * const qgroup_cmd_group_usage[] = {
 	NULL
 };
 
-static int _cmd_qgroup_assign(int assign, int argc, char **argv,
-		const char * const *usage_str)
+static int _cmd_qgroup_assign(const struct cmd_struct *cmd, int assign,
+			      int argc, char **argv)
 {
 	int ret = 0;
 	int fd;
@@ -70,11 +70,11 @@ static int _cmd_qgroup_assign(int assign, int argc, char **argv,
 			}
 		}
 	} else {
-		clean_args_no_options(argc, argv, usage_str);
+		clean_args_no_options(cmd, argc, argv);
 	}
 
 	if (check_argc_exact(argc - optind, 3))
-		usage(usage_str);
+		usage(cmd->usagestr);
 
 	memset(&args, 0, sizeof(args));
 	args.assign = assign;
@@ -218,7 +218,7 @@ static const char * const cmd_qgroup_assign_usage[] = {
 static int cmd_qgroup_assign(const struct cmd_struct *cmd,
 			     int argc, char **argv)
 {
-	return _cmd_qgroup_assign(1, argc, argv, cmd_qgroup_assign_usage);
+	return _cmd_qgroup_assign(cmd, 1, argc, argv);
 }
 static DEFINE_SIMPLE_COMMAND(qgroup_assign, "assign");
 
@@ -231,7 +231,7 @@ static const char * const cmd_qgroup_remove_usage[] = {
 static int cmd_qgroup_remove(const struct cmd_struct *cmd,
 			     int argc, char **argv)
 {
-	return _cmd_qgroup_assign(0, argc, argv, cmd_qgroup_remove_usage);
+	return _cmd_qgroup_assign(cmd, 0, argc, argv);
 }
 static DEFINE_SIMPLE_COMMAND(qgroup_remove, "remove");
 
@@ -246,7 +246,7 @@ static int cmd_qgroup_create(const struct cmd_struct *cmd,
 {
 	int ret;
 
-	clean_args_no_options(argc, argv, cmd_qgroup_create_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	ret = _cmd_qgroup_create(1, argc, argv);
 
@@ -267,7 +267,7 @@ static int cmd_qgroup_destroy(const struct cmd_struct *cmd,
 {
 	int ret;
 
-	clean_args_no_options(argc, argv, cmd_qgroup_destroy_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	ret = _cmd_qgroup_create(0, argc, argv);
 
diff --git a/cmds-quota.c b/cmds-quota.c
index a6e7a6f6..23757f9a 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -71,7 +71,7 @@ static int cmd_quota_enable(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	int ret;
 
-	clean_args_no_options(argc, argv, cmd_quota_enable_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	ret = quota_ctl(BTRFS_QUOTA_CTL_ENABLE, argc, argv);
 
@@ -92,7 +92,7 @@ static int cmd_quota_disable(const struct cmd_struct *cmd,
 {
 	int ret;
 
-	clean_args_no_options(argc, argv, cmd_quota_disable_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	ret = quota_ctl(BTRFS_QUOTA_CTL_DISABLE, argc, argv);
 
diff --git a/cmds-rescue.c b/cmds-rescue.c
index f5a618e1..975ff05a 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -170,7 +170,7 @@ static int cmd_rescue_zero_log(const struct cmd_struct *cmd,
 	char *devname;
 	int ret;
 
-	clean_args_no_options(argc, argv, cmd_rescue_zero_log_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc, 2))
 		usage(cmd_rescue_zero_log_usage);
@@ -223,7 +223,7 @@ static int cmd_rescue_fix_device_size(const struct cmd_struct *cmd,
 	char *devname;
 	int ret;
 
-	clean_args_no_options(argc, argv, cmd_rescue_fix_device_size_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc, 2))
 		usage(cmd_rescue_fix_device_size_usage);
diff --git a/cmds-scrub.c b/cmds-scrub.c
index a762f035..9917a2f4 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -1097,8 +1097,7 @@ static int is_scrub_running_in_kernel(int fd,
 static const char * const cmd_scrub_start_usage[];
 static const char * const cmd_scrub_resume_usage[];
 
-static int scrub_start(const struct cmd_struct *cmd, int argc, char **argv,
-		       bool resume)
+static int scrub_start(int argc, char **argv, bool resume)
 {
 	int fdmnt;
 	int prg_fd = -1;
@@ -1177,14 +1176,16 @@ static int scrub_start(const struct cmd_struct *cmd, int argc, char **argv,
 			break;
 		case '?':
 		default:
-			usage(cmd->usagestr);
+			usage(resume ? cmd_scrub_resume_usage :
+						cmd_scrub_start_usage);
 		}
 	}
 
 	/* try to catch most error cases before forking */
 
 	if (check_argc_exact(argc - optind, 1)) {
-		usage(cmd->usagestr);
+		usage(resume ? cmd_scrub_resume_usage :
+					cmd_scrub_start_usage);
 	}
 
 	spc.progress = NULL;
@@ -1577,7 +1578,7 @@ static const char * const cmd_scrub_start_usage[] = {
 
 static int cmd_scrub_start(const struct cmd_struct *cmd, int argc, char **argv)
 {
-	return scrub_start(cmd, argc, argv, false);
+	return scrub_start(argc, argv, false);
 }
 static DEFINE_SIMPLE_COMMAND(scrub_start, "start");
 
@@ -1594,7 +1595,7 @@ static int cmd_scrub_cancel(const struct cmd_struct *cmd, int argc, char **argv)
 	int fdmnt = -1;
 	DIR *dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_scrub_cancel_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_scrub_cancel_usage);
@@ -1644,7 +1645,7 @@ static const char * const cmd_scrub_resume_usage[] = {
 
 static int cmd_scrub_resume(const struct cmd_struct *cmd, int argc, char **argv)
 {
-	return scrub_start(cmd, argc, argv, true);
+	return scrub_start(argc, argv, true);
 }
 static DEFINE_SIMPLE_COMMAND(scrub_resume, "resume");
 
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 13303db8..c577cc23 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -812,7 +812,7 @@ static int cmd_subvol_get_default(const struct cmd_struct *cmd,
 	u64 default_id;
 	DIR *dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_subvol_get_default_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd_subvol_get_default_usage);
@@ -880,7 +880,7 @@ static int cmd_subvol_set_default(const struct cmd_struct *cmd,
 	char	*subvolid;
 	DIR	*dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_subvol_set_default_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_min(argc - optind, 1) ||
 			check_argc_max(argc - optind, 2))
@@ -945,7 +945,7 @@ static int cmd_subvol_find_new(const struct cmd_struct *cmd,
 	u64 last_gen;
 	DIR *dirstream = NULL;
 
-	clean_args_no_options(argc, argv, cmd_subvol_find_new_usage);
+	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 2))
 		usage(cmd_subvol_find_new_usage);
diff --git a/help.c b/help.c
index b7f06aae..23d6ea8f 100644
--- a/help.c
+++ b/help.c
@@ -86,7 +86,7 @@ int check_argc_max(int nargs, int expected)
  * Unknown short and long options are reported, optionally the @usage is printed
  * before exit.
  */
-void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
+void clean_args_no_options(const struct cmd_struct *cmd, int argc, char *argv[])
 {
 	static const struct option long_options[] = {
 		{NULL, 0, NULL, 0}
@@ -100,8 +100,8 @@ void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
 
 		switch (c) {
 		default:
-			if (usagestr)
-				usage(usagestr);
+			if (cmd->usagestr)
+				usage(cmd->usagestr);
 		}
 	}
 }
@@ -115,7 +115,8 @@ void clean_args_no_options(int argc, char *argv[], const char * const *usagestr)
  * - "-- option1 option2 ..."
  * - "option1 option2 ..."
  */
-void clean_args_no_options_relaxed(int argc, char *argv[], const char * const *usagestr)
+void clean_args_no_options_relaxed(const struct cmd_struct *cmd,
+				   int argc, char *argv[])
 {
 	if (argc <= 1)
 		return;
diff --git a/help.h b/help.h
index a69ea6b2..e642f58d 100644
--- a/help.h
+++ b/help.h
@@ -71,9 +71,10 @@ void help_command_group(const struct cmd_group *grp, int argc, char **argv);
 int check_argc_exact(int nargs, int expected);
 int check_argc_min(int nargs, int expected);
 int check_argc_max(int nargs, int expected);
-void clean_args_no_options(int argc, char *argv[], const char * const *usage);
-void clean_args_no_options_relaxed(int argc, char *argv[],
-		const char * const *usagestr);
+void clean_args_no_options(const struct cmd_struct *cmd,
+			   int argc, char *argv[]);
+void clean_args_no_options_relaxed(const struct cmd_struct *cmd,
+				   int argc, char *argv[]);
 
 void fixup_argv0(char **argv, const char *token);
 void set_argv0(char **argv);
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 16/20] btrfs-progs: pass cmd_struct to usage()
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (14 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 15/20] btrfs-progs: pass cmd_struct to clean_args_no_options{,_relaxed} jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 17/20] btrfs-progs: add support for output formats jeffm
                   ` (3 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

Now that every call site has a cmd_struct, we can just pass the cmd_struct
to usage to print the usager information.  This allows us to interpret
the format flags we'll add later in this series to inform the user of
which output formats any given command supports.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs-calc-size.c         |  2 +-
 btrfs-debug-tree.c        |  2 +-
 btrfs-show-super.c        |  2 +-
 check/main.c              |  4 ++--
 cmds-balance.c            | 14 +++++++-------
 cmds-device.c             | 19 +++++++++----------
 cmds-fi-du.c              |  4 ++--
 cmds-fi-usage.c           |  4 ++--
 cmds-filesystem.c         | 18 +++++++++---------
 cmds-inspect-dump-super.c |  4 ++--
 cmds-inspect-dump-tree.c  |  4 ++--
 cmds-inspect-tree-stats.c |  4 ++--
 cmds-inspect.c            | 16 ++++++++--------
 cmds-property.c           | 22 +++++++++-------------
 cmds-qgroup.c             | 18 +++++++++---------
 cmds-quota.c              |  8 ++++----
 cmds-receive.c            |  4 ++--
 cmds-replace.c            | 12 ++++++------
 cmds-rescue.c             | 12 ++++++------
 cmds-restore.c            |  8 ++++----
 cmds-scrub.c              | 25 ++++++++++---------------
 cmds-send.c               |  2 +-
 cmds-subvolume.c          | 30 +++++++++++++++---------------
 help.c                    |  6 +++---
 help.h                    |  2 +-
 25 files changed, 118 insertions(+), 128 deletions(-)

diff --git a/btrfs-calc-size.c b/btrfs-calc-size.c
index 908e830f..4c8fcc19 100644
--- a/btrfs-calc-size.c
+++ b/btrfs-calc-size.c
@@ -30,7 +30,7 @@ int main(int argc, char **argv)
 "\nthe tool has been deprecated, please use 'btrfs inspect-internal tree-stats' instead\n");
 
 	if (argc > 1 && !strcmp(argv[1], "--help"))
-		usage(cmd->usagestr);
+		usage(cmd);
 
 	ret = cmd_execute(cmd, argc, argv);
 
diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
index 7f254519..49a2e949 100644
--- a/btrfs-debug-tree.c
+++ b/btrfs-debug-tree.c
@@ -30,7 +30,7 @@ int main(int argc, char **argv)
 	set_argv0(argv);
 
 	if (argc > 1 && !strcmp(argv[1], "--help"))
-		usage(cmd->usagestr);
+		usage(cmd);
 
 	radix_tree_init();
 
diff --git a/btrfs-show-super.c b/btrfs-show-super.c
index ee717c33..b4a5b693 100644
--- a/btrfs-show-super.c
+++ b/btrfs-show-super.c
@@ -31,7 +31,7 @@ int main(int argc, char **argv)
 "\nthe tool has been deprecated, please use 'btrfs inspect-internal dump-super' instead\n");
 
 	if (argc > 1 && !strcmp(argv[1], "--help"))
-		usage(cmd->usagestr);
+		usage(cmd);
 
 	ret = cmd_execute(cmd, argc, argv);
 
diff --git a/check/main.c b/check/main.c
index bd31fb9f..0bb8633a 100644
--- a/check/main.c
+++ b/check/main.c
@@ -9530,7 +9530,7 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
 				break;
 			case '?':
 			case 'h':
-				usage(cmd_check_usage);
+				usage(cmd);
 			case GETOPT_VAL_REPAIR:
 				printf("enabling repair mode\n");
 				repair = 1;
@@ -9581,7 +9581,7 @@ static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_check_usage);
+		usage(cmd);
 
 	if (ctx.progress_enabled) {
 		ctx.tp = TASK_NOTHING;
diff --git a/cmds-balance.c b/cmds-balance.c
index 488fffcc..c639459f 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -585,12 +585,12 @@ static int cmd_balance_start(const struct cmd_struct *cmd,
 			background = 1;
 			break;
 		default:
-			usage(cmd_balance_start_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_balance_start_usage);
+		usage(cmd);
 
 	/*
 	 * allow -s only under --force, otherwise do with system chunks
@@ -692,7 +692,7 @@ static int cmd_balance_pause(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_balance_pause_usage);
+		usage(cmd);
 
 	path = argv[optind];
 
@@ -732,7 +732,7 @@ static int cmd_balance_cancel(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_balance_cancel_usage);
+		usage(cmd);
 
 	path = argv[optind];
 
@@ -773,7 +773,7 @@ static int cmd_balance_resume(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_balance_resume_usage);
+		usage(cmd);
 
 	path = argv[optind];
 
@@ -856,12 +856,12 @@ static int cmd_balance_status(const struct cmd_struct *cmd,
 			verbose = 1;
 			break;
 		default:
-			usage(cmd_balance_status_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_balance_status_usage);
+		usage(cmd);
 
 	path = argv[optind];
 
diff --git a/cmds-device.c b/cmds-device.c
index 5be748f7..6c74ca8e 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -77,12 +77,12 @@ static int cmd_device_add(const struct cmd_struct *cmd,
 			force = 1;
 			break;
 		default:
-			usage(cmd_device_add_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_min(argc - optind, 2))
-		usage(cmd_device_add_usage);
+		usage(cmd);
 
 	last_dev = argc - 1;
 	mntpnt = argv[last_dev];
@@ -149,12 +149,11 @@ static int _cmd_device_remove(const struct cmd_struct *cmd,
 	char	*mntpnt;
 	int i, fdmnt, ret = 0;
 	DIR	*dirstream = NULL;
-	const char * const *usagestr = cmd->usagestr;
 
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_min(argc - optind, 2))
-		usage(usagestr);
+		usage(cmd);
 
 	mntpnt = argv[argc - 1];
 
@@ -290,13 +289,13 @@ static int cmd_device_scan(const struct cmd_struct *cmd, int argc, char **argv)
 			all = 1;
 			break;
 		default:
-			usage(cmd_device_scan_usage);
+			usage(cmd);
 		}
 	}
 	devstart = optind;
 
 	if (all && check_argc_max(argc - optind, 1))
-		usage(cmd_device_scan_usage);
+		usage(cmd);
 
 	if (all || argc - optind == 0) {
 		printf("Scanning for Btrfs filesystems\n");
@@ -351,7 +350,7 @@ static int cmd_device_ready(const struct cmd_struct *cmd, int argc, char **argv)
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_device_ready_usage);
+		usage(cmd);
 
 	fd = open("/dev/btrfs-control", O_RDWR);
 	if (fd < 0) {
@@ -434,12 +433,12 @@ static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
 			break;
 		case '?':
 		default:
-			usage(cmd_device_stats_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_device_stats_usage);
+		usage(cmd);
 
 	dev_path = argv[optind];
 
@@ -577,7 +576,7 @@ static int cmd_device_usage(const struct cmd_struct *cmd, int argc, char **argv)
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_min(argc - optind, 1))
-		usage(cmd_device_usage_usage);
+		usage(cmd);
 
 	for (i = optind; i < argc; i++) {
 		int fd;
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
index a86f4ad6..ebcb0c15 100644
--- a/cmds-fi-du.c
+++ b/cmds-fi-du.c
@@ -580,12 +580,12 @@ static int cmd_filesystem_du(const struct cmd_struct *cmd,
 			summarize = 1;
 			break;
 		default:
-			usage(cmd_filesystem_du_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_min(argc - optind, 1))
-		usage(cmd_filesystem_du_usage);
+		usage(cmd);
 
 	kernel_version = get_running_kernel_version();
 
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 36684762..6da96411 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -988,12 +988,12 @@ static int cmd_filesystem_usage(const struct cmd_struct *cmd,
 			tabular = 1;
 			break;
 		default:
-			usage(cmd_filesystem_usage_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_min(argc - optind, 1))
-		usage(cmd_filesystem_usage_usage);
+		usage(cmd);
 
 	for (i = optind; i < argc; i++) {
 		int fd;
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index b793532b..8ddb7ea5 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -130,7 +130,7 @@ static int cmd_filesystem_df(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_filesystem_df_usage);
+		usage(cmd);
 
 	path = argv[optind];
 
@@ -705,17 +705,17 @@ static int cmd_filesystem_show(const struct cmd_struct *cmd,
 			where = BTRFS_SCAN_MOUNTED;
 			break;
 		default:
-			usage(cmd_filesystem_show_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_max(argc, optind + 1))
-		usage(cmd_filesystem_show_usage);
+		usage(cmd);
 
 	if (argc > optind) {
 		search = argv[optind];
 		if (*search == 0)
-			usage(cmd_filesystem_show_usage);
+			usage(cmd);
 		type = check_arg_type(search);
 
 		/*
@@ -825,7 +825,7 @@ static int cmd_filesystem_sync(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_filesystem_sync_usage);
+		usage(cmd);
 
 	path = argv[optind];
 
@@ -974,12 +974,12 @@ static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
 			recursive = 1;
 			break;
 		default:
-			usage(cmd_filesystem_defrag_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_min(argc - optind, 1))
-		usage(cmd_filesystem_defrag_usage);
+		usage(cmd);
 
 	memset(&defrag_global_range, 0, sizeof(defrag_global_range));
 	defrag_global_range.start = start;
@@ -1105,7 +1105,7 @@ static int cmd_filesystem_resize(const struct cmd_struct *cmd,
 	clean_args_no_options_relaxed(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 2))
-		usage(cmd_filesystem_resize_usage);
+		usage(cmd);
 
 	amount = argv[optind];
 	path = argv[optind + 1];
@@ -1179,7 +1179,7 @@ static int cmd_filesystem_label(const struct cmd_struct *cmd,
 
 	if (check_argc_min(argc - optind, 1) ||
 			check_argc_max(argc - optind, 2))
-		usage(cmd_filesystem_label_usage);
+		usage(cmd);
 
 	if (argc - optind > 1) {
 		return set_label(argv[optind], argv[optind + 1]);
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
index ca503a2e..6341d2ac 100644
--- a/cmds-inspect-dump-super.c
+++ b/cmds-inspect-dump-super.c
@@ -573,12 +573,12 @@ static int cmd_inspect_dump_super(const struct cmd_struct *cmd,
 			all = 0;
 			break;
 		default:
-			usage(cmd_inspect_dump_super_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_min(argc - optind, 1))
-		usage(cmd_inspect_dump_super_usage);
+		usage(cmd);
 
 	for (i = optind; i < argc; i++) {
 		filename = argv[i];
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
index 5cc39a88..22bc9cb9 100644
--- a/cmds-inspect-dump-tree.c
+++ b/cmds-inspect-dump-tree.c
@@ -288,12 +288,12 @@ static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
 			break;
 			}
 		default:
-			usage(cmd_inspect_dump_tree_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_inspect_dump_tree_usage);
+		usage(cmd);
 
 	ret = check_arg_type(argv[optind]);
 	if (ret != BTRFS_ARG_BLKDEV && ret != BTRFS_ARG_REG) {
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
index 64ca3d0b..e5e68562 100644
--- a/cmds-inspect-tree-stats.c
+++ b/cmds-inspect-tree-stats.c
@@ -444,12 +444,12 @@ static int cmd_inspect_tree_stats(const struct cmd_struct *cmd,
 			no_pretty = 1;
 			break;
 		default:
-			usage(cmd_inspect_tree_stats_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1)) {
-		usage(cmd_inspect_tree_stats_usage);
+		usage(cmd);
 	}
 
 	ret = check_mounted(argv[optind]);
diff --git a/cmds-inspect.c b/cmds-inspect.c
index ece8c8d4..bc86e999 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -105,12 +105,12 @@ static int cmd_inspect_inode_resolve(const struct cmd_struct *cmd,
 			verbose = 1;
 			break;
 		default:
-			usage(cmd_inspect_inode_resolve_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 2))
-		usage(cmd_inspect_inode_resolve_usage);
+		usage(cmd);
 
 	fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1);
 	if (fd < 0)
@@ -167,12 +167,12 @@ static int cmd_inspect_logical_resolve(const struct cmd_struct *cmd,
 			size = arg_strtou64(optarg);
 			break;
 		default:
-			usage(cmd_inspect_logical_resolve_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 2))
-		usage(cmd_inspect_logical_resolve_usage);
+		usage(cmd);
 
 	size = min(size, (u64)SZ_64K);
 	inodes = malloc(size);
@@ -280,7 +280,7 @@ static int cmd_inspect_subvolid_resolve(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 2))
-		usage(cmd_inspect_subvolid_resolve_usage);
+		usage(cmd);
 
 	fd = btrfs_open_dir(argv[optind + 1], &dirstream, 1);
 	if (fd < 0) {
@@ -323,7 +323,7 @@ static int cmd_inspect_rootid(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_inspect_rootid_usage);
+		usage(cmd);
 
 	fd = btrfs_open_file_or_dir(argv[optind], &dirstream, 1);
 	if (fd < 0) {
@@ -617,11 +617,11 @@ static int cmd_inspect_min_dev_size(const struct cmd_struct *cmd,
 			devid = arg_strtou64(optarg);
 			break;
 		default:
-			usage(cmd_inspect_min_dev_size_usage);
+			usage(cmd);
 		}
 	}
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_inspect_min_dev_size_usage);
+		usage(cmd);
 
 	fd = btrfs_open_dir(argv[optind], &dirstream, 1);
 	if (fd < 0) {
diff --git a/cmds-property.c b/cmds-property.c
index 5684443c..4aa2f18f 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -256,8 +256,7 @@ out:
 
 }
 
-static void parse_args(int argc, char **argv,
-		       const char * const *usage_str,
+static void parse_args(const struct cmd_struct *cmd, int argc, char **argv,
 		       int *types, char **object,
 		       char **name, char **value, int min_nonopt_args)
 {
@@ -276,7 +275,7 @@ static void parse_args(int argc, char **argv,
 			type_str = optarg;
 			break;
 		default:
-			usage(usage_str);
+			usage(cmd);
 		}
 	}
 
@@ -287,7 +286,7 @@ static void parse_args(int argc, char **argv,
 
 	if (check_argc_min(argc - optind, min_nonopt_args) ||
 	    check_argc_max(argc - optind, max_nonopt_args))
-		usage(usage_str);
+		usage(cmd);
 
 	*types = 0;
 	if (type_str) {
@@ -304,7 +303,7 @@ static void parse_args(int argc, char **argv,
 			*types = prop_object_dev;
 		} else {
 			error("invalid object type: %s", type_str);
-			usage(usage_str);
+			usage(cmd);
 		}
 	}
 
@@ -319,11 +318,11 @@ static void parse_args(int argc, char **argv,
 		if (ret < 0) {
 			error("failed to detect object type: %s",
 				strerror(-ret));
-			usage(usage_str);
+			usage(cmd);
 		}
 		if (!*types) {
 			error("object is not a btrfs object: %s", *object);
-			usage(usage_str);
+			usage(cmd);
 		}
 	}
 }
@@ -349,8 +348,7 @@ static int cmd_property_get(const struct cmd_struct *cmd,
 	char *name = NULL;
 	int types = 0;
 
-	parse_args(argc, argv, cmd_property_get_usage, &types, &object, &name,
-		   NULL, 1);
+	parse_args(cmd, argc, argv, &types, &object, &name, NULL, 1);
 
 	if (name)
 		ret = setget_prop(types, object, name, NULL);
@@ -378,8 +376,7 @@ static int cmd_property_set(const struct cmd_struct *cmd,
 	char *value = NULL;
 	int types = 0;
 
-	parse_args(argc, argv, cmd_property_set_usage, &types,
-		   &object, &name, &value, 3);
+	parse_args(cmd, argc, argv, &types, &object, &name, &value, 3);
 
 	ret = setget_prop(types, object, name, value);
 
@@ -402,8 +399,7 @@ static int cmd_property_list(const struct cmd_struct *cmd,
 	char *object = NULL;
 	int types = 0;
 
-	parse_args(argc, argv, cmd_property_list_usage,
-		   &types, &object, NULL, NULL, 1);
+	parse_args(cmd, argc, argv, &types, &object, NULL, NULL, 1);
 
 	ret = dump_props(types, object, 1);
 
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 6e59c559..986f272c 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -74,7 +74,7 @@ static int _cmd_qgroup_assign(const struct cmd_struct *cmd, int assign,
 	}
 
 	if (check_argc_exact(argc - optind, 3))
-		usage(cmd->usagestr);
+		usage(cmd);
 
 	memset(&args, 0, sizeof(args));
 	args.assign = assign;
@@ -251,7 +251,7 @@ static int cmd_qgroup_create(const struct cmd_struct *cmd,
 	ret = _cmd_qgroup_create(1, argc, argv);
 
 	if (ret < 0)
-		usage(cmd_qgroup_create_usage);
+		usage(cmd);
 	return ret;
 }
 static DEFINE_SIMPLE_COMMAND(qgroup_create, "create");
@@ -272,7 +272,7 @@ static int cmd_qgroup_destroy(const struct cmd_struct *cmd,
 	ret = _cmd_qgroup_create(0, argc, argv);
 
 	if (ret < 0)
-		usage(cmd_qgroup_destroy_usage);
+		usage(cmd);
 	return ret;
 }
 static DEFINE_SIMPLE_COMMAND(qgroup_destroy, "destroy");
@@ -366,7 +366,7 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
 			ret = btrfs_qgroup_parse_sort_string(optarg,
 							     &comparer_set);
 			if (ret)
-				usage(cmd_qgroup_show_usage);
+				usage(cmd);
 			break;
 		case GETOPT_VAL_SYNC:
 			sync = 1;
@@ -375,13 +375,13 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
 			verbose = true;
 			break;
 		default:
-			usage(cmd_qgroup_show_usage);
+			usage(cmd);
 		}
 	}
 	btrfs_qgroup_setup_units(unit_mode);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_qgroup_show_usage);
+		usage(cmd);
 
 	path = argv[optind];
 	fd = btrfs_open_dir(path, &dirstream, 1);
@@ -457,12 +457,12 @@ static int cmd_qgroup_limit(const struct cmd_struct *cmd, int argc, char **argv)
 			exclusive = 1;
 			break;
 		default:
-			usage(cmd_qgroup_limit_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_min(argc - optind, 2))
-		usage(cmd_qgroup_limit_usage);
+		usage(cmd);
 
 	if (!parse_limit(argv[optind], &size)) {
 		error("invalid size argument: %s", argv[optind]);
@@ -501,7 +501,7 @@ static int cmd_qgroup_limit(const struct cmd_struct *cmd, int argc, char **argv)
 		args.qgroupid = parse_qgroupid(argv[optind + 1]);
 		path = argv[optind + 2];
 	} else
-		usage(cmd_qgroup_limit_usage);
+		usage(cmd);
 
 	fd = btrfs_open_dir(path, &dirstream, 1);
 	if (fd < 0)
diff --git a/cmds-quota.c b/cmds-quota.c
index 23757f9a..c634c4e9 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -76,7 +76,7 @@ static int cmd_quota_enable(const struct cmd_struct *cmd, int argc, char **argv)
 	ret = quota_ctl(BTRFS_QUOTA_CTL_ENABLE, argc, argv);
 
 	if (ret < 0)
-		usage(cmd_quota_enable_usage);
+		usage(cmd);
 	return ret;
 }
 static DEFINE_SIMPLE_COMMAND(quota_enable, "enable");
@@ -97,7 +97,7 @@ static int cmd_quota_disable(const struct cmd_struct *cmd,
 	ret = quota_ctl(BTRFS_QUOTA_CTL_DISABLE, argc, argv);
 
 	if (ret < 0)
-		usage(cmd_quota_disable_usage);
+		usage(cmd);
 	return ret;
 }
 static DEFINE_SIMPLE_COMMAND(quota_disable, "disable");
@@ -140,7 +140,7 @@ static int cmd_quota_rescan(const struct cmd_struct *cmd, int argc, char **argv)
 			wait_for_completion = 1;
 			break;
 		default:
-			usage(cmd_quota_rescan_usage);
+			usage(cmd);
 		}
 	}
 
@@ -150,7 +150,7 @@ static int cmd_quota_rescan(const struct cmd_struct *cmd, int argc, char **argv)
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_quota_rescan_usage);
+		usage(cmd);
 
 	memset(&args, 0, sizeof(args));
 
diff --git a/cmds-receive.c b/cmds-receive.c
index 93c1838d..5429c607 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -1352,9 +1352,9 @@ static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
 	}
 
 	if (dump && check_argc_exact(argc - optind, 0))
-		usage(cmd_receive_usage);
+		usage(cmd);
 	if (!dump && check_argc_exact(argc - optind, 1))
-		usage(cmd_receive_usage);
+		usage(cmd);
 
 	tomnt = argv[optind];
 
diff --git a/cmds-replace.c b/cmds-replace.c
index f1e76bdf..2057d69e 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -148,7 +148,7 @@ static int cmd_replace_start(const struct cmd_struct *cmd,
 			break;
 		case '?':
 		default:
-			usage(cmd_replace_start_usage);
+			usage(cmd);
 		}
 	}
 
@@ -157,7 +157,7 @@ static int cmd_replace_start(const struct cmd_struct *cmd,
 		 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_AVOID :
 		 BTRFS_IOCTL_DEV_REPLACE_CONT_READING_FROM_SRCDEV_MODE_ALWAYS;
 	if (check_argc_exact(argc - optind, 3))
-		usage(cmd_replace_start_usage);
+		usage(cmd);
 	path = argv[optind + 2];
 
 	fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
@@ -343,12 +343,12 @@ static int cmd_replace_status(const struct cmd_struct *cmd,
 			break;
 		case '?':
 		default:
-			usage(cmd_replace_status_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_replace_status_usage);
+		usage(cmd);
 
 	path = argv[optind];
 	fd = btrfs_open_dir(path, &dirstream, 1);
@@ -510,12 +510,12 @@ static int cmd_replace_cancel(const struct cmd_struct *cmd,
 		switch (c) {
 		case '?':
 		default:
-			usage(cmd_replace_cancel_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_replace_cancel_usage);
+		usage(cmd);
 
 	path = argv[optind];
 	fd = btrfs_open_dir(path, &dirstream, 1);
diff --git a/cmds-rescue.c b/cmds-rescue.c
index 975ff05a..f96d8010 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -66,12 +66,12 @@ static int cmd_rescue_chunk_recover(const struct cmd_struct *cmd,
 			break;
 		case 'h':
 		default:
-			usage(cmd_rescue_chunk_recover_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_rescue_chunk_recover_usage);
+		usage(cmd);
 
 	file = argv[optind];
 
@@ -134,11 +134,11 @@ static int cmd_rescue_super_recover(const struct cmd_struct *cmd,
 			yes = 1;
 			break;
 		default:
-			usage(cmd_rescue_super_recover_usage);
+			usage(cmd);
 		}
 	}
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_rescue_super_recover_usage);
+		usage(cmd);
 
 	dname = argv[optind];
 	ret = check_mounted(dname);
@@ -173,7 +173,7 @@ static int cmd_rescue_zero_log(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc, 2))
-		usage(cmd_rescue_zero_log_usage);
+		usage(cmd);
 
 	devname = argv[optind];
 	ret = check_mounted(devname);
@@ -226,7 +226,7 @@ static int cmd_rescue_fix_device_size(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc, 2))
-		usage(cmd_rescue_fix_device_size_usage);
+		usage(cmd);
 
 	devname = argv[optind];
 	ret = check_mounted(devname);
diff --git a/cmds-restore.c b/cmds-restore.c
index e850ec97..87cfb582 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -1415,7 +1415,7 @@ static const char * const cmd_restore_usage[] = {
 	NULL
 };
 
-static int cmd_restore(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_restore(const struct cmd_struct *cmd, int argc, char **argv)
 {
 	struct btrfs_root *root;
 	struct btrfs_key key;
@@ -1518,14 +1518,14 @@ static int cmd_restore(const struct cmd_struct *unused, int argc, char **argv)
 				get_xattrs = 1;
 				break;
 			default:
-				usage(cmd_restore_usage);
+				usage(cmd);
 		}
 	}
 
 	if (!list_roots && check_argc_min(argc - optind, 2))
-		usage(cmd_restore_usage);
+		usage(cmd);
 	else if (list_roots && check_argc_min(argc - optind, 1))
-		usage(cmd_restore_usage);
+		usage(cmd);
 
 	if (fs_location && root_objectid) {
 		fprintf(stderr, "don't use -f and -r at the same time.\n");
diff --git a/cmds-scrub.c b/cmds-scrub.c
index 9917a2f4..872c996b 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -1094,10 +1094,8 @@ static int is_scrub_running_in_kernel(int fd,
 	return 0;
 }
 
-static const char * const cmd_scrub_start_usage[];
-static const char * const cmd_scrub_resume_usage[];
-
-static int scrub_start(int argc, char **argv, bool resume)
+static int scrub_start(const struct cmd_struct *cmd, int argc, char **argv,
+		       bool resume)
 {
 	int fdmnt;
 	int prg_fd = -1;
@@ -1176,17 +1174,14 @@ static int scrub_start(int argc, char **argv, bool resume)
 			break;
 		case '?':
 		default:
-			usage(resume ? cmd_scrub_resume_usage :
-						cmd_scrub_start_usage);
+			usage(cmd);
 		}
 	}
 
 	/* try to catch most error cases before forking */
 
-	if (check_argc_exact(argc - optind, 1)) {
-		usage(resume ? cmd_scrub_resume_usage :
-					cmd_scrub_start_usage);
-	}
+	if (check_argc_exact(argc - optind, 1))
+		usage(cmd);
 
 	spc.progress = NULL;
 	if (do_quiet && do_print)
@@ -1578,7 +1573,7 @@ static const char * const cmd_scrub_start_usage[] = {
 
 static int cmd_scrub_start(const struct cmd_struct *cmd, int argc, char **argv)
 {
-	return scrub_start(argc, argv, false);
+	return scrub_start(cmd, argc, argv, false);
 }
 static DEFINE_SIMPLE_COMMAND(scrub_start, "start");
 
@@ -1598,7 +1593,7 @@ static int cmd_scrub_cancel(const struct cmd_struct *cmd, int argc, char **argv)
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_scrub_cancel_usage);
+		usage(cmd);
 
 	path = argv[optind];
 
@@ -1645,7 +1640,7 @@ static const char * const cmd_scrub_resume_usage[] = {
 
 static int cmd_scrub_resume(const struct cmd_struct *cmd, int argc, char **argv)
 {
-	return scrub_start(argc, argv, true);
+	return scrub_start(cmd, argc, argv, true);
 }
 static DEFINE_SIMPLE_COMMAND(scrub_resume, "resume");
 
@@ -1691,12 +1686,12 @@ static int cmd_scrub_status(const struct cmd_struct *cmd, int argc, char **argv)
 			break;
 		case '?':
 		default:
-			usage(cmd_scrub_status_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_scrub_status_usage);
+		usage(cmd);
 
 	path = argv[optind];
 
diff --git a/cmds-send.c b/cmds-send.c
index bd501576..e2d87785 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -643,7 +643,7 @@ static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
 	}
 
 	if (check_argc_min(argc - optind, 1))
-		usage(cmd_send_usage);
+		usage(cmd);
 
 	if (outname[0]) {
 		int tmpfd;
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index c577cc23..dc529315 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -150,12 +150,12 @@ static int cmd_subvol_create(const struct cmd_struct *cmd,
 			}
 			break;
 		default:
-			usage(cmd_subvol_create_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_subvol_create_usage);
+		usage(cmd);
 
 	dst = argv[optind];
 
@@ -295,12 +295,12 @@ static int cmd_subvol_delete(const struct cmd_struct *cmd,
 			verbose++;
 			break;
 		default:
-			usage(cmd_subvol_delete_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_min(argc - optind, 1))
-		usage(cmd_subvol_delete_usage);
+		usage(cmd);
 
 	if (verbose > 0) {
 		printf("Transaction commit: %s\n",
@@ -633,7 +633,7 @@ out:
 	if (comparer_set)
 		free(comparer_set);
 	if (uerr)
-		usage(cmd_subvol_list_usage);
+		usage(cmd);
 	return !!ret;
 }
 static DEFINE_SIMPLE_COMMAND(subvol_list, "list");
@@ -698,12 +698,12 @@ static int cmd_subvol_snapshot(const struct cmd_struct *cmd,
 			}
 			break;
 		default:
-			usage(cmd_subvol_snapshot_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 2))
-		usage(cmd_subvol_snapshot_usage);
+		usage(cmd);
 
 	subvol = argv[optind];
 	dst = argv[optind + 1];
@@ -815,7 +815,7 @@ static int cmd_subvol_get_default(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_subvol_get_default_usage);
+		usage(cmd);
 
 	subvol = argv[1];
 	fd = btrfs_open_dir(subvol, &dirstream, 1);
@@ -884,7 +884,7 @@ static int cmd_subvol_set_default(const struct cmd_struct *cmd,
 
 	if (check_argc_min(argc - optind, 1) ||
 			check_argc_max(argc - optind, 2))
-		usage(cmd_subvol_set_default_usage);
+		usage(cmd);
 
 	if (argc - optind == 1) {
 		/* path to the subvolume is specified */
@@ -948,7 +948,7 @@ static int cmd_subvol_find_new(const struct cmd_struct *cmd,
 	clean_args_no_options(cmd, argc, argv);
 
 	if (check_argc_exact(argc - optind, 2))
-		usage(cmd_subvol_find_new_usage);
+		usage(cmd);
 
 	subvol = argv[optind];
 	last_gen = arg_strtou64(argv[optind + 1]);
@@ -1037,17 +1037,17 @@ static int cmd_subvol_show(const struct cmd_struct *cmd, int argc, char **argv)
 			by_uuid = 1;
 			break;
 		default:
-			usage(cmd_subvol_show_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_exact(argc - optind, 1))
-		usage(cmd_subvol_show_usage);
+		usage(cmd);
 
 	if (by_rootid && by_uuid) {
 		error(
 		"options --rootid and --uuid cannot be used at the same time");
-		usage(cmd_subvol_show_usage);
+		usage(cmd);
 	}
 
 	memset(&get_ri, 0, sizeof(get_ri));
@@ -1369,12 +1369,12 @@ static int cmd_subvol_sync(const struct cmd_struct *cmd, int argc, char **argv)
 			}
 			break;
 		default:
-			usage(cmd_subvol_sync_usage);
+			usage(cmd);
 		}
 	}
 
 	if (check_argc_min(argc - optind, 1))
-		usage(cmd_subvol_sync_usage);
+		usage(cmd);
 
 	fd = btrfs_open_dir(argv[optind], &dirstream, 1);
 	if (fd < 0) {
diff --git a/help.c b/help.c
index 23d6ea8f..6ee93161 100644
--- a/help.c
+++ b/help.c
@@ -101,7 +101,7 @@ void clean_args_no_options(const struct cmd_struct *cmd, int argc, char *argv[])
 		switch (c) {
 		default:
 			if (cmd->usagestr)
-				usage(cmd->usagestr);
+				usage(cmd);
 		}
 	}
 }
@@ -240,9 +240,9 @@ void usage_command(const struct cmd_struct *cmd, bool full, bool err)
 }
 
 __attribute__((noreturn))
-void usage(const char * const *usagestr)
+void usage(const struct cmd_struct *cmd)
 {
-	usage_command_usagestr(usagestr, NULL, true, true);
+	usage_command_usagestr(cmd->usagestr, NULL, true, true);
 	exit(1);
 }
 
diff --git a/help.h b/help.h
index e642f58d..7468cf8b 100644
--- a/help.h
+++ b/help.h
@@ -56,7 +56,7 @@ struct cmd_struct;
 struct cmd_group;
 
 __attribute__((noreturn))
-void usage(const char * const *usagestr);
+void usage(const struct cmd_struct *cmd);
 void usage_command(const struct cmd_struct *cmd, bool full, bool err);
 void usage_command_group(const struct cmd_group *grp, bool all, bool err);
 void usage_command_group_short(const struct cmd_group *grp);
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 17/20] btrfs-progs: add support for output formats
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (15 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 16/20] btrfs-progs: pass cmd_struct to usage() jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 18/20] btrfs-progs: add generic support for json output jeffm
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

This adds a global --format option to request extended output formats
from each command.  Most of it is plumbing a new cmd_context structure
that's established at the beginning of argument parsing into the command
callbacks.  That structure currently only contains the output mode enum.

We currently only support text mode.  Command help reports what
output formats are available for each command.  Global help reports
what valid formats are.

If an invalid format is requested, an error is reported and we global usage
is dumped that lists the valid formats.

Each command sets a bitmask that describes which formats it is capable
of outputting.  If a globally valid format is requested of a command
that doesn't support it, an error is reported and command usage dumped.

Commands don't need to specify that they support text output.  All
commands are required to output text.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs-debug-tree.c        |   3 +-
 btrfs.c                   | 110 ++++++++++++++++++++++++++++++++++++++--------
 check/main.c              |   3 +-
 cmds-balance.c            |  16 +++++--
 cmds-device.c             |  31 +++++++++----
 cmds-fi-du.c              |   1 +
 cmds-fi-usage.c           |   1 +
 cmds-filesystem.c         |  14 ++++--
 cmds-inspect-dump-super.c |   1 +
 cmds-inspect-dump-tree.c  |   1 +
 cmds-inspect-tree-stats.c |   1 +
 cmds-inspect.c            |  10 ++++-
 cmds-property.c           |   6 ++-
 cmds-qgroup.c             |  17 +++++--
 cmds-quota.c              |  14 ++++--
 cmds-receive.c            |   3 +-
 cmds-replace.c            |   8 +++-
 cmds-rescue.c             |   9 +++-
 cmds-restore.c            |   3 +-
 cmds-scrub.c              |  21 ++++++---
 cmds-send.c               |   3 +-
 cmds-subvolume.c          |  24 +++++++---
 commands.h                |  32 +++++++++++---
 help.c                    |  51 ++++++++++++++++-----
 help.h                    |   2 +
 25 files changed, 301 insertions(+), 84 deletions(-)

diff --git a/btrfs-debug-tree.c b/btrfs-debug-tree.c
index 49a2e949..8cd05d53 100644
--- a/btrfs-debug-tree.c
+++ b/btrfs-debug-tree.c
@@ -26,6 +26,7 @@ int main(int argc, char **argv)
 {
 	const struct cmd_struct *cmd = &cmd_struct_inspect_dump_tree;
 	int ret;
+	struct cmd_context cmdcxt = {};
 
 	set_argv0(argv);
 
@@ -34,7 +35,7 @@ int main(int argc, char **argv)
 
 	radix_tree_init();
 
-	ret = cmd_execute(cmd, argc, argv);
+	ret = cmd_execute(cmd, &cmdcxt, argc, argv);
 
 	btrfs_close_all_devices();
 
diff --git a/btrfs.c b/btrfs.c
index 49128182..32b8e090 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -26,7 +26,7 @@
 #include "help.h"
 
 static const char * const btrfs_cmd_group_usage[] = {
-	"btrfs [--help] [--version] <group> [<group>...] <command> [<args>]",
+	"btrfs [--help] [--version] [--format <format>] <group> [<group>...] <command> [<args>]",
 	NULL
 };
 
@@ -98,13 +98,36 @@ parse_command_token(const char *arg, const struct cmd_group *grp)
 	return cmd;
 }
 
+static bool cmd_provides_output_format(const struct cmd_struct *cmd,
+				       const struct cmd_context *cmdcxt)
+{
+	if (!cmdcxt->output_mode)
+		return true;
+
+	return (1 << cmdcxt->output_mode) & cmd->cmd_format_flags;
+}
+
 static void handle_help_options_next_level(const struct cmd_struct *cmd,
-		int argc, char **argv)
+					   const struct cmd_context *cmdcxt,
+					   int argc, char **argv)
 {
+	int err = 0;
+
 	if (argc < 2)
 		return;
 
-	if (!strcmp(argv[1], "--help")) {
+	/* Check if the command can provide the requested output format */
+	if (!cmd->next && !cmd_provides_output_format(cmd, cmdcxt)) {
+		ASSERT(cmdcxt->output_mode >= 0);
+		ASSERT(cmdcxt->output_mode < CMD_OUTPUT_MAX);
+		fprintf(stderr,
+			"error: %s output is unsupported for this command.\n\n",
+			cmd_outputs[cmdcxt->output_mode]);
+
+		err = 1;
+	}
+
+	if (!strcmp(argv[1], "--help") || err) {
 		if (cmd->next) {
 			argc--;
 			argv++;
@@ -113,12 +136,13 @@ static void handle_help_options_next_level(const struct cmd_struct *cmd,
 			usage_command(cmd, true, false);
 		}
 
-		exit(0);
+		exit(err);
 	}
 }
 
-int handle_command_group(const struct cmd_group *grp, int argc,
-			 char **argv)
+int handle_command_group(const struct cmd_group *grp,
+			 const struct cmd_context *cmdcxt,
+			 int argc, char **argv)
 
 {
 	const struct cmd_struct *cmd;
@@ -132,10 +156,10 @@ int handle_command_group(const struct cmd_group *grp, int argc,
 
 	cmd = parse_command_token(argv[0], grp);
 
-	handle_help_options_next_level(cmd, argc, argv);
+	handle_help_options_next_level(cmd, cmdcxt, argc, argv);
 
 	fixup_argv0(argv, cmd->token);
-	return cmd_execute(cmd, argc, argv);
+	return cmd_execute(cmd, cmdcxt, argc, argv);
 }
 
 static const struct cmd_group btrfs_cmd_group;
@@ -148,7 +172,8 @@ static const char * const cmd_help_usage[] = {
 	NULL
 };
 
-static int cmd_help(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_help(const struct cmd_struct *unused,
+		    const struct cmd_context *cmdcxt, int argc, char **argv)
 {
 	help_command_group(&btrfs_cmd_group, argc, argv);
 	return 0;
@@ -162,25 +187,66 @@ static const char * const cmd_version_usage[] = {
 	NULL
 };
 
-static int cmd_version(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_version(const struct cmd_struct *unused,
+		       const struct cmd_context *cmdcxt, int argc, char **argv)
 {
 	printf("%s\n", PACKAGE_STRING);
 	return 0;
 }
 static DEFINE_SIMPLE_COMMAND(version, "version");
 
+static void print_output_formats(FILE *outf)
+{
+	int i;
+
+	fputs("Options for --format are:", outf);
+	for (i = 0; i < CMD_OUTPUT_MAX; i++)
+		fprintf(outf, "%s\"%s\"", i ? ", " : " ", cmd_outputs[i]);
+	fputs("\n", outf);
+
+	/* No extended formats anywhere */
+	if (CMD_OUTPUT_TEXT + 1 != CMD_OUTPUT_MAX)
+		fputs("Extended output formats may not be available for all commands.\n",
+		      outf);
+}
+
+static void handle_output_format(struct cmd_context *cmdcxt,
+				 const char *format)
+{
+	int i;
+
+	for (i = 0; i < CMD_OUTPUT_MAX; i++) {
+		if (!strcasecmp(format, cmd_outputs[i])) {
+			cmdcxt->output_mode = i;
+			break;
+		}
+	}
+
+	/* Print error and usage for invalid format */
+	if (i == CMD_OUTPUT_MAX) {
+		cmdcxt->output_mode = CMD_OUTPUT_TEXT;
+		fprintf(stderr, "error: invalid output format \"%s\"\n\n",
+			format);
+		usage_command_group(&btrfs_cmd_group, false, true);
+		print_output_formats(stderr);
+		exit(1);
+	}
+}
+
 /*
  * Parse global options, between binary name and first non-option argument
  * after processing all valid options (including those with arguments).
  *
  * Returns index to argv where parsting stopped, optind is reset to 1
  */
-static int handle_global_options(int argc, char **argv)
+static int handle_global_options(struct cmd_context *cmdcxt,
+				 int argc, char **argv)
 {
-	enum { OPT_HELP = 256, OPT_VERSION, OPT_FULL };
+	enum { OPT_HELP = 256, OPT_VERSION, OPT_FULL, OPT_FORMAT };
 	static const struct option long_options[] = {
 		{ "help", no_argument, NULL, OPT_HELP },
 		{ "version", no_argument, NULL, OPT_VERSION },
+		{ "format", required_argument, NULL, OPT_FORMAT },
 		{ "full", no_argument, NULL, OPT_FULL },
 		{ NULL, 0, NULL, 0}
 	};
@@ -201,6 +267,9 @@ static int handle_global_options(int argc, char **argv)
 		case OPT_HELP: break;
 		case OPT_VERSION: break;
 		case OPT_FULL: break;
+		case OPT_FORMAT:
+			handle_output_format(cmdcxt, optarg);
+			break;
 		default:
 			fprintf(stderr, "Unknown global option: %s\n",
 					argv[optind - 1]);
@@ -214,7 +283,8 @@ static int handle_global_options(int argc, char **argv)
 	return shift;
 }
 
-void handle_special_globals(int shift, int argc, char **argv)
+void handle_special_globals(const struct cmd_context *cmdcxt, int shift,
+			    int argc, char **argv)
 {
 	bool has_help = false;
 	bool has_full = false;
@@ -231,13 +301,14 @@ void handle_special_globals(int shift, int argc, char **argv)
 		if (has_full)
 			usage_command_group(&btrfs_cmd_group, true, false);
 		else
-			cmd_execute(&cmd_struct_help, argc, argv);
+			cmd_execute(&cmd_struct_help, cmdcxt, argc, argv);
+		print_output_formats(stdout);
 		exit(0);
 	}
 
 	for (i = 0; i < shift; i++)
 		if (strcmp(argv[i], "--version") == 0) {
-			cmd_execute(&cmd_struct_version, argc, argv);
+			cmd_execute(&cmd_struct_version, cmdcxt, argc, argv);
 			exit(0);
 		}
 }
@@ -269,6 +340,7 @@ int main(int argc, char **argv)
 {
 	const struct cmd_struct *cmd;
 	const char *bname;
+	struct cmd_context cmdcxt = { .output_mode = CMD_OUTPUT_TEXT, };
 	int ret;
 
 	btrfs_config_init();
@@ -283,8 +355,8 @@ int main(int argc, char **argv)
 	} else {
 		int shift;
 
-		shift = handle_global_options(argc, argv);
-		handle_special_globals(shift, argc, argv);
+		shift = handle_global_options(&cmdcxt, argc, argv);
+		handle_special_globals(&cmdcxt, shift, argc, argv);
 		while (shift-- > 0) {
 			argc--;
 			argv++;
@@ -297,13 +369,13 @@ int main(int argc, char **argv)
 
 	cmd = parse_command_token(argv[0], &btrfs_cmd_group);
 
-	handle_help_options_next_level(cmd, argc, argv);
+	handle_help_options_next_level(cmd, &cmdcxt, argc, argv);
 
 	crc32c_optimization_init();
 
 	fixup_argv0(argv, cmd->token);
 
-	ret = cmd_execute(cmd, argc, argv);
+	ret = cmd_execute(cmd, &cmdcxt, argc, argv);
 
 	btrfs_close_all_devices();
 
diff --git a/check/main.c b/check/main.c
index 0bb8633a..6c74ca8f 100644
--- a/check/main.c
+++ b/check/main.c
@@ -9440,7 +9440,8 @@ static const char * const cmd_check_usage[] = {
 	NULL
 };
 
-static int cmd_check(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_check(const struct cmd_struct *cmd,
+		     const struct cmd_context *cmdcxt, int argc, char **argv)
 {
 	struct cache_tree root_cache;
 	struct btrfs_root *root;
diff --git a/cmds-balance.c b/cmds-balance.c
index c639459f..c17b9ee3 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -516,6 +516,7 @@ static const char * const cmd_balance_start_usage[] = {
 };
 
 static int cmd_balance_start(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	struct btrfs_ioctl_balance_args args;
@@ -682,6 +683,7 @@ static const char * const cmd_balance_pause_usage[] = {
 };
 
 static int cmd_balance_pause(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	const char *path;
@@ -722,6 +724,7 @@ static const char * const cmd_balance_cancel_usage[] = {
 };
 
 static int cmd_balance_cancel(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
 	const char *path;
@@ -762,6 +765,7 @@ static const char * const cmd_balance_resume_usage[] = {
 };
 
 static int cmd_balance_resume(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
 	struct btrfs_ioctl_balance_args args;
@@ -831,6 +835,7 @@ static const char * const cmd_balance_status_usage[] = {
  *   0 : When there is no pending balance or completed
  */
 static int cmd_balance_status(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
 	struct btrfs_ioctl_balance_args args;
@@ -909,7 +914,9 @@ out:
 }
 static DEFINE_SIMPLE_COMMAND(balance_status, "status");
 
-static int cmd_balance_full(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_balance_full(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	struct btrfs_ioctl_balance_args args;
 
@@ -919,7 +926,7 @@ static int cmd_balance_full(const struct cmd_struct *cmd, int argc, char **argv)
 	return do_balance(argv[1], &args, BALANCE_START_NOWARN);
 }
 static DEFINE_COMMAND(balance_full, "--full-balance", cmd_balance_full,
-		      NULL, NULL, CMD_HIDDEN);
+		      NULL, NULL, CMD_HIDDEN, 0);
 
 static const char balance_cmd_group_info[] =
 "balance data across devices, or change block groups using filters";
@@ -936,7 +943,8 @@ static const struct cmd_group balance_cmd_group = {
 	}
 };
 
-static int cmd_balance(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_balance(const struct cmd_struct *unused,
+		       const struct cmd_context *cmdcxt, int argc, char **argv)
 {
 	if (argc == 2 && strcmp("start", argv[1]) != 0) {
 		/* old 'btrfs filesystem balance <path>' syntax */
@@ -948,7 +956,7 @@ static int cmd_balance(const struct cmd_struct *unused, int argc, char **argv)
 		return do_balance(argv[1], &args, 0);
 	}
 
-	return handle_command_group(&balance_cmd_group, argc, argv);
+	return handle_command_group(&balance_cmd_group, cmdcxt, argc, argv);
 }
 
 DEFINE_GROUP_COMMAND(balance, "balance");
diff --git a/cmds-device.c b/cmds-device.c
index 6c74ca8e..ac9e82b1 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -49,6 +49,7 @@ static const char * const cmd_device_add_usage[] = {
 };
 
 static int cmd_device_add(const struct cmd_struct *cmd,
+			  const struct cmd_context *cmdcxt,
 			  int argc, char **argv)
 {
 	char	*mntpnt;
@@ -144,6 +145,7 @@ error_out:
 static DEFINE_SIMPLE_COMMAND(device_add, "add");
 
 static int _cmd_device_remove(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
 	char	*mntpnt;
@@ -238,9 +240,10 @@ static const char * const cmd_device_remove_usage[] = {
 };
 
 static int cmd_device_remove(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
-	return _cmd_device_remove(cmd, argc, argv);
+	return _cmd_device_remove(cmd, cmdcxt, argc, argv);
 }
 static DEFINE_SIMPLE_COMMAND(device_remove, "remove");
 
@@ -253,12 +256,13 @@ static const char * const cmd_device_delete_usage[] = {
 };
 
 static int cmd_device_delete(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
-	return _cmd_device_remove(cmd, argc, argv);
+	return _cmd_device_remove(cmd, cmdcxt, argc, argv);
 }
 static DEFINE_COMMAND(device_delete, "delete", cmd_device_delete,
-		      cmd_device_delete_usage, NULL, CMD_ALIAS);
+		      cmd_device_delete_usage, NULL, CMD_ALIAS, 0);
 
 static const char * const cmd_device_scan_usage[] = {
 	"btrfs device scan [(-d|--all-devices)|<device> [<device>...]]",
@@ -267,7 +271,9 @@ static const char * const cmd_device_scan_usage[] = {
 	NULL
 };
 
-static int cmd_device_scan(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_device_scan(const struct cmd_struct *cmd,
+			   const struct cmd_context *cmdcxt,
+			   int argc, char **argv)
 {
 	int i;
 	int devstart;
@@ -340,7 +346,9 @@ static const char * const cmd_device_ready_usage[] = {
 	NULL
 };
 
-static int cmd_device_ready(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_device_ready(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	struct	btrfs_ioctl_vol_args args;
 	int	fd;
@@ -399,7 +407,9 @@ static const char * const cmd_device_stats_usage[] = {
 	NULL
 };
 
-static int cmd_device_stats(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_device_stats(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	char *dev_path;
 	struct btrfs_ioctl_fs_info_args fi_args;
@@ -565,7 +575,9 @@ out:
 	return ret;
 }
 
-static int cmd_device_usage(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_device_usage(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	unsigned unit_mode;
 	int ret = 0;
@@ -618,8 +630,9 @@ static const struct cmd_group device_cmd_group = {
 	}
 };
 
-static int cmd_device(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_device(const struct cmd_struct *unused,
+		      const struct cmd_context *cmdcxt, int argc, char **argv)
 {
-	return handle_command_group(&device_cmd_group, argc, argv);
+	return handle_command_group(&device_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND_TOKEN(device);
diff --git a/cmds-fi-du.c b/cmds-fi-du.c
index ebcb0c15..c85c0063 100644
--- a/cmds-fi-du.c
+++ b/cmds-fi-du.c
@@ -558,6 +558,7 @@ static const char * const cmd_filesystem_du_usage[] = {
 };
 
 static int cmd_filesystem_du(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	int ret = 0, err = 0;
diff --git a/cmds-fi-usage.c b/cmds-fi-usage.c
index 6da96411..5d220cc2 100644
--- a/cmds-fi-usage.c
+++ b/cmds-fi-usage.c
@@ -966,6 +966,7 @@ static const char * const cmd_filesystem_usage_usage[] = {
 };
 
 static int cmd_filesystem_usage(const struct cmd_struct *cmd,
+				const struct cmd_context *cmdcxt,
 				int argc, char **argv)
 {
 	int ret = 0;
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 8ddb7ea5..27e0865c 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -116,6 +116,7 @@ static void print_df(struct btrfs_ioctl_space_args *sargs, unsigned unit_mode)
 }
 
 static int cmd_filesystem_df(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	struct btrfs_ioctl_space_args *sargs = NULL;
@@ -668,6 +669,7 @@ static const char * const cmd_filesystem_show_usage[] = {
 };
 
 static int cmd_filesystem_show(const struct cmd_struct *cmd,
+			       const struct cmd_context *cmdcxt,
 			       int argc, char **argv)
 {
 	LIST_HEAD(all_uuids);
@@ -816,6 +818,7 @@ static const char * const cmd_filesystem_sync_usage[] = {
 };
 
 static int cmd_filesystem_sync(const struct cmd_struct *cmd,
+			       const struct cmd_context *cmdcxt,
 			       int argc, char **argv)
 {
 	int 	fd, res;
@@ -914,6 +917,7 @@ error:
 }
 
 static int cmd_filesystem_defrag(const struct cmd_struct *cmd,
+				 const struct cmd_context *cmdcxt,
 				 int argc, char **argv)
 {
 	int fd;
@@ -1094,6 +1098,7 @@ static const char * const cmd_filesystem_resize_usage[] = {
 };
 
 static int cmd_filesystem_resize(const struct cmd_struct *cmd,
+				 const struct cmd_context *cmdcxt,
 				 int argc, char **argv)
 {
 	struct btrfs_ioctl_vol_args	args;
@@ -1173,6 +1178,7 @@ static const char * const cmd_filesystem_label_usage[] = {
 };
 
 static int cmd_filesystem_label(const struct cmd_struct *cmd,
+				const struct cmd_context *cmdcxt,
 				int argc, char **argv)
 {
 	clean_args_no_options(cmd, argc, argv);
@@ -1203,9 +1209,10 @@ static const char * const cmd_filesystem_balance_usage[] = {
 };
 
 static int cmd_filesystem_balance(const struct cmd_struct *unused,
+				  const struct cmd_context *cmdcxt,
 				  int argc, char **argv)
 {
-	return cmd_execute(&cmd_struct_balance, argc, argv);
+	return cmd_execute(&cmd_struct_balance, cmdcxt, argc, argv);
 }
 
 /*
@@ -1215,7 +1222,7 @@ static int cmd_filesystem_balance(const struct cmd_struct *unused,
  * for historical compatibility and is hidden.
  */
 static DEFINE_COMMAND(filesystem_balance, "balance", cmd_filesystem_balance,
-		      cmd_filesystem_balance_usage, NULL, CMD_HIDDEN);
+		      cmd_filesystem_balance_usage, NULL, CMD_HIDDEN, 0);
 
 static const char filesystem_cmd_group_info[] =
 "overall filesystem tasks and information";
@@ -1236,8 +1243,9 @@ static const struct cmd_group filesystem_cmd_group = {
 };
 
 static int cmd_filesystem(const struct cmd_struct *unused,
+			  const struct cmd_context *cmdcxt,
 			  int argc, char **argv)
 {
-	return handle_command_group(&filesystem_cmd_group, argc, argv);
+	return handle_command_group(&filesystem_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND_TOKEN(filesystem);
diff --git a/cmds-inspect-dump-super.c b/cmds-inspect-dump-super.c
index 6341d2ac..da171779 100644
--- a/cmds-inspect-dump-super.c
+++ b/cmds-inspect-dump-super.c
@@ -505,6 +505,7 @@ static const char * const cmd_inspect_dump_super_usage[] = {
 };
 
 static int cmd_inspect_dump_super(const struct cmd_struct *cmd,
+				  const struct cmd_context *cmdcxt,
 				  int argc, char **argv)
 {
 	int all = 0;
diff --git a/cmds-inspect-dump-tree.c b/cmds-inspect-dump-tree.c
index 22bc9cb9..9e0b950b 100644
--- a/cmds-inspect-dump-tree.c
+++ b/cmds-inspect-dump-tree.c
@@ -202,6 +202,7 @@ static const char * const cmd_inspect_dump_tree_usage[] = {
 };
 
 static int cmd_inspect_dump_tree(const struct cmd_struct *cmd,
+				 const struct cmd_context *cmdcxt,
 				 int argc, char **argv)
 {
 	struct btrfs_root *root;
diff --git a/cmds-inspect-tree-stats.c b/cmds-inspect-tree-stats.c
index e5e68562..768af025 100644
--- a/cmds-inspect-tree-stats.c
+++ b/cmds-inspect-tree-stats.c
@@ -428,6 +428,7 @@ static const char * const cmd_inspect_tree_stats_usage[] = {
 };
 
 static int cmd_inspect_tree_stats(const struct cmd_struct *cmd,
+				  const struct cmd_context *cmdcxt,
 				  int argc, char **argv)
 {
 	struct btrfs_key key;
diff --git a/cmds-inspect.c b/cmds-inspect.c
index bc86e999..ade9db7e 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -88,6 +88,7 @@ static const char * const cmd_inspect_inode_resolve_usage[] = {
 };
 
 static int cmd_inspect_inode_resolve(const struct cmd_struct *cmd,
+				     const struct cmd_context *cmdcxt,
 				     int argc, char **argv)
 {
 	int fd;
@@ -136,6 +137,7 @@ static const char * const cmd_inspect_logical_resolve_usage[] = {
 };
 
 static int cmd_inspect_logical_resolve(const struct cmd_struct *cmd,
+				       const struct cmd_context *cmdcxt,
 				       int argc, char **argv)
 {
 	int ret;
@@ -269,6 +271,7 @@ static const char * const cmd_inspect_subvolid_resolve_usage[] = {
 };
 
 static int cmd_inspect_subvolid_resolve(const struct cmd_struct *cmd,
+					const struct cmd_context *cmdcxt,
 					int argc, char **argv)
 {
 	int ret;
@@ -313,6 +316,7 @@ static const char* const cmd_inspect_rootid_usage[] = {
 };
 
 static int cmd_inspect_rootid(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
 	int ret;
@@ -593,6 +597,7 @@ out:
 }
 
 static int cmd_inspect_min_dev_size(const struct cmd_struct *cmd,
+				    const struct cmd_context *cmdcxt,
 				    int argc, char **argv)
 {
 	int ret;
@@ -653,8 +658,9 @@ static const struct cmd_group inspect_cmd_group = {
 	}
 };
 
-static int cmd_inspect(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_inspect(const struct cmd_struct *unused,
+		       const struct cmd_context *cmdcxt, int argc, char **argv)
 {
-	return handle_command_group(&inspect_cmd_group, argc, argv);
+	return handle_command_group(&inspect_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND(inspect, "inspect");
diff --git a/cmds-property.c b/cmds-property.c
index 4aa2f18f..498fa456 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -341,6 +341,7 @@ static const char * const cmd_property_get_usage[] = {
 };
 
 static int cmd_property_get(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
 			    int argc, char **argv)
 {
 	int ret;
@@ -368,6 +369,7 @@ static const char * const cmd_property_set_usage[] = {
 };
 
 static int cmd_property_set(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
 			    int argc, char **argv)
 {
 	int ret;
@@ -393,6 +395,7 @@ static const char * const cmd_property_list_usage[] = {
 };
 
 static int cmd_property_list(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	int ret;
@@ -420,8 +423,9 @@ static const struct cmd_group property_cmd_group = {
 };
 
 static int cmd_property(const struct cmd_struct *unused,
+			const struct cmd_context *cmdcxt,
 			int argc, char **argv)
 {
-	return handle_command_group(&property_cmd_group, argc, argv);
+	return handle_command_group(&property_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND_TOKEN(property);
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index 986f272c..f9e81e30 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -216,6 +216,7 @@ static const char * const cmd_qgroup_assign_usage[] = {
 };
 
 static int cmd_qgroup_assign(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	return _cmd_qgroup_assign(cmd, 1, argc, argv);
@@ -229,6 +230,7 @@ static const char * const cmd_qgroup_remove_usage[] = {
 };
 
 static int cmd_qgroup_remove(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	return _cmd_qgroup_assign(cmd, 0, argc, argv);
@@ -242,6 +244,7 @@ static const char * const cmd_qgroup_create_usage[] = {
 };
 
 static int cmd_qgroup_create(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	int ret;
@@ -263,6 +266,7 @@ static const char * const cmd_qgroup_destroy_usage[] = {
 };
 
 static int cmd_qgroup_destroy(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
 	int ret;
@@ -300,7 +304,9 @@ static const char * const cmd_qgroup_show_usage[] = {
 	NULL
 };
 
-static int cmd_qgroup_show(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_qgroup_show(const struct cmd_struct *cmd,
+			   const struct cmd_context *cmdcxt,
+			   int argc, char **argv)
 {
 	char *path;
 	int ret = 0;
@@ -434,7 +440,9 @@ static const char * const cmd_qgroup_limit_usage[] = {
 	NULL
 };
 
-static int cmd_qgroup_limit(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_qgroup_limit(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	int ret = 0;
 	int fd;
@@ -532,8 +540,9 @@ static const struct cmd_group qgroup_cmd_group = {
 	}
 };
 
-static int cmd_qgroup(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_qgroup(const struct cmd_struct *unused,
+		      const struct cmd_context *cmdcxt, int argc, char **argv)
 {
-	return handle_command_group(&qgroup_cmd_group, argc, argv);
+	return handle_command_group(&qgroup_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND_TOKEN(qgroup);
diff --git a/cmds-quota.c b/cmds-quota.c
index c634c4e9..2a88d4c4 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -67,7 +67,9 @@ static const char * const cmd_quota_enable_usage[] = {
 	NULL
 };
 
-static int cmd_quota_enable(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_quota_enable(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	int ret;
 
@@ -88,6 +90,7 @@ static const char * const cmd_quota_disable_usage[] = {
 };
 
 static int cmd_quota_disable(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	int ret;
@@ -111,7 +114,9 @@ static const char * const cmd_quota_rescan_usage[] = {
 	NULL
 };
 
-static int cmd_quota_rescan(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_quota_rescan(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	int ret = 0;
 	int fd;
@@ -214,8 +219,9 @@ static const struct cmd_group quota_cmd_group = {
 	}
 };
 
-static int cmd_quota(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_quota(const struct cmd_struct *unused,
+		     const struct cmd_context *cmdcxt, int argc, char **argv)
 {
-	return handle_command_group(&quota_cmd_group, argc, argv);
+	return handle_command_group(&quota_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND_TOKEN(quota);
diff --git a/cmds-receive.c b/cmds-receive.c
index 5429c607..57ce2272 100644
--- a/cmds-receive.c
+++ b/cmds-receive.c
@@ -1279,7 +1279,8 @@ static const char * const cmd_receive_usage[] = {
 	NULL
 };
 
-static int cmd_receive(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_receive(const struct cmd_struct *cmd,
+		       const struct cmd_context *cmdcxt, int argc, char **argv)
 {
 	char *tomnt = NULL;
 	char fromfile[PATH_MAX];
diff --git a/cmds-replace.c b/cmds-replace.c
index 2057d69e..64c93537 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -115,6 +115,7 @@ static const char *const cmd_replace_start_usage[] = {
 };
 
 static int cmd_replace_start(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	struct btrfs_ioctl_dev_replace_args start_args = {0};
@@ -327,6 +328,7 @@ static const char *const cmd_replace_status_usage[] = {
 };
 
 static int cmd_replace_status(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
 	int fd;
@@ -497,6 +499,7 @@ static const char *const cmd_replace_cancel_usage[] = {
 };
 
 static int cmd_replace_cancel(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
 	struct btrfs_ioctl_dev_replace_args args = {0};
@@ -557,8 +560,9 @@ static const struct cmd_group replace_cmd_group = {
 	}
 };
 
-static int cmd_replace(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_replace(const struct cmd_struct *unused,
+		       const struct cmd_context *cmdcxt, int argc, char **argv)
 {
-	return handle_command_group(&replace_cmd_group, argc, argv);
+	return handle_command_group(&replace_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND_TOKEN(replace);
diff --git a/cmds-rescue.c b/cmds-rescue.c
index f96d8010..ec1ea222 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -46,6 +46,7 @@ static const char * const cmd_rescue_chunk_recover_usage[] = {
 };
 
 static int cmd_rescue_chunk_recover(const struct cmd_struct *cmd,
+				    const struct cmd_context *cmdcxt,
 				    int argc, char *argv[])
 {
 	int ret = 0;
@@ -115,6 +116,7 @@ static const char * const cmd_rescue_super_recover_usage[] = {
  *   4 : Abort to recover bad superblocks
  */
 static int cmd_rescue_super_recover(const struct cmd_struct *cmd,
+				    const struct cmd_context *cmdcxt,
 				    int argc, char **argv)
 {
 	int ret;
@@ -162,6 +164,7 @@ static const char * const cmd_rescue_zero_log_usage[] = {
 };
 
 static int cmd_rescue_zero_log(const struct cmd_struct *cmd,
+			       const struct cmd_context *cmdcxt,
 			       int argc, char **argv)
 {
 	struct btrfs_root *root;
@@ -217,6 +220,7 @@ static const char * const cmd_rescue_fix_device_size_usage[] = {
 };
 
 static int cmd_rescue_fix_device_size(const struct cmd_struct *cmd,
+				      const struct cmd_context *cmdcxt,
 				      int argc, char **argv)
 {
 	struct btrfs_fs_info *fs_info;
@@ -269,8 +273,9 @@ static const struct cmd_group rescue_cmd_group = {
 	}
 };
 
-static int cmd_rescue(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_rescue(const struct cmd_struct *unused,
+		      const struct cmd_context *cmdcxt, int argc, char **argv)
 {
-	return handle_command_group(&rescue_cmd_group, argc, argv);
+	return handle_command_group(&rescue_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND_TOKEN(rescue);
diff --git a/cmds-restore.c b/cmds-restore.c
index 87cfb582..03c90cb6 100644
--- a/cmds-restore.c
+++ b/cmds-restore.c
@@ -1415,7 +1415,8 @@ static const char * const cmd_restore_usage[] = {
 	NULL
 };
 
-static int cmd_restore(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_restore(const struct cmd_struct *cmd,
+		       const struct cmd_context *cmdcxt, int argc, char **argv)
 {
 	struct btrfs_root *root;
 	struct btrfs_key key;
diff --git a/cmds-scrub.c b/cmds-scrub.c
index 872c996b..d4d5a10c 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -1571,7 +1571,9 @@ static const char * const cmd_scrub_start_usage[] = {
 	NULL
 };
 
-static int cmd_scrub_start(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_scrub_start(const struct cmd_struct *cmd,
+			   const struct cmd_context *cmdcxt,
+			   int argc, char **argv)
 {
 	return scrub_start(cmd, argc, argv, false);
 }
@@ -1583,7 +1585,9 @@ static const char * const cmd_scrub_cancel_usage[] = {
 	NULL
 };
 
-static int cmd_scrub_cancel(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_scrub_cancel(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	char *path;
 	int ret;
@@ -1638,7 +1642,9 @@ static const char * const cmd_scrub_resume_usage[] = {
 	NULL
 };
 
-static int cmd_scrub_resume(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_scrub_resume(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	return scrub_start(cmd, argc, argv, true);
 }
@@ -1653,7 +1659,9 @@ static const char * const cmd_scrub_status_usage[] = {
 	NULL
 };
 
-static int cmd_scrub_status(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_scrub_status(const struct cmd_struct *cmd,
+			    const struct cmd_context *cmdcxt,
+			    int argc, char **argv)
 {
 	char *path;
 	struct btrfs_ioctl_fs_info_args fi_args;
@@ -1799,9 +1807,10 @@ static const struct cmd_group scrub_cmd_group = {
 	}
 };
 
-static int cmd_scrub(const struct cmd_struct *unused, int argc, char **argv)
+static int cmd_scrub(const struct cmd_struct *unused,
+		     const struct cmd_context *cmdcxt, int argc, char **argv)
 {
-	return handle_command_group(&scrub_cmd_group, argc, argv);
+	return handle_command_group(&scrub_cmd_group, cmdcxt, argc, argv);
 }
 
 DEFINE_GROUP_COMMAND_TOKEN(scrub);
diff --git a/cmds-send.c b/cmds-send.c
index e2d87785..c810f842 100644
--- a/cmds-send.c
+++ b/cmds-send.c
@@ -523,7 +523,8 @@ static const char * const cmd_send_usage[] = {
 	NULL
 };
 
-static int cmd_send(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_send(const struct cmd_struct *cmd,
+		    const struct cmd_context *cmdcxt, int argc, char **argv)
 {
 	char *subvol = NULL;
 	int ret;
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index dc529315..3498ff21 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -117,6 +117,7 @@ static const char * const cmd_subvol_create_usage[] = {
 };
 
 static int cmd_subvol_create(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	int	retval, res, len;
@@ -253,6 +254,7 @@ static const char * const cmd_subvol_delete_usage[] = {
 };
 
 static int cmd_subvol_delete(const struct cmd_struct *cmd,
+			     const struct cmd_context *cmdcxt,
 			     int argc, char **argv)
 {
 	int res, ret = 0;
@@ -479,7 +481,9 @@ static const char * const cmd_subvol_list_usage[] = {
 	NULL,
 };
 
-static int cmd_subvol_list(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_subvol_list(const struct cmd_struct *cmd,
+			   const struct cmd_context *cmdcxt,
+			   int argc, char **argv)
 {
 	struct btrfs_list_filter_set *filter_set;
 	struct btrfs_list_comparer_set *comparer_set;
@@ -652,6 +656,7 @@ static const char * const cmd_subvol_snapshot_usage[] = {
 };
 
 static int cmd_subvol_snapshot(const struct cmd_struct *cmd,
+			       const struct cmd_context *cmdcxt,
 			       int argc, char **argv)
 {
 	char	*subvol, *dst;
@@ -803,6 +808,7 @@ static const char * const cmd_subvol_get_default_usage[] = {
 };
 
 static int cmd_subvol_get_default(const struct cmd_struct *cmd,
+				  const struct cmd_context *cmdcxt,
 				  int argc, char **argv)
 {
 	int fd = -1;
@@ -872,6 +878,7 @@ static const char * const cmd_subvol_set_default_usage[] = {
 };
 
 static int cmd_subvol_set_default(const struct cmd_struct *cmd,
+				  const struct cmd_context *cmdcxt,
 				  int argc, char **argv)
 {
 	int	ret=0, fd;
@@ -937,6 +944,7 @@ static const char * const cmd_subvol_find_new_usage[] = {
 };
 
 static int cmd_subvol_find_new(const struct cmd_struct *cmd,
+			       const struct cmd_context *cmdcxt,
 			       int argc, char **argv)
 {
 	int fd;
@@ -992,7 +1000,9 @@ static const char * const cmd_subvol_show_usage[] = {
 	NULL
 };
 
-static int cmd_subvol_show(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_subvol_show(const struct cmd_struct *cmd,
+			   const struct cmd_context *cmdcxt,
+			   int argc, char **argv)
 {
 	struct root_info get_ri;
 	struct btrfs_list_filter_set *filter_set = NULL;
@@ -1343,7 +1353,9 @@ static int enumerate_dead_subvols(int fd, u64 **ids)
 	return idx;
 }
 
-static int cmd_subvol_sync(const struct cmd_struct *cmd, int argc, char **argv)
+static int cmd_subvol_sync(const struct cmd_struct *cmd,
+			   const struct cmd_context *cmdcxt,
+			   int argc, char **argv)
 {
 	int fd = -1;
 	int i;
@@ -1454,9 +1466,9 @@ static const struct cmd_group subvolume_cmd_group = {
 	}
 };
 
-static int cmd_subvolume(const struct cmd_struct *unused,
-			 int argc, char **argv)
+int cmd_subvolume(const struct cmd_struct *unused,
+		  const struct cmd_context *cmdcxt, int argc, char **argv)
 {
-	return handle_command_group(&subvolume_cmd_group, argc, argv);
+	return handle_command_group(&subvolume_cmd_group, cmdcxt, argc, argv);
 }
 DEFINE_GROUP_COMMAND_TOKEN(subvolume);
diff --git a/commands.h b/commands.h
index 4c5469ac..83316c6d 100644
--- a/commands.h
+++ b/commands.h
@@ -17,6 +17,17 @@
 #ifndef __BTRFS_COMMANDS_H__
 #define __BTRFS_COMMANDS_H__
 
+enum cmd_output {
+	CMD_OUTPUT_TEXT = 0,
+	CMD_OUTPUT_MAX,
+};
+
+#define CMD_OUTPUT_FLAG(x)	(1 << (CMD_OUTPUT_##x))
+
+struct cmd_context {
+	enum cmd_output output_mode;
+};
+
 enum {
 	CMD_HIDDEN = (1 << 0),	/* should not be in help listings */
 	CMD_ALIAS = (1 << 1),	/* alias of next command in cmd_group */
@@ -24,7 +35,8 @@ enum {
 
 struct cmd_struct {
 	const char *token;
-	int (*fn)(const struct cmd_struct *cmd, int argc, char **argv);
+	int (*fn)(const struct cmd_struct *cmd,
+		  const struct cmd_context *cmdcxt, int argc, char **argv);
 
 	/*
 	 * Usage strings
@@ -54,6 +66,8 @@ struct cmd_struct {
 
 	/* CMD_* flags above */
 	int flags;
+
+	unsigned int cmd_format_flags;
 };
 
 /*
@@ -65,7 +79,8 @@ struct cmd_struct {
 	extern const struct cmd_struct __CMD_NAME(name)
 
 /* Define a command with all members specified */
-#define DEFINE_COMMAND(name, _token, _fn, _usagestr, _group, _flags)	\
+#define DEFINE_COMMAND(name, _token, _fn, _usagestr, _group, _flags,	\
+		       _cmd_format_flags)				\
 	const struct cmd_struct __CMD_NAME(name) =			\
 		{							\
 			.token = (_token),				\
@@ -73,6 +88,7 @@ struct cmd_struct {
 			.usagestr = (_usagestr),			\
 			.next = (_group),				\
 			.flags = (_flags),				\
+			.cmd_format_flags = (_cmd_format_flags),	\
 		}
 
 /*
@@ -82,7 +98,7 @@ struct cmd_struct {
  */
 #define DEFINE_SIMPLE_COMMAND(name, token)				\
 	DEFINE_COMMAND(name, token, cmd_ ##name,			\
-		       cmd_ ##name ##_usage, NULL, 0)
+		       cmd_ ##name ##_usage, NULL, 0, 0)
 
 /*
  * Define a command group callback.
@@ -91,7 +107,7 @@ struct cmd_struct {
  */
 #define DEFINE_GROUP_COMMAND(name, token)				\
 	DEFINE_COMMAND(name, token, cmd_ ##name,			\
-		       NULL, &(name ## _cmd_group), 0)
+		       NULL, &(name ## _cmd_group), 0, 0)
 
 /*
  * Define a command group callback when the name and the string are
@@ -108,13 +124,15 @@ struct cmd_group {
 };
 
 static inline int cmd_execute(const struct cmd_struct *cmd,
+			      const struct cmd_context *cmdcxt,
 			      int argc, char **argv)
 {
-	return cmd->fn(cmd, argc, argv);
+	return cmd->fn(cmd, cmdcxt, argc, argv);
 }
 
-int handle_command_group(const struct cmd_group *grp, int argc,
-			 char **argv);
+int handle_command_group(const struct cmd_group *grp,
+			 const struct cmd_context *cmdcxt,
+			 int argc, char **argv);
 
 extern const char * const generic_cmd_help_usage[];
 
diff --git a/help.c b/help.c
index 6ee93161..063e9740 100644
--- a/help.c
+++ b/help.c
@@ -28,6 +28,11 @@
 #define USAGE_LONG		2U
 #define USAGE_OPTIONS		4U
 #define USAGE_LISTING		8U
+#define USAGE_FORMAT		16U
+
+const char *cmd_outputs[CMD_OUTPUT_MAX] = {
+	"text",
+};
 
 static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
 
@@ -126,6 +131,7 @@ void clean_args_no_options_relaxed(const struct cmd_struct *cmd,
 }
 
 static int do_usage_one_command(const char * const *usagestr,
+				unsigned int format_flags,
 				unsigned int flags, FILE *outf)
 {
 	int pad = 4;
@@ -193,12 +199,32 @@ static int do_usage_one_command(const char * const *usagestr,
 	while (*usagestr)
 		fprintf(outf, "%*s%s\n", pad, "", *usagestr++);
 
+	if (flags & USAGE_FORMAT) {
+		/* We always support text */
+		format_flags |= (1 << CMD_OUTPUT_TEXT);
+
+		fputs("\n\tThis command supports output in ", outf);
+		if (format_flags == (1 << CMD_OUTPUT_TEXT))
+			fputs("text mode only.\n", outf);
+		else {
+			int i;
+
+			fputs("the following formats:", outf);
+			for (i = 0; i < CMD_OUTPUT_MAX; i++) {
+				if (format_flags & (1 << i))
+					fprintf(outf, " %s", cmd_outputs[i]);
+			}
+			fputs("\n", outf);
+		}
+	}
+
 	return 0;
 }
 
 static int usage_command_internal(const char * const *usagestr,
-				  const char *token, bool full, bool lst,
-				  bool alias, FILE *outf)
+				  const char *token,
+				  unsigned int format_flags, bool full,
+				  bool lst, bool alias, FILE *outf)
 {
 	unsigned int flags = 0;
 	int ret;
@@ -206,11 +232,11 @@ static int usage_command_internal(const char * const *usagestr,
 	if (!alias)
 		flags |= USAGE_SHORT;
 	if (full)
-		flags |= USAGE_LONG | USAGE_OPTIONS;
+		flags |= USAGE_LONG | USAGE_OPTIONS | USAGE_FORMAT;
 	if (lst)
 		flags |= USAGE_LISTING;
 
-	ret = do_usage_one_command(usagestr, flags, outf);
+	ret = do_usage_one_command(usagestr, format_flags, flags, outf);
 	switch (ret) {
 	case -1:
 		fprintf(outf, "No usage for '%s'\n", token);
@@ -224,25 +250,28 @@ static int usage_command_internal(const char * const *usagestr,
 }
 
 static void usage_command_usagestr(const char * const *usagestr,
-				   const char *token, bool full, bool err)
+				   const char *token, unsigned int format_flags,
+				   bool full, bool err)
 {
 	FILE *outf = err ? stderr : stdout;
 	int ret;
 
-	ret = usage_command_internal(usagestr, token, full, false, false, outf);
+	ret = usage_command_internal(usagestr, token, format_flags,
+				     full, false, false, outf);
 	if (!ret)
 		fputc('\n', outf);
 }
 
 void usage_command(const struct cmd_struct *cmd, bool full, bool err)
 {
-	usage_command_usagestr(cmd->usagestr, cmd->token, full, err);
+	usage_command_usagestr(cmd->usagestr, cmd->token,
+			       cmd->cmd_format_flags, full, err);
 }
 
 __attribute__((noreturn))
 void usage(const struct cmd_struct *cmd)
 {
-	usage_command_usagestr(cmd->usagestr, NULL, true, true);
+	usage_command_usagestr(cmd->usagestr, NULL, 0, true, true);
 	exit(1);
 }
 
@@ -266,7 +295,8 @@ static void usage_command_group_internal(const struct cmd_group *grp, bool full,
 				do_sep = 0;
 			}
 
-			usage_command_internal(cmd->usagestr, cmd->token, full,
+			usage_command_internal(cmd->usagestr, cmd->token,
+					       cmd->cmd_format_flags, full,
 					       true, cmd->flags & CMD_ALIAS,
 					       outf);
 			if (cmd->flags & CMD_ALIAS)
@@ -377,7 +407,8 @@ void help_ambiguous_token(const char *arg, const struct cmd_group *grp)
 	exit(1);
 }
 
-void help_command_group(const struct cmd_group *grp, int argc, char **argv)
+void help_command_group(const struct cmd_group *grp, int argc,
+			char **argv)
 {
 	bool full = false;
 
diff --git a/help.h b/help.h
index 7468cf8b..5b338a49 100644
--- a/help.h
+++ b/help.h
@@ -55,6 +55,8 @@
 struct cmd_struct;
 struct cmd_group;
 
+extern const char *cmd_outputs[];
+
 __attribute__((noreturn))
 void usage(const struct cmd_struct *cmd);
 void usage_command(const struct cmd_struct *cmd, bool full, bool err);
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 18/20] btrfs-progs: add generic support for json output
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (16 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 17/20] btrfs-progs: add support for output formats jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 19/20] btrfs-progs: qgroups: add json output for usage command jeffm
  2018-03-08  2:40 ` [PATCH 20/20] btrfs-progs: handle command groups directly for common case jeffm
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

This patch adds support for JSON and JSON-compat output.  The latter is
intended to be compatible with Javascript's integers being represented
as 64-bit floats, with only 53 bits usable for the integer component.
Compat mode output will post 64-bit integers as an array of two 32-bit
integers in [high, low] format.

This patch also adds support for reporting which output formats a
command supports as well as detection of the json-c library.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 Makefile.inc.in |  4 ++--
 commands.h      | 13 +++++++++++++
 configure.ac    |  6 ++++++
 help.c          | 25 ++++++++++++++-----------
 4 files changed, 35 insertions(+), 13 deletions(-)

diff --git a/Makefile.inc.in b/Makefile.inc.in
index 56271903..68bddbed 100644
--- a/Makefile.inc.in
+++ b/Makefile.inc.in
@@ -18,9 +18,9 @@ BTRFSRESTORE_ZSTD = @BTRFSRESTORE_ZSTD@
 SUBST_CFLAGS = @CFLAGS@
 SUBST_LDFLAGS = @LDFLAGS@
 
-LIBS_BASE = @UUID_LIBS@ @BLKID_LIBS@ -L. -pthread
+LIBS_BASE = @UUID_LIBS@ @BLKID_LIBS@ @JSON_LIBS@ -L. -pthread
 LIBS_COMP = @ZLIB_LIBS@ @LZO2_LIBS@ @ZSTD_LIBS@
-STATIC_LIBS_BASE = @UUID_LIBS_STATIC@ @BLKID_LIBS_STATIC@ -L. -pthread
+STATIC_LIBS_BASE = @UUID_LIBS_STATIC@ @BLKID_LIBS_STATIC@ @JSON_LIBS_STATIC@ -L. -pthread
 STATIC_LIBS_COMP = @ZLIB_LIBS_STATIC@ @LZO2_LIBS_STATIC@ @ZSTD_LIBS_STATIC@
 
 prefix ?= @prefix@
diff --git a/commands.h b/commands.h
index 83316c6d..bf74eaf8 100644
--- a/commands.h
+++ b/commands.h
@@ -19,9 +19,22 @@
 
 enum cmd_output {
 	CMD_OUTPUT_TEXT = 0,
+#ifdef HAVE_JSON
+	CMD_OUTPUT_JSON,
+	CMD_OUTPUT_JSON_COMPAT,
+#endif
 	CMD_OUTPUT_MAX,
 };
 
+/*
+ * If we don't have the JSON library, map the flags to text to avoid
+ * more ifdefs elsewhere.
+ */
+#ifndef HAVE_JSON
+#define CMD_OUTPUT_JSON		CMD_OUTPUT_TEXT
+#define CMD_OUTPUT_JSON_COMPAT	CMD_OUTPUT_TEXT
+#endif
+
 #define CMD_OUTPUT_FLAG(x)	(1 << (CMD_OUTPUT_##x))
 
 struct cmd_context {
diff --git a/configure.ac b/configure.ac
index 56d17c3a..6aec672a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -197,6 +197,12 @@ PKG_STATIC(UUID_LIBS_STATIC, [uuid])
 PKG_CHECK_MODULES(ZLIB, [zlib])
 PKG_STATIC(ZLIB_LIBS_STATIC, [zlib])
 
+PKG_CHECK_MODULES(JSON, [json-c], [
+	AC_DEFINE(HAVE_JSON, [1], [Have JSON]),
+	PKG_STATIC(JSON_LIBS_STATIC, [json-c], [
+		AC_DEFINE(HAVE_JSON_STATIC, [1], [Have JSON static])], [true])
+	], [true])
+
 AC_ARG_ENABLE([zstd],
 	AS_HELP_STRING([--disable-zstd], [build without zstd support]),
 	[], [enable_zstd=yes]
diff --git a/help.c b/help.c
index 063e9740..f1710621 100644
--- a/help.c
+++ b/help.c
@@ -32,6 +32,10 @@
 
 const char *cmd_outputs[CMD_OUTPUT_MAX] = {
 	"text",
+#ifdef HAVE_JSON
+	[CMD_OUTPUT_JSON] = "json",
+	[CMD_OUTPUT_JSON_COMPAT] = "json:compat",
+#endif
 };
 
 static char argv0_buf[ARGV0_BUF_SIZE] = "btrfs";
@@ -186,18 +190,17 @@ static int do_usage_one_command(const char * const *usagestr,
 		fprintf(outf, "%*s%s\n", pad, "", *usagestr++);
 
 	/* options (optional) */
-	if (!*usagestr || ((flags & USAGE_OPTIONS) == 0))
-		return 0;
-
-	/*
-	 * options (if present) should always (even if there is no long
-	 * description) be prepended with an empty line, skip it
-	 */
-	usagestr++;
+	if (*usagestr && (flags & USAGE_OPTIONS)) {
+		/*
+		 * options (if present) should always (even if there is no long
+		 * description) be prepended with an empty line, skip it
+		 */
+		usagestr++;
 
-	fputc('\n', outf);
-	while (*usagestr)
-		fprintf(outf, "%*s%s\n", pad, "", *usagestr++);
+		fputc('\n', outf);
+		while (*usagestr)
+			fprintf(outf, "%*s%s\n", pad, "", *usagestr++);
+	}
 
 	if (flags & USAGE_FORMAT) {
 		/* We always support text */
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 19/20] btrfs-progs: qgroups: add json output for usage command
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (17 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 18/20] btrfs-progs: add generic support for json output jeffm
@ 2018-03-08  2:40 ` jeffm
  2018-03-08  2:40 ` [PATCH 20/20] btrfs-progs: handle command groups directly for common case jeffm
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

One of the common requests I receive is for 'df' like facilities
for subvolume usage.  Really, the request is for monitoring tools to be
able to understand when subvolumes may be approaching quota in the same
manner traditional file systems approach ENOSPC.

This patch allows us to export the qgroups data in a machine-readable
format so that monitoring tools can parse it easily.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 Documentation/btrfs-qgroup.asciidoc |   3 +
 cmds-qgroup.c                       |  22 +++-
 qgroup.c                            | 222 ++++++++++++++++++++++++++++++++++++
 qgroup.h                            |   8 +-
 4 files changed, 251 insertions(+), 4 deletions(-)

diff --git a/Documentation/btrfs-qgroup.asciidoc b/Documentation/btrfs-qgroup.asciidoc
index 360b3269..7863a4d9 100644
--- a/Documentation/btrfs-qgroup.asciidoc
+++ b/Documentation/btrfs-qgroup.asciidoc
@@ -87,6 +87,9 @@ the btrfs filesystem identified by <path>.
 *show* [options] <path>::
 Show all qgroups in the btrfs filesystem identified by <path>.
 +
+If enabled, this command supports extended output in json and json:compat modes.
+Use of either json mode implies -P and --raw.
++
 `Options`
 +
 -p::::
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index f9e81e30..fd637a45 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -301,6 +301,10 @@ static const char * const cmd_qgroup_show_usage[] = {
 	"               you can use '+' or '-' in front of each item.",
 	"               (+:ascending, -:descending, ascending default)",
 	"--sync         force sync of the filesystem before getting info",
+	"",
+#ifdef HAVE_JSON
+	"json and json:compat output implies -P and --raw.",
+#endif
 	NULL
 };
 
@@ -386,6 +390,10 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd,
 	}
 	btrfs_qgroup_setup_units(unit_mode);
 
+	if (cmdcxt->output_mode == CMD_OUTPUT_JSON ||
+	    cmdcxt->output_mode == CMD_OUTPUT_JSON_COMPAT)
+		unit_mode = UNITS_RAW;
+
 	if (check_argc_exact(argc - optind, 1))
 		usage(cmd);
 
@@ -420,7 +428,15 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd,
 					BTRFS_QGROUP_FILTER_PARENT,
 					qgroupid);
 	}
-	ret = btrfs_show_qgroups(fd, filter_set, comparer_set, verbose);
+
+	if (cmdcxt->output_mode == CMD_OUTPUT_JSON ||
+	    cmdcxt->output_mode == CMD_OUTPUT_JSON_COMPAT) {
+		bool compat = (cmdcxt->output_mode == CMD_OUTPUT_JSON_COMPAT);
+
+		ret = btrfs_qgroup_output_json(fd, filter_set, comparer_set,
+					       compat);
+	} else
+		ret = btrfs_show_qgroups(fd, filter_set, comparer_set, verbose);
 	close_file_or_dir(fd, dirstream);
 	free(filter_set);
 	free(comparer_set);
@@ -428,7 +444,9 @@ static int cmd_qgroup_show(const struct cmd_struct *cmd,
 out:
 	return !!ret;
 }
-static DEFINE_SIMPLE_COMMAND(qgroup_show, "show");
+static DEFINE_COMMAND(qgroup_show, "show", cmd_qgroup_show,
+		      cmd_qgroup_show_usage, NULL, 0,
+		      CMD_OUTPUT_FLAG(JSON)|CMD_OUTPUT_FLAG(JSON_COMPAT));
 
 static const char * const cmd_qgroup_limit_usage[] = {
 	"btrfs qgroup limit [options] <size>|none [<qgroupid>] <path>",
diff --git a/qgroup.c b/qgroup.c
index d076b1de..95d443db 100644
--- a/qgroup.c
+++ b/qgroup.c
@@ -16,12 +16,16 @@
  * Boston, MA 021110-1307, USA.
  */
 
+#include "version.h"
 #include "qgroup.h"
 #include <sys/ioctl.h>
 #include "ctree.h"
 #include "ioctl.h"
 #include "utils.h"
 #include <errno.h>
+#ifdef HAVE_JSON
+#include <json-c/json.h>
+#endif
 
 #define BTRFS_QGROUP_NFILTERS_INCREASE (2 * BTRFS_QGROUP_FILTER_MAX)
 #define BTRFS_QGROUP_NCOMPS_INCREASE (2 * BTRFS_QGROUP_COMP_MAX)
@@ -1398,6 +1402,224 @@ int btrfs_show_qgroups(int fd,
 	return ret;
 }
 
+#ifdef HAVE_JSON
+#define QGROUPID_FORMAT_BUF_LEN (20 + 20 + 1 + 1)
+static void format_qgroupid(char *buf, size_t size, u64 qgroupid)
+{
+	int ret;
+
+	ret = snprintf(buf, size, "%llu/%llu",
+		       btrfs_qgroup_level(qgroupid),
+		       btrfs_qgroup_subvid(qgroupid));
+	ASSERT(ret < sizeof(buf));
+}
+
+static json_object *export_one_u64(u64 value, bool compat)
+{
+	json_object *array, *tmp;
+
+	if (!compat)
+		return json_object_new_int64(value);
+
+	array = json_object_new_array();
+	if (!array)
+		return NULL;
+
+	tmp = json_object_new_int(value >> 32);
+	if (!tmp)
+		goto failure;
+	json_object_array_add(array, tmp);
+
+	tmp = json_object_new_int(value & 0xffffffff);
+	if (!tmp)
+		goto failure;
+	json_object_array_add(array, tmp);
+
+	return array;
+failure:
+	json_object_put(array);
+	return NULL;
+}
+
+static bool export_one_qgroup(json_object *container,
+			     const struct btrfs_qgroup *qgroup, bool compat)
+{
+	char buf[QGROUPID_FORMAT_BUF_LEN];
+	json_object *obj, *tmp;
+
+	obj = json_object_new_object();
+	if (!obj)
+		return false;
+
+	format_qgroupid(buf, sizeof(buf), qgroup->qgroupid);
+	tmp = json_object_new_string(buf);
+	if (!tmp)
+		return false;
+	json_object_object_add(obj, "qgroupid", tmp);
+
+	tmp = export_one_u64(qgroup->qgroupid, compat);
+	if (!tmp)
+		goto failure;
+	json_object_object_add(obj, "qgroupid_raw", tmp);
+
+	tmp = export_one_u64(qgroup->info.generation, compat);
+	if (!tmp)
+		goto failure;
+	json_object_object_add(obj, "generation", tmp);
+
+	tmp = export_one_u64(qgroup->info.referenced, compat);
+	if (!tmp)
+		goto failure;
+	json_object_object_add(obj, "referenced_bytes", tmp);
+
+	tmp = export_one_u64(qgroup->info.exclusive, compat);
+	if (!tmp)
+		goto failure;
+	json_object_object_add(obj, "exclusive_bytes", tmp);
+
+	tmp = export_one_u64(qgroup->limit.max_referenced, compat);
+	if (!tmp)
+		goto failure;
+	json_object_object_add(obj, "referenced_limit_bytes", tmp);
+
+	tmp = export_one_u64(qgroup->limit.max_exclusive, compat);
+	if (!tmp)
+		goto failure;
+	json_object_object_add(obj, "exclusive_limit_bytes", tmp);
+
+	if (btrfs_qgroup_level(qgroup->qgroupid) == 0) {
+		if (qgroup->pathname) {
+			tmp = json_object_new_string(qgroup->pathname);
+			if (!tmp)
+				goto failure;
+			json_object_object_add(obj, "pathname", tmp);
+		}
+	} else {
+		json_object *array = json_object_new_array();
+		struct btrfs_qgroup_list *list = NULL;
+
+		if (!array)
+			goto failure;
+		json_object_object_add(obj, "members", array);
+
+		list_for_each_entry(list, &qgroup->qgroups, next_qgroup) {
+			struct btrfs_qgroup *member = list->qgroup;
+			char buf2[QGROUPID_FORMAT_BUF_LEN];
+
+			format_qgroupid(buf2, sizeof(buf2), member->qgroupid);
+			tmp = json_object_new_string(buf2);
+			if (!tmp)
+				goto failure;
+
+			json_object_array_add(array, tmp);
+		}
+	}
+
+	json_object_object_add(container, buf, obj);
+	return true;
+failure:
+	json_object_put(obj);
+	return false;
+}
+
+#define BTRFS_JSON_WARNING \
+"This data contains 64-bit values that are incompatible with Javascript. Export in compatibility mode using --format=json:compat."
+
+static void export_all_qgroups(const struct qgroup_lookup *qgroup_lookup,
+			       bool compat)
+{
+
+	struct rb_node *n;
+	const char *json;
+	json_object *container, *dict, *obj;
+	struct btrfs_qgroup *entry;
+	int json_flags = JSON_C_TO_STRING_PRETTY;
+
+#ifdef JSON_C_TO_STRING_NOSLASHESCAPE
+	json_flags |= JSON_C_TO_STRING_NOSLASHESCAPE;
+#endif
+
+	container = json_object_new_object();
+	if (!container)
+		goto failure_msg;
+
+	obj = json_object_new_string(BTRFS_BUILD_VERSION);
+	if (!obj)
+		goto failure;
+	json_object_object_add(container, "exporter", obj);
+
+	if (!compat) {
+		obj = json_object_new_string(BTRFS_JSON_WARNING);
+		if (!obj)
+			goto failure;
+		json_object_object_add(container, "compatibility-warning", obj);
+
+		obj = json_object_new_string("64-bit");
+		if (!obj)
+			goto failure;
+		json_object_object_add(container, "u64-format", obj);
+	} else {
+		obj = json_object_new_string("array");
+		if (!obj)
+			goto failure;
+		json_object_object_add(container, "u64-format", obj);
+	}
+
+	dict = json_object_new_object();
+	if (!dict)
+		goto failure;
+	json_object_object_add(container, "qgroup_data", dict);
+
+	n = rb_first(&qgroup_lookup->root);
+	while (n) {
+		entry = rb_entry(n, struct btrfs_qgroup, sort_node);
+		if (!export_one_qgroup(dict, entry, compat))
+			goto failure;
+		n = rb_next(n);
+	}
+
+	json = json_object_to_json_string_ext(container, json_flags);
+	if (!json)
+		goto failure;
+
+	puts(json);
+
+	/* clean up container */
+	json_object_put(container);
+	return;
+
+failure:
+	json_object_put(container);
+failure_msg:
+	error("Failed to create JSON object.");
+}
+#endif
+
+int btrfs_qgroup_output_json(int fd,
+			     struct btrfs_qgroup_filter_set *filter_set,
+			     struct btrfs_qgroup_comparer_set *comp_set,
+			     bool compat)
+{
+#ifdef HAVE_JSON
+	struct qgroup_lookup qgroup_lookup;
+	struct qgroup_lookup sort_tree;
+	int ret = 0;
+
+	ret = qgroups_search_all(fd, &qgroup_lookup);
+	if (ret)
+		return ret;
+	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
+				  filter_set, comp_set);
+	export_all_qgroups(&sort_tree, compat);
+
+	__free_all_qgroups(&qgroup_lookup);
+
+	return ret;
+#else
+	return 0;
+#endif
+}
+
 int btrfs_qgroup_parse_sort_string(const char *opt_arg,
 				   struct btrfs_qgroup_comparer_set **comps)
 {
diff --git a/qgroup.h b/qgroup.h
index 688f92b2..4344c7d8 100644
--- a/qgroup.h
+++ b/qgroup.h
@@ -95,8 +95,12 @@ struct btrfs_qgroup_stats {
 
 int btrfs_qgroup_parse_sort_string(const char *opt_arg,
 				struct btrfs_qgroup_comparer_set **comps);
-int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
-		       struct btrfs_qgroup_comparer_set *, bool verbose);
+int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *filter_set,
+		       struct btrfs_qgroup_comparer_set *comp_set,
+		       bool verbose);
+int btrfs_qgroup_output_json(int fd, struct btrfs_qgroup_filter_set *filter_set,
+			     struct btrfs_qgroup_comparer_set *comp_set,
+			     bool compat);
 void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column);
 void btrfs_qgroup_setup_units(unsigned unit_mode);
 struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void);
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 20/20] btrfs-progs: handle command groups directly for common case
  2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
                   ` (18 preceding siblings ...)
  2018-03-08  2:40 ` [PATCH 19/20] btrfs-progs: qgroups: add json output for usage command jeffm
@ 2018-03-08  2:40 ` jeffm
  19 siblings, 0 replies; 31+ messages in thread
From: jeffm @ 2018-03-08  2:40 UTC (permalink / raw)
  To: linux-btrfs; +Cc: Jeff Mahoney

From: Jeff Mahoney <jeffm@suse.com>

Most command groups just pass their own command group to
handle_command_group.  We can remove the explicit definitions
of command group callbacks by passing the cmd_struct to
handle_command_group and allowing it to resolve the group from it.

Signed-off-by: Jeff Mahoney <jeffm@suse.com>
---
 btrfs.c           | 14 +++++++-------
 cmds-balance.c    |  6 +++---
 cmds-device.c     |  5 -----
 cmds-filesystem.c |  6 ------
 cmds-inspect.c    |  5 -----
 cmds-property.c   |  6 ------
 cmds-qgroup.c     |  5 -----
 cmds-quota.c      |  5 -----
 cmds-replace.c    |  5 -----
 cmds-rescue.c     |  5 -----
 cmds-scrub.c      |  6 ------
 cmds-subvolume.c  |  5 -----
 commands.h        |  7 ++++---
 13 files changed, 14 insertions(+), 66 deletions(-)

diff --git a/btrfs.c b/btrfs.c
index 32b8e090..427e14c8 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -140,26 +140,26 @@ static void handle_help_options_next_level(const struct cmd_struct *cmd,
 	}
 }
 
-int handle_command_group(const struct cmd_group *grp,
+int handle_command_group(const struct cmd_struct *cmd,
 			 const struct cmd_context *cmdcxt,
 			 int argc, char **argv)
 
 {
-	const struct cmd_struct *cmd;
+	const struct cmd_struct *subcmd;
 
 	argc--;
 	argv++;
 	if (argc < 1) {
-		usage_command_group(grp, false, false);
+		usage_command_group(cmd->next, false, false);
 		exit(1);
 	}
 
-	cmd = parse_command_token(argv[0], grp);
+	subcmd = parse_command_token(argv[0], cmd->next);
 
-	handle_help_options_next_level(cmd, cmdcxt, argc, argv);
+	handle_help_options_next_level(subcmd, cmdcxt, argc, argv);
 
-	fixup_argv0(argv, cmd->token);
-	return cmd_execute(cmd, cmdcxt, argc, argv);
+	fixup_argv0(argv, subcmd->token);
+	return cmd_execute(subcmd, cmdcxt, argc, argv);
 }
 
 static const struct cmd_group btrfs_cmd_group;
diff --git a/cmds-balance.c b/cmds-balance.c
index c17b9ee3..e414ca27 100644
--- a/cmds-balance.c
+++ b/cmds-balance.c
@@ -943,7 +943,7 @@ static const struct cmd_group balance_cmd_group = {
 	}
 };
 
-static int cmd_balance(const struct cmd_struct *unused,
+static int cmd_balance(const struct cmd_struct *cmd,
 		       const struct cmd_context *cmdcxt, int argc, char **argv)
 {
 	if (argc == 2 && strcmp("start", argv[1]) != 0) {
@@ -956,7 +956,7 @@ static int cmd_balance(const struct cmd_struct *unused,
 		return do_balance(argv[1], &args, 0);
 	}
 
-	return handle_command_group(&balance_cmd_group, cmdcxt, argc, argv);
+	return handle_command_group(cmd, cmdcxt, argc, argv);
 }
 
-DEFINE_GROUP_COMMAND(balance, "balance");
+DEFINE_COMMAND(balance, "balance", cmd_balance, NULL, &balance_cmd_group, 0, 0);
diff --git a/cmds-device.c b/cmds-device.c
index ac9e82b1..f8c0ff20 100644
--- a/cmds-device.c
+++ b/cmds-device.c
@@ -630,9 +630,4 @@ static const struct cmd_group device_cmd_group = {
 	}
 };
 
-static int cmd_device(const struct cmd_struct *unused,
-		      const struct cmd_context *cmdcxt, int argc, char **argv)
-{
-	return handle_command_group(&device_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND_TOKEN(device);
diff --git a/cmds-filesystem.c b/cmds-filesystem.c
index 27e0865c..e3e54864 100644
--- a/cmds-filesystem.c
+++ b/cmds-filesystem.c
@@ -1242,10 +1242,4 @@ static const struct cmd_group filesystem_cmd_group = {
 	}
 };
 
-static int cmd_filesystem(const struct cmd_struct *unused,
-			  const struct cmd_context *cmdcxt,
-			  int argc, char **argv)
-{
-	return handle_command_group(&filesystem_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND_TOKEN(filesystem);
diff --git a/cmds-inspect.c b/cmds-inspect.c
index ade9db7e..561a0fbd 100644
--- a/cmds-inspect.c
+++ b/cmds-inspect.c
@@ -658,9 +658,4 @@ static const struct cmd_group inspect_cmd_group = {
 	}
 };
 
-static int cmd_inspect(const struct cmd_struct *unused,
-		       const struct cmd_context *cmdcxt, int argc, char **argv)
-{
-	return handle_command_group(&inspect_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND(inspect, "inspect");
diff --git a/cmds-property.c b/cmds-property.c
index 498fa456..58f6c48a 100644
--- a/cmds-property.c
+++ b/cmds-property.c
@@ -422,10 +422,4 @@ static const struct cmd_group property_cmd_group = {
 	}
 };
 
-static int cmd_property(const struct cmd_struct *unused,
-			const struct cmd_context *cmdcxt,
-			int argc, char **argv)
-{
-	return handle_command_group(&property_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND_TOKEN(property);
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
index fd637a45..e20c1159 100644
--- a/cmds-qgroup.c
+++ b/cmds-qgroup.c
@@ -558,9 +558,4 @@ static const struct cmd_group qgroup_cmd_group = {
 	}
 };
 
-static int cmd_qgroup(const struct cmd_struct *unused,
-		      const struct cmd_context *cmdcxt, int argc, char **argv)
-{
-	return handle_command_group(&qgroup_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND_TOKEN(qgroup);
diff --git a/cmds-quota.c b/cmds-quota.c
index 2a88d4c4..7b524123 100644
--- a/cmds-quota.c
+++ b/cmds-quota.c
@@ -219,9 +219,4 @@ static const struct cmd_group quota_cmd_group = {
 	}
 };
 
-static int cmd_quota(const struct cmd_struct *unused,
-		     const struct cmd_context *cmdcxt, int argc, char **argv)
-{
-	return handle_command_group(&quota_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND_TOKEN(quota);
diff --git a/cmds-replace.c b/cmds-replace.c
index 64c93537..8b2091c9 100644
--- a/cmds-replace.c
+++ b/cmds-replace.c
@@ -560,9 +560,4 @@ static const struct cmd_group replace_cmd_group = {
 	}
 };
 
-static int cmd_replace(const struct cmd_struct *unused,
-		       const struct cmd_context *cmdcxt, int argc, char **argv)
-{
-	return handle_command_group(&replace_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND_TOKEN(replace);
diff --git a/cmds-rescue.c b/cmds-rescue.c
index ec1ea222..364d6dd9 100644
--- a/cmds-rescue.c
+++ b/cmds-rescue.c
@@ -273,9 +273,4 @@ static const struct cmd_group rescue_cmd_group = {
 	}
 };
 
-static int cmd_rescue(const struct cmd_struct *unused,
-		      const struct cmd_context *cmdcxt, int argc, char **argv)
-{
-	return handle_command_group(&rescue_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND_TOKEN(rescue);
diff --git a/cmds-scrub.c b/cmds-scrub.c
index d4d5a10c..b0238032 100644
--- a/cmds-scrub.c
+++ b/cmds-scrub.c
@@ -1807,10 +1807,4 @@ static const struct cmd_group scrub_cmd_group = {
 	}
 };
 
-static int cmd_scrub(const struct cmd_struct *unused,
-		     const struct cmd_context *cmdcxt, int argc, char **argv)
-{
-	return handle_command_group(&scrub_cmd_group, cmdcxt, argc, argv);
-}
-
 DEFINE_GROUP_COMMAND_TOKEN(scrub);
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 3498ff21..7a420a64 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -1466,9 +1466,4 @@ static const struct cmd_group subvolume_cmd_group = {
 	}
 };
 
-int cmd_subvolume(const struct cmd_struct *unused,
-		  const struct cmd_context *cmdcxt, int argc, char **argv)
-{
-	return handle_command_group(&subvolume_cmd_group, cmdcxt, argc, argv);
-}
 DEFINE_GROUP_COMMAND_TOKEN(subvolume);
diff --git a/commands.h b/commands.h
index bf74eaf8..65be8742 100644
--- a/commands.h
+++ b/commands.h
@@ -107,7 +107,8 @@ struct cmd_struct {
 /*
  * Define a command for the common case - just a name and string.
  * It's assumed that the callback is called cmd_<name> and the usage
- * array is named cmd_<name>_usage.
+ * array is named cmd_<name>_usage.  Text is the only supported output
+ * format.
  */
 #define DEFINE_SIMPLE_COMMAND(name, token)				\
 	DEFINE_COMMAND(name, token, cmd_ ##name,			\
@@ -119,7 +120,7 @@ struct cmd_struct {
  * struct cmd_group is called <name>_cmd_group.
  */
 #define DEFINE_GROUP_COMMAND(name, token)				\
-	DEFINE_COMMAND(name, token, cmd_ ##name,			\
+	DEFINE_COMMAND(name, token, handle_command_group,		\
 		       NULL, &(name ## _cmd_group), 0, 0)
 
 /*
@@ -143,7 +144,7 @@ static inline int cmd_execute(const struct cmd_struct *cmd,
 	return cmd->fn(cmd, cmdcxt, argc, argv);
 }
 
-int handle_command_group(const struct cmd_group *grp,
+int handle_command_group(const struct cmd_struct *cmd,
 			 const struct cmd_context *cmdcxt,
 			 int argc, char **argv);
 
-- 
2.12.3


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH 06/20] btrfs-progs: qgroups: add pathname to show output
  2018-03-08  2:40 ` [PATCH 06/20] btrfs-progs: qgroups: add pathname to show output jeffm
@ 2018-03-08  5:33   ` Qu Wenruo
  2018-03-08 14:25     ` Jeff Mahoney
  0 siblings, 1 reply; 31+ messages in thread
From: Qu Wenruo @ 2018-03-08  5:33 UTC (permalink / raw)
  To: jeffm, linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 5735 bytes --]



On 2018年03月08日 10:40, jeffm@suse.com wrote:
> From: Jeff Mahoney <jeffm@suse.com>
> 
> The btrfs qgroup show command currently only exports qgroup IDs,
> forcing the user to resolve which subvolume each corresponds to.
> 
> This patch adds pathname resolution to qgroup show so that when
> the -P option is used, the last column contains the pathname of
> the root of the subvolume it describes.  In the case of nested
> qgroups, it will show the number of member qgroups or the paths
> of the members if the -v option is used.
> 
> Pathname can also be used as a sort parameter.
> 
> Signed-off-by: Jeff Mahoney <jeffm@suse.com>

Reviewed-by: Qu Wenruo <wqu@suse.com>

Except one nitpick inlined below.

[snip]
>  	}
> +	if (bq->pathname)
> +		free((void *)bq->pathname);

What about just free(bq->pathname);?

Is this (void *) used to get around the const prefix?

Thanks,
Qu

>  	free(bq);
>  }
>  
> @@ -1107,7 +1228,7 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>  				info = (struct btrfs_qgroup_info_item *)
>  				       (args.buf + off);
>  
> -				ret = update_qgroup_info(qgroup_lookup,
> +				ret = update_qgroup_info(fd, qgroup_lookup,
>  							 qgroupid, info);
>  				break;
>  			case BTRFS_QGROUP_LIMIT_KEY:
> @@ -1115,7 +1236,7 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>  				limit = (struct btrfs_qgroup_limit_item *)
>  					(args.buf + off);
>  
> -				ret = update_qgroup_limit(qgroup_lookup,
> +				ret = update_qgroup_limit(fd, qgroup_lookup,
>  							  qgroupid, limit);
>  				break;
>  			case BTRFS_QGROUP_RELATION_KEY:
> @@ -1159,7 +1280,7 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>  	return ret;
>  }
>  
> -static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
> +static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup, bool verbose)
>  {
>  
>  	struct rb_node *n;
> @@ -1170,14 +1291,15 @@ static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup)
>  	n = rb_first(&qgroup_lookup->root);
>  	while (n) {
>  		entry = rb_entry(n, struct btrfs_qgroup, sort_node);
> -		print_single_qgroup_table(entry);
> +		print_single_qgroup_table(entry, verbose);
>  		n = rb_next(n);
>  	}
>  }
>  
>  int btrfs_show_qgroups(int fd,
>  		       struct btrfs_qgroup_filter_set *filter_set,
> -		       struct btrfs_qgroup_comparer_set *comp_set)
> +		       struct btrfs_qgroup_comparer_set *comp_set,
> +		       bool verbose)
>  {
>  
>  	struct qgroup_lookup qgroup_lookup;
> @@ -1189,7 +1311,7 @@ int btrfs_show_qgroups(int fd,
>  		return ret;
>  	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
>  				  filter_set, comp_set);
> -	print_all_qgroups(&sort_tree);
> +	print_all_qgroups(&sort_tree, verbose);
>  
>  	__free_all_qgroups(&qgroup_lookup);
>  	return ret;
> diff --git a/qgroup.h b/qgroup.h
> index 875fbdf3..f7ab7de5 100644
> --- a/qgroup.h
> +++ b/qgroup.h
> @@ -59,11 +59,13 @@ enum btrfs_qgroup_column_enum {
>  	BTRFS_QGROUP_MAX_EXCL,
>  	BTRFS_QGROUP_PARENT,
>  	BTRFS_QGROUP_CHILD,
> +	BTRFS_QGROUP_PATHNAME,
>  	BTRFS_QGROUP_ALL,
>  };
>  
>  enum btrfs_qgroup_comp_enum {
>  	BTRFS_QGROUP_COMP_QGROUPID,
> +	BTRFS_QGROUP_COMP_PATHNAME,
>  	BTRFS_QGROUP_COMP_RFER,
>  	BTRFS_QGROUP_COMP_EXCL,
>  	BTRFS_QGROUP_COMP_MAX_RFER,
> @@ -80,7 +82,7 @@ enum btrfs_qgroup_filter_enum {
>  int btrfs_qgroup_parse_sort_string(const char *opt_arg,
>  				struct btrfs_qgroup_comparer_set **comps);
>  int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
> -		       struct btrfs_qgroup_comparer_set *);
> +		       struct btrfs_qgroup_comparer_set *, bool verbose);
>  void btrfs_qgroup_setup_print_column(enum btrfs_qgroup_column_enum column);
>  void btrfs_qgroup_setup_units(unsigned unit_mode);
>  struct btrfs_qgroup_filter_set *btrfs_qgroup_alloc_filter_set(void);
> diff --git a/utils.c b/utils.c
> index e9cb3a82..7b7f87f1 100644
> --- a/utils.c
> +++ b/utils.c
> @@ -2556,15 +2556,9 @@ out:
>  	return ret;
>  }
>  
> -int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri, u64 r_id)
> +int get_subvol_info_by_rootid_fd(int fd, struct root_info *get_ri, u64 r_id)
>  {
> -	int fd;
>  	int ret;
> -	DIR *dirstream = NULL;
> -
> -	fd = btrfs_open_dir(mnt, &dirstream, 1);
> -	if (fd < 0)
> -		return -EINVAL;
>  
>  	memset(get_ri, 0, sizeof(*get_ri));
>  	get_ri->root_id = r_id;
> @@ -2574,6 +2568,21 @@ int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri, u64 r_i
>  	else
>  		ret = btrfs_get_subvol(fd, get_ri);
>  
> +	return ret;
> +}
> +
> +int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri,
> +			      u64 r_id)
> +{
> +	int fd;
> +	int ret;
> +	DIR *dirstream = NULL;
> +
> +	fd = btrfs_open_dir(mnt, &dirstream, 1);
> +	if (fd < 0)
> +		return -EINVAL;
> +
> +	ret = get_subvol_info_by_rootid_fd(fd, get_ri, r_id);
>  	if (ret)
>  		error("can't find rootid '%llu' on '%s': %d", r_id, mnt, ret);
>  
> diff --git a/utils.h b/utils.h
> index b871c9ff..722d3c48 100644
> --- a/utils.h
> +++ b/utils.h
> @@ -154,6 +154,8 @@ int test_isdir(const char *path);
>  
>  const char *subvol_strip_mountpoint(const char *mnt, const char *full_path);
>  int get_subvol_info(const char *fullpath, struct root_info *get_ri);
> +int get_subvol_info_by_rootid_fd(int fd, struct root_info *get_ri,
> +				 u64 rootid_arg);
>  int get_subvol_info_by_rootid(const char *mnt, struct root_info *get_ri,
>  							u64 rootid_arg);
>  int get_subvol_info_by_uuid(const char *mnt, struct root_info *get_ri,
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 520 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 07/20] btrfs-progs: qgroups: introduce and use info and limit structures
  2018-03-08  2:40 ` [PATCH 07/20] btrfs-progs: qgroups: introduce and use info and limit structures jeffm
@ 2018-03-08  5:34   ` Qu Wenruo
  0 siblings, 0 replies; 31+ messages in thread
From: Qu Wenruo @ 2018-03-08  5:34 UTC (permalink / raw)
  To: jeffm, linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 8511 bytes --]



On 2018年03月08日 10:40, jeffm@suse.com wrote:
> From: Jeff Mahoney <jeffm@suse.com>
> 
> We use structures to pass the info and limit from the kernel as items
> but store the individual values separately in btrfs_qgroup.  We already
> have a btrfs_qgroup_limit structure that's used for setting the limit.
> 
> This patch introduces a btrfs_qgroup_info structure and uses that and
> btrfs_qgroup_limit in btrfs_qgroup.
> 
> Signed-off-by: Jeff Mahoney <jeffm@suse.com>

Reviewed-by: Qu Wenruo <wqu@suse.com>

Thanks,
Qu

> ---
>  qgroup.c | 82 ++++++++++++++++++++++++++++++++++------------------------------
>  qgroup.h |  8 +++++++
>  2 files changed, 52 insertions(+), 38 deletions(-)
> 
> diff --git a/qgroup.c b/qgroup.c
> index 5600da99..57815718 100644
> --- a/qgroup.c
> +++ b/qgroup.c
> @@ -46,20 +46,12 @@ struct btrfs_qgroup {
>  	/*
>  	 * info_item
>  	 */
> -	u64 generation;
> -	u64 rfer;	/*referenced*/
> -	u64 rfer_cmpr;	/*referenced compressed*/
> -	u64 excl;	/*exclusive*/
> -	u64 excl_cmpr;	/*exclusive compressed*/
> +	struct btrfs_qgroup_info info;
>  
>  	/*
>  	 *limit_item
>  	 */
> -	u64 flags;	/*which limits are set*/
> -	u64 max_rfer;
> -	u64 max_excl;
> -	u64 rsv_rfer;
> -	u64 rsv_excl;
> +	struct btrfs_qgroup_limit limit;
>  
>  	/*qgroups this group is member of*/
>  	struct list_head qgroups;
> @@ -260,6 +252,11 @@ void print_pathname_column(struct btrfs_qgroup *qgroup, bool verbose)
>  		fputs("<missing>", stdout);
>  }
>  
> +static int print_u64(u64 value, int unit_mode, int max_len)
> +{
> +	return printf("%*s", max_len, pretty_size_mode(value, unit_mode));
> +}
> +
>  static void print_qgroup_column(struct btrfs_qgroup *qgroup,
>  				enum btrfs_qgroup_column_enum column,
>  				bool verbose)
> @@ -279,24 +276,26 @@ static void print_qgroup_column(struct btrfs_qgroup *qgroup,
>  		print_qgroup_column_add_blank(BTRFS_QGROUP_QGROUPID, len);
>  		break;
>  	case BTRFS_QGROUP_RFER:
> -		len = printf("%*s", max_len, pretty_size_mode(qgroup->rfer, unit_mode));
> +		len = print_u64(qgroup->info.referenced, unit_mode, max_len);
>  		break;
>  	case BTRFS_QGROUP_EXCL:
> -		len = printf("%*s", max_len, pretty_size_mode(qgroup->excl, unit_mode));
> +		len = print_u64(qgroup->info.exclusive, unit_mode, max_len);
>  		break;
>  	case BTRFS_QGROUP_PARENT:
>  		len = print_parent_column(qgroup);
>  		print_qgroup_column_add_blank(BTRFS_QGROUP_PARENT, len);
>  		break;
>  	case BTRFS_QGROUP_MAX_RFER:
> -		if (qgroup->flags & BTRFS_QGROUP_LIMIT_MAX_RFER)
> -			len = printf("%*s", max_len, pretty_size_mode(qgroup->max_rfer, unit_mode));
> +		if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_RFER)
> +			len = print_u64(qgroup->limit.max_referenced,
> +					unit_mode, max_len);
>  		else
>  			len = printf("%*s", max_len, "none");
>  		break;
>  	case BTRFS_QGROUP_MAX_EXCL:
> -		if (qgroup->flags & BTRFS_QGROUP_LIMIT_MAX_EXCL)
> -			len = printf("%*s", max_len, pretty_size_mode(qgroup->max_excl, unit_mode));
> +		if (qgroup->limit.flags & BTRFS_QGROUP_LIMIT_MAX_EXCL)
> +			len = print_u64(qgroup->limit.max_exclusive,
> +					unit_mode, max_len);
>  		else
>  			len = printf("%*s", max_len, "none");
>  		break;
> @@ -439,9 +438,9 @@ static int comp_entry_with_rfer(struct btrfs_qgroup *entry1,
>  {
>  	int ret;
>  
> -	if (entry1->rfer > entry2->rfer)
> +	if (entry1->info.referenced > entry2->info.referenced)
>  		ret = 1;
> -	else if (entry1->rfer < entry2->rfer)
> +	else if (entry1->info.referenced < entry2->info.referenced)
>  		ret = -1;
>  	else
>  		ret = 0;
> @@ -455,9 +454,9 @@ static int comp_entry_with_excl(struct btrfs_qgroup *entry1,
>  {
>  	int ret;
>  
> -	if (entry1->excl > entry2->excl)
> +	if (entry1->info.exclusive > entry2->info.exclusive)
>  		ret = 1;
> -	else if (entry1->excl < entry2->excl)
> +	else if (entry1->info.exclusive < entry2->info.exclusive)
>  		ret = -1;
>  	else
>  		ret = 0;
> @@ -471,9 +470,9 @@ static int comp_entry_with_max_rfer(struct btrfs_qgroup *entry1,
>  {
>  	int ret;
>  
> -	if (entry1->max_rfer > entry2->max_rfer)
> +	if (entry1->limit.max_referenced > entry2->limit.max_referenced)
>  		ret = 1;
> -	else if (entry1->max_rfer < entry2->max_rfer)
> +	else if (entry1->limit.max_referenced < entry2->limit.max_referenced)
>  		ret = -1;
>  	else
>  		ret = 0;
> @@ -487,9 +486,9 @@ static int comp_entry_with_max_excl(struct btrfs_qgroup *entry1,
>  {
>  	int ret;
>  
> -	if (entry1->max_excl > entry2->max_excl)
> +	if (entry1->limit.max_exclusive > entry2->limit.max_exclusive)
>  		ret = 1;
> -	else if (entry1->max_excl < entry2->max_excl)
> +	else if (entry1->limit.max_exclusive < entry2->limit.max_exclusive)
>  		ret = -1;
>  	else
>  		ret = 0;
> @@ -747,11 +746,13 @@ static int update_qgroup_info(int fd, struct qgroup_lookup *qgroup_lookup,
>  	if (IS_ERR_OR_NULL(bq))
>  		return PTR_ERR(bq);
>  
> -	bq->generation = btrfs_stack_qgroup_info_generation(info);
> -	bq->rfer = btrfs_stack_qgroup_info_referenced(info);
> -	bq->rfer_cmpr = btrfs_stack_qgroup_info_referenced_compressed(info);
> -	bq->excl = btrfs_stack_qgroup_info_exclusive(info);
> -	bq->excl_cmpr = btrfs_stack_qgroup_info_exclusive_compressed(info);
> +	bq->info.generation = btrfs_stack_qgroup_info_generation(info);
> +	bq->info.referenced = btrfs_stack_qgroup_info_referenced(info);
> +	bq->info.referenced_compressed =
> +			btrfs_stack_qgroup_info_referenced_compressed(info);
> +	bq->info.exclusive = btrfs_stack_qgroup_info_exclusive(info);
> +	bq->info.exclusive_compressed =
> +			btrfs_stack_qgroup_info_exclusive_compressed(info);
>  
>  	return 0;
>  }
> @@ -766,11 +767,14 @@ static int update_qgroup_limit(int fd, struct qgroup_lookup *qgroup_lookup,
>  	if (IS_ERR_OR_NULL(bq))
>  		return PTR_ERR(bq);
>  
> -	bq->flags = btrfs_stack_qgroup_limit_flags(limit);
> -	bq->max_rfer = btrfs_stack_qgroup_limit_max_referenced(limit);
> -	bq->max_excl = btrfs_stack_qgroup_limit_max_exclusive(limit);
> -	bq->rsv_rfer = btrfs_stack_qgroup_limit_rsv_referenced(limit);
> -	bq->rsv_excl = btrfs_stack_qgroup_limit_rsv_exclusive(limit);
> +	bq->limit.flags = btrfs_stack_qgroup_limit_flags(limit);
> +	bq->limit.max_referenced =
> +			btrfs_stack_qgroup_limit_max_referenced(limit);
> +	bq->limit.max_exclusive =
> +			btrfs_stack_qgroup_limit_max_exclusive(limit);
> +	bq->limit.rsv_referenced =
> +			btrfs_stack_qgroup_limit_rsv_referenced(limit);
> +	bq->limit.rsv_exclusive = btrfs_stack_qgroup_limit_rsv_exclusive(limit);
>  
>  	return 0;
>  }
> @@ -1063,22 +1067,24 @@ static void __update_columns_max_len(struct btrfs_qgroup *bq,
>  			btrfs_qgroup_columns[column].max_len = len;
>  		break;
>  	case BTRFS_QGROUP_RFER:
> -		len = strlen(pretty_size_mode(bq->rfer, unit_mode));
> +		len = strlen(pretty_size_mode(bq->info.referenced, unit_mode));
>  		if (btrfs_qgroup_columns[column].max_len < len)
>  			btrfs_qgroup_columns[column].max_len = len;
>  		break;
>  	case BTRFS_QGROUP_EXCL:
> -		len = strlen(pretty_size_mode(bq->excl, unit_mode));
> +		len = strlen(pretty_size_mode(bq->info.exclusive, unit_mode));
>  		if (btrfs_qgroup_columns[column].max_len < len)
>  			btrfs_qgroup_columns[column].max_len = len;
>  		break;
>  	case BTRFS_QGROUP_MAX_RFER:
> -		len = strlen(pretty_size_mode(bq->max_rfer, unit_mode));
> +		len = strlen(pretty_size_mode(bq->limit.max_referenced,
> +			     unit_mode));
>  		if (btrfs_qgroup_columns[column].max_len < len)
>  			btrfs_qgroup_columns[column].max_len = len;
>  		break;
>  	case BTRFS_QGROUP_MAX_EXCL:
> -		len = strlen(pretty_size_mode(bq->max_excl, unit_mode));
> +		len = strlen(pretty_size_mode(bq->limit.max_exclusive,
> +			     unit_mode));
>  		if (btrfs_qgroup_columns[column].max_len < len)
>  			btrfs_qgroup_columns[column].max_len = len;
>  		break;
> diff --git a/qgroup.h b/qgroup.h
> index f7ab7de5..5e71349c 100644
> --- a/qgroup.h
> +++ b/qgroup.h
> @@ -79,6 +79,14 @@ enum btrfs_qgroup_filter_enum {
>  	BTRFS_QGROUP_FILTER_MAX,
>  };
>  
> +struct btrfs_qgroup_info {
> +	u64 generation;
> +	u64 referenced;
> +	u64 referenced_compressed;
> +	u64 exclusive;
> +	u64 exclusive_compressed;
> +};
> +
>  int btrfs_qgroup_parse_sort_string(const char *opt_arg,
>  				struct btrfs_qgroup_comparer_set **comps);
>  int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 520 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query
  2018-03-08  2:40 ` [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query jeffm
@ 2018-03-08  5:54   ` Qu Wenruo
  2018-03-08 15:21     ` Jeff Mahoney
  0 siblings, 1 reply; 31+ messages in thread
From: Qu Wenruo @ 2018-03-08  5:54 UTC (permalink / raw)
  To: jeffm, linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 10013 bytes --]



On 2018年03月08日 10:40, jeffm@suse.com wrote:
> From: Jeff Mahoney <jeffm@suse.com>
> 
> The only mechanism we have in the progs for searching qgroups is to load
> all of them and filter the results.  This works for qgroup show but
> to add quota information to 'btrfs subvoluem show' it's pretty wasteful.
> 
> This patch splits out setting up the search and performing the search so
> we can search for a single qgroupid more easily.  Since TREE_SEARCH
> will give results that don't strictly match the search terms, we add
> a filter to match only the results we care about.
> 
> Signed-off-by: Jeff Mahoney <jeffm@suse.com>
> ---
>  qgroup.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++---------------
>  qgroup.h |   7 ++++
>  2 files changed, 116 insertions(+), 34 deletions(-)
> 
> diff --git a/qgroup.c b/qgroup.c
> index 57815718..d076b1de 100644
> --- a/qgroup.c
> +++ b/qgroup.c
> @@ -1165,11 +1165,30 @@ static inline void print_status_flag_warning(u64 flags)
>  		warning("qgroup data inconsistent, rescan recommended");
>  }
>  
> -static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
> +static bool key_in_range(const struct btrfs_key *key,
> +			 const struct btrfs_ioctl_search_key *sk)
> +{
> +	if (key->objectid < sk->min_objectid ||
> +	    key->objectid > sk->max_objectid)
> +		return false;
> +
> +	if (key->type < sk->min_type ||
> +	    key->type > sk->max_type)
> +		return false;
> +
> +	if (key->offset < sk->min_offset ||
> +	    key->offset > sk->max_offset)
> +		return false;
> +
> +	return true;
> +}

Even with the key_in_range() check here, we are still following the tree
search slice behavior:

tree search will still gives us all the items in key range from
(min_objectid, min_type, min_offset) to
(max_objectid, max_type, max_offset).

I don't see much different between the tree search ioctl and this one.

> +
> +static int __qgroups_search(int fd, struct btrfs_ioctl_search_args *args,
> +			    struct qgroup_lookup *qgroup_lookup)
>  {
>  	int ret;
> -	struct btrfs_ioctl_search_args args;
> -	struct btrfs_ioctl_search_key *sk = &args.key;
> +	struct btrfs_ioctl_search_key *sk = &args->key;
> +	struct btrfs_ioctl_search_key filter_key = args->key;
>  	struct btrfs_ioctl_search_header *sh;
>  	unsigned long off = 0;
>  	unsigned int i;
> @@ -1180,30 +1199,15 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>  	u64 qgroupid;
>  	u64 qgroupid1;
>  
> -	memset(&args, 0, sizeof(args));
> -
> -	sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
> -	sk->max_type = BTRFS_QGROUP_RELATION_KEY;
> -	sk->min_type = BTRFS_QGROUP_STATUS_KEY;
> -	sk->max_objectid = (u64)-1;
> -	sk->max_offset = (u64)-1;
> -	sk->max_transid = (u64)-1;
> -	sk->nr_items = 4096;
> -
>  	qgroup_lookup_init(qgroup_lookup);
>  
>  	while (1) {
> -		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
> +		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, args);
>  		if (ret < 0) {
> -			if (errno == ENOENT) {
> -				error("can't list qgroups: quotas not enabled");
> +			if (errno == ENOENT)
>  				ret = -ENOTTY;
> -			} else {
> -				error("can't list qgroups: %s",
> -				       strerror(errno));
> +			else
>  				ret = -errno;
> -			}
> -
>  			break;
>  		}
>  
> @@ -1217,37 +1221,46 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>  		 * read the root_ref item it contains
>  		 */
>  		for (i = 0; i < sk->nr_items; i++) {
> -			sh = (struct btrfs_ioctl_search_header *)(args.buf +
> +			struct btrfs_key key;
> +
> +			sh = (struct btrfs_ioctl_search_header *)(args->buf +
>  								  off);
>  			off += sizeof(*sh);
>  
> -			switch (btrfs_search_header_type(sh)) {
> +			key.objectid = btrfs_search_header_objectid(sh);
> +			key.type = btrfs_search_header_type(sh);
> +			key.offset = btrfs_search_header_offset(sh);
> +
> +			if (!key_in_range(&key, &filter_key))
> +				goto next;

It still looks like that most other qgroup info will get calculated.

> +
> +			switch (key.type) {
>  			case BTRFS_QGROUP_STATUS_KEY:
>  				si = (struct btrfs_qgroup_status_item *)
> -				     (args.buf + off);
> +				     (args->buf + off);
>  				flags = btrfs_stack_qgroup_status_flags(si);
>  
>  				print_status_flag_warning(flags);
>  				break;
>  			case BTRFS_QGROUP_INFO_KEY:
> -				qgroupid = btrfs_search_header_offset(sh);
> +				qgroupid = key.offset;
>  				info = (struct btrfs_qgroup_info_item *)
> -				       (args.buf + off);
> +				       (args->buf + off);
>  
>  				ret = update_qgroup_info(fd, qgroup_lookup,
>  							 qgroupid, info);
>  				break;
>  			case BTRFS_QGROUP_LIMIT_KEY:
> -				qgroupid = btrfs_search_header_offset(sh);
> +				qgroupid = key.offset;
>  				limit = (struct btrfs_qgroup_limit_item *)
> -					(args.buf + off);
> +					(args->buf + off);
>  
>  				ret = update_qgroup_limit(fd, qgroup_lookup,
>  							  qgroupid, limit);
>  				break;
>  			case BTRFS_QGROUP_RELATION_KEY:
> -				qgroupid = btrfs_search_header_offset(sh);
> -				qgroupid1 = btrfs_search_header_objectid(sh);
> +				qgroupid = key.offset;
> +				qgroupid1 = key.objectid;
>  
>  				if (qgroupid < qgroupid1)
>  					break;
> @@ -1262,15 +1275,16 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>  			if (ret)
>  				return ret;
>  
> +next:
>  			off += btrfs_search_header_len(sh);
>  
>  			/*
>  			 * record the mins in sk so we can make sure the
>  			 * next search doesn't repeat this root
>  			 */
> -			sk->min_type = btrfs_search_header_type(sh);
> -			sk->min_offset = btrfs_search_header_offset(sh);
> -			sk->min_objectid = btrfs_search_header_objectid(sh);
> +			sk->min_type = key.type;
> +			sk->min_offset = key.offset;
> +			sk->min_objectid = key.objectid;
>  		}
>  		sk->nr_items = 4096;
>  		/*
> @@ -1286,6 +1300,67 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>  	return ret;
>  }
>  
> +static int qgroups_search_all(int fd, struct qgroup_lookup *qgroup_lookup)
> +{
> +	struct btrfs_ioctl_search_args args = {
> +		.key = {
> +			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
> +			.max_type = BTRFS_QGROUP_RELATION_KEY,
> +			.min_type = BTRFS_QGROUP_STATUS_KEY,
> +			.max_objectid = (u64)-1,
> +			.max_offset = (u64)-1,
> +			.max_transid = (u64)-1,
> +			.nr_items = 4096,
> +		},
> +	};
> +	int ret;
> +
> +	ret = __qgroups_search(fd, &args, qgroup_lookup);
> +	if (ret == -ENOTTY)
> +		error("can't list qgroups: quotas not enabled");
> +	else if (ret < 0)
> +		error("can't list qgroups: %s", strerror(-ret));
> +	return ret;
> +}
> +
> +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats)
> +{
> +	struct btrfs_ioctl_search_args args = {
> +		.key = {
> +			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
> +			.min_type = BTRFS_QGROUP_INFO_KEY,
> +			.max_type = BTRFS_QGROUP_LIMIT_KEY,
> +			.max_objectid = 0,
> +			.min_offset = qgroupid,
> +			.max_offset = qgroupid,

Just keep in mind that such search could include unrelated qgroup info
into the result, key_in_range() check won't help much due to the limit
of how tree search works.

> +			.max_transid = (u64)-1,
> +			.nr_items = 4096,
> +		},
> +	};
> +	struct qgroup_lookup qgroup_lookup;
> +	struct btrfs_qgroup *qgroup;
> +	struct rb_node *n;
> +	int ret;
> +
> +	ret = __qgroups_search(fd, &args, &qgroup_lookup);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = -ENODATA;
> +	n = rb_first(&qgroup_lookup.root);

And in worst case, if we're query some qgroup which doesn't exist (maybe
manually deleted), then due to limit of tree search, @qgroup_lookup
could contain unrelated qgroup info.

In that case, rb_first() could gives us some unrelated qgroup.

For example, we have qgroups info for 5, 258, 259, 1/1, and we're trying
to query qgroup 257 (deleted manually), then we will get the following
result from tree search:

	item 3 key (0 QGROUP_INFO 0/258) itemoff 16131 itemsize 40
	item 4 key (0 QGROUP_INFO 0/259) itemoff 16131 itemsize 40
	item 5 key (0 QGROUP_INFO 1/1) itemoff 16091 itemsize 40
	item 6 key (0 QGROUP_LIMIT 0/5) itemoff 16051 itemsize 40

So in @qgroup_lookup, the first item will be qgroup 258 instead of the
non-exist qgroup 257.

Either caller needs extra check or we need to do extra check in this
function.

Thanks,
Qu

> +	if (n) {
> +		qgroup = rb_entry(n, struct btrfs_qgroup, rb_node);
> +		stats->qgroupid = qgroup->qgroupid;
> +		stats->info = qgroup->info;
> +		stats->limit = qgroup->limit;
> +
> +		ret = 0;
> +	}
> +
> +	__free_all_qgroups(&qgroup_lookup);
> +	return ret;
> +}
> +
>  static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup, bool verbose)
>  {
>  
> @@ -1312,7 +1387,7 @@ int btrfs_show_qgroups(int fd,
>  	struct qgroup_lookup sort_tree;
>  	int ret;
>  
> -	ret = __qgroups_search(fd, &qgroup_lookup);
> +	ret = qgroups_search_all(fd, &qgroup_lookup);
>  	if (ret)
>  		return ret;
>  	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
> diff --git a/qgroup.h b/qgroup.h
> index 5e71349c..688f92b2 100644
> --- a/qgroup.h
> +++ b/qgroup.h
> @@ -87,6 +87,12 @@ struct btrfs_qgroup_info {
>  	u64 exclusive_compressed;
>  };
>  
> +struct btrfs_qgroup_stats {
> +	u64 qgroupid;
> +	struct btrfs_qgroup_info info;
> +	struct btrfs_qgroup_limit limit;
> +};
> +
>  int btrfs_qgroup_parse_sort_string(const char *opt_arg,
>  				struct btrfs_qgroup_comparer_set **comps);
>  int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
> @@ -105,4 +111,5 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
>  int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
>  			    int type);
>  
> +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats);
>  #endif
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 520 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 10/20] btrfs-progs: help: convert ints used as bools to bool
  2018-03-08  2:40 ` [PATCH 10/20] btrfs-progs: help: convert ints used as bools to bool jeffm
@ 2018-03-08  5:55   ` Qu Wenruo
  0 siblings, 0 replies; 31+ messages in thread
From: Qu Wenruo @ 2018-03-08  5:55 UTC (permalink / raw)
  To: jeffm, linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 5743 bytes --]



On 2018年03月08日 10:40, jeffm@suse.com wrote:
> From: Jeff Mahoney <jeffm@suse.com>
> 
> We use an int for 'full', 'all', and 'err' when we really mean a boolean.
> 
> Signed-off-by: Jeff Mahoney <jeffm@suse.com>

Reviewed-by: Qu Wenruo <wqu@suse.com>

Thanks,
Qu

> ---
>  btrfs.c | 14 +++++++-------
>  help.c  | 25 +++++++++++++------------
>  help.h  |  4 ++--
>  3 files changed, 22 insertions(+), 21 deletions(-)
> 
> diff --git a/btrfs.c b/btrfs.c
> index 2d39f2ce..fec1a135 100644
> --- a/btrfs.c
> +++ b/btrfs.c
> @@ -109,7 +109,7 @@ static void handle_help_options_next_level(const struct cmd_struct *cmd,
>  			argv++;
>  			help_command_group(cmd->next, argc, argv);
>  		} else {
> -			usage_command(cmd, 1, 0);
> +			usage_command(cmd, true, false);
>  		}
>  
>  		exit(0);
> @@ -125,7 +125,7 @@ int handle_command_group(const struct cmd_group *grp, int argc,
>  	argc--;
>  	argv++;
>  	if (argc < 1) {
> -		usage_command_group(grp, 0, 0);
> +		usage_command_group(grp, false, false);
>  		exit(1);
>  	}
>  
> @@ -212,20 +212,20 @@ static int handle_global_options(int argc, char **argv)
>  
>  void handle_special_globals(int shift, int argc, char **argv)
>  {
> -	int has_help = 0;
> -	int has_full = 0;
> +	bool has_help = false;
> +	bool has_full = false;
>  	int i;
>  
>  	for (i = 0; i < shift; i++) {
>  		if (strcmp(argv[i], "--help") == 0)
> -			has_help = 1;
> +			has_help = true;
>  		else if (strcmp(argv[i], "--full") == 0)
> -			has_full = 1;
> +			has_full = true;
>  	}
>  
>  	if (has_help) {
>  		if (has_full)
> -			usage_command_group(&btrfs_cmd_group, 1, 0);
> +			usage_command_group(&btrfs_cmd_group, true, false);
>  		else
>  			cmd_help(argc, argv);
>  		exit(0);
> diff --git a/help.c b/help.c
> index 311a4320..ef7986b4 100644
> --- a/help.c
> +++ b/help.c
> @@ -196,8 +196,8 @@ static int do_usage_one_command(const char * const *usagestr,
>  }
>  
>  static int usage_command_internal(const char * const *usagestr,
> -				  const char *token, int full, int lst,
> -				  int alias, FILE *outf)
> +				  const char *token, bool full, bool lst,
> +				  bool alias, FILE *outf)
>  {
>  	unsigned int flags = 0;
>  	int ret;
> @@ -223,17 +223,17 @@ static int usage_command_internal(const char * const *usagestr,
>  }
>  
>  static void usage_command_usagestr(const char * const *usagestr,
> -				   const char *token, int full, int err)
> +				   const char *token, bool full, bool err)
>  {
>  	FILE *outf = err ? stderr : stdout;
>  	int ret;
>  
> -	ret = usage_command_internal(usagestr, token, full, 0, 0, outf);
> +	ret = usage_command_internal(usagestr, token, full, false, false, outf);
>  	if (!ret)
>  		fputc('\n', outf);
>  }
>  
> -void usage_command(const struct cmd_struct *cmd, int full, int err)
> +void usage_command(const struct cmd_struct *cmd, bool full, bool err)
>  {
>  	usage_command_usagestr(cmd->usagestr, cmd->token, full, err);
>  }
> @@ -241,11 +241,11 @@ void usage_command(const struct cmd_struct *cmd, int full, int err)
>  __attribute__((noreturn))
>  void usage(const char * const *usagestr)
>  {
> -	usage_command_usagestr(usagestr, NULL, 1, 1);
> +	usage_command_usagestr(usagestr, NULL, true, true);
>  	exit(1);
>  }
>  
> -static void usage_command_group_internal(const struct cmd_group *grp, int full,
> +static void usage_command_group_internal(const struct cmd_group *grp, bool full,
>  					 FILE *outf)
>  {
>  	const struct cmd_struct *cmd = grp->commands;
> @@ -265,7 +265,8 @@ static void usage_command_group_internal(const struct cmd_group *grp, int full,
>  			}
>  
>  			usage_command_internal(cmd->usagestr, cmd->token, full,
> -					       1, cmd->flags & CMD_ALIAS, outf);
> +					       true, cmd->flags & CMD_ALIAS,
> +					       outf);
>  			if (cmd->flags & CMD_ALIAS)
>  				putchar('\n');
>  			continue;
> @@ -327,7 +328,7 @@ void usage_command_group_short(const struct cmd_group *grp)
>  	fprintf(stderr, "All command groups have their manual page named 'btrfs-<group>'.\n");
>  }
>  
> -void usage_command_group(const struct cmd_group *grp, int full, int err)
> +void usage_command_group(const struct cmd_group *grp, bool full, bool err)
>  {
>  	const char * const *usagestr = grp->usagestr;
>  	FILE *outf = err ? stderr : stdout;
> @@ -350,7 +351,7 @@ __attribute__((noreturn))
>  void help_unknown_token(const char *arg, const struct cmd_group *grp)
>  {
>  	fprintf(stderr, "%s: unknown token '%s'\n", get_argv0_buf(), arg);
> -	usage_command_group(grp, 0, 1);
> +	usage_command_group(grp, false, true);
>  	exit(1);
>  }
>  
> @@ -372,13 +373,13 @@ void help_ambiguous_token(const char *arg, const struct cmd_group *grp)
>  
>  void help_command_group(const struct cmd_group *grp, int argc, char **argv)
>  {
> -	int full = 0;
> +	bool full = false;
>  
>  	if (argc > 1) {
>  		if (!strcmp(argv[1], "--full"))
>  			full = 1;
>  	}
>  
> -	usage_command_group(grp, full, 0);
> +	usage_command_group(grp, full, false);
>  }
>  
> diff --git a/help.h b/help.h
> index efeded30..a69ea6b2 100644
> --- a/help.h
> +++ b/help.h
> @@ -57,8 +57,8 @@ struct cmd_group;
>  
>  __attribute__((noreturn))
>  void usage(const char * const *usagestr);
> -void usage_command(const struct cmd_struct *cmd, int full, int err);
> -void usage_command_group(const struct cmd_group *grp, int all, int err);
> +void usage_command(const struct cmd_struct *cmd, bool full, bool err);
> +void usage_command_group(const struct cmd_group *grp, bool all, bool err);
>  void usage_command_group_short(const struct cmd_group *grp);
>  
>  __attribute__((noreturn))
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 520 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 06/20] btrfs-progs: qgroups: add pathname to show output
  2018-03-08  5:33   ` Qu Wenruo
@ 2018-03-08 14:25     ` Jeff Mahoney
  0 siblings, 0 replies; 31+ messages in thread
From: Jeff Mahoney @ 2018-03-08 14:25 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 1043 bytes --]

On 3/8/18 12:33 AM, Qu Wenruo wrote:
> 
> 
> On 2018年03月08日 10:40, jeffm@suse.com wrote:
>> From: Jeff Mahoney <jeffm@suse.com>
>>
>> The btrfs qgroup show command currently only exports qgroup IDs,
>> forcing the user to resolve which subvolume each corresponds to.
>>
>> This patch adds pathname resolution to qgroup show so that when
>> the -P option is used, the last column contains the pathname of
>> the root of the subvolume it describes.  In the case of nested
>> qgroups, it will show the number of member qgroups or the paths
>> of the members if the -v option is used.
>>
>> Pathname can also be used as a sort parameter.
>>
>> Signed-off-by: Jeff Mahoney <jeffm@suse.com>
> 
> Reviewed-by: Qu Wenruo <wqu@suse.com>
> 
> Except one nitpick inlined below.
> 
> [snip]
>>  	}
>> +	if (bq->pathname)
>> +		free((void *)bq->pathname);
> 
> What about just free(bq->pathname);?
> 
> Is this (void *) used to get around the const prefix?

Yes.

Thanks,

-Jeff

-- 
Jeff Mahoney
SUSE Labs


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 854 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query
  2018-03-08  5:54   ` Qu Wenruo
@ 2018-03-08 15:21     ` Jeff Mahoney
  2018-03-09  0:27       ` Qu Wenruo
  0 siblings, 1 reply; 31+ messages in thread
From: Jeff Mahoney @ 2018-03-08 15:21 UTC (permalink / raw)
  To: Qu Wenruo, linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 11125 bytes --]

On 3/8/18 12:54 AM, Qu Wenruo wrote:
> 
> 
> On 2018年03月08日 10:40, jeffm@suse.com wrote:
>> From: Jeff Mahoney <jeffm@suse.com>
>>
>> The only mechanism we have in the progs for searching qgroups is to load
>> all of them and filter the results.  This works for qgroup show but
>> to add quota information to 'btrfs subvoluem show' it's pretty wasteful.
>>
>> This patch splits out setting up the search and performing the search so
>> we can search for a single qgroupid more easily.  Since TREE_SEARCH
>> will give results that don't strictly match the search terms, we add
>> a filter to match only the results we care about.
>>
>> Signed-off-by: Jeff Mahoney <jeffm@suse.com>
>> ---
>>  qgroup.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++---------------
>>  qgroup.h |   7 ++++
>>  2 files changed, 116 insertions(+), 34 deletions(-)
>>
>> diff --git a/qgroup.c b/qgroup.c
>> index 57815718..d076b1de 100644
>> --- a/qgroup.c
>> +++ b/qgroup.c
>> @@ -1165,11 +1165,30 @@ static inline void print_status_flag_warning(u64 flags)
>>  		warning("qgroup data inconsistent, rescan recommended");
>>  }
>>  
>> -static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>> +static bool key_in_range(const struct btrfs_key *key,
>> +			 const struct btrfs_ioctl_search_key *sk)
>> +{
>> +	if (key->objectid < sk->min_objectid ||
>> +	    key->objectid > sk->max_objectid)
>> +		return false;
>> +
>> +	if (key->type < sk->min_type ||
>> +	    key->type > sk->max_type)
>> +		return false;
>> +
>> +	if (key->offset < sk->min_offset ||
>> +	    key->offset > sk->max_offset)
>> +		return false;
>> +
>> +	return true;
>> +}
> 
> Even with the key_in_range() check here, we are still following the tree
> search slice behavior:
> 
> tree search will still gives us all the items in key range from
> (min_objectid, min_type, min_offset) to
> (max_objectid, max_type, max_offset).
> 
> I don't see much different between the tree search ioctl and this one.

It's fundamentally different.

The one in the kernel has a silly interface.  It should be min_key and
max_key since the components aren't evaluated independently.  It
effectively treats min_key and max_key as 136-bit values and returns
everything between them, inclusive.  That's the slice behavior, as you
call it.

This key_in_range treats each component separately and acts as a filter
on the slice returned from the kernel.  If we request min/max_offset =
259, the caller will not get anything without offset = 259.

I suppose, ultimately, this could also be done using a filter on the
rbtree using the existing interface but that seems even more wasteful.

-Jeff

>> +
>> +static int __qgroups_search(int fd, struct btrfs_ioctl_search_args *args,
>> +			    struct qgroup_lookup *qgroup_lookup)
>>  {
>>  	int ret;
>> -	struct btrfs_ioctl_search_args args;
>> -	struct btrfs_ioctl_search_key *sk = &args.key;
>> +	struct btrfs_ioctl_search_key *sk = &args->key;
>> +	struct btrfs_ioctl_search_key filter_key = args->key;
>>  	struct btrfs_ioctl_search_header *sh;
>>  	unsigned long off = 0;
>>  	unsigned int i;
>> @@ -1180,30 +1199,15 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>  	u64 qgroupid;
>>  	u64 qgroupid1;
>>  
>> -	memset(&args, 0, sizeof(args));
>> -
>> -	sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
>> -	sk->max_type = BTRFS_QGROUP_RELATION_KEY;
>> -	sk->min_type = BTRFS_QGROUP_STATUS_KEY;
>> -	sk->max_objectid = (u64)-1;
>> -	sk->max_offset = (u64)-1;
>> -	sk->max_transid = (u64)-1;
>> -	sk->nr_items = 4096;
>> -
>>  	qgroup_lookup_init(qgroup_lookup);
>>  
>>  	while (1) {
>> -		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
>> +		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, args);
>>  		if (ret < 0) {
>> -			if (errno == ENOENT) {
>> -				error("can't list qgroups: quotas not enabled");
>> +			if (errno == ENOENT)
>>  				ret = -ENOTTY;
>> -			} else {
>> -				error("can't list qgroups: %s",
>> -				       strerror(errno));
>> +			else
>>  				ret = -errno;
>> -			}
>> -
>>  			break;
>>  		}
>>  
>> @@ -1217,37 +1221,46 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>  		 * read the root_ref item it contains
>>  		 */
>>  		for (i = 0; i < sk->nr_items; i++) {
>> -			sh = (struct btrfs_ioctl_search_header *)(args.buf +
>> +			struct btrfs_key key;
>> +
>> +			sh = (struct btrfs_ioctl_search_header *)(args->buf +
>>  								  off);
>>  			off += sizeof(*sh);
>>  
>> -			switch (btrfs_search_header_type(sh)) {
>> +			key.objectid = btrfs_search_header_objectid(sh);
>> +			key.type = btrfs_search_header_type(sh);
>> +			key.offset = btrfs_search_header_offset(sh);
>> +
>> +			if (!key_in_range(&key, &filter_key))
>> +				goto next;
> 
> It still looks like that most other qgroup info will get calculated.
> 
>> +
>> +			switch (key.type) {
>>  			case BTRFS_QGROUP_STATUS_KEY:
>>  				si = (struct btrfs_qgroup_status_item *)
>> -				     (args.buf + off);
>> +				     (args->buf + off);
>>  				flags = btrfs_stack_qgroup_status_flags(si);
>>  
>>  				print_status_flag_warning(flags);
>>  				break;
>>  			case BTRFS_QGROUP_INFO_KEY:
>> -				qgroupid = btrfs_search_header_offset(sh);
>> +				qgroupid = key.offset;
>>  				info = (struct btrfs_qgroup_info_item *)
>> -				       (args.buf + off);
>> +				       (args->buf + off);
>>  
>>  				ret = update_qgroup_info(fd, qgroup_lookup,
>>  							 qgroupid, info);
>>  				break;
>>  			case BTRFS_QGROUP_LIMIT_KEY:
>> -				qgroupid = btrfs_search_header_offset(sh);
>> +				qgroupid = key.offset;
>>  				limit = (struct btrfs_qgroup_limit_item *)
>> -					(args.buf + off);
>> +					(args->buf + off);
>>  
>>  				ret = update_qgroup_limit(fd, qgroup_lookup,
>>  							  qgroupid, limit);
>>  				break;
>>  			case BTRFS_QGROUP_RELATION_KEY:
>> -				qgroupid = btrfs_search_header_offset(sh);
>> -				qgroupid1 = btrfs_search_header_objectid(sh);
>> +				qgroupid = key.offset;
>> +				qgroupid1 = key.objectid;
>>  
>>  				if (qgroupid < qgroupid1)
>>  					break;
>> @@ -1262,15 +1275,16 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>  			if (ret)
>>  				return ret;
>>  
>> +next:
>>  			off += btrfs_search_header_len(sh);
>>  
>>  			/*
>>  			 * record the mins in sk so we can make sure the
>>  			 * next search doesn't repeat this root
>>  			 */
>> -			sk->min_type = btrfs_search_header_type(sh);
>> -			sk->min_offset = btrfs_search_header_offset(sh);
>> -			sk->min_objectid = btrfs_search_header_objectid(sh);
>> +			sk->min_type = key.type;
>> +			sk->min_offset = key.offset;
>> +			sk->min_objectid = key.objectid;
>>  		}
>>  		sk->nr_items = 4096;
>>  		/*
>> @@ -1286,6 +1300,67 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>  	return ret;
>>  }
>>  
>> +static int qgroups_search_all(int fd, struct qgroup_lookup *qgroup_lookup)
>> +{
>> +	struct btrfs_ioctl_search_args args = {
>> +		.key = {
>> +			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
>> +			.max_type = BTRFS_QGROUP_RELATION_KEY,
>> +			.min_type = BTRFS_QGROUP_STATUS_KEY,
>> +			.max_objectid = (u64)-1,
>> +			.max_offset = (u64)-1,
>> +			.max_transid = (u64)-1,
>> +			.nr_items = 4096,
>> +		},
>> +	};
>> +	int ret;
>> +
>> +	ret = __qgroups_search(fd, &args, qgroup_lookup);
>> +	if (ret == -ENOTTY)
>> +		error("can't list qgroups: quotas not enabled");
>> +	else if (ret < 0)
>> +		error("can't list qgroups: %s", strerror(-ret));
>> +	return ret;
>> +}
>> +
>> +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats)
>> +{
>> +	struct btrfs_ioctl_search_args args = {
>> +		.key = {
>> +			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
>> +			.min_type = BTRFS_QGROUP_INFO_KEY,
>> +			.max_type = BTRFS_QGROUP_LIMIT_KEY,
>> +			.max_objectid = 0,
>> +			.min_offset = qgroupid,
>> +			.max_offset = qgroupid,
> 
> Just keep in mind that such search could include unrelated qgroup info
> into the result, key_in_range() check won't help much due to the limit
> of how tree search works.
> 
>> +			.max_transid = (u64)-1,
>> +			.nr_items = 4096,
>> +		},
>> +	};
>> +	struct qgroup_lookup qgroup_lookup;
>> +	struct btrfs_qgroup *qgroup;
>> +	struct rb_node *n;
>> +	int ret;
>> +
>> +	ret = __qgroups_search(fd, &args, &qgroup_lookup);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	ret = -ENODATA;
>> +	n = rb_first(&qgroup_lookup.root);
> 
> And in worst case, if we're query some qgroup which doesn't exist (maybe
> manually deleted), then due to limit of tree search, @qgroup_lookup
> could contain unrelated qgroup info.
> 
> In that case, rb_first() could gives us some unrelated qgroup.
> 
> For example, we have qgroups info for 5, 258, 259, 1/1, and we're trying
> to query qgroup 257 (deleted manually), then we will get the following
> result from tree search:
> 
> 	item 3 key (0 QGROUP_INFO 0/258) itemoff 16131 itemsize 40
> 	item 4 key (0 QGROUP_INFO 0/259) itemoff 16131 itemsize 40
> 	item 5 key (0 QGROUP_INFO 1/1) itemoff 16091 itemsize 40
> 	item 6 key (0 QGROUP_LIMIT 0/5) itemoff 16051 itemsize 40
> 
> So in @qgroup_lookup, the first item will be qgroup 258 instead of the
> non-exist qgroup 257.
> 
> Either caller needs extra check or we need to do extra check in this
> function.
>
> Thanks,
> Qu
> 
>> +	if (n) {
>> +		qgroup = rb_entry(n, struct btrfs_qgroup, rb_node);
>> +		stats->qgroupid = qgroup->qgroupid;
>> +		stats->info = qgroup->info;
>> +		stats->limit = qgroup->limit;
>> +
>> +		ret = 0;
>> +	}
>> +
>> +	__free_all_qgroups(&qgroup_lookup);
>> +	return ret;
>> +}
>> +
>>  static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup, bool verbose)
>>  {
>>  
>> @@ -1312,7 +1387,7 @@ int btrfs_show_qgroups(int fd,
>>  	struct qgroup_lookup sort_tree;
>>  	int ret;
>>  
>> -	ret = __qgroups_search(fd, &qgroup_lookup);
>> +	ret = qgroups_search_all(fd, &qgroup_lookup);
>>  	if (ret)
>>  		return ret;
>>  	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
>> diff --git a/qgroup.h b/qgroup.h
>> index 5e71349c..688f92b2 100644
>> --- a/qgroup.h
>> +++ b/qgroup.h
>> @@ -87,6 +87,12 @@ struct btrfs_qgroup_info {
>>  	u64 exclusive_compressed;
>>  };
>>  
>> +struct btrfs_qgroup_stats {
>> +	u64 qgroupid;
>> +	struct btrfs_qgroup_info info;
>> +	struct btrfs_qgroup_limit limit;
>> +};
>> +
>>  int btrfs_qgroup_parse_sort_string(const char *opt_arg,
>>  				struct btrfs_qgroup_comparer_set **comps);
>>  int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
>> @@ -105,4 +111,5 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
>>  int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
>>  			    int type);
>>  
>> +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats);
>>  #endif
>>
> 


-- 
Jeff Mahoney
SUSE Labs


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 854 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query
  2018-03-08 15:21     ` Jeff Mahoney
@ 2018-03-09  0:27       ` Qu Wenruo
  0 siblings, 0 replies; 31+ messages in thread
From: Qu Wenruo @ 2018-03-09  0:27 UTC (permalink / raw)
  To: Jeff Mahoney, linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 11863 bytes --]



On 2018年03月08日 23:21, Jeff Mahoney wrote:
> On 3/8/18 12:54 AM, Qu Wenruo wrote:
>>
>>
>> On 2018年03月08日 10:40, jeffm@suse.com wrote:
>>> From: Jeff Mahoney <jeffm@suse.com>
>>>
>>> The only mechanism we have in the progs for searching qgroups is to load
>>> all of them and filter the results.  This works for qgroup show but
>>> to add quota information to 'btrfs subvoluem show' it's pretty wasteful.
>>>
>>> This patch splits out setting up the search and performing the search so
>>> we can search for a single qgroupid more easily.  Since TREE_SEARCH
>>> will give results that don't strictly match the search terms, we add
>>> a filter to match only the results we care about.
>>>
>>> Signed-off-by: Jeff Mahoney <jeffm@suse.com>
>>> ---
>>>  qgroup.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++---------------
>>>  qgroup.h |   7 ++++
>>>  2 files changed, 116 insertions(+), 34 deletions(-)
>>>
>>> diff --git a/qgroup.c b/qgroup.c
>>> index 57815718..d076b1de 100644
>>> --- a/qgroup.c
>>> +++ b/qgroup.c
>>> @@ -1165,11 +1165,30 @@ static inline void print_status_flag_warning(u64 flags)
>>>  		warning("qgroup data inconsistent, rescan recommended");
>>>  }
>>>  
>>> -static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>> +static bool key_in_range(const struct btrfs_key *key,
>>> +			 const struct btrfs_ioctl_search_key *sk)
>>> +{
>>> +	if (key->objectid < sk->min_objectid ||
>>> +	    key->objectid > sk->max_objectid)
>>> +		return false;
>>> +
>>> +	if (key->type < sk->min_type ||
>>> +	    key->type > sk->max_type)
>>> +		return false;
>>> +
>>> +	if (key->offset < sk->min_offset ||
>>> +	    key->offset > sk->max_offset)
>>> +		return false;
>>> +
>>> +	return true;
>>> +}
>>
>> Even with the key_in_range() check here, we are still following the tree
>> search slice behavior:
>>
>> tree search will still gives us all the items in key range from
>> (min_objectid, min_type, min_offset) to
>> (max_objectid, max_type, max_offset).
>>
>> I don't see much different between the tree search ioctl and this one.
> 
> It's fundamentally different.
> 
> The one in the kernel has a silly interface.  It should be min_key and
> max_key since the components aren't evaluated independently.  It
> effectively treats min_key and max_key as 136-bit values and returns
> everything between them, inclusive.  That's the slice behavior, as you
> call it.
> 
> This key_in_range treats each component separately and acts as a filter
> on the slice returned from the kernel.  If we request min/max_offset =
> 259, the caller will not get anything without offset = 259.

You're right.

I'm just an idiot, the separate check on objectid/type/offset breaks the
slice behavior so we won't found any thing unrelated.


So the current design is completely fine, even we try to query something
doesn't exist we won't have any noise.

Thanks for the filter and this really helps a lot.

Reviewed-by: Qu Wenruo <wqu@suse.com>

Thanks,
Qu

> 
> I suppose, ultimately, this could also be done using a filter on the
> rbtree using the existing interface but that seems even more wasteful.
> 
> -Jeff
> 
>>> +
>>> +static int __qgroups_search(int fd, struct btrfs_ioctl_search_args *args,
>>> +			    struct qgroup_lookup *qgroup_lookup)
>>>  {
>>>  	int ret;
>>> -	struct btrfs_ioctl_search_args args;
>>> -	struct btrfs_ioctl_search_key *sk = &args.key;
>>> +	struct btrfs_ioctl_search_key *sk = &args->key;
>>> +	struct btrfs_ioctl_search_key filter_key = args->key;
>>>  	struct btrfs_ioctl_search_header *sh;
>>>  	unsigned long off = 0;
>>>  	unsigned int i;
>>> @@ -1180,30 +1199,15 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>>  	u64 qgroupid;
>>>  	u64 qgroupid1;
>>>  
>>> -	memset(&args, 0, sizeof(args));
>>> -
>>> -	sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
>>> -	sk->max_type = BTRFS_QGROUP_RELATION_KEY;
>>> -	sk->min_type = BTRFS_QGROUP_STATUS_KEY;
>>> -	sk->max_objectid = (u64)-1;
>>> -	sk->max_offset = (u64)-1;
>>> -	sk->max_transid = (u64)-1;
>>> -	sk->nr_items = 4096;
>>> -
>>>  	qgroup_lookup_init(qgroup_lookup);
>>>  
>>>  	while (1) {
>>> -		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
>>> +		ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, args);
>>>  		if (ret < 0) {
>>> -			if (errno == ENOENT) {
>>> -				error("can't list qgroups: quotas not enabled");
>>> +			if (errno == ENOENT)
>>>  				ret = -ENOTTY;
>>> -			} else {
>>> -				error("can't list qgroups: %s",
>>> -				       strerror(errno));
>>> +			else
>>>  				ret = -errno;
>>> -			}
>>> -
>>>  			break;
>>>  		}
>>>  
>>> @@ -1217,37 +1221,46 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>>  		 * read the root_ref item it contains
>>>  		 */
>>>  		for (i = 0; i < sk->nr_items; i++) {
>>> -			sh = (struct btrfs_ioctl_search_header *)(args.buf +
>>> +			struct btrfs_key key;
>>> +
>>> +			sh = (struct btrfs_ioctl_search_header *)(args->buf +
>>>  								  off);
>>>  			off += sizeof(*sh);
>>>  
>>> -			switch (btrfs_search_header_type(sh)) {
>>> +			key.objectid = btrfs_search_header_objectid(sh);
>>> +			key.type = btrfs_search_header_type(sh);
>>> +			key.offset = btrfs_search_header_offset(sh);
>>> +
>>> +			if (!key_in_range(&key, &filter_key))
>>> +				goto next;
>>
>> It still looks like that most other qgroup info will get calculated.
>>
>>> +
>>> +			switch (key.type) {
>>>  			case BTRFS_QGROUP_STATUS_KEY:
>>>  				si = (struct btrfs_qgroup_status_item *)
>>> -				     (args.buf + off);
>>> +				     (args->buf + off);
>>>  				flags = btrfs_stack_qgroup_status_flags(si);
>>>  
>>>  				print_status_flag_warning(flags);
>>>  				break;
>>>  			case BTRFS_QGROUP_INFO_KEY:
>>> -				qgroupid = btrfs_search_header_offset(sh);
>>> +				qgroupid = key.offset;
>>>  				info = (struct btrfs_qgroup_info_item *)
>>> -				       (args.buf + off);
>>> +				       (args->buf + off);
>>>  
>>>  				ret = update_qgroup_info(fd, qgroup_lookup,
>>>  							 qgroupid, info);
>>>  				break;
>>>  			case BTRFS_QGROUP_LIMIT_KEY:
>>> -				qgroupid = btrfs_search_header_offset(sh);
>>> +				qgroupid = key.offset;
>>>  				limit = (struct btrfs_qgroup_limit_item *)
>>> -					(args.buf + off);
>>> +					(args->buf + off);
>>>  
>>>  				ret = update_qgroup_limit(fd, qgroup_lookup,
>>>  							  qgroupid, limit);
>>>  				break;
>>>  			case BTRFS_QGROUP_RELATION_KEY:
>>> -				qgroupid = btrfs_search_header_offset(sh);
>>> -				qgroupid1 = btrfs_search_header_objectid(sh);
>>> +				qgroupid = key.offset;
>>> +				qgroupid1 = key.objectid;
>>>  
>>>  				if (qgroupid < qgroupid1)
>>>  					break;
>>> @@ -1262,15 +1275,16 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>>  			if (ret)
>>>  				return ret;
>>>  
>>> +next:
>>>  			off += btrfs_search_header_len(sh);
>>>  
>>>  			/*
>>>  			 * record the mins in sk so we can make sure the
>>>  			 * next search doesn't repeat this root
>>>  			 */
>>> -			sk->min_type = btrfs_search_header_type(sh);
>>> -			sk->min_offset = btrfs_search_header_offset(sh);
>>> -			sk->min_objectid = btrfs_search_header_objectid(sh);
>>> +			sk->min_type = key.type;
>>> +			sk->min_offset = key.offset;
>>> +			sk->min_objectid = key.objectid;
>>>  		}
>>>  		sk->nr_items = 4096;
>>>  		/*
>>> @@ -1286,6 +1300,67 @@ static int __qgroups_search(int fd, struct qgroup_lookup *qgroup_lookup)
>>>  	return ret;
>>>  }
>>>  
>>> +static int qgroups_search_all(int fd, struct qgroup_lookup *qgroup_lookup)
>>> +{
>>> +	struct btrfs_ioctl_search_args args = {
>>> +		.key = {
>>> +			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
>>> +			.max_type = BTRFS_QGROUP_RELATION_KEY,
>>> +			.min_type = BTRFS_QGROUP_STATUS_KEY,
>>> +			.max_objectid = (u64)-1,
>>> +			.max_offset = (u64)-1,
>>> +			.max_transid = (u64)-1,
>>> +			.nr_items = 4096,
>>> +		},
>>> +	};
>>> +	int ret;
>>> +
>>> +	ret = __qgroups_search(fd, &args, qgroup_lookup);
>>> +	if (ret == -ENOTTY)
>>> +		error("can't list qgroups: quotas not enabled");
>>> +	else if (ret < 0)
>>> +		error("can't list qgroups: %s", strerror(-ret));
>>> +	return ret;
>>> +}
>>> +
>>> +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats)
>>> +{
>>> +	struct btrfs_ioctl_search_args args = {
>>> +		.key = {
>>> +			.tree_id = BTRFS_QUOTA_TREE_OBJECTID,
>>> +			.min_type = BTRFS_QGROUP_INFO_KEY,
>>> +			.max_type = BTRFS_QGROUP_LIMIT_KEY,
>>> +			.max_objectid = 0,
>>> +			.min_offset = qgroupid,
>>> +			.max_offset = qgroupid,
>>
>> Just keep in mind that such search could include unrelated qgroup info
>> into the result, key_in_range() check won't help much due to the limit
>> of how tree search works.
>>
>>> +			.max_transid = (u64)-1,
>>> +			.nr_items = 4096,
>>> +		},
>>> +	};
>>> +	struct qgroup_lookup qgroup_lookup;
>>> +	struct btrfs_qgroup *qgroup;
>>> +	struct rb_node *n;
>>> +	int ret;
>>> +
>>> +	ret = __qgroups_search(fd, &args, &qgroup_lookup);
>>> +	if (ret < 0)
>>> +		return ret;
>>> +
>>> +	ret = -ENODATA;
>>> +	n = rb_first(&qgroup_lookup.root);
>>
>> And in worst case, if we're query some qgroup which doesn't exist (maybe
>> manually deleted), then due to limit of tree search, @qgroup_lookup
>> could contain unrelated qgroup info.
>>
>> In that case, rb_first() could gives us some unrelated qgroup.
>>
>> For example, we have qgroups info for 5, 258, 259, 1/1, and we're trying
>> to query qgroup 257 (deleted manually), then we will get the following
>> result from tree search:
>>
>> 	item 3 key (0 QGROUP_INFO 0/258) itemoff 16131 itemsize 40
>> 	item 4 key (0 QGROUP_INFO 0/259) itemoff 16131 itemsize 40
>> 	item 5 key (0 QGROUP_INFO 1/1) itemoff 16091 itemsize 40
>> 	item 6 key (0 QGROUP_LIMIT 0/5) itemoff 16051 itemsize 40
>>
>> So in @qgroup_lookup, the first item will be qgroup 258 instead of the
>> non-exist qgroup 257.
>>
>> Either caller needs extra check or we need to do extra check in this
>> function.
>>
>> Thanks,
>> Qu
>>
>>> +	if (n) {
>>> +		qgroup = rb_entry(n, struct btrfs_qgroup, rb_node);
>>> +		stats->qgroupid = qgroup->qgroupid;
>>> +		stats->info = qgroup->info;
>>> +		stats->limit = qgroup->limit;
>>> +
>>> +		ret = 0;
>>> +	}
>>> +
>>> +	__free_all_qgroups(&qgroup_lookup);
>>> +	return ret;
>>> +}
>>> +
>>>  static void print_all_qgroups(struct qgroup_lookup *qgroup_lookup, bool verbose)
>>>  {
>>>  
>>> @@ -1312,7 +1387,7 @@ int btrfs_show_qgroups(int fd,
>>>  	struct qgroup_lookup sort_tree;
>>>  	int ret;
>>>  
>>> -	ret = __qgroups_search(fd, &qgroup_lookup);
>>> +	ret = qgroups_search_all(fd, &qgroup_lookup);
>>>  	if (ret)
>>>  		return ret;
>>>  	__filter_and_sort_qgroups(&qgroup_lookup, &sort_tree,
>>> diff --git a/qgroup.h b/qgroup.h
>>> index 5e71349c..688f92b2 100644
>>> --- a/qgroup.h
>>> +++ b/qgroup.h
>>> @@ -87,6 +87,12 @@ struct btrfs_qgroup_info {
>>>  	u64 exclusive_compressed;
>>>  };
>>>  
>>> +struct btrfs_qgroup_stats {
>>> +	u64 qgroupid;
>>> +	struct btrfs_qgroup_info info;
>>> +	struct btrfs_qgroup_limit limit;
>>> +};
>>> +
>>>  int btrfs_qgroup_parse_sort_string(const char *opt_arg,
>>>  				struct btrfs_qgroup_comparer_set **comps);
>>>  int btrfs_show_qgroups(int fd, struct btrfs_qgroup_filter_set *,
>>> @@ -105,4 +111,5 @@ int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
>>>  int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
>>>  			    int type);
>>>  
>>> +int btrfs_qgroup_query(int fd, u64 qgroupid, struct btrfs_qgroup_stats *stats);
>>>  #endif
>>>
>>
> 
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 520 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 13/20] btrfs-progs: use cmd_struct as command entry point
  2018-03-08  2:40 ` [PATCH 13/20] btrfs-progs: use cmd_struct as command entry point jeffm
@ 2018-03-12  3:11   ` Jeff Mahoney
  2018-03-12  3:24   ` Jeff Mahoney
  1 sibling, 0 replies; 31+ messages in thread
From: Jeff Mahoney @ 2018-03-12  3:11 UTC (permalink / raw)
  To: linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 2103 bytes --]

On 3/7/18 9:40 PM, jeffm@suse.com wrote:
> From: Jeff Mahoney <jeffm@suse.com>
> diff --git a/cmds-inspect.c b/cmds-inspect.c
> index afd7fe48..12f200b3 100644
> --- a/cmds-inspect.c
> +++ b/cmds-inspect.c
> @@ -625,33 +629,27 @@ static int cmd_inspect_min_dev_size(int argc, char **argv)
>  out:
>  	return !!ret;
>  }
> +static DEFINE_SIMPLE_COMMAND(inspect_min_dev_size, "min-dev-size");
>  
>  static const char inspect_cmd_group_info[] =
>  "query various internal information";
>  
> -const struct cmd_group inspect_cmd_group = {
> +static const struct cmd_group inspect_cmd_group = {
>  	inspect_cmd_group_usage, inspect_cmd_group_info, {
> -		{ "inode-resolve", cmd_inspect_inode_resolve,
> -			cmd_inspect_inode_resolve_usage, NULL, 0 },
> -		{ "logical-resolve", cmd_inspect_logical_resolve,
> -			cmd_inspect_logical_resolve_usage, NULL, 0 },
> -		{ "subvolid-resolve", cmd_inspect_subvolid_resolve,
> -			cmd_inspect_subvolid_resolve_usage, NULL, 0 },
> -		{ "rootid", cmd_inspect_rootid, cmd_inspect_rootid_usage, NULL,
> -			0 },
> -		{ "min-dev-size", cmd_inspect_min_dev_size,
> -			cmd_inspect_min_dev_size_usage, NULL, 0 },
> -		{ "dump-tree", cmd_inspect_dump_tree,
> -				cmd_inspect_dump_tree_usage, NULL, 0 },
> -		{ "dump-super", cmd_inspect_dump_super,
> -				cmd_inspect_dump_super_usage, NULL, 0 },
> -		{ "tree-stats", cmd_inspect_tree_stats,
> -				cmd_inspect_tree_stats_usage, NULL, 0 },
> -		NULL_CMD_STRUCT
> +		&cmd_struct_inspect_inode_resolve,
> +		&cmd_struct_inspect_logical_resolve,
> +		&cmd_struct_inspect_subvolid_resolve,
> +		&cmd_struct_inspect_rootid,
> +		&cmd_struct_inspect_min_dev_size,
> +		&cmd_struct_inspect_dump_tree,
> +		&cmd_struct_inspect_dump_super,
> +		&cmd_struct_inspect_tree_stats,
> +		NULL
>  	}
>  };
>  
> -int cmd_inspect(int argc, char **argv)
> +static int cmd_inspect(int argc, char **argv)
>  {
>  	return handle_command_group(&inspect_cmd_group, argc, argv);
>  }
> +DEFINE_GROUP_COMMAND(inspect, "inspect");

"inspect-internal"

-Jeff


-- 
Jeff Mahoney
SUSE Labs


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 13/20] btrfs-progs: use cmd_struct as command entry point
  2018-03-08  2:40 ` [PATCH 13/20] btrfs-progs: use cmd_struct as command entry point jeffm
  2018-03-12  3:11   ` Jeff Mahoney
@ 2018-03-12  3:24   ` Jeff Mahoney
  1 sibling, 0 replies; 31+ messages in thread
From: Jeff Mahoney @ 2018-03-12  3:24 UTC (permalink / raw)
  To: linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 385 bytes --]

On 3/7/18 9:40 PM, jeffm@suse.com wrote:
> diff --git a/cmds-filesystem.c b/cmds-filesystem.c
> index 62112705..ec038f2f 100644
> --- a/cmds-filesystem.c
> +++ b/cmds-filesystem.c
> @@ -1075,6 +1078,7 @@ next:
>  
>  	return !!defrag_global_errors;
>  }
> +static DEFINE_SIMPLE_COMMAND(filesystem_defrag, "defrag");

"defragment"

-Jeff

-- 
Jeff Mahoney
SUSE Labs


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 01/20] btrfs-progs: quota: Add -W option to rescan to wait without starting rescan
  2018-03-08  2:40 ` [PATCH 01/20] btrfs-progs: quota: Add -W option to rescan to wait without starting rescan jeffm
@ 2018-05-03  5:17   ` Qu Wenruo
  0 siblings, 0 replies; 31+ messages in thread
From: Qu Wenruo @ 2018-05-03  5:17 UTC (permalink / raw)
  To: jeffm, linux-btrfs


[-- Attachment #1.1: Type: text/plain, Size: 3787 bytes --]

Gentle ping.

This patch can be applied independently and is pretty critical to expose
rescan and transaction race.

It would be quite an important part for crafting test case.

Thanks,
Qu

On 2018年03月08日 10:40, jeffm@suse.com wrote:
> From: Jeff Mahoney <jeffm@suse.com>
> 
> This patch adds a new -W option to wait for a rescan without starting a
> new operation.  This is useful for things like xfstests where we want
> do to do a "btrfs quota enable" and not continue until the subsequent
> rescan has finished.
> 
> In addition to documenting the new option in the man page, I've cleaned
> up the rescan entry to document the -w option a bit better.
> 
> Reviewed-by: Qu Wenruo <wqu@suse.com>
> Signed-off-by: Jeff Mahoney <jeffm@suse.com>
> ---
>  Documentation/btrfs-quota.asciidoc | 10 +++++++---
>  cmds-quota.c                       | 20 ++++++++++++++------
>  2 files changed, 21 insertions(+), 9 deletions(-)
> 
> diff --git a/Documentation/btrfs-quota.asciidoc b/Documentation/btrfs-quota.asciidoc
> index 85ebf729..0b64a69b 100644
> --- a/Documentation/btrfs-quota.asciidoc
> +++ b/Documentation/btrfs-quota.asciidoc
> @@ -238,15 +238,19 @@ Disable subvolume quota support for a filesystem.
>  *enable* <path>::
>  Enable subvolume quota support for a filesystem.
>  
> -*rescan* [-s] <path>::
> +*rescan* [-s|-w|-W] <path>::
>  Trash all qgroup numbers and scan the metadata again with the current config.
>  +
>  `Options`
>  +
>  -s::::
> -show status of a running rescan operation.
> +Show status of a running rescan operation.
> +
>  -w::::
> -wait for rescan operation to finish(can be already in progress).
> +Start rescan operation and wait until it has finished before exiting.  If a rescan is already running, wait until it finishes and then exit without starting a new one.
> +
> +-W::::
> +Wait for rescan operation to finish and then exit.  If a rescan is not already running, exit silently.
>  
>  EXIT STATUS
>  -----------
> diff --git a/cmds-quota.c b/cmds-quota.c
> index 745889d1..7f933495 100644
> --- a/cmds-quota.c
> +++ b/cmds-quota.c
> @@ -120,14 +120,20 @@ static int cmd_quota_rescan(int argc, char **argv)
>  	int wait_for_completion = 0;
>  
>  	while (1) {
> -		int c = getopt(argc, argv, "sw");
> +		int c = getopt(argc, argv, "swW");
>  		if (c < 0)
>  			break;
>  		switch (c) {
>  		case 's':
>  			ioctlnum = BTRFS_IOC_QUOTA_RESCAN_STATUS;
>  			break;
> +		case 'W':
> +			ioctlnum = 0;
> +			wait_for_completion = 1;
> +			break;
>  		case 'w':
> +			/* Reset it in case the user did both -W and -w */
> +			ioctlnum = BTRFS_IOC_QUOTA_RESCAN;
>  			wait_for_completion = 1;
>  			break;
>  		default:
> @@ -135,8 +141,8 @@ static int cmd_quota_rescan(int argc, char **argv)
>  		}
>  	}
>  
> -	if (ioctlnum != BTRFS_IOC_QUOTA_RESCAN && wait_for_completion) {
> -		error("switch -w cannot be used with -s");
> +	if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN_STATUS && wait_for_completion) {
> +		error("switches -w/-W cannot be used with -s");
>  		return 1;
>  	}
>  
> @@ -150,8 +156,10 @@ static int cmd_quota_rescan(int argc, char **argv)
>  	if (fd < 0)
>  		return 1;
>  
> -	ret = ioctl(fd, ioctlnum, &args);
> -	e = errno;
> +	if (ioctlnum) {
> +		ret = ioctl(fd, ioctlnum, &args);
> +		e = errno;
> +	}
>  
>  	if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN_STATUS) {
>  		close_file_or_dir(fd, dirstream);
> @@ -167,7 +175,7 @@ static int cmd_quota_rescan(int argc, char **argv)
>  		return 0;
>  	}
>  
> -	if (ret == 0) {
> +	if (ioctlnum == BTRFS_IOC_QUOTA_RESCAN && ret == 0) {
>  		printf("quota rescan started\n");
>  		fflush(stdout);
>  	} else if (ret < 0 && (!wait_for_completion || e != EINPROGRESS)) {
> 


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2018-05-03  5:18 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-03-08  2:40 [PATCH v2 00/20] btrfs-progs: qgroups usability jeffm
2018-03-08  2:40 ` [PATCH 01/20] btrfs-progs: quota: Add -W option to rescan to wait without starting rescan jeffm
2018-05-03  5:17   ` Qu Wenruo
2018-03-08  2:40 ` [PATCH 02/20] btrfs-progs: qgroups: fix misleading index check jeffm
2018-03-08  2:40 ` [PATCH 03/20] btrfs-progs: constify pathnames passed as arguments jeffm
2018-03-08  2:40 ` [PATCH 04/20] btrfs-progs: btrfs-list: add rb_entry helpers for root_info jeffm
2018-03-08  2:40 ` [PATCH 05/20] btrfs-progs: btrfs-list: add btrfs_cleanup_root_info jeffm
2018-03-08  2:40 ` [PATCH 06/20] btrfs-progs: qgroups: add pathname to show output jeffm
2018-03-08  5:33   ` Qu Wenruo
2018-03-08 14:25     ` Jeff Mahoney
2018-03-08  2:40 ` [PATCH 07/20] btrfs-progs: qgroups: introduce and use info and limit structures jeffm
2018-03-08  5:34   ` Qu Wenruo
2018-03-08  2:40 ` [PATCH 08/20] btrfs-progs: qgroups: introduce btrfs_qgroup_query jeffm
2018-03-08  5:54   ` Qu Wenruo
2018-03-08 15:21     ` Jeff Mahoney
2018-03-09  0:27       ` Qu Wenruo
2018-03-08  2:40 ` [PATCH 09/20] btrfs-progs: subvolume: add quota info to btrfs sub show jeffm
2018-03-08  2:40 ` [PATCH 10/20] btrfs-progs: help: convert ints used as bools to bool jeffm
2018-03-08  5:55   ` Qu Wenruo
2018-03-08  2:40 ` [PATCH 11/20] btrfs-progs: reorder placement of help declarations for send/receive jeffm
2018-03-08  2:40 ` [PATCH 12/20] btrfs-progs: filesystem balance: split out special handling jeffm
2018-03-08  2:40 ` [PATCH 13/20] btrfs-progs: use cmd_struct as command entry point jeffm
2018-03-12  3:11   ` Jeff Mahoney
2018-03-12  3:24   ` Jeff Mahoney
2018-03-08  2:40 ` [PATCH 14/20] btrfs-progs: pass cmd_struct to command callback function jeffm
2018-03-08  2:40 ` [PATCH 15/20] btrfs-progs: pass cmd_struct to clean_args_no_options{,_relaxed} jeffm
2018-03-08  2:40 ` [PATCH 16/20] btrfs-progs: pass cmd_struct to usage() jeffm
2018-03-08  2:40 ` [PATCH 17/20] btrfs-progs: add support for output formats jeffm
2018-03-08  2:40 ` [PATCH 18/20] btrfs-progs: add generic support for json output jeffm
2018-03-08  2:40 ` [PATCH 19/20] btrfs-progs: qgroups: add json output for usage command jeffm
2018-03-08  2:40 ` [PATCH 20/20] btrfs-progs: handle command groups directly for common case jeffm

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.