ioctl: add decoding support for btrfs ioctls

* btrfs.c: New file.
* file_ioctl.c: Likewise.
* Makefile.am (strace_SOURCES): Add them.
* configure.ac (AC_CHECK_HEADERS): Add linux/btrfs.h.
(AC_CHECK_MEMBERS): Add struct btrfs_ioctl_feature_flags.compat_flags,
struct btrfs_ioctl_fs_info_args.nodesize,
struct btrfs_ioctl_defrag_range_args.start,
and struct btrfs_ioctl_search_args_v2.buf_size.
(AC_CHECK_DECLS): Add BTRFS_COMPRESS_* enums.
* defs.h (btrfs_ioctl, file_ioctl): New prototypes.
* ioctl.c (ioctl_decode) [HAVE_LINUX_BTRFS_H]: Use btrfs_ioctl.
* xlat/btrfs_balance_args.in: New file.
* xlat/btrfs_balance_ctl_cmds.in: Likewise.
* xlat/btrfs_balance_flags.in: Likewise.
* xlat/btrfs_balance_state.in: Likewise.
* xlat/btrfs_compress_types.in: Likewise.
* xlat/btrfs_defrag_flags.in: Likewise.
* xlat/btrfs_dev_replace_cmds.in: Likewise.
* xlat/btrfs_dev_replace_results.in: Likewise.
* xlat/btrfs_dev_replace_state.in: Likewise.
* xlat/btrfs_dev_stats_flags.in: Likewise.
* xlat/btrfs_dev_stats_values.in: Likewise.
* xlat/btrfs_features_compat.in: Likewise.
* xlat/btrfs_features_compat_ro.in: Likewise.
* xlat/btrfs_features_incompat.in: Likewise.
* xlat/btrfs_key_types.in: Likewise.
* xlat/btrfs_qgroup_ctl_cmds.in: Likewise.
* xlat/btrfs_qgroup_inherit_flags.in: Likewise.
* xlat/btrfs_qgroup_limit_flags.in: Likewise.
* xlat/btrfs_qgroup_status_flags.in: Likewise.
* xlat/btrfs_scrub_flags.in: Likewise.
* xlat/btrfs_send_flags.in: Likewise.
* xlat/btrfs_snap_flags_v2.in: Likewise.
* xlat/btrfs_space_info_flags.in: Likewise.
* xlat/btrfs_tree_objectids.in: Likewise.
diff --git a/Makefile.am b/Makefile.am
index 1585a7c..c165b81 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -81,6 +81,7 @@
 	bjm.c		\
 	block.c		\
 	bpf.c		\
+	btrfs.c		\
 	cacheflush.c	\
 	capability.c	\
 	caps0.h		\
@@ -110,6 +111,7 @@
 	fetch_struct_statfs.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..7a374c5
