diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index dabad1bc861723932fd7f25da1d86cdd0a641ce9..48143e32411c4cabfdd56bebe41c8fdc522c9665 100644 --- a/fs/ext4/ext4_jbd2.h +++ b/fs/ext4/ext4_jbd2.h @@ -227,6 +227,9 @@ int ext4_reserve_inode_write(handle_t *handle, struct inode *inode, int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode); +int ext4_expand_extra_isize(struct inode *inode, + unsigned int new_extra_isize, + struct ext4_iloc *iloc); /* * Wrapper functions with which ext4 calls into JBD. */ diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 713b67e85f7331dd8bdd4dc170483bedcc6c1b46..c774bdc22759b1602341da70a0b3fec7b8830f2c 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -5702,6 +5702,42 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode, return err; } +static int __ext4_expand_extra_isize(struct inode *inode, + unsigned int new_extra_isize, + struct ext4_iloc *iloc, + handle_t *handle, int *no_expand) +{ + struct ext4_inode *raw_inode; + struct ext4_xattr_ibody_header *header; + int error; + + raw_inode = ext4_raw_inode(iloc); + + header = IHDR(inode, raw_inode); + + /* No extended attributes present */ + if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR) || + header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) { + memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE + + EXT4_I(inode)->i_extra_isize, 0, + new_extra_isize - EXT4_I(inode)->i_extra_isize); + EXT4_I(inode)->i_extra_isize = new_extra_isize; + return 0; + } + + /* try to expand with EAs present */ + error = ext4_expand_extra_isize_ea(inode, new_extra_isize, + raw_inode, handle); + if (error) { + /* + * Inode size expansion failed; don't try again + */ + *no_expand = 1; + } + + return error; +} + /* * Expand an inode by new_extra_isize bytes. * Returns 0 on success or negative error number on failure. @@ -5711,8 +5747,6 @@ static int ext4_try_to_expand_extra_isize(struct inode *inode, struct ext4_iloc iloc, handle_t *handle) { - struct ext4_inode *raw_inode; - struct ext4_xattr_ibody_header *header; int no_expand; int error; @@ -5736,32 +5770,53 @@ static int ext4_try_to_expand_extra_isize(struct inode *inode, if (ext4_write_trylock_xattr(inode, &no_expand) == 0) return -EBUSY; - raw_inode = ext4_raw_inode(&iloc); + error = __ext4_expand_extra_isize(inode, new_extra_isize, &iloc, + handle, &no_expand); + ext4_write_unlock_xattr(inode, &no_expand); - header = IHDR(inode, raw_inode); + return error; +} - /* No extended attributes present */ - if (!ext4_test_inode_state(inode, EXT4_STATE_XATTR) || - header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) { - memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE + - EXT4_I(inode)->i_extra_isize, 0, - new_extra_isize - EXT4_I(inode)->i_extra_isize); - EXT4_I(inode)->i_extra_isize = new_extra_isize; - ext4_write_unlock_xattr(inode, &no_expand); - return 0; +int ext4_expand_extra_isize(struct inode *inode, + unsigned int new_extra_isize, + struct ext4_iloc *iloc) +{ + handle_t *handle; + int no_expand; + int error, rc; + + if (ext4_test_inode_state(inode, EXT4_STATE_NO_EXPAND)) { + brelse(iloc->bh); + return -EOVERFLOW; } - /* try to expand with EAs present */ - error = ext4_expand_extra_isize_ea(inode, new_extra_isize, - raw_inode, handle); + handle = ext4_journal_start(inode, EXT4_HT_INODE, + EXT4_DATA_TRANS_BLOCKS(inode->i_sb)); + if (IS_ERR(handle)) { + error = PTR_ERR(handle); + brelse(iloc->bh); + return error; + } + + ext4_write_lock_xattr(inode, &no_expand); + + BUFFER_TRACE(iloc.bh, "get_write_access"); + error = ext4_journal_get_write_access(handle, iloc->bh); if (error) { - /* - * Inode size expansion failed; don't try again - */ - no_expand = 1; + brelse(iloc->bh); + goto out_stop; } - ext4_write_unlock_xattr(inode, &no_expand); + error = __ext4_expand_extra_isize(inode, new_extra_isize, iloc, + handle, &no_expand); + + rc = ext4_mark_iloc_dirty(handle, inode, iloc); + if (!error) + error = rc; + + ext4_write_unlock_xattr(inode, &no_expand); +out_stop: + ext4_journal_stop(handle); return error; } diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index 4f4a8391585c4a3805d97e4df1e9ccd77a788682..afb66d4ab5cfb895ce99574f18740621c37815db 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -349,11 +349,14 @@ static int ext4_ioctl_setproject(struct file *filp, __u32 projid) raw_inode = ext4_raw_inode(&iloc); if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) { - err = -EOVERFLOW; + err = ext4_expand_extra_isize(inode, + EXT4_SB(sb)->s_want_extra_isize, + &iloc); + if (err) + goto out_unlock; + } else { brelse(iloc.bh); - goto out_unlock; } - brelse(iloc.bh); dquot_initialize(inode);