diff options
author | Jiaying Zhang <jiayingz@google.com> | 2010-02-24 09:52:53 -0500 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2010-02-24 09:52:53 -0500 |
commit | c8d46e41bc744c8fa0092112af3942fcd46c8b18 (patch) | |
tree | fd8bad55c3c9903b6c798b838396bc832bbf7f4b /fs | |
parent | 73b50c1c92666d326b5fa2c945d46509f2f6d91f (diff) |
ext4: Add flag to files with blocks intentionally past EOF
fallocate() may potentially instantiate blocks past EOF, depending
on the flags used when it is called.
e2fsck currently has a test for blocks past i_size, and it
sometimes trips up - noticeably on xfstests 013 which runs fsstress.
This patch from Jiayang does fix it up - it (along with
e2fsprogs updates and other patches recently from Aneesh) has
survived many fsstress runs in a row.
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Jiaying Zhang <jiayingz@google.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext4/ext4.h | 5 | ||||
-rw-r--r-- | fs/ext4/extents.c | 22 | ||||
-rw-r--r-- | fs/ext4/inode.c | 9 | ||||
-rw-r--r-- | fs/ext4/ioctl.c | 9 |
4 files changed, 41 insertions, 4 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 509437ffb71b..74664ca19e22 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -285,10 +285,11 @@ struct flex_groups { | |||
285 | #define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ | 285 | #define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ |
286 | #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ | 286 | #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ |
287 | #define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ | 287 | #define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ |
288 | #define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ | ||
288 | #define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ | 289 | #define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ |
289 | 290 | ||
290 | #define EXT4_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */ | 291 | #define EXT4_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ |
291 | #define EXT4_FL_USER_MODIFIABLE 0x000B80FF /* User modifiable flags */ | 292 | #define EXT4_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ |
292 | 293 | ||
293 | /* Flags that should be inherited by new inodes from their parent. */ | 294 | /* Flags that should be inherited by new inodes from their parent. */ |
294 | #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\ | 295 | #define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\ |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 7d54850f7136..a2c21aa09e2b 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -3186,7 +3186,7 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, | |||
3186 | { | 3186 | { |
3187 | struct ext4_ext_path *path = NULL; | 3187 | struct ext4_ext_path *path = NULL; |
3188 | struct ext4_extent_header *eh; | 3188 | struct ext4_extent_header *eh; |
3189 | struct ext4_extent newex, *ex; | 3189 | struct ext4_extent newex, *ex, *last_ex; |
3190 | ext4_fsblk_t newblock; | 3190 | ext4_fsblk_t newblock; |
3191 | int err = 0, depth, ret, cache_type; | 3191 | int err = 0, depth, ret, cache_type; |
3192 | unsigned int allocated = 0; | 3192 | unsigned int allocated = 0; |
@@ -3367,6 +3367,19 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode, | |||
3367 | EXT4_STATE_DIO_UNWRITTEN); | 3367 | EXT4_STATE_DIO_UNWRITTEN); |
3368 | } | 3368 | } |
3369 | } | 3369 | } |
3370 | |||
3371 | if (unlikely(EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL)) { | ||
3372 | if (eh->eh_entries) { | ||
3373 | last_ex = EXT_LAST_EXTENT(eh); | ||
3374 | if (iblock + ar.len > le32_to_cpu(last_ex->ee_block) | ||
3375 | + ext4_ext_get_actual_len(last_ex)) | ||
3376 | EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL; | ||
3377 | } else { | ||
3378 | WARN_ON(eh->eh_entries == 0); | ||
3379 | ext4_error(inode->i_sb, __func__, | ||
3380 | "inode#%lu, eh->eh_entries = 0!", inode->i_ino); | ||
3381 | } | ||
3382 | } | ||
3370 | err = ext4_ext_insert_extent(handle, inode, path, &newex, flags); | 3383 | err = ext4_ext_insert_extent(handle, inode, path, &newex, flags); |
3371 | if (err) { | 3384 | if (err) { |
3372 | /* free data blocks we just allocated */ | 3385 | /* free data blocks we just allocated */ |
@@ -3500,6 +3513,13 @@ static void ext4_falloc_update_inode(struct inode *inode, | |||
3500 | i_size_write(inode, new_size); | 3513 | i_size_write(inode, new_size); |
3501 | if (new_size > EXT4_I(inode)->i_disksize) | 3514 | if (new_size > EXT4_I(inode)->i_disksize) |
3502 | ext4_update_i_disksize(inode, new_size); | 3515 | ext4_update_i_disksize(inode, new_size); |
3516 | } else { | ||
3517 | /* | ||
3518 | * Mark that we allocate beyond EOF so the subsequent truncate | ||
3519 | * can proceed even if the new size is the same as i_size. | ||
3520 | */ | ||
3521 | if (new_size > i_size_read(inode)) | ||
3522 | EXT4_I(inode)->i_flags |= EXT4_EOFBLOCKS_FL; | ||
3503 | } | 3523 | } |
3504 | 3524 | ||
3505 | } | 3525 | } |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index ecac8c5a6f5c..edb7edc99f71 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4456,6 +4456,8 @@ void ext4_truncate(struct inode *inode) | |||
4456 | if (!ext4_can_truncate(inode)) | 4456 | if (!ext4_can_truncate(inode)) |
4457 | return; | 4457 | return; |
4458 | 4458 | ||
4459 | EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL; | ||
4460 | |||
4459 | if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC)) | 4461 | if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC)) |
4460 | ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE); | 4462 | ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE); |
4461 | 4463 | ||
@@ -5305,7 +5307,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) | |||
5305 | } | 5307 | } |
5306 | 5308 | ||
5307 | if (S_ISREG(inode->i_mode) && | 5309 | if (S_ISREG(inode->i_mode) && |
5308 | attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) { | 5310 | attr->ia_valid & ATTR_SIZE && |
5311 | (attr->ia_size < inode->i_size || | ||
5312 | (EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL))) { | ||
5309 | handle_t *handle; | 5313 | handle_t *handle; |
5310 | 5314 | ||
5311 | handle = ext4_journal_start(inode, 3); | 5315 | handle = ext4_journal_start(inode, 3); |
@@ -5336,6 +5340,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr) | |||
5336 | goto err_out; | 5340 | goto err_out; |
5337 | } | 5341 | } |
5338 | } | 5342 | } |
5343 | /* ext4_truncate will clear the flag */ | ||
5344 | if ((EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL)) | ||
5345 | ext4_truncate(inode); | ||
5339 | } | 5346 | } |
5340 | 5347 | ||
5341 | rc = inode_setattr(inode, attr); | 5348 | rc = inode_setattr(inode, attr); |
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index b63d193126db..2220feb2dcc1 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c | |||
@@ -92,6 +92,15 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) | |||
92 | flags &= ~EXT4_EXTENTS_FL; | 92 | flags &= ~EXT4_EXTENTS_FL; |
93 | } | 93 | } |
94 | 94 | ||
95 | if (flags & EXT4_EOFBLOCKS_FL) { | ||
96 | /* we don't support adding EOFBLOCKS flag */ | ||
97 | if (!(oldflags & EXT4_EOFBLOCKS_FL)) { | ||
98 | err = -EOPNOTSUPP; | ||
99 | goto flags_out; | ||
100 | } | ||
101 | } else if (oldflags & EXT4_EOFBLOCKS_FL) | ||
102 | ext4_truncate(inode); | ||
103 | |||
95 | handle = ext4_journal_start(inode, 1); | 104 | handle = ext4_journal_start(inode, 1); |
96 | if (IS_ERR(handle)) { | 105 | if (IS_ERR(handle)) { |
97 | err = PTR_ERR(handle); | 106 | err = PTR_ERR(handle); |