Skip to content

Commit

Permalink
ext4: Add flag to files with blocks intentionally past EOF
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jiaying Zhang authored and Theodore Ts'o committed Feb 24, 2010
1 parent 73b50c1 commit c8d46e4
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 4 deletions.
5 changes: 3 additions & 2 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,10 +285,11 @@ struct flex_groups {
#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */
#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */
#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */
#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */
#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */

#define EXT4_FL_USER_VISIBLE 0x000BDFFF /* User visible flags */
#define EXT4_FL_USER_MODIFIABLE 0x000B80FF /* User modifiable flags */
#define EXT4_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */
#define EXT4_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */

/* Flags that should be inherited by new inodes from their parent. */
#define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\
Expand Down
22 changes: 21 additions & 1 deletion fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -3186,7 +3186,7 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
{
struct ext4_ext_path *path = NULL;
struct ext4_extent_header *eh;
struct ext4_extent newex, *ex;
struct ext4_extent newex, *ex, *last_ex;
ext4_fsblk_t newblock;
int err = 0, depth, ret, cache_type;
unsigned int allocated = 0;
Expand Down Expand Up @@ -3367,6 +3367,19 @@ int ext4_ext_get_blocks(handle_t *handle, struct inode *inode,
EXT4_STATE_DIO_UNWRITTEN);
}
}

if (unlikely(EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL)) {
if (eh->eh_entries) {
last_ex = EXT_LAST_EXTENT(eh);
if (iblock + ar.len > le32_to_cpu(last_ex->ee_block)
+ ext4_ext_get_actual_len(last_ex))
EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL;
} else {
WARN_ON(eh->eh_entries == 0);
ext4_error(inode->i_sb, __func__,
"inode#%lu, eh->eh_entries = 0!", inode->i_ino);
}
}
err = ext4_ext_insert_extent(handle, inode, path, &newex, flags);
if (err) {
/* free data blocks we just allocated */
Expand Down Expand Up @@ -3500,6 +3513,13 @@ static void ext4_falloc_update_inode(struct inode *inode,
i_size_write(inode, new_size);
if (new_size > EXT4_I(inode)->i_disksize)
ext4_update_i_disksize(inode, new_size);
} else {
/*
* Mark that we allocate beyond EOF so the subsequent truncate
* can proceed even if the new size is the same as i_size.
*/
if (new_size > i_size_read(inode))
EXT4_I(inode)->i_flags |= EXT4_EOFBLOCKS_FL;
}

}
Expand Down
9 changes: 8 additions & 1 deletion fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -4456,6 +4456,8 @@ void ext4_truncate(struct inode *inode)
if (!ext4_can_truncate(inode))
return;

EXT4_I(inode)->i_flags &= ~EXT4_EOFBLOCKS_FL;

if (inode->i_size == 0 && !test_opt(inode->i_sb, NO_AUTO_DA_ALLOC))
ext4_set_inode_state(inode, EXT4_STATE_DA_ALLOC_CLOSE);

Expand Down Expand Up @@ -5305,7 +5307,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
}

if (S_ISREG(inode->i_mode) &&
attr->ia_valid & ATTR_SIZE && attr->ia_size < inode->i_size) {
attr->ia_valid & ATTR_SIZE &&
(attr->ia_size < inode->i_size ||
(EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL))) {
handle_t *handle;

handle = ext4_journal_start(inode, 3);
Expand Down Expand Up @@ -5336,6 +5340,9 @@ int ext4_setattr(struct dentry *dentry, struct iattr *attr)
goto err_out;
}
}
/* ext4_truncate will clear the flag */
if ((EXT4_I(inode)->i_flags & EXT4_EOFBLOCKS_FL))
ext4_truncate(inode);
}

rc = inode_setattr(inode, attr);
Expand Down
9 changes: 9 additions & 0 deletions fs/ext4/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,15 @@ long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
flags &= ~EXT4_EXTENTS_FL;
}

if (flags & EXT4_EOFBLOCKS_FL) {
/* we don't support adding EOFBLOCKS flag */
if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
err = -EOPNOTSUPP;
goto flags_out;
}
} else if (oldflags & EXT4_EOFBLOCKS_FL)
ext4_truncate(inode);

handle = ext4_journal_start(inode, 1);
if (IS_ERR(handle)) {
err = PTR_ERR(handle);
Expand Down

0 comments on commit c8d46e4

Please sign in to comment.