]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - fs/ext4/inode.c
ext4: Expand extra_inodes space per the s_{want,min}_extra_isize fields
[mirror_ubuntu-artful-kernel.git] / fs / ext4 / inode.c
index b83f91edebd1ee659cdcc832b65a3d4ba46488be..f6d8528c4f55383aab120282ca04955072967e0a 100644 (file)
@@ -3105,6 +3105,39 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
        return err;
 }
 
+/*
+ * Expand an inode by new_extra_isize bytes.
+ * Returns 0 on success or negative error number on failure.
+ */
+int ext4_expand_extra_isize(struct inode *inode, unsigned int new_extra_isize,
+                       struct ext4_iloc iloc, handle_t *handle)
+{
+       struct ext4_inode *raw_inode;
+       struct ext4_xattr_ibody_header *header;
+       struct ext4_xattr_entry *entry;
+
+       if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
+               return 0;
+
+       raw_inode = ext4_raw_inode(&iloc);
+
+       header = IHDR(inode, raw_inode);
+       entry = IFIRST(header);
+
+       /* No extended attributes present */
+       if (!(EXT4_I(inode)->i_state & EXT4_STATE_XATTR) ||
+               header->h_magic != cpu_to_le32(EXT4_XATTR_MAGIC)) {
+               memset((void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE, 0,
+                       new_extra_isize);
+               EXT4_I(inode)->i_extra_isize = new_extra_isize;
+               return 0;
+       }
+
+       /* try to expand with EAs present */
+       return ext4_expand_extra_isize_ea(inode, new_extra_isize,
+                                         raw_inode, handle);
+}
+
 /*
  * What we do here is to mark the in-core inode as clean with respect to inode
  * dirtiness (it may still be data-dirty).
@@ -3129,10 +3162,38 @@ ext4_reserve_inode_write(handle_t *handle, struct inode *inode,
 int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
 {
        struct ext4_iloc iloc;
-       int err;
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       static unsigned int mnt_count;
+       int err, ret;
 
        might_sleep();
        err = ext4_reserve_inode_write(handle, inode, &iloc);
+       if (EXT4_I(inode)->i_extra_isize < sbi->s_want_extra_isize &&
+           !(EXT4_I(inode)->i_state & EXT4_STATE_NO_EXPAND)) {
+               /*
+                * We need extra buffer credits since we may write into EA block
+                * with this same handle. If journal_extend fails, then it will
+                * only result in a minor loss of functionality for that inode.
+                * If this is felt to be critical, then e2fsck should be run to
+                * force a large enough s_min_extra_isize.
+                */
+               if ((jbd2_journal_extend(handle,
+                            EXT4_DATA_TRANS_BLOCKS(inode->i_sb))) == 0) {
+                       ret = ext4_expand_extra_isize(inode,
+                                                     sbi->s_want_extra_isize,
+                                                     iloc, handle);
+                       if (ret) {
+                               EXT4_I(inode)->i_state |= EXT4_STATE_NO_EXPAND;
+                               if (mnt_count != sbi->s_es->s_mnt_count) {
+                                       ext4_warning(inode->i_sb, __FUNCTION__,
+                                       "Unable to expand inode %lu. Delete"
+                                       " some EAs or run e2fsck.",
+                                       inode->i_ino);
+                                       mnt_count = sbi->s_es->s_mnt_count;
+                               }
+                       }
+               }
+       }
        if (!err)
                err = ext4_mark_iloc_dirty(handle, inode, &iloc);
        return err;