[PATCH 3/3] Add btrfs ioctl support.

Jeff Mahoney jeffm at suse.com
Thu Apr 28 15:27:01 UTC 2016


* btrfs.c: New file.
* file_ioctl.c: New file.
* Makefile.am (strace_SOURCES): Add it.
* defs.h [LINUX] (btrfs_ioctl): New function.
* ioctls.c (ioctl_decode) [LINUX]: Use it to decode BTRFS_* ioctls.
* configure.ac: Add check for struct btrfs_ioctl_feature_flags.
  Add check for struct btrfs_ioctl_fs_info_args.nodesize.
  Add struct btrfs_ioctl_defrag_range_args.
* xlat/btrfs_balance_flags.in: New file.
* xlat/btrfs_defrag_flags.in: New file.
* xlat/btrfs_dev_replace_cmds.in: New file.
* xlat/btrfs_dev_replace_results.in: New file.
* xlat/btrfs_dev_replace_state.in: New file.
* xlat/btrfs_dev_stats_flags.in: New file.
* xlat/btrfs_dev_stats_values.in: New file.
* xlat/btrfs_features_compat_ro.in: New file.
* xlat/btrfs_features_incompat.in: New file.
* xlat/btrfs_key_types.in: New file.
* xlat/btrfs_qgroup_ctl_cmds.in: New file.
* xlat/btrfs_qgroup_inherit_flags.in: New file.
* xlat/btrfs_qgroup_limit_flags.in: New file.
* xlat/btrfs_qgroup_status_flags.in: New file.
* xlat/btrfs_scrub_flags.in: New file.
* xlat/btrfs_snap_flags_v2.in: New file.
* xlat/btrfs_space_info_flags.in: New file.
* xlat/btrfs_tree_objectids.in: New file.
---
 Makefile.am                        |    2 +
 btrfs.c                            | 1298 ++++++++++++++++++++++++++++++++++++
 configure.ac                       |    8 +
 defs.h                             |    2 +
 ioctl.c                            |    4 +
 xlat/btrfs_balance_flags.in        |   16 +
 xlat/btrfs_defrag_flags.in         |    2 +
 xlat/btrfs_dev_replace_cmds.in     |    3 +
 xlat/btrfs_dev_replace_results.in  |    4 +
 xlat/btrfs_dev_replace_state.in    |    5 +
 xlat/btrfs_dev_stats_flags.in      |    1 +
 xlat/btrfs_dev_stats_values.in     |   11 +
 xlat/btrfs_features_compat.in      |    0
 xlat/btrfs_features_compat_ro.in   |    1 +
 xlat/btrfs_features_incompat.in    |   10 +
 xlat/btrfs_key_types.in            |   40 ++
 xlat/btrfs_qgroup_ctl_cmds.in      |    3 +
 xlat/btrfs_qgroup_inherit_flags.in |    1 +
 xlat/btrfs_qgroup_limit_flags.in   |    6 +
 xlat/btrfs_qgroup_status_flags.in  |    2 +
 xlat/btrfs_scrub_flags.in          |    1 +
 xlat/btrfs_snap_flags_v2.in        |    3 +
 xlat/btrfs_space_info_flags.in     |   11 +
 xlat/btrfs_tree_objectids.in       |   12 +
 24 files changed, 1446 insertions(+)
 create mode 100644 btrfs.c
 create mode 100644 xlat/btrfs_balance_flags.in
 create mode 100644 xlat/btrfs_defrag_flags.in
 create mode 100644 xlat/btrfs_dev_replace_cmds.in
 create mode 100644 xlat/btrfs_dev_replace_results.in
 create mode 100644 xlat/btrfs_dev_replace_state.in
 create mode 100644 xlat/btrfs_dev_stats_flags.in
 create mode 100644 xlat/btrfs_dev_stats_values.in
 create mode 100644 xlat/btrfs_features_compat.in
 create mode 100644 xlat/btrfs_features_compat_ro.in
 create mode 100644 xlat/btrfs_features_incompat.in
 create mode 100644 xlat/btrfs_key_types.in
 create mode 100644 xlat/btrfs_qgroup_ctl_cmds.in
 create mode 100644 xlat/btrfs_qgroup_inherit_flags.in
 create mode 100644 xlat/btrfs_qgroup_limit_flags.in
 create mode 100644 xlat/btrfs_qgroup_status_flags.in
 create mode 100644 xlat/btrfs_scrub_flags.in
 create mode 100644 xlat/btrfs_snap_flags_v2.in
 create mode 100644 xlat/btrfs_space_info_flags.in
 create mode 100644 xlat/btrfs_tree_objectids.in

diff --git a/Makefile.am b/Makefile.am
index e15c2d9..9daccff 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -75,6 +75,7 @@ strace_SOURCES =	\
 	bjm.c		\
 	block.c		\
 	bpf.c		\
+	btrfs.c		\
 	cacheflush.c	\
 	capability.c	\
 	caps0.h		\
@@ -103,6 +104,7 @@ strace_SOURCES =	\
 	fetch_struct_flock.c \
 	file.c		\
 	file_handle.c	\
+	file_ioctl.c	\
 	flock.c		\
 	flock.h		\
 	futex.c		\