--- /dev/null
+++ b/btrfs.c
@@ -0,0 +1,1369 @@
+/*
+ * Copyright (c) 2016 Jeff Mahoney <jeffm@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_args.h"
+#include "xlat/btrfs_balance_ctl_cmds.h"
+#include "xlat/btrfs_balance_flags.h"
+#include "xlat/btrfs_balance_state.h"
+#include "xlat/btrfs_compress_types.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_send_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 = '\0';
+	return ret;
+}
+
+static void
+print_u64(const char *name, uint64_t value)
+{
+	tprintf(", %s=%" PRIu64, name, value);
+	if (value == UINT64_MAX)
+		tprints(" /* UINT64_MAX */");
+}
+
+#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=", name);
+	printflags64(btrfs_space_info_flags, bba->profiles,
+		     "BTRFS_BLOCK_GROUP_???");
+	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_args, bba->flags, "BTRFS_BALANCE_ARGS_???");
+	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) {
+		tprints(", state=");
+		printflags64(btrfs_balance_state, balance_args.state,
+			     "BTRFS_BALANCE_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 uint64_t
+data_container_record_offset(unsigned int index)
+{
+	return offsetof(struct btrfs_data_container, val[index]);
+}
+
+static void
+btrfs_print_logical_ino_container(struct tcb *tcp, uint64_t inodes_addr)
+{
+	struct btrfs_data_container container;
+	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("[");
+
+	for (i = 0; i < container.elem_cnt; i += 3, printed++) {
+		uint64_t offset = data_container_record_offset(i);
+		uint64_t record[3];
+
+		if (i)
+			tprints(", ");
+
+		if (printed > max_strlen ||
+		    umoven(tcp, inodes_addr + offset, 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;
+
+	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("[");
+
+	for (i = 0; i < container.elem_cnt; i++) {
+		uint64_t offset = data_container_record_offset(i);
+		uint64_t ptr;
+
+		if (i)
+			tprints(", ");
+
+		if (i > max_strlen ||
+		    umoven(tcp, fspath_addr + offset, sizeof(ptr), &ptr)) {
+			tprints("...");
+			break;
+		}
+
+		printpath(tcp,
+			  fspath_addr + data_container_record_offset(0) + 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)
+		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 {
+		uint32_t i;
+
+		tprints("[");
+		for (i = 0; i < inherit.num_qgroups; i++) {
+			uint64_t offset = offsetof(typeof(inherit), qgroups[i]);
+			uint64_t record;
+			if (i)
+				tprints(", ");
+			if (i > max_strlen ||
+			    umoven(tcp, qgi_addr + offset,
+				   sizeof(record), &record)) {
+				tprints("...");
+				break;
+			}
+
+			tprintf("%" PRIu64, record);
+		}
+		tprints("]");
+	}
+	tprints("}");
+}
+
+static void
+print_key_value_internal(struct tcb *tcp, const char *name, uint64_t value)
+{
+	if (value) {
+		tprintf(", %s=%" PRIu64, name, value);
+		if (value == UINT64_MAX)
+			tprints(" /* UINT64_MAX */");
+	}
+}
+#define print_key_value(tcp, key, name)					\
+	print_key_value_internal((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, bool print_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);
+
+		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);
+		if (print_size)
+			tprintf(", buf_size=%" PRIu64, buf_size);
+		tprints("}");
+		return;
+	}
+	tprintf("{key={nr_items=%u}", key->nr_items);
+	if (print_size)
+		tprintf(", buf_size=%" PRIu64, buf_size);
+	tprints(", buf=");
+	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:
+	/*
+	 * The codes for these ioctls are based on each accepting a
+	 * vol_args but none of them actually consume an argument.
+	 */
+	case BTRFS_IOC_DEFRAG:
+	case BTRFS_IOC_BALANCE:
+		break;
+
+	/* take a signed int */
+	case BTRFS_IOC_BALANCE_CTL: {
+		tprints(", ");
+		printxvals(arg, "BTRFS_BALANCE_CTL_???",
+			   btrfs_balance_ctl_cmds, NULL);
+		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=", (uint64_t)args.start);
+
+		tprintf("%" PRIu64, args.len);
+		if (args.len == UINT64_MAX)
+			tprints(" /* UINT64_MAX */");
+
+		tprints(", flags=");
+		printflags64(btrfs_defrag_flags, args.flags,
+			     "BTRFS_DEFRAG_RANGE_???");
+		tprintf(", extent_thresh=%u, compress_type=",
+			args.extent_thresh);
+		printxvals(args.compress_type, "BTRFS_COMPRESS_???",
+			   btrfs_compress_types, NULL);
+		tprints("}");
+		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(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			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, max_nr_items;
+
+		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 ", flags=", args.nr_items);
+		printflags64(btrfs_dev_stats_flags, args.flags,
+			     "BTRFS_DEV_STATS_???");
+
+		if (entering(tcp)) {
+			tprints("}");
+			return 0;
+		}
+
+		/*
+		 * The structure has a 1k limit; Let's make sure we don't
+		 * go off into the middle of nowhere with a bad nr_items
+		 * value.
+		 */
+		max_nr_items = sizeof(args) - offsetof(typeof(args), values);
+
+		tprints(", [");
+		for (i = 0; i < args.nr_items; i++) {
+			const char *name = xlookup(btrfs_dev_stats_values, i);
+			if (i)
+				tprints(", ");
+			if (i > max_nr_items) {
+				tprints("/* overflow */");
+				break;
+			}
+			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;
+			else
+				tcp->auxstr = NULL;
+
+			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);
+			tprintf(", fspath=0x%" PRI__x64 "}", args.fspath);
+			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);
+			tprintf(", inodes=0x%" PRI__x64 "}", args.inodes);
+			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);
+		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);
+				tprintf("%" PRI__u64, args.end);
+				if (args.end == UINT64_MAX)
+					tprints(" /* UINT64_MAX */");
+				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_offset;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		buf_offset = offsetof(struct btrfs_ioctl_search_args, buf);
+		btrfs_print_tree_search(tcp, &args.key, arg + buf_offset,
+					sizeof(args.buf), false);
+		if (entering(tcp))
+			return 0;
+		break;
+	}
+
+	case BTRFS_IOC_TREE_SEARCH_V2: { /* RW */
+		struct btrfs_ioctl_search_args_v2 args;
+		uint64_t buf_offset;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp)) {
+			if (tcp->u_error == EOVERFLOW) {
+				tcp->u_error = 0;
+				if (!umove_or_printaddr(tcp, arg, &args))
+					tprintf(" => {buf_size=%" PRIu64 "}",
+						(uint64_t)args.buf_size);
+				tcp->u_error = EOVERFLOW;
+			}
+			break;
+		} else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		buf_offset = offsetof(struct btrfs_ioctl_search_args_v2, buf);
+		btrfs_print_tree_search(tcp, &args.key, arg + buf_offset,
+					args.buf_size, true);
+		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__d64 ", clone_sources_count=%" PRI__u64
+			", clone_sources=", args.send_fd,
+			args.clone_sources_count);
+
+		if (abbrev(tcp)) {
+			tprints("...");
+		} else {
+			tprints("[");
+			base_addr = (unsigned long)args.clone_sources;
+			for (i = 0; i < args.clone_sources_count; i++) {
+				uint64_t offset = sizeof(uint64_t) * i;
+				uint64_t record;
+				if (i)
+					tprints(", ");
+				if (i > max_strlen ||
+				    umoven(tcp, base_addr + offset,
+					   sizeof(record), &record)) {
+					tprints("...");
+					break;
+				}
+				btrfs_print_objectid(record);
+			}
+			tprints("]");
+		}
+		tprints(", parent_root=");
+		btrfs_print_objectid(args.parent_root);
+		tprints(", flags=");
+		printflags64(btrfs_send_flags, args.flags,
+			     "BTRFS_SEND_FLAGS_???");
+		tprints("}");
+		break;
+	}
+
+	case BTRFS_IOC_SPACE_INFO: { /* RW */
+		struct btrfs_ioctl_space_args 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("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("[");
+
+		for (i = 0; i < args.total_spaces; i++) {
+			struct btrfs_ioctl_space_info info;
+			uint64_t off = offsetof(typeof(args), spaces[i]);
+			if (i)
+				tprints(", ");
+
+			if (i > max_strlen ||
+			    umoven(tcp, arg + off, 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_RESIZE:
+	case BTRFS_IOC_SCAN_DEV:
+	case BTRFS_IOC_ADD_DEV:
+	case BTRFS_IOC_RM_DEV:
+	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__d64 ", 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: { /* code is W, but is actually RW */
+		struct btrfs_ioctl_vol_args_v2 args;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		if (entering(tcp)) {
+			tprintf("{fd=%" PRI__d64 ", 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("}");
+			return 0;
+		}
+		tprintf("{transid=%" PRI__u64 "}", args.transid);
+		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 e50390e..6d2d148 100644
--- a/configure.ac
+++ b/configure.ac
@@ -453,6 +453,18 @@
 ],, [#include <linux/types.h>
 #include <asm/statfs.h>])
 
+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
+		struct btrfs_ioctl_search_args_v2.buf_size
+		]),,, [ #include <stdio.h>
+#include <linux/btrfs.h>])
+        AC_CHECK_DECLS(m4_normalize([BTRFS_COMPRESS_NONE, BTRFS_COMPRESS_ZLIB,
+	                        BTRFS_COMPRESS_LZO]),,,[ #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 3cde145..2382803 100644
--- a/defs.h
+++ b/defs.h
@@ -660,7 +660,9 @@
 extern void print_struct_statfs64(struct tcb *tcp, long, 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/file_ioctl.c b/file_ioctl.c
new file mode 100644
index 0000000..3a735dc
--- /dev/null
+++ b/file_ioctl.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2016 Jeff Mahoney <jeffm@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 <sys/ioctl.h>
+#include <linux/fs.h>
+#ifndef FICLONE
+#define FICLONE         _IOW(0x94, 9, int)
+#endif
+
+#ifndef FICLONERANGE
+#define FICLONERANGE    _IOW(0x94, 13, struct file_clone_range)
+struct file_clone_range {
+	int64_t src_fd;
+	uint64_t src_offset;
+	uint64_t src_length;
+	uint64_t dest_offset;
+};
+#endif
+
+#ifndef FIDEDUPERANGE
+#define FIDEDUPERANGE   _IOWR(0x94, 54, struct file_dedupe_range)
+struct file_dedupe_range_info {
+	int64_t dest_fd;	/* in - destination file */
+	uint64_t dest_offset;	/* in - start of extent in destination */
+	uint64_t bytes_deduped;	/* out - total # of bytes we were able
+				 * to dedupe from this file. */
+	/* status of this dedupe operation:
+	 * < 0 for error
+	 * == FILE_DEDUPE_RANGE_SAME if dedupe succeeds
+	 * == FILE_DEDUPE_RANGE_DIFFERS if data differs
+	 */
+	int32_t status;		/* out - see above description */
+	uint32_t reserved;	/* must be zero */
+};
+
+struct file_dedupe_range {
+	uint64_t src_offset;	/* in - start of extent in source */
+	uint64_t src_length;	/* in - length of extent */
+	uint16_t dest_count;	/* in - total elements in info array */
+	uint16_t reserved1;	/* must be zero */
+	uint32_t reserved2;	/* must be zero */
+	struct file_dedupe_range_info info[0];
+};
+#endif
+
+int
+file_ioctl(struct tcb *tcp, const unsigned int code, const long arg)
+{
+	switch (code) {
+	/* take a signed int */
+	case FICLONE:	/* W */
+		tprintf(", %d", (int)arg);
+		break;
+
+	case FICLONERANGE: { /* W */
+		struct file_clone_range args;
+
+		tprints(", ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		tprintf("{src_fd=%" PRIi64 ", "
+			"src_offset=%" PRIu64 ", "
+			"src_length=%" PRIu64 ", "
+			"dest_offset=%" PRIu64 "}",
+			(int64_t)args.src_fd, (uint64_t)args.src_offset,
+			(uint64_t)args.src_length, (uint64_t)args.dest_offset);
+		break;
+	}
+
+	case FIDEDUPERANGE: { /* RW */
+		struct file_dedupe_range args;
+		uint64_t info_addr;
+		uint16_t i;
+
+		if (entering(tcp))
+			tprints(", ");
+		else if (syserror(tcp))
+			break;
+		else
+			tprints(" => ");
+
+		if (umove_or_printaddr(tcp, arg, &args))
+			break;
+
+		if (entering(tcp)) {
+			tprintf("{src_offset=%" PRIu64 ", "
+				"src_length=%" PRIu64 ", "
+				"dest_count=%hu, info=",
+				(uint64_t)args.src_offset,
+				(uint64_t)args.src_length,
+				(uint16_t)args.dest_count);
+		} else
+			tprints("{info=");
+
+		if (abbrev(tcp)) {
+			tprints("...}");
+		} else {
+			tprints("[");
+			info_addr = arg + offsetof(typeof(args), info);
+			for (i = 0; i < args.dest_count; i++) {
+				struct file_dedupe_range_info info;
+				uint64_t addr = info_addr + sizeof(info) * i;
+				if (i)
+					tprints(", ");
+
+				if (umoven(tcp, addr, sizeof(info), &info)) {
+					tprints("...");
+					break;
+				}
+
+				if (entering(tcp))
+					tprintf("{dest_fd=%" PRIi64 ", "
+						"dest_offset=%" PRIu64 "}",
+						(int64_t)info.dest_fd,
+						(uint64_t)info.dest_offset);
+				else {
+					tprintf("{bytes_deduped=%" PRIu64 ", "
+						"status=%d}",
+						(uint64_t)info.bytes_deduped,
+						info.status);
+				}
+			}
+			tprints("]}");
+		}
+		if (entering(tcp))
+			return 0;
+		break;
+	}
+
+	default:
+		return RVAL_DECODED;
+	};
+
+	return RVAL_DECODED | 1;
+}
diff --git a/ioctl.c b/ioctl.c
index 1b69e97..d82af4b 100644
--- a/ioctl.c
+++ b/ioctl.c
@@ -267,6 +267,10 @@
 	case 0xaa:
 		return uffdio_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_args.in b/xlat/btrfs_balance_args.in
new file mode 100644
index 0000000..ffeaacc
--- /dev/null
+++ b/xlat/btrfs_balance_args.in
@@ -0,0 +1,12 @@
+#val_type uint64_t
+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_balance_ctl_cmds.in b/xlat/btrfs_balance_ctl_cmds.in
new file mode 100644
index 0000000..5cdd0bc
--- /dev/null
+++ b/xlat/btrfs_balance_ctl_cmds.in
@@ -0,0 +1,2 @@
+BTRFS_BALANCE_CTL_PAUSE         1
+BTRFS_BALANCE_CTL_CANCEL        2
diff --git a/xlat/btrfs_balance_flags.in b/xlat/btrfs_balance_flags.in
new file mode 100644
index 0000000..00ae0d1
--- /dev/null
+++ b/xlat/btrfs_balance_flags.in
@@ -0,0 +1,6 @@
+#val_type uint64_t
+BTRFS_BALANCE_DATA              (1ULL << 0)
+BTRFS_BALANCE_SYSTEM            (1ULL << 1)
+BTRFS_BALANCE_METADATA          (1ULL << 2)
+BTRFS_BALANCE_FORCE		(1ULL << 3)
+BTRFS_BALANCE_RESUME		(1ULL << 4)
diff --git a/xlat/btrfs_balance_state.in b/xlat/btrfs_balance_state.in
new file mode 100644
index 0000000..5dbfca5
--- /dev/null
+++ b/xlat/btrfs_balance_state.in
@@ -0,0 +1,3 @@
+BTRFS_BALANCE_STATE_RUNNING     (1ULL << 0)
+BTRFS_BALANCE_STATE_PAUSE_REQ   (1ULL << 1)
+BTRFS_BALANCE_STATE_CANCEL_REQ  (1ULL << 2)
diff --git a/xlat/btrfs_compress_types.in b/xlat/btrfs_compress_types.in
new file mode 100644
index 0000000..c47d59f
--- /dev/null
+++ b/xlat/btrfs_compress_types.in
@@ -0,0 +1,3 @@
+BTRFS_COMPRESS_NONE 0
+BTRFS_COMPRESS_ZLIB 1
+BTRFS_COMPRESS_LZO 2
diff --git a/xlat/btrfs_defrag_flags.in b/xlat/btrfs_defrag_flags.in
new file mode 100644
index 0000000..ae026c8
--- /dev/null
+++ b/xlat/btrfs_defrag_flags.in
@@ -0,0 +1,3 @@
+#val_type uint64_t
+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..18600f0
--- /dev/null
+++ b/xlat/btrfs_dev_replace_cmds.in
@@ -0,0 +1,4 @@
+#val_type uint64_t
+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..bf89282
--- /dev/null
+++ b/xlat/btrfs_dev_replace_results.in
@@ -0,0 +1,5 @@
+#val_type uint64_t
+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..bbc0ba1
--- /dev/null
+++ b/xlat/btrfs_dev_replace_state.in
@@ -0,0 +1,6 @@
+#val_type uint64_t
+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..9ff56c3
--- /dev/null
+++ b/xlat/btrfs_dev_stats_flags.in
@@ -0,0 +1,2 @@
+#val_type uint64_t
+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..ffb7405
--- /dev/null
+++ b/xlat/btrfs_dev_stats_values.in
@@ -0,0 +1,12 @@
+#val_type uint64_t
+#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
--- /dev/null
+++ b/xlat/btrfs_features_compat.in
diff --git a/xlat/btrfs_features_compat_ro.in b/xlat/btrfs_features_compat_ro.in
new file mode 100644
index 0000000..3fd1b6d
--- /dev/null
+++ b/xlat/btrfs_features_compat_ro.in
@@ -0,0 +1,2 @@
+#val_type uint64_t
+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..45baacf
--- /dev/null
+++ b/xlat/btrfs_features_incompat.in
@@ -0,0 +1,11 @@
+#val_type uint64_t
+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..5721e60
--- /dev/null
+++ b/xlat/btrfs_key_types.in
@@ -0,0 +1,42 @@
+#val_type uint64_t
+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
+UINT32_MAX -1U
diff --git a/xlat/btrfs_qgroup_ctl_cmds.in b/xlat/btrfs_qgroup_ctl_cmds.in
new file mode 100644
index 0000000..05e2003
--- /dev/null
+++ b/xlat/btrfs_qgroup_ctl_cmds.in
@@ -0,0 +1,4 @@
+#val_type uint64_t
+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..0e133b9
--- /dev/null
+++ b/xlat/btrfs_qgroup_inherit_flags.in
@@ -0,0 +1,2 @@
+#val_type uint64_t
+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..bac7abe
--- /dev/null
+++ b/xlat/btrfs_qgroup_limit_flags.in
@@ -0,0 +1,7 @@
+#val_type uint64_t
+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..86f60ac
--- /dev/null
+++ b/xlat/btrfs_qgroup_status_flags.in
@@ -0,0 +1,3 @@
+#val_type uint64_t
+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..914de3d
--- /dev/null
+++ b/xlat/btrfs_scrub_flags.in
@@ -0,0 +1,2 @@
+#val_type uint64_t
+BTRFS_SCRUB_READONLY    1
diff --git a/xlat/btrfs_send_flags.in b/xlat/btrfs_send_flags.in
new file mode 100644
index 0000000..652ef39
--- /dev/null
+++ b/xlat/btrfs_send_flags.in
@@ -0,0 +1,3 @@
+BTRFS_SEND_FLAG_NO_FILE_DATA            0x1
+BTRFS_SEND_FLAG_OMIT_STREAM_HEADER      0x2
+BTRFS_SEND_FLAG_OMIT_END_CMD            0x4
diff --git a/xlat/btrfs_snap_flags_v2.in b/xlat/btrfs_snap_flags_v2.in
new file mode 100644
index 0000000..43ea574
--- /dev/null
+++ b/xlat/btrfs_snap_flags_v2.in
@@ -0,0 +1,4 @@
+#val_type uint64_t
+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..be6d195
--- /dev/null
+++ b/xlat/btrfs_space_info_flags.in
@@ -0,0 +1,12 @@
+#val_type uint64_t
+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..0b6e598
--- /dev/null
+++ b/xlat/btrfs_tree_objectids.in
@@ -0,0 +1,14 @@
+#val_type uint64_t
+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
+UINT64_MAX -1ULL