ext4, project: expand inode extra size if possible
authorMiao Xie <miaoxie@huawei.com>
Sun, 6 Aug 2017 05:00:49 +0000 (01:00 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 6 Aug 2017 05:00:49 +0000 (01:00 -0400)
When upgrading from old format, try to set project id
to old file first time, it will return EOVERFLOW, but if
that file is dirtied(touch etc), changing project id will
be allowed, this might be confusing for users, we could
try to expand @i_extra_isize here too.

Reported-by: Zhang Yi <yi.zhang@huawei.com>
Signed-off-by: Miao Xie <miaoxie@huawei.com>
Signed-off-by: Wang Shilong <wshilong@ddn.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/ext4_jbd2.h
fs/ext4/inode.c
fs/ext4/ioctl.c

index dabad1bc861723932fd7f25da1d86cdd0a641ce9..48143e32411c4cabfdd56bebe41c8fdc522c9665 100644 (file)
@@ -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.
  */
index 713b67e85f7331dd8bdd4dc170483bedcc6c1b46..c774bdc22759b1602341da70a0b3fec7b8830f2c 100644 (file)
@@ -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;
 }
 
index 4f4a8391585c4a3805d97e4df1e9ccd77a788682..afb66d4ab5cfb895ce99574f18740621c37815db 100644 (file)
@@ -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);