summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArne Jansen <sensille@gmx.net>2012-08-07 12:37:54 +0200
committerJan Schmidt <list.btrfs@jan-o-sch.net>2012-09-04 11:15:49 +0200
commit89fe5b5f666c247aa3173745fb87c710f3a71a4a (patch)
tree7bf585132371b0982319624b2eec095005831df4
parentf99b1f1a631f3dfb8dc9ad03b9a00bdc2a231d31 (diff)
Btrfs progs: quota groups support
Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net> Signed-off-by: Arne Jansen <sensille@gmx.net>
-rw-r--r--Makefile5
-rw-r--r--btrfs.c2
-rw-r--r--cmds-qgroup.c454
-rw-r--r--cmds-quota.c117
-rw-r--r--cmds-subvolume.c83
-rw-r--r--commands.h7
-rw-r--r--ctree.h91
-rw-r--r--debug-tree.c6
-rw-r--r--ioctl.h64
-rw-r--r--print-tree.c90
-rw-r--r--qgroup.c140
-rw-r--r--qgroup.h31
12 files changed, 1075 insertions, 15 deletions
diff --git a/Makefile b/Makefile
index c0aaa3d7..48949039 100644
--- a/Makefile
+++ b/Makefile
@@ -5,9 +5,10 @@ objects = ctree.o disk-io.o radix-tree.o extent-tree.o print-tree.o \
root-tree.o dir-item.o file-item.o inode-item.o \
inode-map.o crc32c.o rbtree.o extent-cache.o extent_io.o \
volumes.o utils.o btrfs-list.o btrfslabel.o repair.o \
- send-stream.o send-utils.o
+ send-stream.o send-utils.o qgroup.o
cmds_objects = cmds-subvolume.o cmds-filesystem.o cmds-device.o cmds-scrub.o \
- cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o
+ cmds-inspect.o cmds-balance.o cmds-send.o cmds-receive.o \
+ cmds-quota.o cmds-qgroup.o
CHECKFLAGS= -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise \
-Wuninitialized -Wshadow -Wundef
diff --git a/btrfs.c b/btrfs.c
index e9d54f87..a229cee3 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -248,6 +248,8 @@ const struct cmd_group btrfs_cmd_group = {
{ "inspect-internal", cmd_inspect, NULL, &inspect_cmd_group, 0 },
{ "send", cmd_send, NULL, &send_cmd_group, 0 },
{ "receive", cmd_receive, NULL, &receive_cmd_group, 0 },
+ { "quota", cmd_quota, NULL, &quota_cmd_group, 0 },
+ { "qgroup", cmd_qgroup, NULL, &qgroup_cmd_group, 0 },
{ "help", cmd_help, cmd_help_usage, NULL, 0 },
{ "version", cmd_version, cmd_version_usage, NULL, 0 },
{ 0, 0, 0, 0, 0 }
diff --git a/cmds-qgroup.c b/cmds-qgroup.c
new file mode 100644
index 00000000..15534eae
--- /dev/null
+++ b/cmds-qgroup.c
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2012 STRATO. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "ctree.h"
+#include "ioctl.h"
+
+#include "commands.h"
+
+static const char * const qgroup_cmd_group_usage[] = {
+ "btrfs qgroup <command> [options] <path>",
+ NULL
+};
+
+static u64 parse_qgroupid(char *p)
+{
+ char *s = strchr(p, '/');
+ u64 level;
+ u64 id;
+
+ if (!s)
+ return atoll(p);
+ level = atoll(p);
+ id = atoll(s + 1);
+
+ return (level << 48) | id;
+}
+
+static int qgroup_assign(int assign, int argc, char **argv)
+{
+ int ret = 0;
+ int fd;
+ int e;
+ char *path = argv[3];
+ struct btrfs_ioctl_qgroup_assign_args args;
+
+ if (check_argc_exact(argc, 4))
+ return -1;
+
+ memset(&args, 0, sizeof(args));
+ args.assign = assign;
+ args.src = parse_qgroupid(argv[1]);
+ args.dst = parse_qgroupid(argv[2]);
+
+ /*
+ * FIXME src should accept subvol path
+ */
+ if (args.src >= args.dst) {
+ fprintf(stderr, "ERROR: bad relation requested '%s'\n", path);
+ return 12;
+ }
+ fd = open_file_or_dir(path);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ return 12;
+ }
+
+ ret = ioctl(fd, BTRFS_IOC_QGROUP_ASSIGN, &args);
+ e = errno;
+ close(fd);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: unable to assign quota group: %s\n",
+ strerror(e));
+ return 30;
+ }
+ return 0;
+}
+
+static int qgroup_create(int create, int argc, char **argv)
+{
+ int ret = 0;
+ int fd;
+ int e;
+ char *path = argv[2];
+ struct btrfs_ioctl_qgroup_create_args args;
+
+ if (check_argc_exact(argc, 3))
+ return -1;
+
+ memset(&args, 0, sizeof(args));
+ args.create = create;
+ args.qgroupid = parse_qgroupid(argv[1]);
+
+ fd = open_file_or_dir(path);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ return 12;
+ }
+
+ ret = ioctl(fd, BTRFS_IOC_QGROUP_CREATE, &args);
+ e = errno;
+ close(fd);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: unable to create quota group: %s\n",
+ strerror(e));
+ return 30;
+ }
+ return 0;
+}
+
+void print_qgroup_info(u64 objectid, struct btrfs_qgroup_info_item *info)
+{
+ printf("%llu/%llu %lld %lld\n", objectid >> 48,
+ objectid & ((1ll << 48) - 1),
+ btrfs_stack_qgroup_info_referenced(info),
+ btrfs_stack_qgroup_info_exclusive(info));
+}
+
+int list_qgroups(int fd)
+{
+ int ret;
+ struct btrfs_ioctl_search_args args;
+ struct btrfs_ioctl_search_key *sk = &args.key;
+ struct btrfs_ioctl_search_header *sh;
+ unsigned long off = 0;
+ unsigned int i;
+ int e;
+ struct btrfs_qgroup_info_item *info;
+
+ memset(&args, 0, sizeof(args));
+
+ /* search in the quota tree */
+ sk->tree_id = BTRFS_QUOTA_TREE_OBJECTID;
+
+ /*
+ * set the min and max to backref keys. The search will
+ * only send back this type of key now.
+ */
+ sk->max_type = BTRFS_QGROUP_INFO_KEY;
+ sk->min_type = BTRFS_QGROUP_INFO_KEY;
+ sk->max_objectid = 0;
+ sk->max_offset = (u64)-1;
+ sk->max_transid = (u64)-1;
+
+ /* just a big number, doesn't matter much */
+ sk->nr_items = 4096;
+
+ while (1) {
+ ret = ioctl(fd, BTRFS_IOC_TREE_SEARCH, &args);
+ e = errno;
+ if (ret < 0) {
+ fprintf(stderr,
+ "ERROR: can't perform the search - %s\n",
+ strerror(e));
+ return ret;
+ }
+ /* the ioctl returns the number of item it found in nr_items */
+ if (sk->nr_items == 0)
+ break;
+
+ off = 0;
+
+ /*
+ * for each item, pull the key out of the header and then
+ * read the root_ref item it contains
+ */
+ for (i = 0; i < sk->nr_items; i++) {
+ sh = (struct btrfs_ioctl_search_header *)(args.buf +
+ off);
+ off += sizeof(*sh);
+
+ if (sh->objectid != 0)
+ goto done;
+
+ if (sh->type != BTRFS_QGROUP_INFO_KEY)
+ goto done;
+
+ info = (struct btrfs_qgroup_info_item *)
+ (args.buf + off);
+ print_qgroup_info(sh->offset, info);
+
+ off += sh->len;
+
+ /*
+ * record the mins in sk so we can make sure the
+ * next search doesn't repeat this root
+ */
+ sk->min_offset = sh->offset;
+ }
+ sk->nr_items = 4096;
+ /*
+ * this iteration is done, step forward one qgroup for the next
+ * ioctl
+ */
+ if (sk->min_offset < (u64)-1)
+ sk->min_offset++;
+ else
+ break;
+ }
+
+done:
+ return ret;
+}
+
+static int parse_limit(const char *p, unsigned long long *s)
+{
+ char *endptr;
+ unsigned long long size;
+
+ if (strcasecmp(p, "none") == 0) {
+ *s = 0;
+ return 1;
+ }
+ size = strtoull(p, &endptr, 10);
+ switch (*endptr) {
+ case 'T':
+ case 't':
+ size *= 1024;
+ case 'G':
+ case 'g':
+ size *= 1024;
+ case 'M':
+ case 'm':
+ size *= 1024;
+ case 'K':
+ case 'k':
+ size *= 1024;
+ ++endptr;
+ break;
+ case 0:
+ break;
+ default:
+ return 0;
+ }
+
+ if (*endptr)
+ return 0;
+
+ *s = size;
+
+ return 1;
+}
+
+static const char * const cmd_qgroup_assign_usage[] = {
+ "btrfs qgroup assign <src> <dst> <path>",
+ "Enable subvolume qgroup support for a filesystem.",
+ NULL
+};
+
+static int cmd_qgroup_assign(int argc, char **argv)
+{
+ int ret = qgroup_assign(1, argc, argv);
+ if (ret < 0)
+ usage(cmd_qgroup_assign_usage);
+ return ret;
+}
+
+static const char * const cmd_qgroup_remove_usage[] = {
+ "btrfs qgroup remove <src> <dst> <path>",
+ "Remove a subvol from a quota group.",
+ NULL
+};
+
+static int cmd_qgroup_remove(int argc, char **argv)
+{
+ int ret = qgroup_assign(0, argc, argv);
+ if (ret < 0)
+ usage(cmd_qgroup_remove_usage);
+ return ret;
+}
+
+static const char * const cmd_qgroup_create_usage[] = {
+ "btrfs qgroup create <qgroupid> <path>",
+ "Create a subvolume quota group.",
+ NULL
+};
+
+static int cmd_qgroup_create(int argc, char **argv)
+{
+ int ret = qgroup_create(1, argc, argv);
+ if (ret < 0)
+ usage(cmd_qgroup_create_usage);
+ return ret;
+}
+
+static const char * const cmd_qgroup_destroy_usage[] = {
+ "btrfs qgroup destroy <qgroupid> <path>",
+ "Destroy a subvolume quota group.",
+ NULL
+};
+
+static int cmd_qgroup_destroy(int argc, char **argv)
+{
+ int ret = qgroup_create(0, argc, argv);
+ if (ret < 0)
+ usage(cmd_qgroup_destroy_usage);
+ return ret;
+}
+
+static const char * const cmd_qgroup_show_usage[] = {
+ "btrfs qgroup show <path>",
+ "Show all subvolume quota groups.",
+ NULL
+};
+
+static int cmd_qgroup_show(int argc, char **argv)
+{
+ int ret = 0;
+ int fd;
+ char *path = argv[1];
+
+ if (check_argc_exact(argc, 2))
+ usage(cmd_qgroup_show_usage);
+
+ fd = open_file_or_dir(path);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ return 12;
+ }
+
+ ret = list_qgroups(fd);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: can't list qgroups\n");
+ return 30;
+ }
+
+ close(fd);
+
+ return ret;
+}
+
+static const char * const cmd_qgroup_limit_usage[] = {
+ "btrfs qgroup limit [options] <size>|none [<qgroupid>] <path>",
+ "Limit the size of a subvolume quota group.",
+ "",
+ "-c limit amount of data after compression",
+ "-e limit space exclusively assigned to this qgroup",
+ NULL
+};
+
+static int cmd_qgroup_limit(int argc, char **argv)
+{
+ int ret = 0;
+ int fd;
+ int e;
+ char *path;
+ struct btrfs_ioctl_qgroup_limit_args args;
+ unsigned long long size;
+ int compressed = 0;
+ int exclusive = 0;
+
+ optind = 1;
+ while (1) {
+ int c = getopt(argc, argv, "ce");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'c':
+ compressed = 1;
+ break;
+ case 'e':
+ exclusive = 1;
+ break;
+ default:
+ usage(cmd_qgroup_limit_usage);
+ }
+ }
+
+ if (!parse_limit(argv[optind], &size)) {
+ fprintf(stderr, "Invalid size argument given\n");
+ return 1;
+ }
+
+ memset(&args, 0, sizeof(args));
+ args.qgroupid = parse_qgroupid(argv[optind + 1]);
+ if (size) {
+ if (compressed)
+ args.lim.flags |= BTRFS_QGROUP_LIMIT_RFER_CMPR |
+ BTRFS_QGROUP_LIMIT_EXCL_CMPR;
+ if (exclusive) {
+ args.lim.flags |= BTRFS_QGROUP_LIMIT_MAX_EXCL;
+ args.lim.max_exclusive = size;
+ } else {
+ args.lim.flags |= BTRFS_QGROUP_LIMIT_MAX_RFER;
+ args.lim.max_referenced = size;
+ }
+ }
+
+ if (args.qgroupid == 0) {
+ if (check_argc_exact(argc - optind, 2))
+ usage(cmd_qgroup_limit_usage);
+ path = argv[optind + 1];
+ ret = test_issubvolume(path);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: error accessing '%s'\n", path);
+ return 12;
+ }
+ if (!ret) {
+ fprintf(stderr, "ERROR: '%s' is not a subvolume\n",
+ path);
+ return 13;
+ }
+ /*
+ * keep qgroupid at 0, this indicates that the subvolume the
+ * fd refers to is to be limited
+ */
+ } else {
+ if (check_argc_exact(argc - optind, 3))
+ usage(cmd_qgroup_limit_usage);
+ path = argv[optind + 2];
+ }
+
+ fd = open_file_or_dir(path);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ return 12;
+ }
+
+ ret = ioctl(fd, BTRFS_IOC_QGROUP_LIMIT, &args);
+ e = errno;
+ close(fd);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: unable to limit requested quota group: "
+ "%s\n", strerror(e));
+ return 30;
+ }
+ return 0;
+}
+
+const struct cmd_group qgroup_cmd_group = {
+ qgroup_cmd_group_usage, NULL, {
+ { "assign", cmd_qgroup_assign, cmd_qgroup_assign_usage, 0, 0 },
+ { "remove", cmd_qgroup_remove, cmd_qgroup_remove_usage, 0, 0 },
+ { "create", cmd_qgroup_create, cmd_qgroup_create_usage, 0, 0 },
+ { "destroy", cmd_qgroup_destroy,
+ cmd_qgroup_destroy_usage, 0, 0 },
+ { "show", cmd_qgroup_show, cmd_qgroup_show_usage, 0, 0 },
+ { "limit", cmd_qgroup_limit, cmd_qgroup_limit_usage, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+ }
+};
+
+int cmd_qgroup(int argc, char **argv)
+{
+ return handle_command_group(&qgroup_cmd_group, argc, argv);
+}
diff --git a/cmds-quota.c b/cmds-quota.c
new file mode 100644
index 00000000..cf9ad976
--- /dev/null
+++ b/cmds-quota.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2012 STRATO. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "ctree.h"
+#include "ioctl.h"
+
+#include "commands.h"
+
+static const char * const quota_cmd_group_usage[] = {
+ "btrfs quota <command> [options] <path>",
+ NULL
+};
+
+int quota_ctl(int cmd, int argc, char **argv)
+{
+ int ret = 0;
+ int fd;
+ int e;
+ char *path = argv[1];
+ struct btrfs_ioctl_quota_ctl_args args;
+
+ if (check_argc_exact(argc, 2))
+ return -1;
+
+ memset(&args, 0, sizeof(args));
+ args.cmd = cmd;
+
+ fd = open_file_or_dir(path);
+ if (fd < 0) {
+ fprintf(stderr, "ERROR: can't access '%s'\n", path);
+ return 12;
+ }
+
+ ret = ioctl(fd, BTRFS_IOC_QUOTA_CTL, &args);
+ e = errno;
+ close(fd);
+ if (ret < 0) {
+ fprintf(stderr, "ERROR: quota command failed: %s\n",
+ strerror(e));
+ return 30;
+ }
+ return 0;
+}
+
+static const char * const cmd_quota_enable_usage[] = {
+ "btrfs quota enable <path>",
+ "Enable subvolume quota support for a filesystem.",
+ NULL
+};
+
+static int cmd_quota_enable(int argc, char **argv)
+{
+ int ret = quota_ctl(BTRFS_QUOTA_CTL_ENABLE, argc, argv);
+ if (ret < 0)
+ usage(cmd_quota_enable_usage);
+ return ret;
+}
+
+static const char * const cmd_quota_disable_usage[] = {
+ "btrfs quota disable <path>",
+ "Disable subvolume quota support for a filesystem.",
+ NULL
+};
+
+static int cmd_quota_disable(int argc, char **argv)
+{
+ int ret = quota_ctl(BTRFS_QUOTA_CTL_DISABLE, argc, argv);
+ if (ret < 0)
+ usage(cmd_quota_disable_usage);
+ return ret;
+}
+
+static const char * const cmd_quota_rescan_usage[] = {
+ "btrfs quota rescan <path>",
+ "Rescan the subvolume for a changed quota setting.",
+ NULL
+};
+
+static int cmd_quota_rescan(int argc, char **argv)
+{
+ int ret = quota_ctl(BTRFS_QUOTA_CTL_RESCAN, argc, argv);
+ if (ret < 0)
+ usage(cmd_quota_rescan_usage);
+ return ret;
+}
+
+const struct cmd_group quota_cmd_group = {
+ quota_cmd_group_usage, NULL, {
+ { "enable", cmd_quota_enable, cmd_quota_enable_usage, NULL, 0 },
+ { "disable", cmd_quota_disable, cmd_quota_disable_usage, 0, 0 },
+ { "rescan", cmd_quota_rescan, cmd_quota_rescan_usage, NULL, 0 },
+ { 0, 0, 0, 0, 0 }
+ }
+};
+
+int cmd_quota(int argc, char **argv)
+{
+ return handle_command_group(&quota_cmd_group, argc, argv);
+}
diff --git a/cmds-subvolume.c b/cmds-subvolume.c
index 3508ce61..f4aa80ff 100644
--- a/cmds-subvolume.c
+++ b/cmds-subvolume.c
@@ -26,6 +26,7 @@
#include "kerncompat.h"
#include "ioctl.h"
+#include "qgroup.h"
#include "commands.h"
@@ -70,13 +71,35 @@ static int cmd_subvol_create(int argc, char **argv)
int res, fddst, len, e;
char *newname;
char *dstdir;
- struct btrfs_ioctl_vol_args args;
char *dst;
+ struct btrfs_qgroup_inherit *inherit = NULL;
- if (check_argc_exact(argc, 2))
+ optind = 1;
+ while (1) {
+ int c = getopt(argc, argv, "c:i:r");
+ if (c < 0)
+ break;
+
+ switch (c) {
+ case 'c':
+ res = qgroup_inherit_add_copy(&inherit, optarg, 0);
+ if (res)
+ return res;
+ break;
+ case 'i':
+ res = qgroup_inherit_add_group(&inherit, optarg);
+ if (res)
+ return res;
+ break;
+ default:
+ usage(cmd_subvol_create_usage);
+ }
+ }
+
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_subvol_create_usage);
- dst = argv[1];
+ dst = argv[optind];
res = test_isdir(dst);
if(res >= 0 ){
@@ -110,9 +133,27 @@ static int cmd_subvol_create(int argc, char **argv)
}
printf("Create subvolume '%s/%s'\n", dstdir, newname);
- strncpy(args.name, newname, BTRFS_PATH_NAME_MAX);
- args.name[BTRFS_PATH_NAME_MAX-1] = 0;
- res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
+ if (inherit) {
+ struct btrfs_ioctl_vol_args_v2 args;
+
+ memset(&args, 0, sizeof(args));
+ strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
+ args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
+ args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+ args.size = qgroup_inherit_size(inherit);
+ args.qgroup_inherit = inherit;
+
+ res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE_V2, &args);
+ } else {
+ struct btrfs_ioctl_vol_args args;
+
+ memset(&args, 0, sizeof(args));
+ strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
+ args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
+
+ res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
+ }
+
e = errno;
close(fddst);
@@ -122,6 +163,7 @@ static int cmd_subvol_create(int argc, char **argv)
strerror(e));
return 11;
}
+ free(inherit);
return 0;
}
@@ -133,7 +175,7 @@ static int cmd_subvol_create(int argc, char **argv)
* 1-> path exists and it is a subvolume
* -1 -> path is unaccessible
*/
-static int test_issubvolume(char *path)
+int test_issubvolume(char *path)
{
struct stat st;
int res;
@@ -291,19 +333,34 @@ static int cmd_snapshot(int argc, char **argv)
char *newname;
char *dstdir;
struct btrfs_ioctl_vol_args_v2 args;
-
- memset(&args, 0, sizeof(args));
+ struct btrfs_qgroup_inherit *inherit = NULL;
optind = 1;
+ memset(&args, 0, sizeof(args));
while (1) {
- int c = getopt(argc, argv, "r");
+ int c = getopt(argc, argv, "c:i:r");
if (c < 0)
break;
switch (c) {
+ case 'c':
+ res = qgroup_inherit_add_copy(&inherit, optarg, 0);
+ if (res)
+ return res;
+ break;
+ case 'i':
+ res = qgroup_inherit_add_group(&inherit, optarg);
+ if (res)
+ return res;
+ break;
case 'r':
readonly = 1;
break;
+ case 'x':
+ res = qgroup_inherit_add_copy(&inherit, optarg, 1);
+ if (res)
+ return res;
+ break;
default:
usage(cmd_snapshot_usage);
}
@@ -379,6 +436,11 @@ static int cmd_snapshot(int argc, char **argv)
}
args.fd = fd;
+ if (inherit) {
+ args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+ args.size = qgroup_inherit_size(inherit);
+ args.qgroup_inherit = inherit;
+ }
strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
args.name[BTRFS_SUBVOL_NAME_MAX-1] = 0;
res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
@@ -392,6 +454,7 @@ static int cmd_snapshot(int argc, char **argv)
subvol, strerror(e));
return 11;
}
+ free(inherit);
return 0;
}
diff --git a/commands.h b/commands.h
index 1ece87ab..bb6d2dd7 100644
--- a/commands.h
+++ b/commands.h
@@ -90,6 +90,8 @@ extern const struct cmd_group scrub_cmd_group;
extern const struct cmd_group inspect_cmd_group;
extern const struct cmd_group send_cmd_group;
extern const struct cmd_group receive_cmd_group;
+extern const struct cmd_group quota_cmd_group;
+extern const struct cmd_group qgroup_cmd_group;
int cmd_subvolume(int argc, char **argv);
int cmd_filesystem(int argc, char **argv);
@@ -99,3 +101,8 @@ int cmd_scrub(int argc, char **argv);
int cmd_inspect(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);
+
+/* subvolume exported functions */
+int test_issubvolume(char *path);
diff --git a/ctree.h b/ctree.h
index d218b880..7f552291 100644
--- a/ctree.h
+++ b/ctree.h
@@ -59,6 +59,7 @@ struct btrfs_trans_handle;
#define BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
/* holds checksums of all the data extents */
#define BTRFS_CSUM_TREE_OBJECTID 7ULL
+#define BTRFS_QUOTA_TREE_OBJECTID 8ULL
/* for storing balance parameters in the root tree */
@@ -760,12 +761,49 @@ struct btrfs_csum_item {
/* used in struct btrfs_balance_args fields */
#define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1ULL << 48)
+#define BTRFS_QGROUP_STATUS_OFF 0
+#define BTRFS_QGROUP_STATUS_ON 1
+#define BTRFS_QGROUP_STATUS_SCANNING 2
+
+#define BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT (1 << 0)
+
+struct btrfs_qgroup_status_item {
+ __le64 version;
+ __le64 generation;
+ __le64 flags;
+ __le64 scan; /* progress during scanning */
+} __attribute__ ((__packed__));
+
struct btrfs_block_group_item {
__le64 used;
__le64 chunk_objectid;
__le64 flags;
} __attribute__ ((__packed__));
+struct btrfs_qgroup_info_item {
+ __le64 generation;
+ __le64 referenced;
+ __le64 referenced_compressed;
+ __le64 exclusive;
+ __le64 exclusive_compressed;
+} __attribute__ ((__packed__));
+
+/* flags definition for qgroup limits */
+#define BTRFS_QGROUP_LIMIT_MAX_RFER (1ULL << 0)
+#define BTRFS_QGROUP_LIMIT_MAX_EXCL (1ULL << 1)
+#define BTRFS_QGROUP_LIMIT_RSV_RFER (1ULL << 2)
+#define BTRFS_QGROUP_LIMIT_RSV_EXCL (1ULL << 3)
+#define BTRFS_QGROUP_LIMIT_RFER_CMPR (1ULL << 4)
+#define BTRFS_QGROUP_LIMIT_EXCL_CMPR (1ULL << 5)
+
+struct btrfs_qgroup_limit_item {
+ __le64 flags;
+ __le64 max_referenced;
+ __le64 max_exclusive;
+ __le64 rsv_referenced;
+ __le64 rsv_exclusive;
+} __attribute__ ((__packed__));
+
struct btrfs_space_info {
u64 flags;
u64 total_bytes;
@@ -976,6 +1014,14 @@ struct btrfs_root {
#define BTRFS_BALANCE_ITEM_KEY 248
/*
+ * quota groups
+ */
+#define BTRFS_QGROUP_STATUS_KEY 240
+#define BTRFS_QGROUP_INFO_KEY 242
+#define BTRFS_QGROUP_LIMIT_KEY 244
+#define BTRFS_QGROUP_RELATION_KEY 246
+
+/*
* string items are for debugging. They just store a short string of
* data in the FS
*/
@@ -1835,6 +1881,51 @@ BTRFS_SETGET_FUNCS(file_extent_encryption, struct btrfs_file_extent_item,
BTRFS_SETGET_FUNCS(file_extent_other_encoding, struct btrfs_file_extent_item,
other_encoding, 16);
+/* btrfs_qgroup_status_item */
+BTRFS_SETGET_FUNCS(qgroup_status_version, struct btrfs_qgroup_status_item,
+ version, 64);
+BTRFS_SETGET_FUNCS(qgroup_status_generation, struct btrfs_qgroup_status_item,
+ generation, 64);
+BTRFS_SETGET_FUNCS(qgroup_status_flags, struct btrfs_qgroup_status_item,
+ flags, 64);
+BTRFS_SETGET_FUNCS(qgroup_status_scan, struct btrfs_qgroup_status_item,
+ scan, 64);
+
+/* btrfs_qgroup_info_item */
+BTRFS_SETGET_FUNCS(qgroup_info_generation, struct btrfs_qgroup_info_item,
+ generation, 64);
+BTRFS_SETGET_FUNCS(qgroup_info_referenced, struct btrfs_qgroup_info_item,
+ referenced, 64);
+BTRFS_SETGET_FUNCS(qgroup_info_referenced_compressed,
+ struct btrfs_qgroup_info_item, referenced_compressed, 64);
+BTRFS_SETGET_FUNCS(qgroup_info_exclusive, struct btrfs_qgroup_info_item,
+ exclusive, 64);
+BTRFS_SETGET_FUNCS(qgroup_info_exclusive_compressed,
+ struct btrfs_qgroup_info_item, exclusive_compressed, 64);
+
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_generation,
+ struct btrfs_qgroup_info_item, generation, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_referenced,
+ struct btrfs_qgroup_info_item, referenced, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_referenced_compressed,
+ struct btrfs_qgroup_info_item, referenced_compressed, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_exclusive,
+ struct btrfs_qgroup_info_item, exclusive, 64);
+BTRFS_SETGET_STACK_FUNCS(stack_qgroup_info_exclusive_compressed,
+ struct btrfs_qgroup_info_item, exclusive_compressed, 64);
+
+/* btrfs_qgroup_limit_item */
+BTRFS_SETGET_FUNCS(qgroup_limit_flags, struct btrfs_qgroup_limit_item,
+ flags, 64);
+BTRFS_SETGET_FUNCS(qgroup_limit_max_referenced, struct btrfs_qgroup_limit_item,
+ max_referenced, 64);
+BTRFS_SETGET_FUNCS(qgroup_limit_max_exclusive, struct btrfs_qgroup_limit_item,
+ max_exclusive, 64);
+BTRFS_SETGET_FUNCS(qgroup_limit_rsv_referenced, struct btrfs_qgroup_limit_item,
+ rsv_referenced, 64);
+BTRFS_SETGET_FUNCS(qgroup_limit_rsv_exclusive, struct btrfs_qgroup_limit_item,
+ rsv_exclusive, 64);
+
/* this returns the number of file bytes represented by the inline item.
* If an item is compressed, this is the uncompressed size
*/
diff --git a/debug-tree.c b/debug-tree.c
index c497892e..94ffd8ee 100644
--- a/debug-tree.c
+++ b/debug-tree.c
@@ -312,6 +312,12 @@ again:
if (!skip) {
printf("extent checksum");
}
+ break;
+ case BTRFS_QUOTA_TREE_OBJECTID:
+ if (!skip) {
+ printf("quota");
+ }
+ break;
case BTRFS_MULTIPLE_OBJECTIDS:
if (!skip) {
printf("multiple");
diff --git a/ioctl.h b/ioctl.h
index d6f3d07a..6fda3a1d 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -32,14 +32,47 @@ struct btrfs_ioctl_vol_args {
char name[BTRFS_PATH_NAME_MAX + 1];
};
+#define BTRFS_SUBVOL_CREATE_ASYNC (1ULL << 0)
#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
+#define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2)
+
+#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0)
+
+struct btrfs_qgroup_limit {
+ __u64 flags;
+ __u64 max_referenced;
+ __u64 max_exclusive;
+ __u64 rsv_referenced;
+ __u64 rsv_exclusive;
+};
+
+struct btrfs_qgroup_inherit {
+ __u64 flags;
+ __u64 num_qgroups;
+ __u64 num_ref_copies;
+ __u64 num_excl_copies;
+ struct btrfs_qgroup_limit lim;
+ __u64 qgroups[0];
+};
+
+struct btrfs_ioctl_qgroup_limit_args {
+ __u64 qgroupid;
+ struct btrfs_qgroup_limit lim;
+};
+
#define BTRFS_SUBVOL_NAME_MAX 4039
struct btrfs_ioctl_vol_args_v2 {
__s64 fd;
__u64 transid;
__u64 flags;
- __u64 unused[4];
+ union {
+ struct {
+ __u64 size;
+ struct btrfs_qgroup_inherit *qgroup_inherit;
+ };
+ __u64 unused[4];
+ };
char name[BTRFS_SUBVOL_NAME_MAX + 1];
};
@@ -297,6 +330,25 @@ struct btrfs_ioctl_send_args {
__u64 reserved[4]; /* in */
};
+#define BTRFS_QUOTA_CTL_ENABLE 1
+#define BTRFS_QUOTA_CTL_DISABLE 2
+#define BTRFS_QUOTA_CTL_RESCAN 3
+struct btrfs_ioctl_quota_ctl_args {
+ __u64 cmd;
+ __u64 status;
+};
+
+struct btrfs_ioctl_qgroup_assign_args {
+ __u64 assign;
+ __u64 src;
+ __u64 dst;
+};
+
+struct btrfs_ioctl_qgroup_create_args {
+ __u64 create;
+ __u64 qgroupid;
+};
+
/* BTRFS_IOC_SNAP_CREATE is no longer used by the btrfs command */
#define BTRFS_IOC_SNAP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 1, \
struct btrfs_ioctl_vol_args)
@@ -345,6 +397,8 @@ struct btrfs_ioctl_clone_range_args {
struct btrfs_ioctl_space_args)
#define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
struct btrfs_ioctl_vol_args_v2)
+#define BTRFS_IOC_SUBVOL_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 24, \
+ struct btrfs_ioctl_vol_args_v2)
#define BTRFS_IOC_SUBVOL_GETFLAGS _IOR(BTRFS_IOCTL_MAGIC, 25, __u64)
#define BTRFS_IOC_SUBVOL_SETFLAGS _IOW(BTRFS_IOCTL_MAGIC, 26, __u64)
#define BTRFS_IOC_SCRUB _IOWR(BTRFS_IOCTL_MAGIC, 27, \
@@ -370,4 +424,12 @@ struct btrfs_ioctl_clone_range_args {
struct btrfs_ioctl_received_subvol_args)
#define BTRFS_IOC_SEND _IOW(BTRFS_IOCTL_MAGIC, 38, struct btrfs_ioctl_send_args)
+#define BTRFS_IOC_QUOTA_CTL _IOWR(BTRFS_IOCTL_MAGIC, 40, \
+ struct btrfs_ioctl_quota_ctl_args)
+#define BTRFS_IOC_QGROUP_ASSIGN _IOW(BTRFS_IOCTL_MAGIC, 41, \
+ struct btrfs_ioctl_qgroup_assign_args)
+#define BTRFS_IOC_QGROUP_CREATE _IOW(BTRFS_IOCTL_MAGIC, 42, \
+ struct btrfs_ioctl_qgroup_create_args)
+#define BTRFS_IOC_QGROUP_LIMIT _IOR(BTRFS_IOCTL_MAGIC, 43, \
+ struct btrfs_ioctl_qgroup_limit_args)
#endif
diff --git a/print-tree.c b/print-tree.c
index 527c1e45..89fc5b46 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -445,8 +445,20 @@ static void print_key_type(u64 objectid, u8 type)
case BTRFS_STRING_ITEM_KEY:
printf("STRING_ITEM");
break;
+ case BTRFS_QGROUP_STATUS_KEY:
+ printf("BTRFS_STATUS_KEY");
+ break;
+ case BTRFS_QGROUP_RELATION_KEY:
+ printf("BTRFS_QGROUP_RELATION_KEY");
+ break;
+ case BTRFS_QGROUP_INFO_KEY:
+ printf("BTRFS_QGROUP_INFO_KEY");
+ break;
+ case BTRFS_QGROUP_LIMIT_KEY:
+ printf("BTRFS_QGROUP_LIMIT_KEY");
+ break;
default:
- printf("UNKNOWN");
+ printf("UNKNOWN.%d", type);
};
}
@@ -456,6 +468,12 @@ static void print_objectid(u64 objectid, u8 type)
printf("%llu", (unsigned long long)objectid); /* device id */
return;
}
+ switch (type) {
+ case BTRFS_QGROUP_RELATION_KEY:
+ printf("%llu/%llu", objectid >> 48,
+ objectid & ((1ll << 48) - 1));
+ return;
+ }
switch (objectid) {
case BTRFS_ROOT_TREE_OBJECTID:
@@ -508,6 +526,8 @@ static void print_objectid(u64 objectid, u8 type)
break;
case BTRFS_FREE_INO_OBJECTID:
printf("FREE_INO");
+ case BTRFS_QUOTA_TREE_OBJECTID:
+ printf("QUOTA_TREE");
break;
case BTRFS_MULTIPLE_OBJECTIDS:
printf("MULTIPLE");
@@ -527,12 +547,23 @@ void btrfs_print_key(struct btrfs_disk_key *disk_key)
{
u64 objectid = btrfs_disk_key_objectid(disk_key);
u8 type = btrfs_disk_key_type(disk_key);
+ u64 offset = btrfs_disk_key_offset(disk_key);
printf("key (");
print_objectid(objectid, type);
printf(" ");
print_key_type(objectid, type);
- printf(" %llu)", (unsigned long long)btrfs_disk_key_offset(disk_key));
+ switch (type) {
+ case BTRFS_QGROUP_RELATION_KEY:
+ case BTRFS_QGROUP_INFO_KEY:
+ case BTRFS_QGROUP_LIMIT_KEY:
+ printf(" %llu/%llu)", (unsigned long long)(offset >> 48),
+ (unsigned long long)(offset & ((1ll << 48) - 1)));
+ break;
+ default:
+ printf(" %llu)", (unsigned long long)offset);
+ break;
+ }
}
void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
@@ -551,6 +582,9 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
struct btrfs_disk_key disk_key;
struct btrfs_block_group_item bg_item;
struct btrfs_dir_log_item *dlog;
+ struct btrfs_qgroup_info_item *qg_info;
+ struct btrfs_qgroup_limit_item *qg_limit;
+ struct btrfs_qgroup_status_item *qg_status;
u32 nr = btrfs_header_nritems(l);
u64 objectid;
u32 type;
@@ -688,6 +722,58 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
(unsigned long long)
btrfs_dev_extent_length(l, dev_extent));
break;
+ case BTRFS_QGROUP_STATUS_KEY:
+ qg_status = btrfs_item_ptr(l, i,
+ struct btrfs_qgroup_status_item);
+ printf("\t\tversion %llu generation %llu flags %#llx "
+ "scan %lld\n",
+ (unsigned long long)
+ btrfs_qgroup_status_version(l, qg_status),
+ (unsigned long long)
+ btrfs_qgroup_status_generation(l, qg_status),
+ (unsigned long long)
+ btrfs_qgroup_status_flags(l, qg_status),
+ (unsigned long long)
+ btrfs_qgroup_status_scan(l, qg_status));
+ break;
+ case BTRFS_QGROUP_RELATION_KEY:
+ break;
+ case BTRFS_QGROUP_INFO_KEY:
+ qg_info = btrfs_item_ptr(l, i,
+ struct btrfs_qgroup_info_item);
+ printf("\t\tgeneration %llu\n"
+ "\t\treferenced %lld referenced compressed %lld\n"
+ "\t\texclusive %lld exclusive compressed %lld\n",
+ (unsigned long long)
+ btrfs_qgroup_info_generation(l, qg_info),
+ (long long)
+ btrfs_qgroup_info_referenced(l, qg_info),
+ (long long)
+ btrfs_qgroup_info_referenced_compressed(l,
+ qg_info),
+ (long long)
+ btrfs_qgroup_info_exclusive(l, qg_info),
+ (long long)
+ btrfs_qgroup_info_exclusive_compressed(l,
+ qg_info));
+ break;
+ case BTRFS_QGROUP_LIMIT_KEY:
+ qg_limit = btrfs_item_ptr(l, i,
+ struct btrfs_qgroup_limit_item);
+ printf("\t\tflags %llx\n"
+ "\t\tmax referenced %lld max exclusive %lld\n"
+ "\t\trsv referenced %lld rsv exclusive %lld\n",
+ (unsigned long long)
+ btrfs_qgroup_limit_flags(l, qg_limit),
+ (long long)
+ btrfs_qgroup_limit_max_referenced(l, qg_limit),
+ (long long)
+ btrfs_qgroup_limit_max_exclusive(l, qg_limit),
+ (long long)
+ btrfs_qgroup_limit_rsv_referenced(l, qg_limit),
+ (long long)
+ btrfs_qgroup_limit_rsv_exclusive(l, qg_limit));
+ break;
case BTRFS_STRING_ITEM_KEY:
/* dirty, but it's simple */
str = l->data + btrfs_item_ptr_offset(l, i);
diff --git a/qgroup.c b/qgroup.c
new file mode 100644
index 00000000..4083b57e
--- /dev/null
+++ b/qgroup.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 STRATO. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#include "qgroup.h"
+#include "ctree.h"
+
+u64 parse_qgroupid(char *p)
+{
+ char *s = strchr(p, '/');
+ u64 level;
+ u64 id;
+
+ if (!s)
+ return atoll(p);
+ level = atoll(p);
+ id = atoll(s + 1);
+
+ return (level << 48) | id;
+}
+
+int qgroup_inherit_size(struct btrfs_qgroup_inherit *p)
+{
+ return sizeof(*p) + sizeof(p->qgroups[0]) *
+ (p->num_qgroups + 2 * p->num_ref_copies +
+ 2 * p->num_excl_copies);
+}
+
+int qgroup_inherit_realloc(struct btrfs_qgroup_inherit **inherit, int n,
+ int pos)
+{
+ struct btrfs_qgroup_inherit *out;
+ int nitems = 0;
+
+ if (*inherit) {
+ nitems = (*inherit)->num_qgroups +
+ (*inherit)->num_ref_copies +
+ (*inherit)->num_excl_copies;
+ }
+
+ out = calloc(sizeof(*out) + sizeof(out->qgroups[0]) * (nitems + n), 1);
+ if (out == NULL) {
+ fprintf(stderr, "ERROR: Not enough memory\n");
+ return 13;
+ }
+
+ if (*inherit) {
+ struct btrfs_qgroup_inherit *i = *inherit;
+ int s = sizeof(out->qgroups);
+
+ out->num_qgroups = i->num_qgroups;
+ out->num_ref_copies = i->num_ref_copies;
+ out->num_excl_copies = i->num_excl_copies;
+ memcpy(out->qgroups, i->qgroups, pos * s);
+ memcpy(out->qgroups + pos + n, i->qgroups + pos,
+ (nitems - pos) * s);
+ }
+ free(*inherit);
+ *inherit = out;
+
+ return 0;
+}
+
+int qgroup_inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg)
+{
+ int ret;
+ u64 qgroupid = parse_qgroupid(arg);
+ int pos = 0;
+
+ if (qgroupid == 0) {
+ fprintf(stderr, "ERROR: bad qgroup specification\n");
+ return 12;
+ }
+
+ if (*inherit)
+ pos = (*inherit)->num_qgroups;
+ ret = qgroup_inherit_realloc(inherit, 1, pos);
+ if (ret)
+ return ret;
+
+ (*inherit)->qgroups[(*inherit)->num_qgroups++] = qgroupid;
+
+ return 0;
+}
+
+int qgroup_inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
+ int type)
+{
+ int ret;
+ u64 qgroup_src;
+ u64 qgroup_dst;
+ char *p;
+ int pos = 0;
+
+ p = strchr(arg, ':');
+ if (!p) {
+bad:
+ fprintf(stderr, "ERROR: bad copy specification\n");
+ return 12;
+ }
+ *p = 0;
+ qgroup_src = parse_qgroupid(arg);
+ qgroup_dst = parse_qgroupid(p + 1);
+ *p = ':';
+
+ if (!qgroup_src || !qgroup_dst)
+ goto bad;
+
+ if (*inherit)
+ pos = (*inherit)->num_qgroups +
+ (*inherit)->num_ref_copies * 2 * type;
+
+ ret = qgroup_inherit_realloc(inherit, 2, pos);
+ if (ret)
+ return ret;
+
+ (*inherit)->qgroups[pos++] = qgroup_src;
+ (*inherit)->qgroups[pos++] = qgroup_dst;
+
+ if (!type)
+ ++(*inherit)->num_ref_copies;
+ else
+ ++(*inherit)->num_excl_copies;
+
+ return 0;
+}
diff --git a/qgroup.h b/qgroup.h
new file mode 100644
index 00000000..f7af8c53
--- /dev/null
+++ b/qgroup.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2012 STRATO. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License v2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ */
+
+#ifndef _BTRFS_QGROUP_H
+#define _BTRFS_QGROUP_H
+
+#include "ioctl.h"
+
+int qgroup_inherit_size(struct btrfs_qgroup_inherit *p);
+int qgroup_inherit_realloc(struct btrfs_qgroup_inherit **inherit,
+ int incgroups, int inccopies);
+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);
+
+#endif