diff --git a/btrfs.c b/btrfs.c
new file mode 100644
index 0000000..164fa6c
--- /dev/null
+++ b/btrfs.c
@@ -0,0 +1,1298 @@
+/*
+ * Copyright (c) 2016 Jeff Mahoney <jeffm at suse.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "defs.h"
+#include <linux/fs.h>
+#ifdef HAVE_LINUX_BTRFS_H
+/*
+ * Prior to Linux 3.12, the BTRFS_IOC_DEFAULT_SUBVOL used u64 in
+ * its definition, which isn't exported by the kernel.
+ */
+typedef __u64 u64;
+
+#include <linux/btrfs.h>
+
+#ifndef HAVE_STRUCT_BTRFS_IOCTL_FEATURE_FLAGS_COMPAT_FLAGS
+struct btrfs_ioctl_feature_flags {
+	uint64_t compat_flags;
+	uint64_t compat_ro_flags;
+	uint64_t incompat_flags;
+};
+#endif
+
+#ifndef HAVE_STRUCT_BTRFS_IOCTL_DEFRAG_RANGE_ARGS_START
+struct btrfs_ioctl_defrag_range_args {
+	uint64_t start;
+	uint64_t len;
+	uint64_t flags;
+	uint32_t extent_thresh;
+	uint32_t compress_type;
+	uint32_t unused[4];
+};
+#endif
+
+#ifndef BTRFS_SUBVOL_NAME_MAX
+#define BTRFS_SUBVOL_NAME_MAX 4039
+#endif
+
+#ifndef BTRFS_LABEL_SIZE
+#define BTRFS_LABEL_SIZE 256
+#endif
+
+#ifndef BTRFS_FIRST_FREE_OBJECTID
+#define BTRFS_FIRST_FREE_OBJECTID 256ULL
+#endif
+
+#ifndef BTRFS_IOC_GET_FEATURES
+#define BTRFS_IOC_GET_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
+                                   struct btrfs_ioctl_feature_flags)
+#define BTRFS_IOC_SET_FEATURES _IOW(BTRFS_IOCTL_MAGIC, 57, \
+                                   struct btrfs_ioctl_feature_flags[2])
+#define BTRFS_IOC_GET_SUPPORTED_FEATURES _IOR(BTRFS_IOCTL_MAGIC, 57, \
+                                   struct btrfs_ioctl_feature_flags[3])
+#endif
+
+#ifndef BTRFS_IOC_TREE_SEARCH_V2
+#define BTRFS_IOC_TREE_SEARCH_V2 _IOWR(BTRFS_IOCTL_MAGIC, 17, \
+                                           struct btrfs_ioctl_search_args_v2)
+struct btrfs_ioctl_search_args_v2 {
+        struct btrfs_ioctl_search_key key; /* in/out - search parameters */
+        uint64_t buf_size;		   /* in - size of buffer
+                                            * out - on EOVERFLOW: needed size
+                                            *       to store item */
+        uint64_t buf[0];		   /* out - found items */
+};
+#endif
+
+#include "xlat/btrfs_balance_flags.h"
+#include "xlat/btrfs_defrag_flags.h"
+#include "xlat/btrfs_dev_replace_cmds.h"
+#include "xlat/btrfs_dev_replace_results.h"
+#include "xlat/btrfs_dev_replace_state.h"
+#include "xlat/btrfs_dev_stats_flags.h"
+#include "xlat/btrfs_dev_stats_values.h"
+#include "xlat/btrfs_features_compat.h"
+#include "xlat/btrfs_features_compat_ro.h"
+#include "xlat/btrfs_features_incompat.h"
+#include "xlat/btrfs_key_types.h"
+#include "xlat/btrfs_qgroup_ctl_cmds.h"
+#include "xlat/btrfs_qgroup_inherit_flags.h"
+#include "xlat/btrfs_qgroup_limit_flags.h"
+#include "xlat/btrfs_qgroup_status_flags.h"
+#include "xlat/btrfs_scrub_flags.h"
+#include "xlat/btrfs_snap_flags_v2.h"
+#include "xlat/btrfs_space_info_flags.h"
+#include "xlat/btrfs_tree_objectids.h"
+
+static inline char
+prnibble(char v)
+{
+	if (v >= 10)
+		return 'a' + (v - 10);
+	return '0' + v;
+}
+
+/* 8-4-4-4-12 = 36 characters */
+#define UUID_STRING_SIZE 36
+
+/* Formats uuid, returns 0 if it's all zeroes */
+static int
+btrfs_unparse_uuid(unsigned char *uuid, char *out)
+{
+	int i;
+	int ret = 0;
+	for (i = 0; i < BTRFS_UUID_SIZE; i++) {
+		if (i == 4 || i == 6 || i == 8 || i == 10)
+			*out++ = '-';
+		*out++ = prnibble(uuid[i] >> 4);
+		*out++ = prnibble(uuid[i] & 0xf);
+		if (uuid[i])
+			ret = 1;
+	}
+	out[UUID_STRING_SIZE] = '\0';
+	return ret;
+}
+
+static void
+print_u64(const char *name, uint64_t value)
+{
+	tprintf(", %s=%" PRIu64, name, value);
+}
+
+#define print_member_u64(obj, name) print_u64(#name, obj->name)
+
+static void
+btrfs_print_balance_args(const char *name, struct btrfs_balance_args *bba)
+{
+	tprintf(", %s={profiles=%" PRI__u64, name, bba->profiles);
+	print_member_u64(bba, usage);
+	print_member_u64(bba, devid);
+	print_member_u64(bba, pstart);
+	print_member_u64(bba, pend);
+	print_member_u64(bba, vstart);
+	print_member_u64(bba, vend);
+	print_member_u64(bba, target);
+	tprints(", flags=");
+	printflags64(btrfs_balance_flags, bba->flags, "BTRFS_BALANCE_???");
+	tprints("}");
+}
+
+static void
+btrfs_print_balance(struct tcb *tcp, const long arg, bool out)
+{
+	struct btrfs_ioctl_balance_args balance_args;
+
+	if (umove_or_printaddr(tcp, arg, &balance_args))
+		return;
+
+	tprints("{flags=");
+	printflags64(btrfs_balance_flags, balance_args.flags,
+		     "BTRFS_BALANCE_???");
+	if (out)
+		tprintf(", state=%" PRI__u64, balance_args.state);
+
+	if (balance_args.flags & BTRFS_BALANCE_DATA)
+		btrfs_print_balance_args("data", &balance_args.data);
+	if (balance_args.flags & BTRFS_BALANCE_METADATA)
+		btrfs_print_balance_args("meta", &balance_args.meta);
+	if (balance_args.flags & BTRFS_BALANCE_SYSTEM)
+		btrfs_print_balance_args("sys", &balance_args.sys);
+	tprints("}");
+}
+
+static void
+btrfs_print_features(struct btrfs_ioctl_feature_flags *flags)
+{
+	tprints("{compat_flags=");
+	printflags64(btrfs_features_compat, flags->compat_flags,
+		     "BTRFS_FEATURE_COMPAT_???");
+
+	tprints(", compat_ro_flags=");
+	printflags64(btrfs_features_compat_ro, flags->compat_ro_flags,
+		     "BTRFS_FEATURE_COMPAT_RO_???");
+
+	tprints(", incompat_flags=");
+	printflags64(btrfs_features_incompat, flags->incompat_flags,
+		     "BTRFS_FEATURE_INCOMPAT_???");
+	tprints("}");
+}
+
+static void
+btrfs_print_qgroup_limit(struct btrfs_qgroup_limit *lim)
+{
+	tprints("{flags=");
+	printflags64(btrfs_qgroup_limit_flags, lim->flags,
+		     "BTRFS_QGROUP_LIMIT_???");
+	tprintf(", max_rfer=%" PRI__u64 ", max_excl=%" PRI__u64
+		", rsv_rfer=%" PRI__u64 ", rsv_excl=%" PRI__u64 "}",
+		lim->max_rfer, lim->max_excl,
+		lim->rsv_rfer, lim->rsv_excl);
+}
+
+static void
+btrfs_print_key_type(uint32_t type)
+{
+	const char *str = xlookup(btrfs_key_types, type);
+	tprintf("%u", type);
+	if (str)
+		tprintf(" /* %s */", str);
+}
+
+static void
+btrfs_print_objectid(uint64_t objectid)
+{
+	const char *str = xlookup(btrfs_tree_objectids, objectid);
+	tprintf("%" PRIu64, objectid);
+	if (str)
+		tprintf(" /* %s */", str);
+}
+
+static void
+btrfs_print_data_container_header(struct btrfs_data_container *container)
+{
+	tprintf("{bytes_left=%u, bytes_missing=%u, "
+		"elem_cnt=%u, elem_missed=%u, val=",
+		container->bytes_left, container->bytes_missing,
+		container->elem_cnt, container->elem_missed);
+}
+
+static void
+btrfs_print_data_container_footer(void)
+{
+	tprints("}");
+}
+
+static void
+btrfs_print_logical_ino_container(struct tcb *tcp, uint64_t inodes_addr)
+{
+	struct btrfs_data_container container;
+	uint64_t val_addr;
+	uint32_t i;
+	uint32_t printed = 0;
+
+	if (umoven_or_printaddr(tcp, inodes_addr,
+				sizeof(container), &container))
+		return;
+
+	btrfs_print_data_container_header(&container);
+	if (abbrev(tcp)) {
+		tprints("...");
+		btrfs_print_data_container_footer();
+		return;
+	}
+
+	tprints("[");
+
+	val_addr = inodes_addr + offsetof(typeof(container), val);
+	for (i = 0; i < container.elem_cnt; i += 3, printed++) {
+		uint64_t addr = val_addr + sizeof(uint64_t) * i;
+		uint64_t record[3];
+
+		if (i)
+			tprints(", ");
+
+		if (printed > max_strlen ||
+		    umoven(tcp, addr, sizeof(record), record)) {
+			tprints("...");
+			break;
+		}
+		tprintf("{inum=%" PRIu64 ", offset=%" PRIu64
+			", root=%" PRIu64 "}", record[0], record[1], record[2]);
+	}
+	tprints("]");
+	btrfs_print_data_container_footer();
+}
+
+static void
+btrfs_print_ino_path_container(struct tcb *tcp, uint64_t fspath_addr)
+{
+	struct btrfs_data_container container;
+	uint32_t i;
+	uint64_t val_addr;
+
+	if (umoven_or_printaddr(tcp, fspath_addr,
+				sizeof(container), &container))
+		return;
+
+	btrfs_print_data_container_header(&container);
+	if (abbrev(tcp)) {
+		tprints("...");
+		btrfs_print_data_container_footer();
+		return;
+	}
+
+	tprints("[");
+
+	val_addr = fspath_addr + offsetof(typeof(container), val);
+	for (i = 0; i < container.elem_cnt; i++) {
+		uint64_t addr = val_addr + sizeof(uint64_t) * i;
+		uint64_t ptr;
+
+		if (i)
+			tprints(", ");
+
+		if (i > max_strlen || umoven(tcp, addr, sizeof(ptr), &ptr)) {
+			tprints("...");
+			break;
+		}
+
+		printpath(tcp, val_addr + ptr);
+	}
+	tprints("]");
+	btrfs_print_data_container_footer();
+}
+
+static void
+btrfs_print_qgroup_inherit(struct tcb *tcp, uint64_t qgi_addr)
+{
+	struct btrfs_qgroup_inherit inherit;
+
+	if (umoven_or_printaddr(tcp, qgi_addr, sizeof(inherit), &inherit) < 0) {
+		tprintf("%lx", (unsigned long)qgi_addr);
+		return;
+	}
+
+	tprintf("{flags=");
+	printflags64(btrfs_qgroup_inherit_flags, inherit.flags,
+		     "BTRFS_QGROUP_INHERIT_???");
+	tprintf(", num_qgroups=%" PRI__u64 ", num_ref_copies=%" PRI__u64
+		", num_excl_copies=%" PRI__u64 ", lim=",
+		inherit.num_qgroups, inherit.num_ref_copies,
+		inherit.num_excl_copies);
+
+	btrfs_print_qgroup_limit(&inherit.lim);
+
+	tprints(", qgroups=");
+
+	if (abbrev(tcp)) {
+		tprints("...");
+	} else {
+		uint64_t qgroups_addr = qgi_addr +
+					offsetof(typeof(inherit), qgroups);
+		uint32_t i;
+
+		tprints("[");
+		for (i = 0; i < inherit.num_qgroups; i++) {
+			uint64_t record;
+			uint64_t addr = qgroups_addr + sizeof(record) * i;
+			if (i)
+				tprints(", ");
+			if (i > max_strlen ||
+			    umoven(tcp, addr, sizeof(record), &record)) {
+				tprints("...");
+				break;
+			}
+
+			tprintf("%" PRIu64, record);
+		}
+		tprints("]");
+	}
+	tprints("}");
+}
+
+static void
+__print_key_value(struct tcb *tcp, const char *name, uint64_t value)
+{
+	if (value == UINT64_MAX)
+		tprintf(", %s=UINT64_MAX", name);
+	else if (value || !abbrev(tcp))
+		tprintf(", %s=%" PRIu64, name, value);
+}
+#define print_key_value(tcp, key, name)					\
+	__print_key_value((tcp), #name, (key)->name)
+
+static void
+btrfs_print_tree_search(struct tcb *tcp, struct btrfs_ioctl_search_key *key,
+			uint64_t buf_addr, uint64_t buf_size)
+{
+	if (entering(tcp)) {
+		tprintf("{key={tree_id=");
+		btrfs_print_objectid(key->tree_id);
+
+		if (key->min_objectid != BTRFS_FIRST_FREE_OBJECTID ||
+		    !abbrev(tcp)) {
+			tprints(", min_objectid=");
+			btrfs_print_objectid(key->min_objectid);
+		}
+
+		if (key->max_objectid != BTRFS_LAST_FREE_OBJECTID ||
+		    !abbrev(tcp)) {
+			tprints(", max_objectid=");
+			btrfs_print_objectid(key->max_objectid);
+		}
+
+		print_key_value(tcp, key, min_offset);
+		print_key_value(tcp, key, max_offset);
+		print_key_value(tcp, key, min_transid);
+		print_key_value(tcp, key, max_transid);
+		print_key_value(tcp, key, min_offset);
+
+		tprints(", min_type=");
+		btrfs_print_key_type(key->min_type);
+		tprints(", max_type=");
+		btrfs_print_key_type(key->max_type);
+		tprintf(", nr_items=%u}}", key->nr_items);
+		return;
+	}
+	tprintf("{key={nr_items=%u}, buf=", key->nr_items);
+	if (abbrev(tcp))
+		tprints("...");
+	else {
+		uint64_t i;
+		uint64_t off = 0;
+		tprints("[");
+		for (i = 0; i < key->nr_items; i++) {
+			struct btrfs_ioctl_search_header sh;
+			uint64_t addr = buf_addr + off;
+			if (i)
+				tprints(", ");
+			if (i > max_strlen ||
+			    umoven(tcp, addr, sizeof(sh), &sh)) {
+				tprints("...");
+				break;
+			}
+			tprintf("{transid=%" PRI__u64 ", objectid=",
+				sh.transid);
+			btrfs_print_objectid(sh.objectid);
+			tprintf(", offset=%" PRI__u64 ", type=", sh.offset);
+			btrfs_print_key_type(sh.type);
+			tprintf(", len=%u}", sh.len);
+			off += sizeof(sh) + sh.len;
+		}
+		tprints("]");
+	}
+
+	tprints("}");
+}
+
+int
+btrfs_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
+{
+	int ret = 0;
+
+	switch (code) {
+	/* Take no arguments; Command only. */
+	case BTRFS_IOC_TRANS_START:
+	case BTRFS_IOC_TRANS_END:
+	case BTRFS_IOC_SYNC:
+	case BTRFS_IOC_SCRUB_CANCEL:
+	case BTRFS_IOC_QUOTA_RESCAN_WAIT:
+		break;
+
+	/* take a signed int */
+	case BTRFS_IOC_BALANCE_CTL:
+		printnum_int(tcp, arg, ", %d");
+		break;
+
+	/* returns a 64 */
+	case BTRFS_IOC_START_SYNC: /* R */
+		if (entering(tcp))
+			return 0;
+	/* fallthrough */
+	/* take a u64 */
+	case BTRFS_IOC_DEFAULT_SUBVOL: /* W */
+	case BTRFS_IOC_WAIT_SYNC: /* W */
+		tprints(", ");
+		printnum_int64(tcp, arg, "%" PRIu64);
+		break;
+
+	/* u64 but describe a flags bitfield; We can make that symbolic */
+	case BTRFS_IOC_SUBVOL_GETFLAGS: { /* R */
+		uint64_t flags;
+		if (entering(tcp))
+			return 0;
+
+		tprints(", ");
+
+		if (umove_or_printaddr(tcp, arg, &flags))
+			break;
+
+		printflags64(btrfs_snap_flags_v2, flags, "BTRFS_SUBVOL_???");
+		break;
+	}
+	case BTRFS_IOC_SUBVOL_SETFLAGS: { /* W */
+		uint64_t flags;
+
+		tprints(", ");
+
+		if (umove_or_printaddr(tcp, arg, &flags))
+			break;
+
+		printflags64(btrfs_snap_flags_v2, flags, "BTRFS_SUBVOL_???");
+		break;
+	}
+
+	/* More complex types */
+	case BTRFS_IOC_BALANCE_V2: /* RW */
+		if (entering(tcp)) {
+			tprints(", ");
+			btrfs_print_balance(tcp, arg, false);
+			return 0;
+		}
+
+		if (syserror(tcp))
+			break;
+
+		tprints(" => ");
+		btrfs_print_balance(tcp, arg, true);
+		break;
+	case BTRFS_IOC_BALANCE_PROGRESS: /* R */
+		if (entering(tcp))
+			return 0;
+
+		tprints(", ");
+		btrfs_print_balance(tcp, arg, true);
+		break;
+
+	case BTRFS_IOC_DEFRAG_RANGE: {/* W */
+		struct btrfs_ioctl_defrag_range_args args;
+
+		tprints(", ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{start=%" PRIu64 ", len=%" PRIu64 ", flags=",
+			(uint64_t)args.start, (uint64_t)args.len);
+		printflags64(btrfs_defrag_flags, args.flags,
+			     "BTRFS_DEFRAG_RANGE_???");
+		tprintf(", extent_thresh=%u, compress_type=%u}",
+			args.extent_thresh, args.compress_type);
+		break;
+	}
+
+	case BTRFS_IOC_DEV_INFO: { /* RW */
+		struct btrfs_ioctl_dev_info_args args;
+		char uuid[UUID_STRING_SIZE+1];
+		int valid;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+		tprints("{");
+
+		valid = btrfs_unparse_uuid(args.uuid, uuid);
+		if (entering(tcp)) {
+			tprintf("devid=%" PRI__u64, args.devid);
+			if (valid)
+				tprintf(", uuid=%s", uuid);
+			tprints("}");
+			return 0;
+		}
+		if (valid)
+			tprintf("uuid=%s, ", uuid);
+		tprintf("bytes_used=%" PRI__u64 ", "
+			"total_bytes=%" PRI__u64 ", path=",
+			args.bytes_used, args.total_bytes);
+		print_quoted_string((const char *)args.path, sizeof(args.path),
+				    QUOTE_0_TERMINATED);
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_DEV_REPLACE: { /* RW */
+		struct btrfs_ioctl_dev_replace_args args;
+
+		if (entering(tcp))
+			tprints(", ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		if (entering(tcp)) {
+			tprints("{cmd=");
+			printxvals(args.cmd, "BTRFS_IOCTL_DEV_REPLACE_CMD_???",
+				   btrfs_dev_replace_cmds, NULL);
+			if (args.cmd == BTRFS_IOCTL_DEV_REPLACE_CMD_START) {
+				const char *str;
+				tprintf(", start={srcdevid=%" PRI__u64 ", "
+				   "cont_reading_from_srcdev_mode=%" PRI__u64
+				   ", srcdev_name=",
+				   args.start.srcdevid,
+				   args.start.cont_reading_from_srcdev_mode);
+
+				str = (const char*) args.start.srcdev_name;
+				print_quoted_string(str,
+						sizeof(args.start.srcdev_name),
+						QUOTE_0_TERMINATED);
+				tprints(", tgtdev_name=");
+				str = (const char*) args.start.tgtdev_name;
+				print_quoted_string(str,
+						sizeof(args.start.tgtdev_name),
+						QUOTE_0_TERMINATED);
+				tprints("}");
+
+			}
+			tprints("}");
+			return 0;
+		}
+
+		tprints(" => {result=");
+		printxvals(args.result, "BTRFS_IOCTL_DEV_REPLACE_RESULT_???",
+			   btrfs_dev_replace_results, NULL);
+		if (args.cmd == BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS) {
+			char buf[sizeof("HH:MM:SS") + 1];
+			time_t time;
+			tprints(", ");
+			printxvals(args.status.replace_state,
+				   "BTRFS_IOCTL_DEV_REPLACE_STATE_???",
+				   btrfs_dev_replace_state, NULL);
+			tprintf(", progress_1000=%" PRI__u64 " /* ",
+				args.status.progress_1000);
+			if (args.status.progress_1000 <= 1000)
+				tprintf("%" PRI__u64 ".%.2" PRI__u64 "%%",
+					args.status.progress_1000 / 10,
+					args.status.progress_1000 % 10);
+			else
+				tprints("???");
+			tprints(" */ ,");
+
+			time = args.status.time_started;
+			strftime(buf, sizeof(buf), "%T",
+				 localtime(&time));
+			tprintf("time_started=%" PRI__u64" /* %s */, ",
+				args.status.time_started, buf);
+
+			time = args.status.time_stopped;
+			strftime(buf, sizeof(buf), "%T",
+				 localtime(&time));
+			tprintf("time_stopped=%" PRI__u64" /* %s */, ",
+				args.status.time_stopped, buf);
+
+			tprintf("num_write_errors=%" PRI__u64 ", "
+				"num_uncorrectable_read_errors=%" PRI__u64,
+				args.status.num_write_errors,
+				args.status.num_uncorrectable_read_errors);
+		}
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_GET_FEATURES: { /* R */
+		struct btrfs_ioctl_feature_flags flags;
+		if (entering(tcp))
+			return 0;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &flags))
+			break;
+
+		btrfs_print_features(&flags);
+		break;
+	}
+	case BTRFS_IOC_SET_FEATURES: { /* W */
+		struct btrfs_ioctl_feature_flags flarg[2];
+
+		tprints(", ");
+
+		if (umove_or_printaddr(tcp, arg, &flarg))
+			break;
+
+		tprints("[");
+		btrfs_print_features(&flarg[0]);
+		tprints(", ");
+		btrfs_print_features(&flarg[1]);
+		tprints("]");
+		break;
+	}
+
+	case BTRFS_IOC_GET_SUPPORTED_FEATURES: { /* R */
+		struct btrfs_ioctl_feature_flags flarg[3];
+
+		if (entering(tcp))
+			return 0;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &flarg))
+			break;
+
+		tprints("[ /* supported */ ");
+		btrfs_print_features(&flarg[0]);
+
+		tprints(", /* safe to set */ ");
+		btrfs_print_features(&flarg[1]);
+
+		tprints(", /* safe to clear */ ");
+		btrfs_print_features(&flarg[2]);
+		tprints("]");
+
+		break;
+	}
+
+	case BTRFS_IOC_FS_INFO: { /* R */
+		struct btrfs_ioctl_fs_info_args args;
+		char uuid[UUID_STRING_SIZE+1];
+		uint32_t nodesize, sectorsize, clone_alignment;
+#ifndef HAVE_STRUCT_BTRFS_IOCTL_FS_INFO_ARGS_NODESIZE
+		__u32 *reserved32;
+#endif
+
+		if (entering(tcp))
+			return 0;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+#ifdef HAVE_STRUCT_BTRFS_IOCTL_FS_INFO_ARGS_NODESIZE
+		nodesize = args.nodesize,
+		sectorsize = args.sectorsize,
+		clone_alignment = args.clone_alignment;
+#else
+		reserved32 = (__u32 *)args.reserved;
+		nodesize = reserved32[0];
+		sectorsize = reserved32[1];
+		clone_alignment = reserved32[2];
+#endif
+		btrfs_unparse_uuid(args.fsid, uuid);
+
+		tprints("{");
+		tprintf("max_id=%" PRI__u64 ", num_devices=%" PRI__u64 ", "
+			"fsid=%s, nodesize=%u, sectorsize=%u, "
+			"clone_alignment=%u",
+			args.max_id, args.num_devices, uuid,
+			nodesize, sectorsize, clone_alignment);
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_GET_DEV_STATS: { /* RW */
+		struct btrfs_ioctl_get_dev_stats args;
+		uint64_t i;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprints("{");
+
+		if (entering(tcp))
+			tprintf("devid=%" PRI__u64 ", ", args.devid);
+
+		tprintf("nr_items=%" PRI__u64, args.nr_items);
+		printflags64(btrfs_dev_stats_flags, args.flags,
+			     "BTRFS_DEV_STATS_???");
+
+		if (entering(tcp)) {
+			tprints("}");
+			return 0;
+		}
+
+		tprints(", [");
+		for (i = 0; i < args.nr_items; i++) {
+			const char *name = xlookup(btrfs_dev_stats_values, i);
+			if (i)
+				tprints(", ");
+			if (name)
+				tprintf("/* %s */ ", name);
+			tprintf("%" PRI__u64, args.values[i]);
+		}
+		tprints("]}");
+		break;
+	}
+
+	case BTRFS_IOC_INO_LOOKUP: { /* RW */
+		struct btrfs_ioctl_ino_lookup_args args;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		if (entering(tcp)) {
+			/* Use subvolume id of the containing root */
+			if (args.treeid == 0)
+				/* abuse of auxstr to retain state */
+				tcp->auxstr = (void *)1;
+
+			tprints("{treeid=");
+			btrfs_print_objectid(args.treeid);
+			tprints(", objectid=");
+			btrfs_print_objectid(args.objectid);
+			tprints("}");
+			return 0;
+		}
+
+		tprints("{");
+		if (tcp->auxstr) {
+			tprints("treeid=");
+			btrfs_print_objectid(args.treeid);
+			tprints(", ");
+		}
+		tcp->auxstr = NULL;
+
+		tprints("name=");
+		print_quoted_string(args.name, sizeof(args.name),
+				    QUOTE_0_TERMINATED);
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_INO_PATHS: { /* RW */
+		struct btrfs_ioctl_ino_path_args args;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprints("{");
+
+		if (entering(tcp)) {
+			tprintf("inum=%" PRI__u64 ", size=%" PRI__u64 "}",
+				args.inum, args.size);
+			return 0;
+		}
+
+		tprints("fspath=");
+		btrfs_print_ino_path_container(tcp, args.fspath);
+
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_LOGICAL_INO: { /* RW */
+		struct btrfs_ioctl_logical_ino_args args;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprints("{");
+
+		if (entering(tcp)) {
+			tprintf("logical=%" PRI__u64 ", size=%" PRI__u64 "}",
+				args.logical, args.size);
+			return 0;
+		}
+
+		tprints("inodes=");
+		btrfs_print_logical_ino_container(tcp, args.inodes);
+
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_QGROUP_ASSIGN: { /* W */
+		struct btrfs_ioctl_qgroup_assign_args args;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{assign=%" PRI__u64 ", src=%" PRI__u64
+			", dst=%" PRI__u64 "}",
+			args.assign, args.src, args.dst);
+		break;
+	}
+
+	case BTRFS_IOC_QGROUP_CREATE: { /* W */
+		struct btrfs_ioctl_qgroup_create_args args;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{create=%" PRI__u64 ", qgroupid=%" PRI__u64 "}",
+			args.create, args.qgroupid);
+		break;
+	}
+
+	case BTRFS_IOC_QGROUP_LIMIT: { /* R */
+		struct btrfs_ioctl_qgroup_limit_args args;
+
+		if (entering(tcp))
+			return 0;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{qgroupid=%" PRI__u64 ", lim=", args.qgroupid);
+		btrfs_print_qgroup_limit(&args.lim);
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_QUOTA_CTL: { /* W */
+		struct btrfs_ioctl_quota_ctl_args args;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		printxvals(args.cmd, "BTRFS_QUOTA_CTL_???",
+			   btrfs_qgroup_ctl_cmds, NULL);
+		tprints("}");
+
+		break;
+	}
+	case BTRFS_IOC_QUOTA_RESCAN: { /* W */
+		struct btrfs_ioctl_quota_rescan_args args;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{flags=%" PRI__u64 "}", args.flags);
+		break;
+	}
+	case BTRFS_IOC_QUOTA_RESCAN_STATUS: { /* R */
+		struct btrfs_ioctl_quota_rescan_args args;
+
+		if (entering(tcp))
+			return 0;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{flags=%" PRI__u64 ", progress=", args.flags);
+		if (args.progress == UINT64_MAX)
+			tprints("UINT64_MAX");
+		else
+			btrfs_print_objectid(args.progress);
+		tprints("}");
+		break;
+	}
+	case BTRFS_IOC_SET_RECEIVED_SUBVOL: { /* RW */
+#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32
+	case BTRFS_IOC_SET_RECEIVED_SUBVOL_32: { /* RW */
+		struct btrfs_ioctl_received_subvol_args_32 args32;
+#endif
+		struct btrfs_ioctl_received_subvol_args args;
+		char uuid[UUID_STRING_SIZE+1];
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+#ifdef BTRFS_IOC_SET_RECEIVED_SUBVOL_32
+		/*
+		 * This is a compat ioctl for 32 bit tools on
+		 * 64 bit systems.
+		 */
+		if (code == BTRFS_IOC_SET_RECEIVED_SUBVOL_32) {
+			if (umove_or_printaddr(tcp, arg, &args32))
+				break;
+			memcpy(args.uuid, args32.uuid, sizeof(uuid));
+			args.stransid = args32.stransid;
+			args.rtransid = args32.rtransid;
+			args.stime.sec = args32.stime.sec;
+			args.stime.nsec = args32.stime.nsec;
+			args.rtime.sec = args32.rtime.sec;
+			args.rtime.nsec = args32.rtime.nsec;
+			args.flags = args32.flags;
+		} else
+#endif
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		if (entering(tcp)) {
+			btrfs_unparse_uuid((unsigned char *)args.uuid, uuid);
+			tprintf("{uuid=%s, stransid=%" PRI__u64
+				", stime=%" PRI__u64 ".%u, flags=%" PRI__u64
+				"}", uuid, args.stransid, args.stime.sec,
+				args.stime.nsec, args.flags);
+			return 0;
+		}
+		tprintf("{rtransid=%" PRI__u64 ", rtime=%" PRI__u64 ".%u}",
+			args.rtransid, args.rtime.sec, args.rtime.nsec);
+		break;
+	}
+	case BTRFS_IOC_SCRUB: /* RW */
+	case BTRFS_IOC_SCRUB_PROGRESS: { /* RW */
+		struct btrfs_ioctl_scrub_args args;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		if (entering(tcp)) {
+			tprintf("{devid=%" PRI__u64, args.devid);
+			if (code == BTRFS_IOC_SCRUB) {
+				tprintf(", start=%" PRI__u64 ", end=",
+					args.start);
+				if (args.end == UINT64_MAX)
+					tprints("UINT64_MAX");
+				else
+					tprintf("%" PRI__u64, args.end);
+				tprints(", flags=");
+				printflags64(btrfs_scrub_flags, args.flags,
+					     "BTRFS_SCRUB_???");
+			}
+			tprints("}");
+			return 0;
+		}
+		tprintf("{data_extents_scrubbed=%" PRI__u64 ", "
+			"tree_extents_scrubbed=%" PRI__u64 ", "
+			"data_bytes_scrubbed=%" PRI__u64 ", "
+			"tree_bytes_scrubbed=%" PRI__u64 ", "
+			"read_errors=%" PRI__u64 ", "
+			"csum_errors=%" PRI__u64 ", "
+			"verify_errors=%" PRI__u64 ", "
+			"no_csum=%" PRI__u64 ", "
+			"csum_discards=%" PRI__u64 ", "
+			"super_errors=%" PRI__u64 ", "
+			"malloc_errors=%" PRI__u64 ", "
+			"uncorrectable_errors=%" PRI__u64 ", "
+			"corrected_errors=%" PRI__u64 ", "
+			"last_physical=%" PRI__u64 ", "
+			"unverified_errors=%" PRI__u64 "}",
+			args.progress.data_extents_scrubbed,
+			args.progress.tree_extents_scrubbed,
+			args.progress.data_bytes_scrubbed,
+			args.progress.tree_bytes_scrubbed,
+			args.progress.read_errors,
+			args.progress.csum_errors,
+			args.progress.verify_errors,
+			args.progress.no_csum,
+			args.progress.csum_discards,
+			args.progress.super_errors,
+			args.progress.malloc_errors,
+			args.progress.uncorrectable_errors,
+			args.progress.corrected_errors,
+			args.progress.last_physical,
+			args.progress.unverified_errors);
+		break;
+	}
+
+	case BTRFS_IOC_TREE_SEARCH: { /* RW */
+		struct btrfs_ioctl_search_args args;
+		uint64_t buf_addr;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		buf_addr = arg + offsetof(struct btrfs_ioctl_search_args, buf);
+		btrfs_print_tree_search(tcp, &args.key, buf_addr,
+					sizeof(args.buf));
+		if (entering(tcp))
+			return 0;
+		break;
+	}
+
+	case BTRFS_IOC_TREE_SEARCH_V2: { /* RW */
+		struct btrfs_ioctl_search_args_v2 args;
+		uint64_t buf_addr;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		buf_addr = arg + offsetof(struct btrfs_ioctl_search_args, buf);
+		btrfs_print_tree_search(tcp, &args.key, buf_addr,
+					args.buf_size);
+		if (entering(tcp))
+			return 0;
+		break;
+	}
+
+	case BTRFS_IOC_SEND: { /* W */
+		struct btrfs_ioctl_send_args args;
+		uint64_t base_addr;
+		uint64_t i;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{send_fd=%" PRI__s64 ", clone_sources_count=%" PRI__u64
+			", clone_sources=", args.send_fd,
+			args.clone_sources_count);
+
+		if (abbrev(tcp)) {
+			tprints("...}");
+			break;
+		}
+
+		tprints("[");
+		base_addr = (unsigned long)args.clone_sources;
+		for (i = 0; i < args.clone_sources_count; i++) {
+			uint64_t addr = base_addr + sizeof(uint64_t) * i;
+			uint64_t record;
+			if (i)
+				tprints(", ");
+			if (i > max_strlen ||
+			    umoven(tcp, addr, sizeof(record), &record)) {
+				tprints("...");
+				break;
+			}
+			btrfs_print_objectid(record);
+		}
+		tprints("]}");
+		break;
+	}
+
+	case BTRFS_IOC_SPACE_INFO: { /* RW */
+		struct btrfs_ioctl_space_args args;
+		uint64_t spaces_addr;
+		uint64_t i;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprints("{");
+		if (entering(tcp)) {
+			tprintf("space_slots=%" PRI__u64 "}",
+				args.space_slots);
+			return 0;
+		}
+
+		tprintf("total_spaces=%" PRI__u64, args.total_spaces);
+
+		if (args.space_slots == 0 && args.total_spaces) {
+			tprints("}");
+			break;
+		}
+
+		tprints(", spaces=");
+
+		if (abbrev(tcp)) {
+			tprints("...}");
+			break;
+		}
+
+		tprints("[");
+
+		spaces_addr = arg + offsetof(typeof(args), spaces);
+		for (i = 0; i < args.total_spaces; i++) {
+			struct btrfs_ioctl_space_info info;
+			uint64_t addr = spaces_addr + sizeof(info) * i;
+			if (i)
+				tprints(", ");
+
+			if (i > max_strlen ||
+			    umoven(tcp, addr, sizeof(info), &info)) {
+				tprints("...");
+				break;
+			}
+
+			tprints("{flags=");
+			printflags64(btrfs_space_info_flags, info.flags,
+				     "BTRFS_SPACE_INFO_???");
+			tprintf(", total_bytes=%" PRI__u64 ", "
+				"used_bytes=%" PRI__u64 "}",
+				info.total_bytes, info.used_bytes);
+		}
+		tprints("]}");
+		break;
+	}
+
+	case BTRFS_IOC_SNAP_CREATE:
+	case BTRFS_IOC_DEFRAG:
+	case BTRFS_IOC_RESIZE:
+	case BTRFS_IOC_SCAN_DEV:
+	case BTRFS_IOC_ADD_DEV:
+	case BTRFS_IOC_RM_DEV:
+	case BTRFS_IOC_BALANCE:
+	case BTRFS_IOC_SUBVOL_CREATE:
+	case BTRFS_IOC_SNAP_DESTROY:
+	case BTRFS_IOC_DEVICES_READY: { /* W */
+		struct btrfs_ioctl_vol_args args;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{fd=%" PRI__s64 ", name=", args.fd);
+		print_quoted_string(args.name, sizeof(args.name),
+				    QUOTE_0_TERMINATED);
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_SNAP_CREATE_V2:
+	case BTRFS_IOC_SUBVOL_CREATE_V2: { /* W */
+		struct btrfs_ioctl_vol_args_v2 args;
+
+		tprints(", ");
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{fd=%" PRI__s64 ", flags=", args.fd);
+		printflags64(btrfs_snap_flags_v2, args.flags,
+			     "BTRFS_SUBVOL_???");
+		if (args.flags & BTRFS_SUBVOL_QGROUP_INHERIT) {
+			tprintf(", size=%" PRI__u64 ", qgroup_inherit=",
+				args.size);
+
+			btrfs_print_qgroup_inherit(tcp,
+					(unsigned long)args.qgroup_inherit);
+
+		}
+		tprintf(", name=");
+		print_quoted_string(args.name, BTRFS_SUBVOL_NAME_MAX + 1,
+				    QUOTE_0_TERMINATED);
+		tprints("}");
+		break;
+	}
+	case BTRFS_IOC_GET_FSLABEL: /* R */
+	case BTRFS_IOC_SET_FSLABEL: {/* W */
+		char label[BTRFS_LABEL_SIZE];
+		if (code == BTRFS_IOC_GET_FSLABEL && entering(tcp))
+			return 0;
+
+		tprints(", ");
+		if (umoven_or_printaddr(tcp, arg, sizeof(label), label))
+			break;
+		print_quoted_string(label, sizeof(label), QUOTE_0_TERMINATED);
+		break;
+	}
+
+	case BTRFS_IOC_CLONE:			/* FICLONE */
+	case BTRFS_IOC_CLONE_RANGE:		/* FICLONERANGE */
+	case BTRFS_IOC_FILE_EXTENT_SAME:	/* FIDEDUPERANGE */
+		/*
+		 * FICLONE, FICLONERANGE, and FIDEDUPERANGE started out as
+		 * btrfs ioctls and the code was kept for the generic
+		 * implementations.  We use the BTRFS_* names here because
+		 * they will be available on older systems.
+		 */
+		return file_ioctl(tcp, code, arg);
+
+	default:
+		return RVAL_DECODED;
+	};
+	return ret | RVAL_DECODED | 1;
+}
+#endif /* HAVE_LINUX_BTRFS_H */
diff --git a/configure.ac b/configure.ac
index 98907a0..863b22c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -433,6 +433,14 @@ AC_CHECK_HEADERS([linux/bpf.h], [
 	fi
 ])
 
+AC_CHECK_HEADERS([linux/btrfs.h], [
+	AC_CHECK_MEMBERS(m4_normalize([
+		struct btrfs_ioctl_feature_flags.compat_flags,
+		struct btrfs_ioctl_fs_info_args.nodesize,
+		struct btrfs_ioctl_defrag_range_args.start
+		]),,, [ #include <stdio.h>
+#include <linux/btrfs.h>])],)
+
 AC_CHECK_DECLS([sys_errlist])
 AC_CHECK_DECLS(m4_normalize([
 	PTRACE_PEEKUSER,
diff --git a/defs.h b/defs.h
index cc2a0d4..d96dc4e 100644
--- a/defs.h
+++ b/defs.h
@@ -642,7 +642,9 @@ extern const char *sprint_open_modes(int);
 extern void print_seccomp_filter(struct tcb *tcp, unsigned long);
 
 extern int block_ioctl(struct tcb *, const unsigned int, long);
+extern int btrfs_ioctl(struct tcb *, const unsigned int, long);
 extern int evdev_ioctl(struct tcb *, const unsigned int, long);
+extern int file_ioctl(struct tcb *, const unsigned int, long);
 extern int loop_ioctl(struct tcb *, const unsigned int, long);
 extern int mtd_ioctl(struct tcb *, const unsigned int, long);
 extern int ptp_ioctl(struct tcb *, const unsigned int, long);
diff --git a/ioctl.c b/ioctl.c
index f70dc44..3f7eae1 100644
--- a/ioctl.c
+++ b/ioctl.c
@@ -263,6 +263,10 @@ ioctl_decode(struct tcb *tcp)
 	case 'E':
 		return evdev_ioctl(tcp, code, arg);
 #endif
+#ifdef HAVE_LINUX_BTRFS_H
+	case 0x94:
+		return btrfs_ioctl(tcp, code, arg);
+#endif
 	default:
 		break;
 	}
diff --git a/xlat/btrfs_balance_flags.in b/xlat/btrfs_balance_flags.in
new file mode 100644
index 0000000..5745159
--- /dev/null
+++ b/xlat/btrfs_balance_flags.in
@@ -0,0 +1,16 @@
+BTRFS_BALANCE_DATA              (1ULL << 0)
+BTRFS_BALANCE_SYSTEM            (1ULL << 1)
+BTRFS_BALANCE_METADATA          (1ULL << 2)
+BTRFS_BALANCE_FORCE
+BTRFS_BALANCE_RESUME
+BTRFS_BALANCE_ARGS_PROFILES	(1ULL << 0)
+BTRFS_BALANCE_ARGS_USAGE	(1ULL << 1)
+BTRFS_BALANCE_ARGS_DEVID	(1ULL << 2)
+BTRFS_BALANCE_ARGS_DRANGE	(1ULL << 3)
+BTRFS_BALANCE_ARGS_VRANGE	(1ULL << 4)
+BTRFS_BALANCE_ARGS_LIMIT        (1ULL << 5)
+BTRFS_BALANCE_ARGS_LIMIT_RANGE  (1ULL << 6)
+BTRFS_BALANCE_ARGS_STRIPES_RANGE (1ULL << 7)
+BTRFS_BALANCE_ARGS_CONVERT	(1ULL << 8)
+BTRFS_BALANCE_ARGS_SOFT		(1ULL << 9)
+BTRFS_BALANCE_ARGS_USAGE_RANGE	(1ULL << 10)
diff --git a/xlat/btrfs_defrag_flags.in b/xlat/btrfs_defrag_flags.in
new file mode 100644
index 0000000..33b490f
--- /dev/null
+++ b/xlat/btrfs_defrag_flags.in
@@ -0,0 +1,2 @@
+BTRFS_DEFRAG_RANGE_COMPRESS
+BTRFS_DEFRAG_RANGE_START_IO
diff --git a/xlat/btrfs_dev_replace_cmds.in b/xlat/btrfs_dev_replace_cmds.in
new file mode 100644
index 0000000..65bbf3b
--- /dev/null
+++ b/xlat/btrfs_dev_replace_cmds.in
@@ -0,0 +1,3 @@
+BTRFS_IOCTL_DEV_REPLACE_CMD_START
+BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS
+BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL
diff --git a/xlat/btrfs_dev_replace_results.in b/xlat/btrfs_dev_replace_results.in
new file mode 100644
index 0000000..4b75d09
--- /dev/null
+++ b/xlat/btrfs_dev_replace_results.in
@@ -0,0 +1,4 @@
+BTRFS_IOCTL_DEV_REPLACE_RESULT_NO_ERROR
+BTRFS_IOCTL_DEV_REPLACE_RESULT_NOT_STARTED
+BTRFS_IOCTL_DEV_REPLACE_RESULT_ALREADY_STARTED
+BTRFS_IOCTL_DEV_REPLACE_RESULT_SCRUB_INPROGRESS
diff --git a/xlat/btrfs_dev_replace_state.in b/xlat/btrfs_dev_replace_state.in
new file mode 100644
index 0000000..e89e5e6
--- /dev/null
+++ b/xlat/btrfs_dev_replace_state.in
@@ -0,0 +1,5 @@
+BTRFS_IOCTL_DEV_REPLACE_STATE_NEVER_STARTED
+BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED
+BTRFS_IOCTL_DEV_REPLACE_STATE_FINISHED
+BTRFS_IOCTL_DEV_REPLACE_STATE_CANCELED
+BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED
diff --git a/xlat/btrfs_dev_stats_flags.in b/xlat/btrfs_dev_stats_flags.in
new file mode 100644
index 0000000..0d62b14
--- /dev/null
+++ b/xlat/btrfs_dev_stats_flags.in
@@ -0,0 +1 @@
+BTRFS_DEV_STATS_RESET
diff --git a/xlat/btrfs_dev_stats_values.in b/xlat/btrfs_dev_stats_values.in
new file mode 100644
index 0000000..0b843db
--- /dev/null
+++ b/xlat/btrfs_dev_stats_values.in
@@ -0,0 +1,11 @@
+#define HAVE_DECL_BTRFS_DEV_STAT_WRITE_ERRS 1
+#define HAVE_DECL_BTRFS_DEV_STAT_READ_ERRS 1
+#define HAVE_DECL_BTRFS_DEV_STAT_FLUSH_ERRS 1
+#define HAVE_DECL_BTRFS_DEV_STAT_CORRUPTION_ERRS 1
+#define HAVE_DECL_BTRFS_DEV_STAT_GENERATION_ERRS 1
+
+BTRFS_DEV_STAT_WRITE_ERRS
+BTRFS_DEV_STAT_READ_ERRS
+BTRFS_DEV_STAT_FLUSH_ERRS
+BTRFS_DEV_STAT_CORRUPTION_ERRS
+BTRFS_DEV_STAT_GENERATION_ERRS
diff --git a/xlat/btrfs_features_compat.in b/xlat/btrfs_features_compat.in
new file mode 100644
index 0000000..e69de29
diff --git a/xlat/btrfs_features_compat_ro.in b/xlat/btrfs_features_compat_ro.in
new file mode 100644
index 0000000..13808c8
--- /dev/null
+++ b/xlat/btrfs_features_compat_ro.in
@@ -0,0 +1 @@
+BTRFS_FEATURE_COMPAT_RO_FREE_SPACE_TREE (1ULL << 0)
diff --git a/xlat/btrfs_features_incompat.in b/xlat/btrfs_features_incompat.in
new file mode 100644
index 0000000..edb21c2
--- /dev/null
+++ b/xlat/btrfs_features_incompat.in
@@ -0,0 +1,10 @@
+BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF    (1ULL << 0)
+BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL   (1ULL << 1)
+BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS     (1ULL << 2)
+BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO     (1ULL << 3)
+BTRFS_FEATURE_INCOMPAT_COMPRESS_LZOv2   (1ULL << 4)
+BTRFS_FEATURE_INCOMPAT_BIG_METADATA     (1ULL << 5)
+BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF    (1ULL << 6)
+BTRFS_FEATURE_INCOMPAT_RAID56           (1ULL << 7)
+BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA  (1ULL << 8)
+BTRFS_FEATURE_INCOMPAT_NO_HOLES         (1ULL << 9)
diff --git a/xlat/btrfs_key_types.in b/xlat/btrfs_key_types.in
new file mode 100644
index 0000000..27f80e0
--- /dev/null
+++ b/xlat/btrfs_key_types.in
@@ -0,0 +1,40 @@
+BTRFS_INODE_ITEM_KEY            1
+BTRFS_INODE_REF_KEY             12
+BTRFS_INODE_EXTREF_KEY          13
+BTRFS_XATTR_ITEM_KEY            24
+BTRFS_ORPHAN_ITEM_KEY           48
+BTRFS_DIR_LOG_ITEM_KEY  60
+BTRFS_DIR_LOG_INDEX_KEY 72
+BTRFS_DIR_ITEM_KEY      84
+BTRFS_DIR_INDEX_KEY     96
+BTRFS_EXTENT_DATA_KEY   108
+BTRFS_EXTENT_CSUM_KEY   128
+BTRFS_ROOT_ITEM_KEY     132
+BTRFS_ROOT_BACKREF_KEY  144
+BTRFS_ROOT_REF_KEY      156
+BTRFS_EXTENT_ITEM_KEY   168
+BTRFS_METADATA_ITEM_KEY 169
+BTRFS_TREE_BLOCK_REF_KEY        176
+BTRFS_EXTENT_DATA_REF_KEY       178
+BTRFS_EXTENT_REF_V0_KEY         180
+BTRFS_SHARED_BLOCK_REF_KEY      182
+BTRFS_SHARED_DATA_REF_KEY       184
+BTRFS_BLOCK_GROUP_ITEM_KEY 192
+BTRFS_FREE_SPACE_INFO_KEY 198
+BTRFS_FREE_SPACE_EXTENT_KEY 199
+BTRFS_FREE_SPACE_BITMAP_KEY 200
+BTRFS_DEV_EXTENT_KEY    204
+BTRFS_DEV_ITEM_KEY      216
+BTRFS_CHUNK_ITEM_KEY    228
+BTRFS_QGROUP_STATUS_KEY         240
+BTRFS_QGROUP_INFO_KEY           242
+BTRFS_QGROUP_LIMIT_KEY          244
+BTRFS_QGROUP_RELATION_KEY       246
+BTRFS_BALANCE_ITEM_KEY  248
+BTRFS_TEMPORARY_ITEM_KEY        248
+BTRFS_DEV_STATS_KEY             249
+BTRFS_PERSISTENT_ITEM_KEY       249
+BTRFS_DEV_REPLACE_KEY   250
+BTRFS_UUID_KEY_SUBVOL   251
+BTRFS_UUID_KEY_RECEIVED_SUBVOL  252
+BTRFS_STRING_ITEM_KEY   253
diff --git a/xlat/btrfs_qgroup_ctl_cmds.in b/xlat/btrfs_qgroup_ctl_cmds.in
new file mode 100644
index 0000000..8e2aa5f
--- /dev/null
+++ b/xlat/btrfs_qgroup_ctl_cmds.in
@@ -0,0 +1,3 @@
+BTRFS_QUOTA_CTL_ENABLE
+BTRFS_QUOTA_CTL_DISABLE
+BTRFS_QUOTA_CTL_RESCAN__NOTUSED
diff --git a/xlat/btrfs_qgroup_inherit_flags.in b/xlat/btrfs_qgroup_inherit_flags.in
new file mode 100644
index 0000000..803c8c0
--- /dev/null
+++ b/xlat/btrfs_qgroup_inherit_flags.in
@@ -0,0 +1 @@
+BTRFS_QGROUP_INHERIT_SET_LIMITS
diff --git a/xlat/btrfs_qgroup_limit_flags.in b/xlat/btrfs_qgroup_limit_flags.in
new file mode 100644
index 0000000..3bf2f8f
--- /dev/null
+++ b/xlat/btrfs_qgroup_limit_flags.in
@@ -0,0 +1,6 @@
+BTRFS_QGROUP_LIMIT_MAX_RFER     (1ULL << 0)
+BTRFS_QGROUP_LIMIT_MAX_EXCL     (1ULL << 1)
+BTRFS_QGROUP_LIMIT_RSV_RFER     (1ULL << 2)
+BTRFS_QGROUP_LIMIT_RSV_EXCL     (1ULL << 3)
+BTRFS_QGROUP_LIMIT_RFER_CMPR    (1ULL << 4)
+BTRFS_QGROUP_LIMIT_EXCL_CMPR    (1ULL << 5)
diff --git a/xlat/btrfs_qgroup_status_flags.in b/xlat/btrfs_qgroup_status_flags.in
new file mode 100644
index 0000000..10236aa
--- /dev/null
+++ b/xlat/btrfs_qgroup_status_flags.in
@@ -0,0 +1,2 @@
+BTRFS_QGROUP_STATUS_FLAG_ON             (1ULL << 0)
+BTRFS_QGROUP_STATUS_FLAG_RESCAN         (1ULL << 1)
diff --git a/xlat/btrfs_scrub_flags.in b/xlat/btrfs_scrub_flags.in
new file mode 100644
index 0000000..31e4f65
--- /dev/null
+++ b/xlat/btrfs_scrub_flags.in
@@ -0,0 +1 @@
+BTRFS_SCRUB_READONLY    1
diff --git a/xlat/btrfs_snap_flags_v2.in b/xlat/btrfs_snap_flags_v2.in
new file mode 100644
index 0000000..3fe3358
--- /dev/null
+++ b/xlat/btrfs_snap_flags_v2.in
@@ -0,0 +1,3 @@
+BTRFS_SUBVOL_CREATE_ASYNC
+BTRFS_SUBVOL_RDONLY
+BTRFS_SUBVOL_QGROUP_INHERIT
diff --git a/xlat/btrfs_space_info_flags.in b/xlat/btrfs_space_info_flags.in
new file mode 100644
index 0000000..9feaf74
--- /dev/null
+++ b/xlat/btrfs_space_info_flags.in
@@ -0,0 +1,11 @@
+BTRFS_BLOCK_GROUP_DATA          (1ULL << 0)
+BTRFS_BLOCK_GROUP_SYSTEM        (1ULL << 1)
+BTRFS_BLOCK_GROUP_METADATA      (1ULL << 2)
+BTRFS_BLOCK_GROUP_RAID0         (1ULL << 3)
+BTRFS_BLOCK_GROUP_RAID1         (1ULL << 4)
+BTRFS_BLOCK_GROUP_DUP           (1ULL << 5)
+BTRFS_BLOCK_GROUP_RAID10        (1ULL << 6)
+BTRFS_BLOCK_GROUP_RAID5         (1ULL << 7)
+BTRFS_BLOCK_GROUP_RAID6         (1ULL << 8)
+BTRFS_AVAIL_ALLOC_BIT_SINGLE    (1ULL << 48)
+BTRFS_SPACE_INFO_GLOBAL_RSV     (1ULL << 49)
diff --git a/xlat/btrfs_tree_objectids.in b/xlat/btrfs_tree_objectids.in
new file mode 100644
index 0000000..cc89c20
--- /dev/null
+++ b/xlat/btrfs_tree_objectids.in
@@ -0,0 +1,12 @@
+BTRFS_ROOT_TREE_OBJECTID 1ULL
+BTRFS_EXTENT_TREE_OBJECTID 2ULL
+BTRFS_CHUNK_TREE_OBJECTID 3ULL
+BTRFS_DEV_TREE_OBJECTID 4ULL
+BTRFS_FS_TREE_OBJECTID 5ULL
+BTRFS_ROOT_TREE_DIR_OBJECTID 6ULL
+BTRFS_CSUM_TREE_OBJECTID 7ULL
+BTRFS_QUOTA_TREE_OBJECTID 8ULL
+BTRFS_UUID_TREE_OBJECTID 9ULL
+BTRFS_FREE_SPACE_TREE_OBJECTID 10ULL
+BTRFS_FIRST_FREE_OBJECTID 256ULL
+BTRFS_LAST_FREE_OBJECTID -256ULL
-- 
2.7.1





More information about the Strace-devel mailing list