struct page *page = vmf->page;
struct inode *inode = file_inode(vmf->vma->vm_file);
unsigned long length;
- loff_t offset, size;
+ loff_t offset;
ssize_t ret;
lock_page(page);
- size = i_size_read(inode);
- offset = page_offset(page);
- if (page->mapping != inode->i_mapping || offset > size) {
- /* We overload EFAULT to mean page got truncated */
- ret = -EFAULT;
+ ret = page_mkwrite_check_truncate(page, inode);
+ if (ret < 0)
goto out_unlock;
- }
-
- /* page is wholly or partially inside EOF */
- if (offset > size - PAGE_SIZE)
- length = offset_in_page(size);
- else
- length = PAGE_SIZE;
+ length = ret;
+ offset = page_offset(page);
while (length > 0) {
ret = iomap_apply(inode, offset, length,
IOMAP_WRITE | IOMAP_FAULT, ops, page,
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 */