|
@@ -2160,11 +2160,12 @@ int block_read_full_page(struct page *page, get_block_t *get_block)
|
|
|
* truncates. Uses prepare/commit_write to allow the filesystem to
|
|
|
* deal with the hole.
|
|
|
*/
|
|
|
-int generic_cont_expand(struct inode *inode, loff_t size)
|
|
|
+static int __generic_cont_expand(struct inode *inode, loff_t size,
|
|
|
+ pgoff_t index, unsigned int offset)
|
|
|
{
|
|
|
struct address_space *mapping = inode->i_mapping;
|
|
|
struct page *page;
|
|
|
- unsigned long index, offset, limit;
|
|
|
+ unsigned long limit;
|
|
|
int err;
|
|
|
|
|
|
err = -EFBIG;
|
|
@@ -2176,24 +2177,24 @@ int generic_cont_expand(struct inode *inode, loff_t size)
|
|
|
if (size > inode->i_sb->s_maxbytes)
|
|
|
goto out;
|
|
|
|
|
|
- offset = (size & (PAGE_CACHE_SIZE-1)); /* Within page */
|
|
|
-
|
|
|
- /* ugh. in prepare/commit_write, if from==to==start of block, we
|
|
|
- ** skip the prepare. make sure we never send an offset for the start
|
|
|
- ** of a block
|
|
|
- */
|
|
|
- if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
|
|
|
- offset++;
|
|
|
- }
|
|
|
- index = size >> PAGE_CACHE_SHIFT;
|
|
|
err = -ENOMEM;
|
|
|
page = grab_cache_page(mapping, index);
|
|
|
if (!page)
|
|
|
goto out;
|
|
|
err = mapping->a_ops->prepare_write(NULL, page, offset, offset);
|
|
|
- if (!err) {
|
|
|
- err = mapping->a_ops->commit_write(NULL, page, offset, offset);
|
|
|
+ if (err) {
|
|
|
+ /*
|
|
|
+ * ->prepare_write() may have instantiated a few blocks
|
|
|
+ * outside i_size. Trim these off again.
|
|
|
+ */
|
|
|
+ unlock_page(page);
|
|
|
+ page_cache_release(page);
|
|
|
+ vmtruncate(inode, inode->i_size);
|
|
|
+ goto out;
|
|
|
}
|
|
|
+
|
|
|
+ err = mapping->a_ops->commit_write(NULL, page, offset, offset);
|
|
|
+
|
|
|
unlock_page(page);
|
|
|
page_cache_release(page);
|
|
|
if (err > 0)
|
|
@@ -2202,6 +2203,36 @@ out:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
+int generic_cont_expand(struct inode *inode, loff_t size)
|
|
|
+{
|
|
|
+ pgoff_t index;
|
|
|
+ unsigned int offset;
|
|
|
+
|
|
|
+ offset = (size & (PAGE_CACHE_SIZE - 1)); /* Within page */
|
|
|
+
|
|
|
+ /* ugh. in prepare/commit_write, if from==to==start of block, we
|
|
|
+ ** skip the prepare. make sure we never send an offset for the start
|
|
|
+ ** of a block
|
|
|
+ */
|
|
|
+ if ((offset & (inode->i_sb->s_blocksize - 1)) == 0) {
|
|
|
+ /* caller must handle this extra byte. */
|
|
|
+ offset++;
|
|
|
+ }
|
|
|
+ index = size >> PAGE_CACHE_SHIFT;
|
|
|
+
|
|
|
+ return __generic_cont_expand(inode, size, index, offset);
|
|
|
+}
|
|
|
+
|
|
|
+int generic_cont_expand_simple(struct inode *inode, loff_t size)
|
|
|
+{
|
|
|
+ loff_t pos = size - 1;
|
|
|
+ pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
|
|
+ unsigned int offset = (pos & (PAGE_CACHE_SIZE - 1)) + 1;
|
|
|
+
|
|
|
+ /* prepare/commit_write can handle even if from==to==start of block. */
|
|
|
+ return __generic_cont_expand(inode, size, index, offset);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* For moronic filesystems that do not allow holes in file.
|
|
|
* We may have to extend the file.
|
|
@@ -3145,6 +3176,7 @@ EXPORT_SYMBOL(fsync_bdev);
|
|
|
EXPORT_SYMBOL(generic_block_bmap);
|
|
|
EXPORT_SYMBOL(generic_commit_write);
|
|
|
EXPORT_SYMBOL(generic_cont_expand);
|
|
|
+EXPORT_SYMBOL(generic_cont_expand_simple);
|
|
|
EXPORT_SYMBOL(init_buffer);
|
|
|
EXPORT_SYMBOL(invalidate_bdev);
|
|
|
EXPORT_SYMBOL(ll_rw_block);
|