From: Jeff Mahoney Date: Wed, 18 May 2016 22:09:39 +0000 (-0400) Subject: ioctl: add decoding support for btrfs ioctls X-Git-Tag: v4.12~100 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8cc6962216d302525be2ba4575932061bb4abd15;p=strace 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 1585a7cf..c165b81c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -81,6 +81,7 @@ strace_SOURCES = \ bjm.c \ block.c \ bpf.c \ + btrfs.c \ cacheflush.c \ capability.c \ caps0.h \ @@ -110,6 +111,7 @@ strace_SOURCES = \ 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 00000000..7a374c52 --- /dev/null +++ b/btrfs.c @@ -0,0 +1,1369 @@ +/* + * Copyright (c) 2016 Jeff Mahoney + * 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 +#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 + +#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 e50390e7..6d2d1482 100644 --- a/configure.ac +++ b/configure.ac @@ -453,6 +453,18 @@ AC_CHECK_TYPES([struct statfs64], [ ],, [#include #include ]) +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 +#include ]) + AC_CHECK_DECLS(m4_normalize([BTRFS_COMPRESS_NONE, BTRFS_COMPRESS_ZLIB, + BTRFS_COMPRESS_LZO]),,,[ #include +#include ])]) + AC_CHECK_DECLS([sys_errlist]) AC_CHECK_DECLS(m4_normalize([ PTRACE_PEEKUSER, diff --git a/defs.h b/defs.h index 3cde1450..23828038 100644 --- a/defs.h +++ b/defs.h @@ -660,7 +660,9 @@ extern void print_struct_statfs(struct tcb *tcp, long); 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 00000000..3a735dc4 --- /dev/null +++ b/file_ioctl.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016 Jeff Mahoney + * 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 +#include +#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 1b69e977..d82af4b9 100644 --- a/ioctl.c +++ b/ioctl.c @@ -266,6 +266,10 @@ ioctl_decode(struct tcb *tcp) #ifdef HAVE_LINUX_USERFAULTFD_H 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 00000000..ffeaacc0 --- /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 00000000..5cdd0bc5 --- /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 00000000..00ae0d10 --- /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 00000000..5dbfca5e --- /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 00000000..c47d59fc --- /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 00000000..ae026c84 --- /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 00000000..18600f0f --- /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 00000000..bf892823 --- /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 00000000..bbc0ba15 --- /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 00000000..9ff56c33 --- /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 00000000..ffb74055 --- /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 00000000..e69de29b diff --git a/xlat/btrfs_features_compat_ro.in b/xlat/btrfs_features_compat_ro.in new file mode 100644 index 00000000..3fd1b6dc --- /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 00000000..45baacf5 --- /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 00000000..5721e600 --- /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 00000000..05e20036 --- /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 00000000..0e133b9c --- /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 00000000..bac7abed --- /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 00000000..86f60ac5 --- /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 00000000..914de3d1 --- /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 00000000..652ef390 --- /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 00000000..43ea5749 --- /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 00000000..be6d1951 --- /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 00000000..0b6e598c --- /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