diff options
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r-- | fs/fuse/file.c | 66 |
1 files changed, 54 insertions, 12 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 8b984a2cebbd..95da1bc1c826 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -1634,9 +1634,9 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov, | |||
1634 | * and 64bit. Fortunately we can determine which structure the server | 1634 | * and 64bit. Fortunately we can determine which structure the server |
1635 | * used from the size of the reply. | 1635 | * used from the size of the reply. |
1636 | */ | 1636 | */ |
1637 | static int fuse_copy_ioctl_iovec(struct iovec *dst, void *src, | 1637 | static int fuse_copy_ioctl_iovec_old(struct iovec *dst, void *src, |
1638 | size_t transferred, unsigned count, | 1638 | size_t transferred, unsigned count, |
1639 | bool is_compat) | 1639 | bool is_compat) |
1640 | { | 1640 | { |
1641 | #ifdef CONFIG_COMPAT | 1641 | #ifdef CONFIG_COMPAT |
1642 | if (count * sizeof(struct compat_iovec) == transferred) { | 1642 | if (count * sizeof(struct compat_iovec) == transferred) { |
@@ -1680,6 +1680,42 @@ static int fuse_verify_ioctl_iov(struct iovec *iov, size_t count) | |||
1680 | return 0; | 1680 | return 0; |
1681 | } | 1681 | } |
1682 | 1682 | ||
1683 | static int fuse_copy_ioctl_iovec(struct fuse_conn *fc, struct iovec *dst, | ||
1684 | void *src, size_t transferred, unsigned count, | ||
1685 | bool is_compat) | ||
1686 | { | ||
1687 | unsigned i; | ||
1688 | struct fuse_ioctl_iovec *fiov = src; | ||
1689 | |||
1690 | if (fc->minor < 16) { | ||
1691 | return fuse_copy_ioctl_iovec_old(dst, src, transferred, | ||
1692 | count, is_compat); | ||
1693 | } | ||
1694 | |||
1695 | if (count * sizeof(struct fuse_ioctl_iovec) != transferred) | ||
1696 | return -EIO; | ||
1697 | |||
1698 | for (i = 0; i < count; i++) { | ||
1699 | /* Did the server supply an inappropriate value? */ | ||
1700 | if (fiov[i].base != (unsigned long) fiov[i].base || | ||
1701 | fiov[i].len != (unsigned long) fiov[i].len) | ||
1702 | return -EIO; | ||
1703 | |||
1704 | dst[i].iov_base = (void __user *) (unsigned long) fiov[i].base; | ||
1705 | dst[i].iov_len = (size_t) fiov[i].len; | ||
1706 | |||
1707 | #ifdef CONFIG_COMPAT | ||
1708 | if (is_compat && | ||
1709 | (ptr_to_compat(dst[i].iov_base) != fiov[i].base || | ||
1710 | (compat_size_t) dst[i].iov_len != fiov[i].len)) | ||
1711 | return -EIO; | ||
1712 | #endif | ||
1713 | } | ||
1714 | |||
1715 | return 0; | ||
1716 | } | ||
1717 | |||
1718 | |||
1683 | /* | 1719 | /* |
1684 | * For ioctls, there is no generic way to determine how much memory | 1720 | * For ioctls, there is no generic way to determine how much memory |
1685 | * needs to be read and/or written. Furthermore, ioctls are allowed | 1721 | * needs to be read and/or written. Furthermore, ioctls are allowed |
@@ -1740,18 +1776,25 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
1740 | struct fuse_ioctl_out outarg; | 1776 | struct fuse_ioctl_out outarg; |
1741 | struct fuse_req *req = NULL; | 1777 | struct fuse_req *req = NULL; |
1742 | struct page **pages = NULL; | 1778 | struct page **pages = NULL; |
1743 | struct page *iov_page = NULL; | 1779 | struct iovec *iov_page = NULL; |
1744 | struct iovec *in_iov = NULL, *out_iov = NULL; | 1780 | struct iovec *in_iov = NULL, *out_iov = NULL; |
1745 | unsigned int in_iovs = 0, out_iovs = 0, num_pages = 0, max_pages; | 1781 | unsigned int in_iovs = 0, out_iovs = 0, num_pages = 0, max_pages; |
1746 | size_t in_size, out_size, transferred; | 1782 | size_t in_size, out_size, transferred; |
1747 | int err; | 1783 | int err; |
1748 | 1784 | ||
1785 | #if BITS_PER_LONG == 32 | ||
1786 | inarg.flags |= FUSE_IOCTL_32BIT; | ||
1787 | #else | ||
1788 | if (flags & FUSE_IOCTL_COMPAT) | ||
1789 | inarg.flags |= FUSE_IOCTL_32BIT; | ||
1790 | #endif | ||
1791 | |||
1749 | /* assume all the iovs returned by client always fits in a page */ | 1792 | /* assume all the iovs returned by client always fits in a page */ |
1750 | BUILD_BUG_ON(sizeof(struct iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); | 1793 | BUILD_BUG_ON(sizeof(struct fuse_ioctl_iovec) * FUSE_IOCTL_MAX_IOV > PAGE_SIZE); |
1751 | 1794 | ||
1752 | err = -ENOMEM; | 1795 | err = -ENOMEM; |
1753 | pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL); | 1796 | pages = kzalloc(sizeof(pages[0]) * FUSE_MAX_PAGES_PER_REQ, GFP_KERNEL); |
1754 | iov_page = alloc_page(GFP_KERNEL); | 1797 | iov_page = (struct iovec *) __get_free_page(GFP_KERNEL); |
1755 | if (!pages || !iov_page) | 1798 | if (!pages || !iov_page) |
1756 | goto out; | 1799 | goto out; |
1757 | 1800 | ||
@@ -1760,7 +1803,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
1760 | * RETRY from server is not allowed. | 1803 | * RETRY from server is not allowed. |
1761 | */ | 1804 | */ |
1762 | if (!(flags & FUSE_IOCTL_UNRESTRICTED)) { | 1805 | if (!(flags & FUSE_IOCTL_UNRESTRICTED)) { |
1763 | struct iovec *iov = page_address(iov_page); | 1806 | struct iovec *iov = iov_page; |
1764 | 1807 | ||
1765 | iov->iov_base = (void __user *)arg; | 1808 | iov->iov_base = (void __user *)arg; |
1766 | iov->iov_len = _IOC_SIZE(cmd); | 1809 | iov->iov_len = _IOC_SIZE(cmd); |
@@ -1841,7 +1884,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
1841 | 1884 | ||
1842 | /* did it ask for retry? */ | 1885 | /* did it ask for retry? */ |
1843 | if (outarg.flags & FUSE_IOCTL_RETRY) { | 1886 | if (outarg.flags & FUSE_IOCTL_RETRY) { |
1844 | char *vaddr; | 1887 | void *vaddr; |
1845 | 1888 | ||
1846 | /* no retry if in restricted mode */ | 1889 | /* no retry if in restricted mode */ |
1847 | err = -EIO; | 1890 | err = -EIO; |
@@ -1862,14 +1905,14 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
1862 | goto out; | 1905 | goto out; |
1863 | 1906 | ||
1864 | vaddr = kmap_atomic(pages[0], KM_USER0); | 1907 | vaddr = kmap_atomic(pages[0], KM_USER0); |
1865 | err = fuse_copy_ioctl_iovec(page_address(iov_page), vaddr, | 1908 | err = fuse_copy_ioctl_iovec(fc, iov_page, vaddr, |
1866 | transferred, in_iovs + out_iovs, | 1909 | transferred, in_iovs + out_iovs, |
1867 | (flags & FUSE_IOCTL_COMPAT) != 0); | 1910 | (flags & FUSE_IOCTL_COMPAT) != 0); |
1868 | kunmap_atomic(vaddr, KM_USER0); | 1911 | kunmap_atomic(vaddr, KM_USER0); |
1869 | if (err) | 1912 | if (err) |
1870 | goto out; | 1913 | goto out; |
1871 | 1914 | ||
1872 | in_iov = page_address(iov_page); | 1915 | in_iov = iov_page; |
1873 | out_iov = in_iov + in_iovs; | 1916 | out_iov = in_iov + in_iovs; |
1874 | 1917 | ||
1875 | err = fuse_verify_ioctl_iov(in_iov, in_iovs); | 1918 | err = fuse_verify_ioctl_iov(in_iov, in_iovs); |
@@ -1891,8 +1934,7 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
1891 | out: | 1934 | out: |
1892 | if (req) | 1935 | if (req) |
1893 | fuse_put_request(fc, req); | 1936 | fuse_put_request(fc, req); |
1894 | if (iov_page) | 1937 | free_page((unsigned long) iov_page); |
1895 | __free_page(iov_page); | ||
1896 | while (num_pages) | 1938 | while (num_pages) |
1897 | __free_page(pages[--num_pages]); | 1939 | __free_page(pages[--num_pages]); |
1898 | kfree(pages); | 1940 | kfree(pages); |