]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/ext4/xattr.c
Merge tag 'for-linus-20170812' of git://git.infradead.org/linux-mtd
[karo-tx-linux.git] / fs / ext4 / xattr.c
index cff4f41ced612d9b0f344be66a17e434981f8861..82a5af9f66685a6165a59a1f0c6ba603dd332e8b 100644 (file)
@@ -317,28 +317,41 @@ static void ext4_xattr_inode_set_hash(struct inode *ea_inode, u32 hash)
  */
 static int ext4_xattr_inode_read(struct inode *ea_inode, void *buf, size_t size)
 {
-       unsigned long block = 0;
-       struct buffer_head *bh;
-       int blocksize = ea_inode->i_sb->s_blocksize;
-       size_t csize, copied = 0;
-       void *copy_pos = buf;
-
-       while (copied < size) {
-               csize = (size - copied) > blocksize ? blocksize : size - copied;
-               bh = ext4_bread(NULL, ea_inode, block, 0);
-               if (IS_ERR(bh))
-                       return PTR_ERR(bh);
-               if (!bh)
-                       return -EFSCORRUPTED;
+       int blocksize = 1 << ea_inode->i_blkbits;
+       int bh_count = (size + blocksize - 1) >> ea_inode->i_blkbits;
+       int tail_size = (size % blocksize) ?: blocksize;
+       struct buffer_head *bhs_inline[8];
+       struct buffer_head **bhs = bhs_inline;
+       int i, ret;
+
+       if (bh_count > ARRAY_SIZE(bhs_inline)) {
+               bhs = kmalloc_array(bh_count, sizeof(*bhs), GFP_NOFS);
+               if (!bhs)
+                       return -ENOMEM;
+       }
 
-               memcpy(copy_pos, bh->b_data, csize);
-               brelse(bh);
+       ret = ext4_bread_batch(ea_inode, 0 /* block */, bh_count,
+                              true /* wait */, bhs);
+       if (ret)
+               goto free_bhs;
 
-               copy_pos += csize;
-               block += 1;
-               copied += csize;
+       for (i = 0; i < bh_count; i++) {
+               /* There shouldn't be any holes in ea_inode. */
+               if (!bhs[i]) {
+                       ret = -EFSCORRUPTED;
+                       goto put_bhs;
+               }
+               memcpy((char *)buf + blocksize * i, bhs[i]->b_data,
+                      i < bh_count - 1 ? blocksize : tail_size);
        }
-       return 0;
+       ret = 0;
+put_bhs:
+       for (i = 0; i < bh_count; i++)
+               brelse(bhs[i]);
+free_bhs:
+       if (bhs != bhs_inline)
+               kfree(bhs);
+       return ret;
 }
 
 static int ext4_xattr_inode_iget(struct inode *parent, unsigned long ea_ino,
@@ -451,6 +464,7 @@ ext4_xattr_inode_get(struct inode *inode, struct ext4_xattr_entry *entry,
                }
                /* Do not add ea_inode to the cache. */
                ea_inode_cache = NULL;
+               err = 0;
        } else if (err)
                goto out;
 
@@ -1815,9 +1829,6 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
                        ea_bdebug(bs->bh, "modifying in-place");
                        error = ext4_xattr_set_entry(i, s, handle, inode,
                                                     true /* is_block */);
-                       if (!error)
-                               ext4_xattr_block_cache_insert(ea_block_cache,
-                                                             bs->bh);
                        ext4_xattr_block_csum_set(inode, bs->bh);
                        unlock_buffer(bs->bh);
                        if (error == -EFSCORRUPTED)
@@ -1973,6 +1984,7 @@ inserted:
                } else if (bs->bh && s->base == bs->bh->b_data) {
                        /* We were modifying this block in-place. */
                        ea_bdebug(bs->bh, "keeping this block");
+                       ext4_xattr_block_cache_insert(ea_block_cache, bs->bh);
                        new_bh = bs->bh;
                        get_bh(new_bh);
                } else {
@@ -2625,23 +2637,21 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
                               struct ext4_inode *raw_inode, handle_t *handle)
 {
        struct ext4_xattr_ibody_header *header;
-       struct buffer_head *bh = NULL;
+       struct buffer_head *bh;
+       struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+       static unsigned int mnt_count;
        size_t min_offs;
        size_t ifree, bfree;
        int total_ino;
        void *base, *end;
        int error = 0, tried_min_extra_isize = 0;
-       int s_min_extra_isize = le16_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_min_extra_isize);
+       int s_min_extra_isize = le16_to_cpu(sbi->s_es->s_min_extra_isize);
        int isize_diff; /* How much do we need to grow i_extra_isize */
-       int no_expand;
-
-       if (ext4_write_trylock_xattr(inode, &no_expand) == 0)
-               return 0;
 
 retry:
        isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize;
        if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
-               goto out;
+               return 0;
 
        header = IHDR(inode, raw_inode);
 
@@ -2676,6 +2686,7 @@ retry:
                        EXT4_ERROR_INODE(inode, "bad block %llu",
                                         EXT4_I(inode)->i_file_acl);
                        error = -EFSCORRUPTED;
+                       brelse(bh);
                        goto cleanup;
                }
                base = BHDR(bh);
@@ -2683,11 +2694,11 @@ retry:
                min_offs = end - base;
                bfree = ext4_xattr_free_space(BFIRST(bh), &min_offs, base,
                                              NULL);
+               brelse(bh);
                if (bfree + ifree < isize_diff) {
                        if (!tried_min_extra_isize && s_min_extra_isize) {
                                tried_min_extra_isize++;
                                new_extra_isize = s_min_extra_isize;
-                               brelse(bh);
                                goto retry;
                        }
                        error = -ENOSPC;
@@ -2705,7 +2716,6 @@ retry:
                    s_min_extra_isize) {
                        tried_min_extra_isize++;
                        new_extra_isize = s_min_extra_isize;
-                       brelse(bh);
                        goto retry;
                }
                goto cleanup;
@@ -2717,18 +2727,13 @@ shift:
                        EXT4_GOOD_OLD_INODE_SIZE + new_extra_isize,
                        (void *)header, total_ino);
        EXT4_I(inode)->i_extra_isize = new_extra_isize;
-       brelse(bh);
-out:
-       ext4_write_unlock_xattr(inode, &no_expand);
-       return 0;
 
 cleanup:
-       brelse(bh);
-       /*
-        * Inode size expansion failed; don't try again
-        */
-       no_expand = 1;
-       ext4_write_unlock_xattr(inode, &no_expand);
+       if (error && (mnt_count != le16_to_cpu(sbi->s_es->s_mnt_count))) {
+               ext4_warning(inode->i_sb, "Unable to expand inode %lu. Delete some EAs or run e2fsck.",
+                            inode->i_ino);
+               mnt_count = le16_to_cpu(sbi->s_es->s_mnt_count);
+       }
        return error;
 }