btrfs send ioctl does not support 32 bit user space with 64 bit kernel in Linux 4.1.3 ; patch included
Hello,
I am using as kernel Linux 4.1.3 (64bit) and btrfs-prog version 4.0 (32 bit user space). I wanted to use send/receive with btrfs for the first time today and I got the following error:
humbur:~# btrfs send /snap > /dev/null At subvol /snap ERROR: send ioctl failed with -25: Inappropriate ioctl for device ERROR: failed to read stream from kernel. Bad file descriptor
Investigating the source, I noticed that probably the problem is the member clone_sources in the structure
struct btrfs_ioctl_send_args {
__s64 send_fd; /* in */
__u64 clone_sources_count; /* in */
__u64 __user *clone_sources; /* in */
__u64 parent_root; /* in */
__u64 flags; /* in */
__u64 reserved[4]; /* in */
};
in include/uapi/linux/btrfs.h and the missing adaption code in fs/btrfs/ioctl.c. The member clone_sources is only 32 bit wide in case of 32 bit user space. For the ioctl RECEIVED_SUBVOL somebody already added code for the in this case also necessary translation. I took this as a template and wrote a patch (see below). The patch compiles and with the new kernel I seem to get valid data with send (I have to read it back yet, but I get about the expected amount and structure).
This is a proof of concept patch; for example the compiler currently warns for
(args64->clone_sources = (__u64*)args32->clone_sources;
and I would have to investigate how to properly convert the pointer. Further there are probably some issues with the formating of the source code. I also have not tested the 32bit/32bit 64bit/64bit userspace/kernel combinations.
If there is interest, I can resubmit an improved patch. Please CC me in replies, since I have not subscribed to the list.
Sincerely,
Thomas Rohwer
From e4156c38105200fa83913b6a94f07a41631c5f75 Mon Sep 17 00:00:00 2001
From: Thomas <[email protected]>
Date: Wed, 22 Jul 2015 22:03:15 +0200
Subject: [PATCH] add 32 bit adaption code for send ioctl
--- fs/btrfs/ioctl.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/send.c | 11 +-------- fs/btrfs/send.h | 2 +- 3 files changed, 75 insertions(+), 11 deletions(-)
fs/btrfs/ioctl.c +73 -0
@@ -83,6 +83,19 @@ struct btrfs_ioctl_received_subvol_args_32 {#define BTRFS_IOC_SET_RECEIVED_SUBVOL_32 _IOWR(BTRFS_IOCTL_MAGIC, 37, \ struct btrfs_ioctl_received_subvol_args_32) + +struct btrfs_ioctl_send_args_32 { +__s64 send_fd;/* in */ +__u64 clone_sources_count;/* in */ +__u32 clone_sources;/* in */ +__u64 parent_root;/* in */ +__u64 flags;/* in */ +__u64 reserved[4 ];/* in */ +} __attribute__ ((__packed__ )); + +#define BTRFS_IOC_SEND_32 _IOW(BTRFS_IOCTL_MAGIC, 38, \ + struct btrfs_ioctl_send_args_32) +#endif
@@ -4965,6 +4978,43 @@ out:kfree (args64);return ret; } + +static long btrfs_ioctl_send_32 (struct file *file, +void __user *arg) +{ +struct btrfs_ioctl_send_args_32 *args32 =NULL ; +struct btrfs_ioctl_send_args *args64 =NULL ; +int ret =0 ; + + args32 =memdup_user (arg,sizeof (*args32)); +if (IS_ERR (args32)) { + ret =PTR_ERR (args32); + args32 =NULL ; + goto out; + } + + args64 =kmalloc (sizeof (*args64),GFP_NOFS ); +if (!args64) { + ret = -ENOMEM ; + goto out; + } + + args64->send_fd = args32->send_fd; + args64->clone_sources_count = args32->clone_sources_count; + args64->clone_sources = (__u64 *)args32->clone_sources; + args64->parent_root = args32->parent_root; + args64->flags = args32->flags; + + ret =_btrfs_ioctl_send (file, args64); + +// only in arguments, so no copy back to args32 + +out: +kfree (args32); +kfree (args64); +return ret; +} +#endif static long btrfs_ioctl_set_received_subvol(struct file *file,
@@ -4994,6 +5044,27 @@ out:return ret; } +static long btrfs_ioctl_send (struct file *file, +void __user *arg) +{ +struct btrfs_ioctl_send_args *sa =NULL ; +int ret =0 ; + + sa =memdup_user (arg,sizeof (*sa)); +if (IS_ERR (sa)) { + ret =PTR_ERR (sa); + sa =NULL ; + goto out; + } + + ret =_btrfs_ioctl_send (file, sa); + +out: +kfree (sa); +return ret; +} + +static int btrfs_ioctl_get_fslabel (struct file *file,void __user *arg) {struct btrfs_root *root =BTRFS_I (file_inode (file))->root;
@@ -5320,6 +5391,8 @@ long btrfs_ioctl(struct file *file, unsigned int#ifdef CONFIG_64BIT case BTRFS_IOC_SET_RECEIVED_SUBVOL_32 :return btrfs_ioctl_set_received_subvol_32 (file, argp); +case BTRFS_IOC_SEND_32 : +return btrfs_ioctl_send_32 (file, argp);#endif case BTRFS_IOC_SEND :return btrfs_ioctl_send (file, argp);
fs/btrfs/send.c +1 -10
@@ -5663,13 +5663,12 @@ static void btrfs_root_dec_send_in_progress(struct btrfs_root* root)spin_unlock (&root->root_item_lock); } -long btrfs_ioctl_send (struct file *mnt_file,void __user *arg_) +long _btrfs_ioctl_send (struct file *mnt_file,struct btrfs_ioctl_send_args *arg) {int ret =0 ;struct btrfs_root *send_root;struct btrfs_root *clone_root;struct btrfs_fs_info *fs_info; -struct btrfs_ioctl_send_args *arg =NULL ;struct btrfs_key key;struct send_ctx *sctx =NULL ;u32 i;
@@ -5707,13 +5706,6 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_) goto out; } - arg =memdup_user (arg_,sizeof (*arg)); -if (IS_ERR (arg)) { - ret =PTR_ERR (arg); - arg =NULL ; - goto out; - } -if (!access_ok (VERIFY_READ , arg->clone_sources,sizeof (*arg->clone_sources) * arg->clone_sources_count)) {
@@ -5942,7 +5934,6 @@ out:if (sctx && !IS_ERR_OR_NULL (sctx->parent_root))btrfs_root_dec_send_in_progress (sctx->parent_root); -kfree (arg);vfree (clone_sources_tmp);if (sctx) {
fs/btrfs/send.h +1 -1
@@ -130,5 +130,5 @@ enum {#define BTRFS_SEND_A_MAX (__BTRFS_SEND_A_MAX - 1)#ifdef __KERNEL__ -long btrfs_ioctl_send (struct file *mnt_file,void __user *arg); +long _btrfs_ioctl_send (struct file *mnt_file,struct btrfs_ioctl_send_args *arg);#endif
Signature · 1 line
2.1.4