Skip to content

Commit 0ff8947

Browse files
Eric Sandeentytso
authored andcommitted
ext4: fix reservation overflow in ext4_da_write_begin
Delalloc write journal reservations only reserve 1 credit, to update the inode if necessary. However, it may happen once in a filesystem's lifetime that a file will cross the 2G threshold, and require the LARGE_FILE feature to be set in the superblock as well, if it was not set already. This overruns the transaction reservation, and can be demonstrated simply on any ext4 filesystem without the LARGE_FILE feature already set: dd if=/dev/zero of=testfile bs=1 seek=2147483646 count=1 \ conv=notrunc of=testfile sync dd if=/dev/zero of=testfile bs=1 seek=2147483647 count=1 \ conv=notrunc of=testfile leads to: EXT4-fs: ext4_do_update_inode:4296: aborting transaction: error 28 in __ext4_handle_dirty_super EXT4-fs error (device loop0) in ext4_do_update_inode:4301: error 28 EXT4-fs error (device loop0) in ext4_reserve_inode_write:4757: Readonly filesystem EXT4-fs error (device loop0) in ext4_dirty_inode:4876: error 28 EXT4-fs error (device loop0) in ext4_da_write_end:2685: error 28 Adjust the number of credits based on whether the flag is already set, and whether the current write may extend past the LARGE_FILE limit. Signed-off-by: Eric Sandeen <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Reviewed-by: Andreas Dilger <[email protected]> Cc: [email protected]
1 parent f4bb298 commit 0ff8947

File tree

1 file changed

+16
-1
lines changed

1 file changed

+16
-1
lines changed

fs/ext4/inode.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2495,6 +2495,20 @@ static int ext4_nonda_switch(struct super_block *sb)
24952495
return 0;
24962496
}
24972497

2498+
/* We always reserve for an inode update; the superblock could be there too */
2499+
static int ext4_da_write_credits(struct inode *inode, loff_t pos, unsigned len)
2500+
{
2501+
if (likely(EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
2502+
EXT4_FEATURE_RO_COMPAT_LARGE_FILE)))
2503+
return 1;
2504+
2505+
if (pos + len <= 0x7fffffffULL)
2506+
return 1;
2507+
2508+
/* We might need to update the superblock to set LARGE_FILE */
2509+
return 2;
2510+
}
2511+
24982512
static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
24992513
loff_t pos, unsigned len, unsigned flags,
25002514
struct page **pagep, void **fsdata)
@@ -2545,7 +2559,8 @@ static int ext4_da_write_begin(struct file *file, struct address_space *mapping,
25452559
* of file which has an already mapped buffer.
25462560
*/
25472561
retry_journal:
2548-
handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE, 1);
2562+
handle = ext4_journal_start(inode, EXT4_HT_WRITE_PAGE,
2563+
ext4_da_write_credits(inode, pos, len));
25492564
if (IS_ERR(handle)) {
25502565
page_cache_release(page);
25512566
return PTR_ERR(handle);

0 commit comments

Comments
 (0)