Message ID | 20200823195925.117104-1-Filip.Bozuta@syrmia.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | linux-user: Add support for a group of '_V2' btrfs ioctls | expand |
Le 23/08/2020 à 21:59, Filip Bozuta a écrit : > This patch introduces functionality for following btrfs ioctls: > > BTRFS_IOC_SUBVOL_CREATE_V2 - Adding a new btrfs subvolume > > Create a new btrfs subvolume (same as with BTRFS_IOC_SUBVOL_CREATE). > The third ioctl's argument is a pointer to a following type: > > struct btrfs_ioctl_vol_args_v2 { > __s64 fd; > __u64 transid; > __u64 flags; > union { > struct { > __u64 size; > struct btrfs_qgroup_inherit __user *qgroup_inherit; > }; > __u64 unused[4]; > }; > union { > char name[BTRFS_SUBVOL_NAME_MAX + 1]; > __u64 devid; > __u64 subvolid; /* added in kernel version 5.8 */ > }; > }; > > When calling this ioctl, the 'name' field should be filled with > the aproppriate value that contains the name of the subvolume that > is to be created. The flags field can take values that are > 'BTRFS_SUBVOL_RDONLY' or 'BTRFS_SUBVOL_QGROUP_INHERIT'. If the > latter is specified, the field 'qgroup_inherit' should be filled > with aproppriate values of the quota group in which the newly > created subvolume is to be added. The definition of > 'struct btrfs_qgroup_inherit' can be found at: > https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/btrfs.h#L90 > > BTRFS_IOC_SNAP_CREATE_V2 - Adding a btrfs snapshot for a subvolume > > Create a new btrfs snapshot for a specified subvolume (same as with > BTRFS_IOC_SNAP_CREATE). The ioctl's third argument is a pointer to > the above mentioned 'struct btrfs_ioctl_vol_args_v2'. Before calling > this ioctl, field 'fd' should be filled with the aproppriate file > descriptor value for the btrfs subvolume for which the snapshot is > to be created. Also, the 'name' field should be filled with the > aproppriate value that represents the name of the snapshot that is > to be created. The 'flags' field takes the same values as in case > of 'BTRFS_IOC_SUBVOL_CREATE_V2' and represents the same functionality. > > BTRFS_IOC_RM_DEV_V2 - Removing a btrfs device > > Delete a btrfs device (same as with BTRFS_IOC_RM_DEV). The ioctl's third > argument is a pointer to the above mentioned 'struct btrfs_ioctl_vol_args_v2'. > Before calling this ioctl, either the 'name' or the 'devid' field should > be filled with the name or id of the device that is to be removed. Also, the > flags field should be filled either with 0 or 'BTRFS_DEVICE_SPEC_BY_ID' > depending on if the device is gonna be specified via name or id. > > BTRFS_IOC_SNAP_DESTROY_V2 - Removing a btrfs snapshot > > Remove a btrfs snapshot (same as with BTRFS_IOC_SNAP_DESTROY). The ioctl's > third argument is a pointer to the above mentioned 'struct btrfs_ioctl_vol_args_v2'. > Before calling this ioctl, either the 'name' or the 'subvolid' field should > be filled with the name or id of the snapshot that is to be removed. Also, the > flags field should be filled either with 0 or 'BTRFS_SUBVOL_SPEC_BY_ID' > depending on if the snapshot is gonna be specified via name or id. > > Implementation notes: > > Since the third argument of the implemented ioctl's is a > structure that contains unions, a special converting function > 'target_to_host_btrfs_ioctl_vol_args_v2' was defined in 'syscall.c'. > This function is called instead of 'thunk_convert()' to convert > the values of the third argument from target to host. All of > the ioctls in this patch are of type 'IOW' which is why a converting > function from host to target is not required. Also, a separate printing > function was defined in file 'strace.c' that is called instead of > 'thunk_print()' to print the contents of the third argument. > > Signed-off-by: Filip Bozuta <Filip.Bozuta@syrmia.com> > Based-on: <20200811164553.27713-2-Filip.Bozuta@syrmia.com> > Based-on: <20200723210233.349690-4-Filip.Bozuta@syrmia.com> > --- > linux-user/ioctls.h | 16 +++++++ > linux-user/qemu.h | 5 ++ > linux-user/strace.c | 95 ++++++++++++++++++++++++++++++++++++++ > linux-user/syscall.c | 77 ++++++++++++++++++++++++++++++ > linux-user/syscall_defs.h | 28 +++++++++++ > linux-user/syscall_types.h | 5 ++ > thunk.c | 2 +- > 7 files changed, 227 insertions(+), 1 deletion(-) > > diff --git a/linux-user/qemu.h b/linux-user/qemu.h > index a69a0bd347..4add164673 100644 > --- a/linux-user/qemu.h > +++ b/linux-user/qemu.h > @@ -708,6 +708,11 @@ static inline uint64_t target_offset64(uint64_t word0, uint64_t word1) > > void print_termios(void *arg); > > +#if defined(BTRFS_IOC_SUBVOL_CREATE_V2) || defined(BTRFS_IOC_SNAP_CREATE_V2) \ > + || defined(BTRFS_IOC_SNAP_DESTROY_V2) || defined(BTRFS_IOC_RM_DEV_V2) The #if is not needed on the declaration of the symbol. It can be unused or not defined, there will be no error. > +void print_btrfs_ioctl_vol_args_v2(void *arg); > +#endif > + > /* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */ > #ifdef TARGET_ARM > static inline int regpairs_aligned(void *cpu_env, int num) > diff --git a/linux-user/strace.c b/linux-user/strace.c > index b9ba39ce6e..04fe38a846 100644 > --- a/linux-user/strace.c > +++ b/linux-user/strace.c > @@ -9,6 +9,9 @@ > #include <netinet/tcp.h> > #include <linux/if_packet.h> > #include <linux/netlink.h> > +#ifdef CONFIG_BTRFS > +#include <linux/btrfs.h> > +#endif > #include <sched.h> > #include "qemu.h" > ... > @@ -1774,6 +1793,82 @@ print_termios(void *arg) > qemu_log("}"); > } > > +#if defined(BTRFS_IOC_SUBVOL_CREATE_V2) || defined(BTRFS_IOC_SNAP_CREATE_V2) \ > + || defined(BTRFS_IOC_SNAP_DESTROY_V2) || defined(BTRFS_IOC_RM_DEV_V2) > +void > +print_btrfs_ioctl_vol_args_v2(void *arg) > +{ > + struct target_btrfs_ioctl_vol_args_v2 *target_args_v2 = arg; > + uint64_t flags = tswap64(target_args_v2->flags); > + int device_spec_by_id = 0; > + int subvol_spec_by_id = 0; > + > + qemu_log("{fd = %" PRId64 ", flags = ", tswap64(target_args_v2->fd)); > + print_flags(btrfs_args_vol_v2_flags, flags, 0); > + qemu_log(" transid = %" PRIu64 ",", tswap64(target_args_v2->transid)); > + > +#ifdef BTRFS_SUBVOL_QGROUP_INHERIT > + if (flags & BTRFS_SUBVOL_QGROUP_INHERIT) { > + > + struct btrfs_qgroup_inherit *target_inherit; > + abi_long inherit_addr = tswapal(target_args_v2->qgroup_inherit); > + > + target_inherit = lock_user(VERIFY_READ, inherit_addr, > + sizeof(*target_inherit), 1); > + > + if (target_inherit) { > + qemu_log(" size = %" PRIu64, > + tswap64(target_args_v2->size)); > + qemu_log(", qgroup_inherit = {"); > + > + qemu_log("flags = %" PRIu64, > + tswap64(target_inherit->flags)); > + qemu_log(", num_qgroups = %" PRIu64, > + tswap64(target_inherit->num_qgroups)); > + qemu_log(", num_ref_copies = %" PRIu64, > + tswap64(target_inherit->num_ref_copies)); > + qemu_log(", num_excl_copies = %" PRIu64, > + tswap64(target_inherit->num_excl_copies)); > + > + qemu_log(", lim = {"); > + qemu_log("lim.flags = %" PRIu64, > + tswap64(target_inherit->lim.flags)); > + qemu_log(", lim.max_rfer = %" PRIu64, > + tswap64(target_inherit->lim.max_rfer)); > + qemu_log(", lim.max_excl = %" PRIu64, > + tswap64(target_inherit->lim.max_excl)); > + qemu_log(", lim.rsv_rfer = %" PRIu64, > + tswap64(target_inherit->lim.rsv_rfer)); > + qemu_log(", lim.rsv_excl = %" PRIu64 "}", > + tswap64(target_inherit->lim.rsv_excl)); > + > + qemu_log(", ...}, "); > + } > + > + unlock_user(target_inherit, inherit_addr, 0); > + } > +#endif > + > +#ifdef BTRFS_DEVICE_SPEC_BY_ID > + if (flags & BTRFS_DEVICE_SPEC_BY_ID) { > + qemu_log(" devid = %" PRIu64, tswap64(target_args_v2->devid)); > + device_spec_by_id = 1; > + } > +#endif > +#ifdef BTRFS_SUBVOL_SPEC_BY_ID > + if (flags & BTRFS_SUBVOL_SPEC_BY_ID) { > + qemu_log(" subvolid = %" PRIu64, tswap64(target_args_v2->subvolid)); > + subvol_spec_by_id = 1; > + } > +#endif > + if (!device_spec_by_id && !subvol_spec_by_id) { > + qemu_log(" name = \"%s\"", target_args_v2->name); If the name is not NUL terminated we can have an overflow here. > + } > + > + qemu_log("}"); > +} > +#endif > + > #undef UNUSED > > #ifdef TARGET_NR_accept > diff --git a/linux-user/syscall.c b/linux-user/syscall.c > index b58098b5bb..78d7106b80 100644 > --- a/linux-user/syscall.c > +++ b/linux-user/syscall.c > @@ -5668,6 +5668,83 @@ static const StructEntry struct_termios_def = { > .print = print_termios, > }; > > +#if defined(BTRFS_IOC_SUBVOL_CREATE_V2) || defined(BTRFS_IOC_SNAP_CREATE_V2) \ > + || defined(BTRFS_SNAP_DESTROY_V2) || defined(BTRFS_RM_DEV_V2) > +static void target_to_host_btrfs_ioctl_vol_args_v2(void *dst, const void *src) > +{ > + struct btrfs_ioctl_vol_args_v2 *host_args_v2 = dst; > + const struct target_btrfs_ioctl_vol_args_v2 *target_args_v2 = src; > + int device_spec_by_id = 0; > + int subvol_spec_by_id = 0; > + > + __get_user(host_args_v2->fd, &target_args_v2->fd); > + __get_user(host_args_v2->flags, &target_args_v2->flags); > + __get_user(host_args_v2->transid, &target_args_v2->transid); > + > + if (host_args_v2->flags & BTRFS_SUBVOL_QGROUP_INHERIT) { > + struct btrfs_qgroup_inherit *target_inherit; > + abi_long inherit_addr = tswapal(target_args_v2->qgroup_inherit); > + > + target_inherit = lock_user(VERIFY_READ, inherit_addr, > + sizeof(*target_inherit), 1); > + > + host_args_v2->qgroup_inherit = g_new(struct btrfs_qgroup_inherit, 1); Where do you free this memory? And the size is in fact variable and given by size in btrfs_ioctl_vol_args_v2. > + > + if (!target_inherit) { > + host_args_v2->qgroup_inherit = NULL; And you overwrite the pointer here > + } else { > + __get_user(host_args_v2->qgroup_inherit->flags, > + &target_inherit->flags); > + __get_user(host_args_v2->qgroup_inherit->num_qgroups, > + &target_inherit->num_qgroups); > + __get_user(host_args_v2->qgroup_inherit->num_ref_copies, > + &target_inherit->num_ref_copies); > + __get_user(host_args_v2->qgroup_inherit->num_excl_copies, > + &target_inherit->num_excl_copies); > + __get_user(host_args_v2->qgroup_inherit->lim.flags, > + &target_inherit->lim.flags); > + __get_user(host_args_v2->qgroup_inherit->lim.max_rfer, > + &target_inherit->lim.max_rfer); > + __get_user(host_args_v2->qgroup_inherit->lim.max_excl, > + &target_inherit->lim.max_excl); > + __get_user(host_args_v2->qgroup_inherit->lim.rsv_rfer, > + &target_inherit->lim.rsv_rfer); > + __get_user(host_args_v2->qgroup_inherit->lim.rsv_excl, > + &target_inherit->lim.rsv_excl); And what about qgroups? > + } > + > + unlock_user(target_inherit, inherit_addr, 0); > + } > + > +#ifdef BTRFS_DEVICE_SPEC_BY_ID > + if (host_args_v2->flags & BTRFS_DEVICE_SPEC_BY_ID) { > + __get_user(host_args_v2->devid, &target_args_v2->devid); > + device_spec_by_id = 1; > + } > +#endif > +#ifdef BTRFS_SUBVOL_SPEC_BY_ID > + if (host_args_v2->flags & BTRFS_SUBVOL_SPEC_BY_ID) { > + __get_user(host_args_v2->subvolid, &target_args_v2->subvolid); > + subvol_spec_by_id = 1; > + } > +#endif > + > + if (!device_spec_by_id & !subvol_spec_by_id) { > + memcpy(host_args_v2->name, target_args_v2->name, > + BTRFS_SUBVOL_NAME_MAX + 1); > + } > +} > + > +static const StructEntry struct_btrfs_ioctl_vol_args_v2_def = { > + .convert = { NULL, target_to_host_btrfs_ioctl_vol_args_v2}, > + .print = print_btrfs_ioctl_vol_args_v2, > + .align = { __alignof__(struct target_btrfs_ioctl_vol_args_v2), > + __alignof__(struct btrfs_ioctl_vol_args_v2) }, > + .size = { sizeof(struct target_btrfs_ioctl_vol_args_v2), > + sizeof(struct btrfs_ioctl_vol_args_v2) }, > +}; > +#endif > + > static bitmask_transtbl mmap_flags_tbl[] = { > { TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED }, > { TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE }, > diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h > index 969377d622..d2d4d1e1cb 100644 > --- a/linux-user/syscall_defs.h > +++ b/linux-user/syscall_defs.h > @@ -967,6 +967,26 @@ struct target_rtc_pll_info { > #define TARGET_FS_IOC32_GETVERSION TARGET_IOR('v', 1, int) > #define TARGET_FS_IOC32_SETVERSION TARGET_IOW('v', 2, int) > > +#define TARGET_BTRFS_SUBVOL_NAME_MAX 4039 As you use BTRFS_SUBVOL_NAME_MAX in memcpy() of target_to_host_btrfs_ioctl_vol_args_v2, I think you don't need to define the TARGET_ variant (the value is the same for all the targets). > + > +struct target_btrfs_ioctl_vol_args_v2 { > + uint64_t fd; fd is signed in btrfs_ioctl_vol_args_v2 > + uint64_t transid; > + uint64_t flags; > + union { > + struct { > + uint64_t size; > + abi_long qgroup_inherit; > + }; > + uint64_t unused[4]; > + }; > + union { > + char name[TARGET_BTRFS_SUBVOL_NAME_MAX + 1]; > + uint64_t devid; > + uint64_t subvolid; > + }; > +}; > + > /* btrfs ioctls */ > #define TARGET_BTRFS_IOC_SNAP_CREATE TARGET_IOWU(BTRFS_IOCTL_MAGIC, 1) > #define TARGET_BTRFS_IOC_SCAN_DEV TARGET_IOWU(BTRFS_IOCTL_MAGIC, 4) Thanks, Laurent
diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index e3bfe78774..a29ce6b69d 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -209,6 +209,14 @@ #ifdef BTRFS_IOC_DEFAULT_SUBVOL IOCTL(BTRFS_IOC_DEFAULT_SUBVOL, IOC_W, MK_PTR(TYPE_ULONGLONG)) #endif +#ifdef BTRFS_IOC_SNAP_CREATE_V2 + IOCTL(BTRFS_IOC_SNAP_CREATE_V2, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_btrfs_ioctl_vol_args_v2))) +#endif +#ifdef BTRFS_IOC_SUBVOL_CREATE_V2 + IOCTL(BTRFS_IOC_SUBVOL_CREATE_V2, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_btrfs_ioctl_vol_args_v2))) +#endif #ifdef BTRFS_IOC_SUBVOL_GETFLAGS IOCTL(BTRFS_IOC_SUBVOL_GETFLAGS, IOC_R, MK_PTR(TYPE_ULONGLONG)) #endif @@ -281,6 +289,10 @@ IOCTL(BTRFS_IOC_GET_SUPPORTED_FEATURES, IOC_R, MK_PTR(MK_ARRAY(MK_STRUCT(STRUCT_btrfs_ioctl_feature_flags), 3))) #endif +#ifdef BTRFS_IOC_RM_DEV_V2 + IOCTL(BTRFS_IOC_RM_DEV_V2, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_btrfs_ioctl_vol_args_v2))) +#endif #ifdef BTRFS_IOC_LOGICAL_INO_V2 IOCTL(BTRFS_IOC_LOGICAL_INO_V2, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_btrfs_ioctl_logical_ino_args))) @@ -297,6 +309,10 @@ IOCTL(BTRFS_IOC_INO_LOOKUP_USER, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_btrfs_ioctl_ino_lookup_user_args))) #endif +#ifdef BTRFS_IOC_SNAP_DESTROY_V2 + IOCTL(BTRFS_IOC_SNAP_DESTROY_V2, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_btrfs_ioctl_vol_args_v2))) +#endif #ifdef CONFIG_USBFS /* USB ioctls */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index a69a0bd347..4add164673 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -708,6 +708,11 @@ static inline uint64_t target_offset64(uint64_t word0, uint64_t word1) void print_termios(void *arg); +#if defined(BTRFS_IOC_SUBVOL_CREATE_V2) || defined(BTRFS_IOC_SNAP_CREATE_V2) \ + || defined(BTRFS_IOC_SNAP_DESTROY_V2) || defined(BTRFS_IOC_RM_DEV_V2) +void print_btrfs_ioctl_vol_args_v2(void *arg); +#endif + /* ARM EABI and MIPS expect 64bit types aligned even on pairs or registers */ #ifdef TARGET_ARM static inline int regpairs_aligned(void *cpu_env, int num) diff --git a/linux-user/strace.c b/linux-user/strace.c index b9ba39ce6e..04fe38a846 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -9,6 +9,9 @@ #include <netinet/tcp.h> #include <linux/if_packet.h> #include <linux/netlink.h> +#ifdef CONFIG_BTRFS +#include <linux/btrfs.h> +#endif #include <sched.h> #include "qemu.h" @@ -1434,6 +1437,22 @@ UNUSED static struct enums itimer_types[] = { ENUM_END, }; +UNUSED static struct flags btrfs_args_vol_v2_flags[] = { +#ifdef BTRFS_SUBVOL_RDONLY + FLAG_GENERIC(BTRFS_SUBVOL_RDONLY), +#endif +#ifdef BTRFS_SUBVOL_QGROUP_INHERIT + FLAG_GENERIC(BTRFS_SUBVOL_QGROUP_INHERIT), +#endif +#ifdef BTRFS_DEVICE_SPEC_BY_ID + FLAG_GENERIC(BTRFS_DEVICE_SPEC_BY_ID), +#endif +#ifdef BTRFS_SUBVOL_SPEC_BY_ID + FLAG_GENERIC(BTRFS_SUBVOL_SPEC_BY_ID), +#endif + FLAG_END, +}; + /* * print_xxx utility functions. These are used to print syscall * parameters in certain format. All of these have parameter @@ -1774,6 +1793,82 @@ print_termios(void *arg) qemu_log("}"); } +#if defined(BTRFS_IOC_SUBVOL_CREATE_V2) || defined(BTRFS_IOC_SNAP_CREATE_V2) \ + || defined(BTRFS_IOC_SNAP_DESTROY_V2) || defined(BTRFS_IOC_RM_DEV_V2) +void +print_btrfs_ioctl_vol_args_v2(void *arg) +{ + struct target_btrfs_ioctl_vol_args_v2 *target_args_v2 = arg; + uint64_t flags = tswap64(target_args_v2->flags); + int device_spec_by_id = 0; + int subvol_spec_by_id = 0; + + qemu_log("{fd = %" PRId64 ", flags = ", tswap64(target_args_v2->fd)); + print_flags(btrfs_args_vol_v2_flags, flags, 0); + qemu_log(" transid = %" PRIu64 ",", tswap64(target_args_v2->transid)); + +#ifdef BTRFS_SUBVOL_QGROUP_INHERIT + if (flags & BTRFS_SUBVOL_QGROUP_INHERIT) { + + struct btrfs_qgroup_inherit *target_inherit; + abi_long inherit_addr = tswapal(target_args_v2->qgroup_inherit); + + target_inherit = lock_user(VERIFY_READ, inherit_addr, + sizeof(*target_inherit), 1); + + if (target_inherit) { + qemu_log(" size = %" PRIu64, + tswap64(target_args_v2->size)); + qemu_log(", qgroup_inherit = {"); + + qemu_log("flags = %" PRIu64, + tswap64(target_inherit->flags)); + qemu_log(", num_qgroups = %" PRIu64, + tswap64(target_inherit->num_qgroups)); + qemu_log(", num_ref_copies = %" PRIu64, + tswap64(target_inherit->num_ref_copies)); + qemu_log(", num_excl_copies = %" PRIu64, + tswap64(target_inherit->num_excl_copies)); + + qemu_log(", lim = {"); + qemu_log("lim.flags = %" PRIu64, + tswap64(target_inherit->lim.flags)); + qemu_log(", lim.max_rfer = %" PRIu64, + tswap64(target_inherit->lim.max_rfer)); + qemu_log(", lim.max_excl = %" PRIu64, + tswap64(target_inherit->lim.max_excl)); + qemu_log(", lim.rsv_rfer = %" PRIu64, + tswap64(target_inherit->lim.rsv_rfer)); + qemu_log(", lim.rsv_excl = %" PRIu64 "}", + tswap64(target_inherit->lim.rsv_excl)); + + qemu_log(", ...}, "); + } + + unlock_user(target_inherit, inherit_addr, 0); + } +#endif + +#ifdef BTRFS_DEVICE_SPEC_BY_ID + if (flags & BTRFS_DEVICE_SPEC_BY_ID) { + qemu_log(" devid = %" PRIu64, tswap64(target_args_v2->devid)); + device_spec_by_id = 1; + } +#endif +#ifdef BTRFS_SUBVOL_SPEC_BY_ID + if (flags & BTRFS_SUBVOL_SPEC_BY_ID) { + qemu_log(" subvolid = %" PRIu64, tswap64(target_args_v2->subvolid)); + subvol_spec_by_id = 1; + } +#endif + if (!device_spec_by_id && !subvol_spec_by_id) { + qemu_log(" name = \"%s\"", target_args_v2->name); + } + + qemu_log("}"); +} +#endif + #undef UNUSED #ifdef TARGET_NR_accept diff --git a/linux-user/syscall.c b/linux-user/syscall.c index b58098b5bb..78d7106b80 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5668,6 +5668,83 @@ static const StructEntry struct_termios_def = { .print = print_termios, }; +#if defined(BTRFS_IOC_SUBVOL_CREATE_V2) || defined(BTRFS_IOC_SNAP_CREATE_V2) \ + || defined(BTRFS_SNAP_DESTROY_V2) || defined(BTRFS_RM_DEV_V2) +static void target_to_host_btrfs_ioctl_vol_args_v2(void *dst, const void *src) +{ + struct btrfs_ioctl_vol_args_v2 *host_args_v2 = dst; + const struct target_btrfs_ioctl_vol_args_v2 *target_args_v2 = src; + int device_spec_by_id = 0; + int subvol_spec_by_id = 0; + + __get_user(host_args_v2->fd, &target_args_v2->fd); + __get_user(host_args_v2->flags, &target_args_v2->flags); + __get_user(host_args_v2->transid, &target_args_v2->transid); + + if (host_args_v2->flags & BTRFS_SUBVOL_QGROUP_INHERIT) { + struct btrfs_qgroup_inherit *target_inherit; + abi_long inherit_addr = tswapal(target_args_v2->qgroup_inherit); + + target_inherit = lock_user(VERIFY_READ, inherit_addr, + sizeof(*target_inherit), 1); + + host_args_v2->qgroup_inherit = g_new(struct btrfs_qgroup_inherit, 1); + + if (!target_inherit) { + host_args_v2->qgroup_inherit = NULL; + } else { + __get_user(host_args_v2->qgroup_inherit->flags, + &target_inherit->flags); + __get_user(host_args_v2->qgroup_inherit->num_qgroups, + &target_inherit->num_qgroups); + __get_user(host_args_v2->qgroup_inherit->num_ref_copies, + &target_inherit->num_ref_copies); + __get_user(host_args_v2->qgroup_inherit->num_excl_copies, + &target_inherit->num_excl_copies); + __get_user(host_args_v2->qgroup_inherit->lim.flags, + &target_inherit->lim.flags); + __get_user(host_args_v2->qgroup_inherit->lim.max_rfer, + &target_inherit->lim.max_rfer); + __get_user(host_args_v2->qgroup_inherit->lim.max_excl, + &target_inherit->lim.max_excl); + __get_user(host_args_v2->qgroup_inherit->lim.rsv_rfer, + &target_inherit->lim.rsv_rfer); + __get_user(host_args_v2->qgroup_inherit->lim.rsv_excl, + &target_inherit->lim.rsv_excl); + } + + unlock_user(target_inherit, inherit_addr, 0); + } + +#ifdef BTRFS_DEVICE_SPEC_BY_ID + if (host_args_v2->flags & BTRFS_DEVICE_SPEC_BY_ID) { + __get_user(host_args_v2->devid, &target_args_v2->devid); + device_spec_by_id = 1; + } +#endif +#ifdef BTRFS_SUBVOL_SPEC_BY_ID + if (host_args_v2->flags & BTRFS_SUBVOL_SPEC_BY_ID) { + __get_user(host_args_v2->subvolid, &target_args_v2->subvolid); + subvol_spec_by_id = 1; + } +#endif + + if (!device_spec_by_id & !subvol_spec_by_id) { + memcpy(host_args_v2->name, target_args_v2->name, + BTRFS_SUBVOL_NAME_MAX + 1); + } +} + +static const StructEntry struct_btrfs_ioctl_vol_args_v2_def = { + .convert = { NULL, target_to_host_btrfs_ioctl_vol_args_v2}, + .print = print_btrfs_ioctl_vol_args_v2, + .align = { __alignof__(struct target_btrfs_ioctl_vol_args_v2), + __alignof__(struct btrfs_ioctl_vol_args_v2) }, + .size = { sizeof(struct target_btrfs_ioctl_vol_args_v2), + sizeof(struct btrfs_ioctl_vol_args_v2) }, +}; +#endif + static bitmask_transtbl mmap_flags_tbl[] = { { TARGET_MAP_SHARED, TARGET_MAP_SHARED, MAP_SHARED, MAP_SHARED }, { TARGET_MAP_PRIVATE, TARGET_MAP_PRIVATE, MAP_PRIVATE, MAP_PRIVATE }, diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 969377d622..d2d4d1e1cb 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -967,6 +967,26 @@ struct target_rtc_pll_info { #define TARGET_FS_IOC32_GETVERSION TARGET_IOR('v', 1, int) #define TARGET_FS_IOC32_SETVERSION TARGET_IOW('v', 2, int) +#define TARGET_BTRFS_SUBVOL_NAME_MAX 4039 + +struct target_btrfs_ioctl_vol_args_v2 { + uint64_t fd; + uint64_t transid; + uint64_t flags; + union { + struct { + uint64_t size; + abi_long qgroup_inherit; + }; + uint64_t unused[4]; + }; + union { + char name[TARGET_BTRFS_SUBVOL_NAME_MAX + 1]; + uint64_t devid; + uint64_t subvolid; + }; +}; + /* btrfs ioctls */ #define TARGET_BTRFS_IOC_SNAP_CREATE TARGET_IOWU(BTRFS_IOCTL_MAGIC, 1) #define TARGET_BTRFS_IOC_SCAN_DEV TARGET_IOWU(BTRFS_IOCTL_MAGIC, 4) @@ -978,6 +998,10 @@ struct target_rtc_pll_info { #define TARGET_BTRFS_IOC_INO_LOOKUP TARGET_IOWRU(BTRFS_IOCTL_MAGIC, 18) #define TARGET_BTRFS_IOC_DEFAULT_SUBVOL TARGET_IOW(BTRFS_IOCTL_MAGIC, 19,\ abi_ullong) +#define TARGET_BTRFS_IOC_SNAP_CREATE_V2 TARGET_IOW(BTRFS_IOCTL_MAGIC, 23,\ + struct target_btrfs_ioctl_vol_args_v2) +#define TARGET_BTRFS_IOC_SUBVOL_CREATE_V2 TARGET_IOW(BTRFS_IOCTL_MAGIC, 24,\ + struct target_btrfs_ioctl_vol_args_v2) #define TARGET_BTRFS_IOC_SUBVOL_GETFLAGS TARGET_IOR(BTRFS_IOCTL_MAGIC, 25,\ abi_ullong) #define TARGET_BTRFS_IOC_SUBVOL_SETFLAGS TARGET_IOW(BTRFS_IOCTL_MAGIC, 26,\ @@ -999,10 +1023,14 @@ struct target_rtc_pll_info { #define TARGET_BTRFS_IOC_GET_FEATURES TARGET_IORU(BTRFS_IOCTL_MAGIC, 57) #define TARGET_BTRFS_IOC_SET_FEATURES TARGET_IOWU(BTRFS_IOCTL_MAGIC, 57) #define TARGET_BTRFS_IOC_GET_SUPPORTED_FEATURES TARGET_IORU(BTRFS_IOCTL_MAGIC, 57) +#define TARGET_BTRFS_IOC_RM_DEV_V2 TARGET_IOW(BTRFS_IOCTL_MAGIC, 58,\ + struct target_btrfs_ioctl_vol_args_v2) #define TARGET_BTRFS_IOC_LOGICAL_INO_V2 TARGET_IOWRU(BTRFS_IOCTL_MAGIC, 59) #define TARGET_BTRFS_IOC_GET_SUBVOL_INFO TARGET_IORU(BTRFS_IOCTL_MAGIC, 60) #define TARGET_BTRFS_IOC_GET_SUBVOL_ROOTREF TARGET_IOWRU(BTRFS_IOCTL_MAGIC, 61) #define TARGET_BTRFS_IOC_INO_LOOKUP_USER TARGET_IOWRU(BTRFS_IOCTL_MAGIC, 62) +#define TARGET_BTRFS_IOC_SNAP_DESTROY_V2 TARGET_IOW(BTRFS_IOCTL_MAGIC, 63,\ + struct target_btrfs_ioctl_vol_args_v2) /* usb ioctls */ #define TARGET_USBDEVFS_CONTROL TARGET_IOWRU('U', 0) diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 8d4b5b3e92..83aee1fcf9 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -358,6 +358,11 @@ STRUCT(btrfs_ioctl_vol_args, TYPE_LONGLONG, /* fd */ MK_ARRAY(TYPE_CHAR, BTRFS_PATH_NAME_MAX + 1)) /* name */ +#if defined(BTRFS_IOC_SUBVOL_CREATE_V2) || defined(BTRFS_IOC_SNAP_CREATE_V2) \ + || defined(BTRFS_IOC_SNAP_DESTROY_V2) || defined(BTRFS_IOC_RM_DEV_V2) +STRUCT_SPECIAL(btrfs_ioctl_vol_args_v2) +#endif + STRUCT(btrfs_ioctl_timespec, TYPE_ULONGLONG, /* sec */ TYPE_INT) /* nsec */ diff --git a/thunk.c b/thunk.c index 0718325d86..0dfecb644b 100644 --- a/thunk.c +++ b/thunk.c @@ -246,7 +246,7 @@ const argtype *thunk_convert(void *dst, const void *src, assert(*type_ptr < max_struct_entries); se = struct_entries + *type_ptr++; - if (se->convert[0] != NULL) { + if (se->convert[to_host] != NULL) { /* specific conversion is needed */ (*se->convert[to_host])(dst, src); } else {
This patch introduces functionality for following btrfs ioctls: BTRFS_IOC_SUBVOL_CREATE_V2 - Adding a new btrfs subvolume Create a new btrfs subvolume (same as with BTRFS_IOC_SUBVOL_CREATE). The third ioctl's argument is a pointer to a following type: struct btrfs_ioctl_vol_args_v2 { __s64 fd; __u64 transid; __u64 flags; union { struct { __u64 size; struct btrfs_qgroup_inherit __user *qgroup_inherit; }; __u64 unused[4]; }; union { char name[BTRFS_SUBVOL_NAME_MAX + 1]; __u64 devid; __u64 subvolid; /* added in kernel version 5.8 */ }; }; When calling this ioctl, the 'name' field should be filled with the aproppriate value that contains the name of the subvolume that is to be created. The flags field can take values that are 'BTRFS_SUBVOL_RDONLY' or 'BTRFS_SUBVOL_QGROUP_INHERIT'. If the latter is specified, the field 'qgroup_inherit' should be filled with aproppriate values of the quota group in which the newly created subvolume is to be added. The definition of 'struct btrfs_qgroup_inherit' can be found at: https://elixir.bootlin.com/linux/latest/source/include/uapi/linux/btrfs.h#L90 BTRFS_IOC_SNAP_CREATE_V2 - Adding a btrfs snapshot for a subvolume Create a new btrfs snapshot for a specified subvolume (same as with BTRFS_IOC_SNAP_CREATE). The ioctl's third argument is a pointer to the above mentioned 'struct btrfs_ioctl_vol_args_v2'. Before calling this ioctl, field 'fd' should be filled with the aproppriate file descriptor value for the btrfs subvolume for which the snapshot is to be created. Also, the 'name' field should be filled with the aproppriate value that represents the name of the snapshot that is to be created. The 'flags' field takes the same values as in case of 'BTRFS_IOC_SUBVOL_CREATE_V2' and represents the same functionality. BTRFS_IOC_RM_DEV_V2 - Removing a btrfs device Delete a btrfs device (same as with BTRFS_IOC_RM_DEV). The ioctl's third argument is a pointer to the above mentioned 'struct btrfs_ioctl_vol_args_v2'. Before calling this ioctl, either the 'name' or the 'devid' field should be filled with the name or id of the device that is to be removed. Also, the flags field should be filled either with 0 or 'BTRFS_DEVICE_SPEC_BY_ID' depending on if the device is gonna be specified via name or id. BTRFS_IOC_SNAP_DESTROY_V2 - Removing a btrfs snapshot Remove a btrfs snapshot (same as with BTRFS_IOC_SNAP_DESTROY). The ioctl's third argument is a pointer to the above mentioned 'struct btrfs_ioctl_vol_args_v2'. Before calling this ioctl, either the 'name' or the 'subvolid' field should be filled with the name or id of the snapshot that is to be removed. Also, the flags field should be filled either with 0 or 'BTRFS_SUBVOL_SPEC_BY_ID' depending on if the snapshot is gonna be specified via name or id. Implementation notes: Since the third argument of the implemented ioctl's is a structure that contains unions, a special converting function 'target_to_host_btrfs_ioctl_vol_args_v2' was defined in 'syscall.c'. This function is called instead of 'thunk_convert()' to convert the values of the third argument from target to host. All of the ioctls in this patch are of type 'IOW' which is why a converting function from host to target is not required. Also, a separate printing function was defined in file 'strace.c' that is called instead of 'thunk_print()' to print the contents of the third argument. Signed-off-by: Filip Bozuta <Filip.Bozuta@syrmia.com> Based-on: <20200811164553.27713-2-Filip.Bozuta@syrmia.com> Based-on: <20200723210233.349690-4-Filip.Bozuta@syrmia.com> --- linux-user/ioctls.h | 16 +++++++ linux-user/qemu.h | 5 ++ linux-user/strace.c | 95 ++++++++++++++++++++++++++++++++++++++ linux-user/syscall.c | 77 ++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 28 +++++++++++ linux-user/syscall_types.h | 5 ++ thunk.c | 2 +- 7 files changed, 227 insertions(+), 1 deletion(-)