aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJiaying Zhang <jiayingz@google.com>2010-02-24 09:52:53 -0500
committerTheodore Ts'o <tytso@mit.edu>2010-02-24 09:52:53 -0500
commitc8d46e41bc744c8fa0092112af3942fcd46c8b18 (patch)
treefd8bad55c3c9903b6c798b838396bc832bbf7f4b /fs
parent73b50c1c92666d326b5fa2c945d46509f2f6d91f (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.h5
-rw-r--r--fs/ext4/extents.c22
-rw-r--r--fs/ext4/inode.c9
-rw-r--r--fs/ext4/ioctl.c9
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);