diff options
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r-- | fs/fuse/file.c | 50 |
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 | ||
17 | static const struct file_operations fuse_direct_io_file_operations; | 18 | static 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 | */ | ||
1637 | static 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; |