diff options
author | Kazuya Mio <k-mio@sx.jp.nec.com> | 2011-04-10 22:06:36 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2011-04-10 22:06:36 -0400 |
commit | f80da1e70f1ffec3825aa0a1d0801f4896e002b6 (patch) | |
tree | 42976f4e0fc07da78ffce618b7e98b87d82a2bff /fs | |
parent | be4f27d324e8ddd57cc0d4d604fe85ee0425cba9 (diff) |
ext4: Allow indirect-block file to grow the file size to max file size
We can create 4402345721856 byte file with indirect block mapping.
However, if we grow an indirect-block file to the size with ftruncate(),
we can see an ext4 warning. The following patch fixes this problem.
How to reproduce:
# dd if=/dev/zero of=/mnt/mp1/hoge bs=1 count=0 seek=4402345721856
0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.000221428 s, 0.0 kB/s
# tail -n 1 /var/log/messages
Nov 25 15:10:27 test kernel: EXT4-fs warning (device sda8): ext4_block_to_path:345: block 1074791436 > max in inode 12
Signed-off-by: Kazuya Mio <k-mio@sx.jp.nec.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/inode.c | 23 |
1 files changed, 17 insertions, 6 deletions
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 7d11e02ad01d..5560f78690ac 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4429,8 +4429,8 @@ void ext4_truncate(struct inode *inode) | |||
4429 | Indirect chain[4]; | 4429 | Indirect chain[4]; |
4430 | Indirect *partial; | 4430 | Indirect *partial; |
4431 | __le32 nr = 0; | 4431 | __le32 nr = 0; |
4432 | int n; | 4432 | int n = 0; |
4433 | ext4_lblk_t last_block; | 4433 | ext4_lblk_t last_block, max_block; |
4434 | unsigned blocksize = inode->i_sb->s_blocksize; | 4434 | unsigned blocksize = inode->i_sb->s_blocksize; |
4435 | 4435 | ||
4436 | trace_ext4_truncate_enter(inode); | 4436 | trace_ext4_truncate_enter(inode); |
@@ -4455,14 +4455,18 @@ void ext4_truncate(struct inode *inode) | |||
4455 | 4455 | ||
4456 | last_block = (inode->i_size + blocksize-1) | 4456 | last_block = (inode->i_size + blocksize-1) |
4457 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); | 4457 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); |
4458 | max_block = (EXT4_SB(inode->i_sb)->s_bitmap_maxbytes + blocksize-1) | ||
4459 | >> EXT4_BLOCK_SIZE_BITS(inode->i_sb); | ||
4458 | 4460 | ||
4459 | if (inode->i_size & (blocksize - 1)) | 4461 | if (inode->i_size & (blocksize - 1)) |
4460 | if (ext4_block_truncate_page(handle, mapping, inode->i_size)) | 4462 | if (ext4_block_truncate_page(handle, mapping, inode->i_size)) |
4461 | goto out_stop; | 4463 | goto out_stop; |
4462 | 4464 | ||
4463 | n = ext4_block_to_path(inode, last_block, offsets, NULL); | 4465 | if (last_block != max_block) { |
4464 | if (n == 0) | 4466 | n = ext4_block_to_path(inode, last_block, offsets, NULL); |
4465 | goto out_stop; /* error */ | 4467 | if (n == 0) |
4468 | goto out_stop; /* error */ | ||
4469 | } | ||
4466 | 4470 | ||
4467 | /* | 4471 | /* |
4468 | * OK. This truncate is going to happen. We add the inode to the | 4472 | * OK. This truncate is going to happen. We add the inode to the |
@@ -4493,7 +4497,13 @@ void ext4_truncate(struct inode *inode) | |||
4493 | */ | 4497 | */ |
4494 | ei->i_disksize = inode->i_size; | 4498 | ei->i_disksize = inode->i_size; |
4495 | 4499 | ||
4496 | if (n == 1) { /* direct blocks */ | 4500 | if (last_block == max_block) { |
4501 | /* | ||
4502 | * It is unnecessary to free any data blocks if last_block is | ||
4503 | * equal to the indirect block limit. | ||
4504 | */ | ||
4505 | goto out_unlock; | ||
4506 | } else if (n == 1) { /* direct blocks */ | ||
4497 | ext4_free_data(handle, inode, NULL, i_data+offsets[0], | 4507 | ext4_free_data(handle, inode, NULL, i_data+offsets[0], |
4498 | i_data + EXT4_NDIR_BLOCKS); | 4508 | i_data + EXT4_NDIR_BLOCKS); |
4499 | goto do_indirects; | 4509 | goto do_indirects; |
@@ -4553,6 +4563,7 @@ do_indirects: | |||
4553 | ; | 4563 | ; |
4554 | } | 4564 | } |
4555 | 4565 | ||
4566 | out_unlock: | ||
4556 | up_write(&ei->i_data_sem); | 4567 | up_write(&ei->i_data_sem); |
4557 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | 4568 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); |
4558 | ext4_mark_inode_dirty(handle, inode); | 4569 | ext4_mark_inode_dirty(handle, inode); |