aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/pagemap.h
diff options
context:
space:
mode:
authorAndreas Gruenbacher <agruenba@redhat.com>2020-01-06 08:58:23 -0800
committerDarrick J. Wong <darrick.wong@oracle.com>2020-01-06 08:58:23 -0800
commit243145bc4336684c69f95de0a303b31f2e5bf264 (patch)
tree1a5bf570c07209e01f80c93118d0e960545b697d /include/linux/pagemap.h
parentLinux 5.5-rc4 (diff)
downloadlinux-dev-243145bc4336684c69f95de0a303b31f2e5bf264.tar.xz
linux-dev-243145bc4336684c69f95de0a303b31f2e5bf264.zip
fs: Fix page_mkwrite off-by-one errors
The check in block_page_mkwrite that is meant to determine whether an offset is within the inode size is off by one. This bug has been copied into iomap_page_mkwrite and several filesystems (ubifs, ext4, f2fs, ceph). Fix that by introducing a new page_mkwrite_check_truncate helper that checks for truncate and computes the bytes in the page up to EOF. Use the helper in iomap. NOTE from Darrick: The original patch fixed a number of filesystems, but then there were merge conflicts with the f2fs for-next tree; a subsequent re-submission of the patch had different btrfs changes with no explanation; and Christoph complained that each per-fs fix should be a separate patch. In my view that's too much risk to take on, so I decided to drop all the hunks except for iomap, since I've actually QA'd XFS. Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> [darrick: drop everything but the iomap parts] Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Diffstat (limited to 'include/linux/pagemap.h')
-rw-r--r--include/linux/pagemap.h28
1 files changed, 28 insertions, 0 deletions
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index 37a4d9e32cd3..ccb14b6a16b5 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -636,4 +636,32 @@ static inline unsigned long dir_pages(struct inode *inode)
PAGE_SHIFT;
}
+/**
+ * page_mkwrite_check_truncate - check if page was truncated
+ * @page: the page to check
+ * @inode: the inode to check the page against
+ *
+ * Returns the number of bytes in the page up to EOF,
+ * or -EFAULT if the page was truncated.
+ */
+static inline int page_mkwrite_check_truncate(struct page *page,
+ struct inode *inode)
+{
+ loff_t size = i_size_read(inode);
+ pgoff_t index = size >> PAGE_SHIFT;
+ int offset = offset_in_page(size);
+
+ if (page->mapping != inode->i_mapping)
+ return -EFAULT;
+
+ /* page is wholly inside EOF */
+ if (page->index < index)
+ return PAGE_SIZE;
+ /* page is wholly past EOF */
+ if (page->index > index || !offset)
+ return -EFAULT;
+ /* page is partially inside EOF */
+ return offset;
+}
+
#endif /* _LINUX_PAGEMAP_H */