aboutsummaryrefslogtreecommitdiffstats
path: root/fs/fuse/file.c
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2010-11-30 10:39:27 -0500
committerMiklos Szeredi <mszeredi@suse.cz>2010-11-30 10:39:27 -0500
commitd9d318d39dd5cb686660504a3565aac453709ccc (patch)
treeff818170ca69976ad2cfaa77ed788e645c4d80c9 /fs/fuse/file.c
parente8a7e48bb248a1196484d3f8afa53bded2b24e71 (diff)
fuse: fix ioctl when server is 32bit
If a 32bit CUSE server is run on 64bit this results in EIO being returned to the caller. The reason is that FUSE_IOCTL_RETRY reply was defined to use 'struct iovec', which is different on 32bit and 64bit archs. Work around this by looking at the size of the reply to determine which struct was used. This is only needed if CONFIG_COMPAT is defined. A more permanent fix for the interface will be to use the same struct on both 32bit and 64bit. Reported-by: "ccmail111" <ccmail111@yahoo.com> Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> CC: Tejun Heo <tj@kernel.org> CC: <stable@kernel.org> [2.6.31+]
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r--fs/fuse/file.c50
1 files changed, 44 insertions, 6 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 9242d294fe90..0e2e25b114a6 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -13,6 +13,7 @@
13#include <linux/kernel.h> 13#include <linux/kernel.h>
14#include <linux/sched.h> 14#include <linux/sched.h>
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/compat.h>
16 17
17static const struct file_operations fuse_direct_io_file_operations; 18static const struct file_operations fuse_direct_io_file_operations;
18 19
@@ -1628,6 +1629,44 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov,
1628} 1629}
1629 1630
1630/* 1631/*
1632 * CUSE servers compiled on 32bit broke on 64bit kernels because the
1633 * ABI was defined to be 'struct iovec' which is different on 32bit
1634 * and 64bit. Fortunately we can determine which structure the server
1635 * used from the size of the reply.
1636 */
1637static int fuse_copy_ioctl_iovec(struct iovec *dst, void *src,
1638 size_t transferred, unsigned count,
1639 bool is_compat)
1640{
1641#ifdef CONFIG_COMPAT
1642 if (count * sizeof(struct compat_iovec) == transferred) {
1643 struct compat_iovec *ciov = src;
1644 unsigned i;
1645
1646 /*
1647 * With this interface a 32bit server cannot support
1648 * non-compat (i.e. ones coming from 64bit apps) ioctl
1649 * requests
1650 */
1651 if (!is_compat)
1652 return -EINVAL;
1653
1654 for (i = 0; i < count; i++) {
1655 dst[i].iov_base = compat_ptr(ciov[i].iov_base);
1656 dst[i].iov_len = ciov[i].iov_len;
1657 }
1658 return 0;
1659 }
1660#endif
1661
1662 if (count * sizeof(struct iovec) != transferred)
1663 return -EIO;
1664
1665 memcpy(dst, src, transferred);
1666 return 0;
1667}
1668
1669/*
1631 * For ioctls, there is no generic way to determine how much memory 1670 * For ioctls, there is no generic way to determine how much memory
1632 * needs to be read and/or written. Furthermore, ioctls are allowed 1671 * needs to be read and/or written. Furthermore, ioctls are allowed
1633 * to dereference the passed pointer, so the parameter requires deep 1672 * to dereference the passed pointer, so the parameter requires deep
@@ -1808,14 +1847,13 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg,
1808 in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) 1847 in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV)
1809 goto out; 1848 goto out;
1810 1849
1811 err = -EIO;
1812 if ((in_iovs + out_iovs) * sizeof(struct iovec) != transferred)
1813 goto out;
1814
1815 /* okay, copy in iovs and retry */
1816 vaddr = kmap_atomic(pages[0], KM_USER0); 1850 vaddr = kmap_atomic(pages[0], KM_USER0);
1817 memcpy(page_address(iov_page), vaddr, transferred); 1851 err = fuse_copy_ioctl_iovec(page_address(iov_page), vaddr,
1852 transferred, in_iovs + out_iovs,
1853 (flags & FUSE_IOCTL_COMPAT) != 0);
1818 kunmap_atomic(vaddr, KM_USER0); 1854 kunmap_atomic(vaddr, KM_USER0);
1855 if (err)
1856 goto out;
1819 1857
1820 in_iov = page_address(iov_page); 1858 in_iov = page_address(iov_page);
1821 out_iov = in_iov + in_iovs; 1859 out_iov = in_iov + in_iovs;