Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ext3: Improve truncate error handling

New truncate calling convention allows us to handle errors from
ext3_block_truncate_page(). So reorganize the code so that
ext3_block_truncate_page() is called before we change inode size.

This also removes unnecessary block zeroing from error recovery after failed
buffered writes (zeroing isn't needed because we could have never written
non-zero data to disk). We have to be careful and keep zeroing in direct IO
write error recovery because there we might have already overwritten end of the
last file block.

Signed-off-by: Jan Kara <jack@suse.cz>

Jan Kara ee3e77f1 90085930

+63 -38
+63 -38
fs/ext3/inode.c
··· 43 43 #include "acl.h" 44 44 45 45 static int ext3_writepage_trans_blocks(struct inode *inode); 46 + static int ext3_block_truncate_page(struct inode *inode, loff_t from); 46 47 47 48 /* 48 49 * Test whether an inode is a fast symlink. ··· 1208 1207 ext3_truncate(inode); 1209 1208 } 1210 1209 1210 + /* 1211 + * Truncate blocks that were not used by direct IO write. We have to zero out 1212 + * the last file block as well because direct IO might have written to it. 1213 + */ 1214 + static void ext3_truncate_failed_direct_write(struct inode *inode) 1215 + { 1216 + ext3_block_truncate_page(inode, inode->i_size); 1217 + ext3_truncate(inode); 1218 + } 1219 + 1211 1220 static int ext3_write_begin(struct file *file, struct address_space *mapping, 1212 1221 loff_t pos, unsigned len, unsigned flags, 1213 1222 struct page **pagep, void **fsdata) ··· 1858 1847 loff_t end = offset + iov_length(iov, nr_segs); 1859 1848 1860 1849 if (end > isize) 1861 - ext3_truncate_failed_write(inode); 1850 + ext3_truncate_failed_direct_write(inode); 1862 1851 } 1863 1852 if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries)) 1864 1853 goto retry; ··· 1872 1861 /* This is really bad luck. We've written the data 1873 1862 * but cannot extend i_size. Truncate allocated blocks 1874 1863 * and pretend the write failed... */ 1875 - ext3_truncate_failed_write(inode); 1864 + ext3_truncate_failed_direct_write(inode); 1876 1865 ret = PTR_ERR(handle); 1877 1866 goto out; 1878 1867 } ··· 1982 1971 * This required during truncate. We need to physically zero the tail end 1983 1972 * of that block so it doesn't yield old data if the file is later grown. 1984 1973 */ 1985 - static int ext3_block_truncate_page(handle_t *handle, struct page *page, 1986 - struct address_space *mapping, loff_t from) 1974 + static int ext3_block_truncate_page(struct inode *inode, loff_t from) 1987 1975 { 1988 1976 ext3_fsblk_t index = from >> PAGE_CACHE_SHIFT; 1989 - unsigned offset = from & (PAGE_CACHE_SIZE-1); 1977 + unsigned offset = from & (PAGE_CACHE_SIZE - 1); 1990 1978 unsigned blocksize, iblock, length, pos; 1991 - struct inode *inode = mapping->host; 1979 + struct page *page; 1980 + handle_t *handle = NULL; 1992 1981 struct buffer_head *bh; 1993 1982 int err = 0; 1994 1983 1984 + /* Truncated on block boundary - nothing to do */ 1995 1985 blocksize = inode->i_sb->s_blocksize; 1986 + if ((from & (blocksize - 1)) == 0) 1987 + return 0; 1988 + 1989 + page = grab_cache_page(inode->i_mapping, index); 1990 + if (!page) 1991 + return -ENOMEM; 1996 1992 length = blocksize - (offset & (blocksize - 1)); 1997 1993 iblock = index << (PAGE_CACHE_SHIFT - inode->i_sb->s_blocksize_bits); 1998 1994 ··· 2044 2026 goto unlock; 2045 2027 } 2046 2028 2029 + /* data=writeback mode doesn't need transaction to zero-out data */ 2030 + if (!ext3_should_writeback_data(inode)) { 2031 + /* We journal at most one block */ 2032 + handle = ext3_journal_start(inode, 1); 2033 + if (IS_ERR(handle)) { 2034 + clear_highpage(page); 2035 + flush_dcache_page(page); 2036 + err = PTR_ERR(handle); 2037 + goto unlock; 2038 + } 2039 + } 2040 + 2047 2041 if (ext3_should_journal_data(inode)) { 2048 2042 BUFFER_TRACE(bh, "get write access"); 2049 2043 err = ext3_journal_get_write_access(handle, bh); 2050 2044 if (err) 2051 - goto unlock; 2045 + goto stop; 2052 2046 } 2053 2047 2054 2048 zero_user(page, offset, length); ··· 2074 2044 err = ext3_journal_dirty_data(handle, bh); 2075 2045 mark_buffer_dirty(bh); 2076 2046 } 2047 + stop: 2048 + if (handle) 2049 + ext3_journal_stop(handle); 2077 2050 2078 2051 unlock: 2079 2052 unlock_page(page); ··· 2488 2455 struct ext3_inode_info *ei = EXT3_I(inode); 2489 2456 __le32 *i_data = ei->i_data; 2490 2457 int addr_per_block = EXT3_ADDR_PER_BLOCK(inode->i_sb); 2491 - struct address_space *mapping = inode->i_mapping; 2492 2458 int offsets[4]; 2493 2459 Indirect chain[4]; 2494 2460 Indirect *partial; ··· 2495 2463 int n; 2496 2464 long last_block; 2497 2465 unsigned blocksize = inode->i_sb->s_blocksize; 2498 - struct page *page; 2499 2466 2500 2467 trace_ext3_truncate_enter(inode); 2501 2468 ··· 2504 2473 if (inode->i_size == 0 && ext3_should_writeback_data(inode)) 2505 2474 ext3_set_inode_state(inode, EXT3_STATE_FLUSH_ON_CLOSE); 2506 2475 2507 - /* 2508 - * We have to lock the EOF page here, because lock_page() nests 2509 - * outside journal_start(). 2510 - */ 2511 - if ((inode->i_size & (blocksize - 1)) == 0) { 2512 - /* Block boundary? Nothing to do */ 2513 - page = NULL; 2514 - } else { 2515 - page = grab_cache_page(mapping, 2516 - inode->i_size >> PAGE_CACHE_SHIFT); 2517 - if (!page) 2518 - goto out_notrans; 2519 - } 2520 - 2521 2476 handle = start_transaction(inode); 2522 - if (IS_ERR(handle)) { 2523 - if (page) { 2524 - clear_highpage(page); 2525 - flush_dcache_page(page); 2526 - unlock_page(page); 2527 - page_cache_release(page); 2528 - } 2477 + if (IS_ERR(handle)) 2529 2478 goto out_notrans; 2530 - } 2531 2479 2532 2480 last_block = (inode->i_size + blocksize-1) 2533 2481 >> EXT3_BLOCK_SIZE_BITS(inode->i_sb); 2534 - 2535 - if (page) 2536 - ext3_block_truncate_page(handle, page, mapping, inode->i_size); 2537 - 2538 2482 n = ext3_block_to_path(inode, last_block, offsets, NULL); 2539 2483 if (n == 0) 2540 2484 goto out_stop; /* error */ ··· 3257 3251 } 3258 3252 3259 3253 error = ext3_orphan_add(handle, inode); 3254 + if (error) { 3255 + ext3_journal_stop(handle); 3256 + goto err_out; 3257 + } 3260 3258 EXT3_I(inode)->i_disksize = attr->ia_size; 3261 - rc = ext3_mark_inode_dirty(handle, inode); 3262 - if (!error) 3263 - error = rc; 3259 + error = ext3_mark_inode_dirty(handle, inode); 3264 3260 ext3_journal_stop(handle); 3261 + if (error) { 3262 + /* Some hard fs error must have happened. Bail out. */ 3263 + ext3_orphan_del(NULL, inode); 3264 + goto err_out; 3265 + } 3266 + rc = ext3_block_truncate_page(inode, attr->ia_size); 3267 + if (rc) { 3268 + /* Cleanup orphan list and exit */ 3269 + handle = ext3_journal_start(inode, 3); 3270 + if (IS_ERR(handle)) { 3271 + ext3_orphan_del(NULL, inode); 3272 + goto err_out; 3273 + } 3274 + ext3_orphan_del(handle, inode); 3275 + ext3_journal_stop(handle); 3276 + goto err_out; 3277 + } 3265 3278 } 3266 3279 3267 3280 if ((attr->ia_valid & ATTR_SIZE) &&