btrfsprogs/0114-btrfs-progs-add-qgroup-commands.patch
David Sterba db07609875 - btrfs-progs-fix-open_ctree_usage_segfaults.patch: fix
segfaults from bnc#710486 due to unchecked usage of return
  value of open_ctree()
  [fixed compilation warnings]

- pull upstream, replace existing patches, spec update
- update 'restore' utility
  - lzo support
  - tools may now take earlies superblock when opening the fs
  - other fixes
- pull integration-20111030 branch
  - mkfs: force mkfs if desired
  - other fixes
- add btrfs-dump-super to mkinitrd
- other fixes
  - skip non-existent devices or without media
  - documentation updates
  - scrubbing single device
  - graceful error handling when opening fs fails

- updated mkinitrd script to scan devices before mount (bnc#727383)

OBS-URL: https://build.opensuse.org/package/show/filesystems/btrfsprogs?expand=0&rev=115
2011-12-14 23:25:51 +00:00

1226 lines
32 KiB
Diff

From 8bf1c8a06bb4a6296bd21799dfdd6d25fd9c65eb Mon Sep 17 00:00:00 2001
From: Arne Jansen <sensille@gmx.net>
Date: Mon, 10 Oct 2011 17:14:27 +0200
Subject: [PATCH 15/35] btrfs-progs: add qgroup commands
This adds the necessary interface to control the qgroups feature. The
command set is still preliminary and low-level. It is planned to add
some convenience commands to manage common usecases. There's still work
to be done to define a good representation for the end user.
I'll add a manpage in a later version, when we settle on an interface.
See http://sensille.com/qgroups.pdf for an explanation of the concepts.
Commands added by this patch:
btrfs quota enable
Enable subvolume quota support for a filesystem. It creates the quota
tree.
btrfs quota disable
Disable subvolume quota support for a filesystem. It removes the quota
tree and with it all configuration information.
btrfs quota rescan
Triggers a full re-init of quota information, doing a full fs rescan.
Kernel side currently does not implement it yet.
btrfs qgroup assign <srcid> <destid> <path>
Assigns the lower level qgroup src to the higher level qgroup dest in the
btrfs found in <path>. It is used to build qgroup hierarchies.
btrfs qgroup remove <srcid> <destid> <path>
Remove the qgroup src from the qgroup dest.
btrfs qgroup create <qgroupid> <path>
Creates a qgroup with id <qgroupid> for the btrfs found in <path>. It will
not be part of any hierarchy.
btrfs qgroup destroy <qgroupid> <path>
Removes the qgroup qgroupid from the btrfs found in <path>. It will also
be removed from any higher-level qgroups.
btrfs qgroup show
Lists all configured qgroup together with the current exclusive and
referenced values.
btrfs qgroup limit <size>|none <path-to-subvol>
btrfs qgroup limit <size>|none <qgroupid> <path-to-btrfs>
Impose a limit of <size> for the amount of referenced space onto the
qgroup <qgroupid> in btrfs <path-to-btrfs> or onto the qgroup referenced
by <path-to-subvol>. A size of none removes the limit.
Commands extended for this patch:
btrfs subvolume create [-i <qid>] [-c <cspec>] [-x <cspec>] <name>
btrfs subvolume snapshot [-i <qid>] [-c <cspec>] [-x <cspec>] <source> <name>
With the creation of a subvolume either by subvolume create or subvolume
snapshot a qgroup gets created automatically. With -i this qgroup can be
added to a higher level qgroup <qid> at creation time. This option can be
given multiple times.
-c and -x copy the values from one qgroup to another at creation time.
In some usecases this is necessary to keep the values consistent. See
the above mentioned concept paper for details. The copy spec <cspec>
consists of two qgroupid separated by ':', giving <srcid>:<dstid>. -c
copies the 'referenced' value, -x copies the 'exclusive' value. These
options can be given multiple times.
Signed-off-by: Arne Jansen <sensille@gmx.net>
---
btrfs.c | 38 ++++-
btrfs_cmds.c | 585 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
btrfs_cmds.h | 9 +
ctree.h | 91 +++++++++
debug-tree.c | 6 +
ioctl.h | 63 ++++++-
print-tree.c | 100 +++++++++-
7 files changed, 877 insertions(+), 15 deletions(-)
diff --git a/btrfs.c b/btrfs.c
index 52af8f7..92723a1 100644
--- a/btrfs.c
+++ b/btrfs.c
@@ -236,7 +236,7 @@ static struct Command commands[] = {
"Delete the subvolume <subvolume>.",
NULL
},
- { do_create_subvol, 1,
+ { do_create_subvol, -1,
"subvolume create", "[<dest>/]<name>\n"
"Create a subvolume in <dest> (or the current directory if\n"
"not passed).",
@@ -366,6 +366,42 @@ static struct Command commands[] = {
"Remove a device from a filesystem.",
NULL
},
+ { do_quota_enable, -1,
+ "quota enable", "<path>\n"
+ "Enable subvolume quota support for a filesystem."
+ },
+ { do_quota_disable, -1,
+ "quota disable", "<path>\n"
+ "Disable subvolume quota support for a filesystem."
+ },
+ { do_quota_rescan, -1,
+ "quota rescan", "<path>\n"
+ "Rescan the subvolume for a changed quota setting."
+ },
+ { do_qgroup_assign, 3,
+ "qgroup assign", "<src> <dst> <path>\n"
+ "Assign a subvol to a quota group."
+ },
+ { do_qgroup_remove, 3,
+ "qgroup remove", "<src> <dst> <path>\n"
+ "Remove a subvol from a quota group."
+ },
+ { do_qgroup_create, 2,
+ "qgroup create", "<qgroupid> <path>\n"
+ "Create a subvolume quota group."
+ },
+ { do_qgroup_destroy, 2,
+ "qgroup destroy", "<qgroupid> <path>\n"
+ "Destroy a subvolume quota group."
+ },
+ { do_qgroup_show, 1,
+ "qgroup show", "<path>\n"
+ "Show all subvolume quota groups."
+ },
+ { do_qgroup_limit, -2,
+ "qgroup limit", "<size>|none {<qgroupid> <path>|<name>}\n"
+ "Limit the size of a subvolume quota group."
+ },
{ do_ino_to_path, -2,
"inspect-internal inode-resolve", "[-v] <inode> <path>\n"
"get file system paths for the given inode.",
diff --git a/btrfs_cmds.c b/btrfs_cmds.c
index e4c5592..977d33c 100644
--- a/btrfs_cmds.c
+++ b/btrfs_cmds.c
@@ -51,6 +51,11 @@ struct btrfs_ioctl_vol_args { char name[BTRFS_VOL_NAME_MAX]; };
static inline int ioctl(int fd, int define, void *arg) { return 0; }
#endif
+int inherit_add_group(struct btrfs_qgroup_inherit **inherit, char *arg);
+int inherit_add_copy(struct btrfs_qgroup_inherit **inherit, char *arg,
+ int type);
+int inherit_size(struct btrfs_qgroup_inherit *p);
+
/*
* test if path is a subvolume:
* this function return
@@ -432,13 +437,14 @@ int do_clone(int argc, char **argv)
char *newname;
char *dstdir;
struct btrfs_ioctl_vol_args_v2 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, "ri:c:x:");
if (c < 0)
break;
@@ -446,6 +452,21 @@ int do_clone(int argc, char **argv)
case 'r':
readonly = 1;
break;
+ case 'i':
+ res = inherit_add_group(&inherit, optarg);
+ if (res)
+ return res;
+ break;
+ case 'c':
+ res = inherit_add_copy(&inherit, optarg, 0);
+ if (res)
+ return res;
+ break;
+ case 'x':
+ res = inherit_add_copy(&inherit, optarg, 1);
+ if (res)
+ return res;
+ break;
default:
fprintf(stderr,
"Invalid arguments for subvolume snapshot\n");
@@ -526,6 +547,11 @@ int do_clone(int argc, char **argv)
}
args.fd = fd;
+ if (inherit) {
+ args.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+ args.size = inherit_size(inherit);
+ args.qgroup_inherit = inherit;
+ }
strncpy(args.name, newname, BTRFS_SUBVOL_NAME_MAX);
res = ioctl(fddst, BTRFS_IOC_SNAP_CREATE_V2, &args);
e = errno;
@@ -538,6 +564,7 @@ int do_clone(int argc, char **argv)
subvol, strerror(e));
return 11;
}
+ free(inherit);
return 0;
@@ -633,8 +660,44 @@ int do_create_subvol(int argc, char **argv)
int res, fddst, len, e;
char *newname;
char *dstdir;
- struct btrfs_ioctl_vol_args args;
- char *dst = argv[1];
+ char *dst;
+ struct btrfs_qgroup_inherit *inherit = NULL;
+
+ while (1) {
+ int c = getopt(argc, argv, "i:c:x:");
+
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'i':
+ res = inherit_add_group(&inherit, optarg);
+ if (res)
+ return res;
+ break;
+ case 'c':
+ res = inherit_add_copy(&inherit, optarg, 0);
+ if (res)
+ return res;
+ break;
+ case 'x':
+ res = inherit_add_copy(&inherit, optarg, 1);
+ if (res)
+ return res;
+ break;
+ default:
+ fprintf(stderr,
+ "Invalid arguments for subvolume create\n");
+ free(argv);
+ return 1;
+ }
+ }
+ if (argc - optind < 1) {
+ fprintf(stderr, "Invalid arguments for subvolume create\n");
+ free(argv);
+ return 1;
+ }
+
+ dst = argv[optind];
res = test_isdir(dst);
if(res >= 0 ){
@@ -668,8 +731,24 @@ int do_create_subvol(int argc, char **argv)
}
printf("Create subvolume '%s/%s'\n", dstdir, newname);
- strncpy(args.name, newname, BTRFS_PATH_NAME_MAX);
- 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.flags |= BTRFS_SUBVOL_QGROUP_INHERIT;
+ args.size = 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);
+
+ res = ioctl(fddst, BTRFS_IOC_SUBVOL_CREATE, &args);
+ }
e = errno;
close(fddst);
@@ -680,6 +759,8 @@ int do_create_subvol(int argc, char **argv)
return 11;
}
+ free(inherit);
+
return 0;
}
@@ -1861,6 +1942,500 @@ int do_df_filesystem(int nargs, char **argv)
return 0;
}
+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 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 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 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 = inherit_realloc(inherit, 1, pos);
+ if (ret)
+ return ret;
+
+ (*inherit)->qgroups[(*inherit)->num_qgroups++] = qgroupid;
+
+ return 0;
+}
+
+int 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;
+
+printf("inherit: add copy %llx:%llx\n", qgroup_src, qgroup_dst);
+ if (*inherit)
+ pos = (*inherit)->num_qgroups +
+ (*inherit)->num_ref_copies * 2 * type;
+
+ ret = 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;
+}
+
+int quota_ctl(int cmd, int nargs, char **argv)
+{
+ int ret=0, fd, e;
+ char *path = argv[1];
+ struct btrfs_ioctl_quota_ctl_args args;
+
+ 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: unable to enable subvolume quota: %s\n",
+ strerror(e));
+ return 30;
+ }
+ return 0;
+}
+
+int do_quota_enable(int nargs, char **argv)
+{
+ return quota_ctl(BTRFS_QUOTA_CTL_ENABLE, nargs, argv);
+}
+
+int do_quota_disable(int nargs, char **argv)
+{
+ return quota_ctl(BTRFS_QUOTA_CTL_DISABLE, nargs, argv);
+}
+
+int do_quota_rescan(int nargs, char **argv)
+{
+ return quota_ctl(BTRFS_QUOTA_CTL_RESCAN, nargs, argv);
+}
+
+int qgroup_assign(int assign, int nargs, char **argv)
+{
+ int ret=0, fd, e;
+ char *path = argv[3];
+ struct btrfs_ioctl_qgroup_assign_args args;
+
+ 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;
+}
+
+int do_qgroup_assign(int nargs, char **argv)
+{
+ return qgroup_assign(1, nargs, argv);
+}
+
+int do_qgroup_remove(int nargs, char **argv)
+{
+ return qgroup_assign(0, nargs, argv);
+}
+
+int qgroup_create(int create, int nargs, char **argv)
+{
+ int ret=0, fd, e;
+ char *path = argv[2];
+ struct btrfs_ioctl_qgroup_create_args args;
+
+ 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 assign quota group: %s\n",
+ strerror(e));
+ return 30;
+ }
+ return 0;
+}
+
+int do_qgroup_create(int nargs, char **argv)
+{
+ return qgroup_create(1, nargs, argv);
+}
+
+int do_qgroup_destroy(int nargs, char **argv)
+{
+ return qgroup_create(0, nargs, argv);
+}
+
+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;
+ 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;
+}
+
+int do_qgroup_show(int nargs, char **argv)
+{
+ int ret=0, fd;
+ char *path = argv[1];
+
+ 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 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;
+}
+
+int do_qgroup_limit(int argc, char **argv)
+{
+ int ret=0, fd, 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:
+ fprintf(stderr, "Invalid arguments for qgroup limit\n");
+ free(argv);
+ return 1;
+ }
+ }
+
+ if (argc - optind < 2) {
+ fprintf(stderr, "Invalid arguments for qgroup limit\n");
+ free(argv);
+ return 1;
+ }
+
+ if (!parse_limit(argv[optind], &size)) {
+ fprintf(stderr, "Invalid size argument given\n");
+ free(argv);
+ 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) {
+ 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 (argc - optind < 3) {
+ fprintf(stderr, "Invalid arguments for qgroup limit\n");
+ free(argv);
+ return 1;
+ }
+ 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 quota group: %s\n",
+ strerror(e));
+ return 30;
+ }
+ return 0;
+}
+
static int __ino_to_path_fd(u64 inum, int fd, int verbose, const char *prepend)
{
int ret;
diff --git a/btrfs_cmds.h b/btrfs_cmds.h
index 2cd0ac1..78d3d02 100644
--- a/btrfs_cmds.h
+++ b/btrfs_cmds.h
@@ -44,6 +44,15 @@ int find_updated_files(int fd, u64 root_id, u64 oldest_gen);
int do_find_newer(int argc, char **argv);
int do_change_label(int argc, char **argv);
int open_file_or_dir(const char *fname);
+int do_quota_enable(int argc, char **argv);
+int do_quota_disable(int argc, char **argv);
+int do_quota_rescan(int argc, char **argv);
+int do_qgroup_assign(int argc, char **argv);
+int do_qgroup_remove(int argc, char **argv);
+int do_qgroup_create(int argc, char **argv);
+int do_qgroup_destroy(int argc, char **argv);
+int do_qgroup_show(int argc, char **argv);
+int do_qgroup_limit(int argc, char **argv);
int do_ino_to_path(int nargs, char **argv);
int do_logical_to_ino(int nargs, char **argv);
char *path_for_root(int fd, u64 root);
diff --git a/ctree.h b/ctree.h
index 58ea3d3..e09d01c 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 restripe params in the root tree */
@@ -714,12 +715,49 @@ struct btrfs_csum_item {
#define BTRFS_AVAIL_ALLOC_BIT_SINGLE (1 << 7)
#define BTRFS_BLOCK_GROUP_RESERVED (1 << 7)
+#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;
@@ -921,6 +959,14 @@ struct btrfs_root {
#define BTRFS_CHUNK_ITEM_KEY 228
/*
+ * 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
*/
@@ -1747,6 +1793,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 2aeabfd..c158e24 100644
--- a/debug-tree.c
+++ b/debug-tree.c
@@ -295,6 +295,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 af8b18b..081c209 100644
--- a/ioctl.h
+++ b/ioctl.h
@@ -32,13 +32,45 @@ struct btrfs_ioctl_vol_args {
};
#define BTRFS_SUBVOL_RDONLY (1ULL << 1)
+#define BTRFS_SUBVOL_QGROUP_INHERIT (1ULL << 2)
+
+#define BTRFS_QGROUP_INHERIT_SET_LIMITS (1ULL << 0)
+
#define BTRFS_SUBVOL_NAME_MAX 4039
+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;
+};
+
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];
};
@@ -263,6 +295,25 @@ struct btrfs_ioctl_logical_ino_args {
__u64 inodes;
};
+#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)
@@ -303,6 +354,8 @@ struct btrfs_ioctl_logical_ino_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_SCRUB _IOWR(BTRFS_IOCTL_MAGIC, 27, \
struct btrfs_ioctl_scrub_args)
#define BTRFS_IOC_SCRUB_CANCEL _IO(BTRFS_IOCTL_MAGIC, 28)
@@ -322,4 +375,12 @@ struct btrfs_ioctl_logical_ino_args {
#define BTRFS_IOC_RESTRIPE_CTL _IOW(BTRFS_IOCTL_MAGIC, 33, int)
#define BTRFS_IOC_RESTRIPE_PROGRESS _IOR(BTRFS_IOCTL_MAGIC, 34, \
struct btrfs_ioctl_restripe_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 49f98af..4e31108 100644
--- a/print-tree.c
+++ b/print-tree.c
@@ -198,11 +198,9 @@ static void print_extent_item(struct extent_buffer *eb, int slot)
struct btrfs_tree_block_info *info;
info = (struct btrfs_tree_block_info *)(ei + 1);
btrfs_tree_block_key(eb, info, &key);
- printf("\t\ttree block key (%llu %x %llu) level %d\n",
- (unsigned long long)btrfs_disk_key_objectid(&key),
- key.type,
- (unsigned long long)btrfs_disk_key_offset(&key),
- btrfs_tree_block_level(eb, info));
+ printf("\t\ttree block key ");
+ btrfs_print_key(&key);
+ printf(" level %d\n", btrfs_tree_block_level(eb, info));
iref = (struct btrfs_extent_inline_ref *)(info + 1);
} else {
iref = (struct btrfs_extent_inline_ref *)(ei + 1);
@@ -354,8 +352,20 @@ static void print_key_type(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("%d", type);
};
}
@@ -365,6 +375,12 @@ static void print_objectid(unsigned long long objectid, u8 type)
printf("%llu", 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:
@@ -412,6 +428,9 @@ static void print_objectid(unsigned long long objectid, u8 type)
case BTRFS_EXTENT_CSUM_OBJECTID:
printf("EXTENT_CSUM");
break;
+ case BTRFS_QUOTA_TREE_OBJECTID:
+ printf("QUOTA_TREE");
+ break;
case BTRFS_MULTIPLE_OBJECTIDS:
printf("MULTIPLE");
break;
@@ -422,20 +441,31 @@ static void print_objectid(unsigned long long objectid, u8 type)
}
/* fall-thru */
default:
- printf("%llu", objectid);
+ printf("%lld", (long long)objectid);
}
}
void btrfs_print_key(struct btrfs_disk_key *disk_key)
{
u8 type;
+ u64 offset = (unsigned long long)btrfs_disk_key_offset(disk_key);
printf("key (");
type = btrfs_disk_key_type(disk_key);
print_objectid((unsigned long long)btrfs_disk_key_objectid(disk_key),
type);
printf(" ");
print_key_type(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)", offset >> 48,
+ offset & ((1ll << 48) - 1));
+ break;
+ default:
+ printf(" %lld)", (long long)offset);
+ break;
+ }
}
void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
@@ -456,6 +486,9 @@ void btrfs_print_leaf(struct btrfs_root *root, struct extent_buffer *l)
struct btrfs_root_item root_item;
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);
u32 type;
@@ -601,6 +634,57 @@ 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 0x%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));
+ 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),
+ (unsigned long long)
+ btrfs_qgroup_info_referenced(l, qg_info),
+ (unsigned long long)
+ btrfs_qgroup_info_referenced_compressed(l,
+ qg_info),
+ (unsigned long long)
+ btrfs_qgroup_info_exclusive(l, qg_info),
+ (unsigned 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),
+ (unsigned long long)
+ btrfs_qgroup_limit_max_referenced(l, qg_limit),
+ (unsigned long long)
+ btrfs_qgroup_limit_max_exclusive(l, qg_limit),
+ (unsigned long long)
+ btrfs_qgroup_limit_rsv_referenced(l, qg_limit),
+ (unsigned 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);
--
1.7.6.233.gd79bc