Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[linux-2.6-microblaze.git] / fs / ext4 / namei.c
index db4ba99..3a31b66 100644 (file)
@@ -54,6 +54,7 @@ static struct buffer_head *ext4_append(handle_t *handle,
                                        struct inode *inode,
                                        ext4_lblk_t *block)
 {
+       struct ext4_map_blocks map;
        struct buffer_head *bh;
        int err;
 
@@ -63,6 +64,21 @@ static struct buffer_head *ext4_append(handle_t *handle,
                return ERR_PTR(-ENOSPC);
 
        *block = inode->i_size >> inode->i_sb->s_blocksize_bits;
+       map.m_lblk = *block;
+       map.m_len = 1;
+
+       /*
+        * We're appending new directory block. Make sure the block is not
+        * allocated yet, otherwise we will end up corrupting the
+        * directory.
+        */
+       err = ext4_map_blocks(NULL, inode, &map, 0);
+       if (err < 0)
+               return ERR_PTR(err);
+       if (err) {
+               EXT4_ERROR_INODE(inode, "Logical block already allocated");
+               return ERR_PTR(-EFSCORRUPTED);
+       }
 
        bh = ext4_bread(handle, inode, *block, EXT4_GET_BLOCKS_CREATE);
        if (IS_ERR(bh))
@@ -110,6 +126,13 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode,
        struct ext4_dir_entry *dirent;
        int is_dx_block = 0;
 
+       if (block >= inode->i_size) {
+               ext4_error_inode(inode, func, line, block,
+                      "Attempting to read directory block (%u) that is past i_size (%llu)",
+                      block, inode->i_size);
+               return ERR_PTR(-EFSCORRUPTED);
+       }
+
        if (ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_EIO))
                bh = ERR_PTR(-EIO);
        else
@@ -3067,11 +3090,8 @@ bool ext4_empty_dir(struct inode *inode)
                de = (struct ext4_dir_entry_2 *) (bh->b_data +
                                        (offset & (sb->s_blocksize - 1)));
                if (ext4_check_dir_entry(inode, NULL, de, bh,
-                                        bh->b_data, bh->b_size, offset)) {
-                       offset = (offset | (sb->s_blocksize - 1)) + 1;
-                       continue;
-               }
-               if (le32_to_cpu(de->inode)) {
+                                        bh->b_data, bh->b_size, offset) ||
+                   le32_to_cpu(de->inode)) {
                        brelse(bh);
                        return false;
                }