|
@@ -43,6 +43,7 @@
|
|
|
#include "acl.h"
|
|
|
|
|
|
static int ext3_writepage_trans_blocks(struct inode *inode);
|
|
|
+static int ext3_block_truncate_page(struct inode *inode, loff_t from);
|
|
|
|
|
|
/*
|
|
|
* Test whether an inode is a fast symlink.
|
|
@@ -1207,6 +1208,16 @@ static void ext3_truncate_failed_write(struct inode *inode)
|
|
|
ext3_truncate(inode);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Truncate blocks that were not used by direct IO write. We have to zero out
|
|
|
+ * the last file block as well because direct IO might have written to it.
|
|
|
+ */
|
|
|
+static void ext3_truncate_failed_direct_write(struct inode *inode)
|
|
|
+{
|
|
|
+ ext3_block_truncate_page(inode, inode->i_size);
|
|
|
+ ext3_truncate(inode);
|
|
|
+}
|
|
|
+
|
|
|
static int ext3_write_begin(struct file *file, struct address_space *mapping,
|
|
|
loff_t pos, unsigned len, unsigned flags,
|
|
|
struct page **pagep, void **fsdata)
|
|
@@ -1847,7 +1858,7 @@ retry:
|
|
|
loff_t end = offset + iov_length(iov, nr_segs);
|
|
|
|
|
|
if (end > isize)
|
|
|
- ext3_truncate_failed_write(inode);
|
|
|
+ ext3_truncate_failed_direct_write(inode);
|
|
|
}
|
|
|
if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))
|
|
|
goto retry;
|
|
@@ -1861,7 +1872,7 @@ retry:
|
|
|
/* This is really bad luck. We've written the data
|
|
|
* but cannot extend i_size. Truncate allocated blocks
|
|
|
* and pretend the write failed... */
|
|
|
- ext3_truncate_failed_write(inode);
|
|
|
+ ext3_truncate_failed_direct_write(inode);
|
|
|
ret = PTR_ERR(handle);
|
|
|
goto out;
|
|
|
}
|
|
@@ -1971,17 +1982,24 @@ void ext3_set_aops(struct inode *inode)
|
|
|
* This required during truncate. We need to physically zero the tail end
|
|
|
* of that block so it doesn't yield old data if the file is later grown.
|
|
|
*/
|
|
|
-static int ext3_block_truncate_page(handle_t *handle, struct page *page,
|
|
|
- struct address_space *mapping, loff_t from)
|
|
|
+static int ext3_block_truncate_page(struct inode *inode, loff_t from)
|
|
|
{
|
|
|
ext3_fsblk_t index = from >> PAGE_CACHE_SHIFT;
|
|
|
- unsigned offset = from & (PAGE_CACHE_SIZE-1);
|
|
|
+ unsigned offset = from & (PAGE_CACHE_SIZE - 1);
|
|
|
unsigned blocksize, iblock, length, pos;
|
|
|
- struct inode *inode = mapping->host;
|
|
|
+ struct page *page;
|
|
|
+ handle_t *handle = NULL;
|
|
|
struct buffer_head *bh;
|
|
|
int err = 0;
|
|
|
|
|
|
+ /* Truncated on block boundary - nothing to do */
|
|
|
blocksize = inode->i_sb->s_blocksize;
|
|
|
+ if ((from & (blocksize - 1)) == 0)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ page = grab_cache_page(inode->i_mapping, index);
|
|
|
+ if (!page)
|
|
|
+ return -ENOMEM;
|
|
|
length = blocksize - (offset & (blocksize - 1));
|
|
|
iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits);
|
|
|
|
|
@@ -2026,11 +2044,23 @@ static int ext3_block_truncate_page(handle_t *handle, struct page *page,
|
|
|
goto unlock;
|
|
|
}
|
|
|
|
|
|
+ /* data=writeback mode doesn't need transaction to zero-out data */
|
|
|
+ if (!ext3_should_writeback_data(inode)) {
|
|
|
+ /* We journal at most one block */
|
|
|
+ handle = ext3_journal_start(inode, 1);
|
|
|
+ if (IS_ERR(handle)) {
|
|
|
+ clear_highpage(page);
|
|
|
+ flush_dcache_page(page);
|
|
|
+ err = PTR_ERR(handle);
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (ext3_should_journal_data(inode)) {
|
|
|
BUFFER_TRACE(bh, "get write access");
|
|
|
err = ext3_journal_get_write_access(handle, bh);
|
|
|
if (err)
|
|
|
- goto unlock;
|
|
|
+ goto stop;
|
|
|
}
|
|
|
|
|
|
zero_user(page, offset, length);
|
|
@@ -2044,6 +2074,9 @@ static int ext3_block_truncate_page(handle_t *handle, struct page *page,
|
|
|
err = ext3_journal_dirty_data(handle, bh);
|
|
|
mark_buffer_dirty(bh);
|
|
|
}
|
|
|
+stop:
|
|
|
+ if (handle)
|
|
|
+ ext3_journal_stop(handle);
|
|
|
|
|
|
unlock:
|
|
|
unlock_page(page);
|
|
@@ -2455,7 +2488,6 @@ void ext3_truncate(struct inode *inode)
|
|
|
struct ext3_inode_info *ei = EXT3_I(inode);
|
|
|
__le32 *i_data = ei->i_data;
|
|
|
int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb);
|
|
|
- struct address_space *mapping = inode->i_mapping;
|
|
|
int offsets[4];
|
|
|
Indirect chain[4];
|
|
|
Indirect *partial;
|
|
@@ -2463,7 +2495,6 @@ void ext3_truncate(struct inode *inode)
|
|
|
int n;
|
|
|
long last_block;
|
|
|
unsigned blocksize = inode->i_sb->s_blocksize;
|
|
|
- struct page *page;
|
|
|
|
|
|
trace_ext3_truncate_enter(inode);
|
|
|
|
|
@@ -2473,37 +2504,12 @@ void ext3_truncate(struct inode *inode)
|
|
|
if (inode->i_size == 0 && ext3_should_writeback_data(inode))
|
|
|
ext3_set_inode_state(inode, EXT3_STATE_FLUSH_ON_CLOSE);
|
|
|
|
|
|
- /*
|
|
|
- * We have to lock the EOF page here, because lock_page() nests
|
|
|
- * outside journal_start().
|
|
|
- */
|
|
|
- if ((inode->i_size & (blocksize - 1)) == 0) {
|
|
|
- /* Block boundary? Nothing to do */
|
|
|
- page = NULL;
|
|
|
- } else {
|
|
|
- page = grab_cache_page(mapping,
|
|
|
- inode->i_size >> PAGE_CACHE_SHIFT);
|
|
|
- if (!page)
|
|
|
- goto out_notrans;
|
|
|
- }
|
|
|
-
|
|
|
handle = start_transaction(inode);
|
|
|
- if (IS_ERR(handle)) {
|
|
|
- if (page) {
|
|
|
- clear_highpage(page);
|
|
|
- flush_dcache_page(page);
|
|
|
- unlock_page(page);
|
|
|
- page_cache_release(page);
|
|
|
- }
|
|
|
+ if (IS_ERR(handle))
|
|
|
goto out_notrans;
|
|
|
- }
|
|
|
|
|
|
last_block = (inode->i_size + blocksize-1)
|
|
|
>> EXT3_BLOCK_SIZE_BITS(inode->i_sb);
|
|
|
-
|
|
|
- if (page)
|
|
|
- ext3_block_truncate_page(handle, page, mapping, inode->i_size);
|
|
|
-
|
|
|
n = ext3_block_to_path(inode, last_block, offsets, NULL);
|
|
|
if (n == 0)
|
|
|
goto out_stop; /* error */
|
|
@@ -3251,11 +3257,30 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
|
|
|
}
|
|
|
|
|
|
error = ext3_orphan_add(handle, inode);
|
|
|
+ if (error) {
|
|
|
+ ext3_journal_stop(handle);
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
EXT3_I(inode)->i_disksize = attr->ia_size;
|
|
|
- rc = ext3_mark_inode_dirty(handle, inode);
|
|
|
- if (!error)
|
|
|
- error = rc;
|
|
|
+ error = ext3_mark_inode_dirty(handle, inode);
|
|
|
ext3_journal_stop(handle);
|
|
|
+ if (error) {
|
|
|
+ /* Some hard fs error must have happened. Bail out. */
|
|
|
+ ext3_orphan_del(NULL, inode);
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
+ rc = ext3_block_truncate_page(inode, attr->ia_size);
|
|
|
+ if (rc) {
|
|
|
+ /* Cleanup orphan list and exit */
|
|
|
+ handle = ext3_journal_start(inode, 3);
|
|
|
+ if (IS_ERR(handle)) {
|
|
|
+ ext3_orphan_del(NULL, inode);
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
+ ext3_orphan_del(handle, inode);
|
|
|
+ ext3_journal_stop(handle);
|
|
|
+ goto err_out;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if ((attr->ia_valid & ATTR_SIZE) &&
|