diff options
Diffstat (limited to 'fs/fuse/file.c')
-rw-r--r-- | fs/fuse/file.c | 82 |
1 files changed, 76 insertions, 6 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index c8224587123f..6c2717d421e8 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 | ||
@@ -134,6 +135,7 @@ EXPORT_SYMBOL_GPL(fuse_do_open); | |||
134 | void fuse_finish_open(struct inode *inode, struct file *file) | 135 | void fuse_finish_open(struct inode *inode, struct file *file) |
135 | { | 136 | { |
136 | struct fuse_file *ff = file->private_data; | 137 | struct fuse_file *ff = file->private_data; |
138 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
137 | 139 | ||
138 | if (ff->open_flags & FOPEN_DIRECT_IO) | 140 | if (ff->open_flags & FOPEN_DIRECT_IO) |
139 | file->f_op = &fuse_direct_io_file_operations; | 141 | file->f_op = &fuse_direct_io_file_operations; |
@@ -141,6 +143,15 @@ void fuse_finish_open(struct inode *inode, struct file *file) | |||
141 | invalidate_inode_pages2(inode->i_mapping); | 143 | invalidate_inode_pages2(inode->i_mapping); |
142 | if (ff->open_flags & FOPEN_NONSEEKABLE) | 144 | if (ff->open_flags & FOPEN_NONSEEKABLE) |
143 | nonseekable_open(inode, file); | 145 | nonseekable_open(inode, file); |
146 | if (fc->atomic_o_trunc && (file->f_flags & O_TRUNC)) { | ||
147 | struct fuse_inode *fi = get_fuse_inode(inode); | ||
148 | |||
149 | spin_lock(&fc->lock); | ||
150 | fi->attr_version = ++fc->attr_version; | ||
151 | i_size_write(inode, 0); | ||
152 | spin_unlock(&fc->lock); | ||
153 | fuse_invalidate_attr(inode); | ||
154 | } | ||
144 | } | 155 | } |
145 | 156 | ||
146 | int fuse_open_common(struct inode *inode, struct file *file, bool isdir) | 157 | int fuse_open_common(struct inode *inode, struct file *file, bool isdir) |
@@ -1617,6 +1628,58 @@ static int fuse_ioctl_copy_user(struct page **pages, struct iovec *iov, | |||
1617 | return 0; | 1628 | return 0; |
1618 | } | 1629 | } |
1619 | 1630 | ||
1631 | /* Make sure iov_length() won't overflow */ | ||
1632 | static int fuse_verify_ioctl_iov(struct iovec *iov, size_t count) | ||
1633 | { | ||
1634 | size_t n; | ||
1635 | u32 max = FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT; | ||
1636 | |||
1637 | for (n = 0; n < count; n++) { | ||
1638 | if (iov->iov_len > (size_t) max) | ||
1639 | return -ENOMEM; | ||
1640 | max -= iov->iov_len; | ||
1641 | } | ||
1642 | return 0; | ||
1643 | } | ||
1644 | |||
1645 | /* | ||
1646 | * CUSE servers compiled on 32bit broke on 64bit kernels because the | ||
1647 | * ABI was defined to be 'struct iovec' which is different on 32bit | ||
1648 | * and 64bit. Fortunately we can determine which structure the server | ||
1649 | * used from the size of the reply. | ||
1650 | */ | ||
1651 | static int fuse_copy_ioctl_iovec(struct iovec *dst, void *src, | ||
1652 | size_t transferred, unsigned count, | ||
1653 | bool is_compat) | ||
1654 | { | ||
1655 | #ifdef CONFIG_COMPAT | ||
1656 | if (count * sizeof(struct compat_iovec) == transferred) { | ||
1657 | struct compat_iovec *ciov = src; | ||
1658 | unsigned i; | ||
1659 | |||
1660 | /* | ||
1661 | * With this interface a 32bit server cannot support | ||
1662 | * non-compat (i.e. ones coming from 64bit apps) ioctl | ||
1663 | * requests | ||
1664 | */ | ||
1665 | if (!is_compat) | ||
1666 | return -EINVAL; | ||
1667 | |||
1668 | for (i = 0; i < count; i++) { | ||
1669 | dst[i].iov_base = compat_ptr(ciov[i].iov_base); | ||
1670 | dst[i].iov_len = ciov[i].iov_len; | ||
1671 | } | ||
1672 | return 0; | ||
1673 | } | ||
1674 | #endif | ||
1675 | |||
1676 | if (count * sizeof(struct iovec) != transferred) | ||
1677 | return -EIO; | ||
1678 | |||
1679 | memcpy(dst, src, transferred); | ||
1680 | return 0; | ||
1681 | } | ||
1682 | |||
1620 | /* | 1683 | /* |
1621 | * For ioctls, there is no generic way to determine how much memory | 1684 | * For ioctls, there is no generic way to determine how much memory |
1622 | * needs to be read and/or written. Furthermore, ioctls are allowed | 1685 | * needs to be read and/or written. Furthermore, ioctls are allowed |
@@ -1798,18 +1861,25 @@ long fuse_do_ioctl(struct file *file, unsigned int cmd, unsigned long arg, | |||
1798 | in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) | 1861 | in_iovs + out_iovs > FUSE_IOCTL_MAX_IOV) |
1799 | goto out; | 1862 | goto out; |
1800 | 1863 | ||
1801 | err = -EIO; | ||
1802 | if ((in_iovs + out_iovs) * sizeof(struct iovec) != transferred) | ||
1803 | goto out; | ||
1804 | |||
1805 | /* okay, copy in iovs and retry */ | ||
1806 | vaddr = kmap_atomic(pages[0], KM_USER0); | 1864 | vaddr = kmap_atomic(pages[0], KM_USER0); |
1807 | memcpy(page_address(iov_page), vaddr, transferred); | 1865 | err = fuse_copy_ioctl_iovec(page_address(iov_page), vaddr, |
1866 | transferred, in_iovs + out_iovs, | ||
1867 | (flags & FUSE_IOCTL_COMPAT) != 0); | ||
1808 | kunmap_atomic(vaddr, KM_USER0); | 1868 | kunmap_atomic(vaddr, KM_USER0); |
1869 | if (err) | ||
1870 | goto out; | ||
1809 | 1871 | ||
1810 | in_iov = page_address(iov_page); | 1872 | in_iov = page_address(iov_page); |
1811 | out_iov = in_iov + in_iovs; | 1873 | out_iov = in_iov + in_iovs; |
1812 | 1874 | ||
1875 | err = fuse_verify_ioctl_iov(in_iov, in_iovs); | ||
1876 | if (err) | ||
1877 | goto out; | ||
1878 | |||
1879 | err = fuse_verify_ioctl_iov(out_iov, out_iovs); | ||
1880 | if (err) | ||
1881 | goto out; | ||
1882 | |||
1813 | goto retry; | 1883 | goto retry; |
1814 | } | 1884 | } |
1815 | 1885 | ||