* We release `count' blocks on disk, but (last - first) may be greater
  * than `count' because there can be holes in there.
  */
-static void ext4_clear_blocks(handle_t *handle, struct inode *inode,
-                             struct buffer_head *bh,
-                             ext4_fsblk_t block_to_free,
-                             unsigned long count, __le32 *first,
-                             __le32 *last)
+static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
+                            struct buffer_head *bh,
+                            ext4_fsblk_t block_to_free,
+                            unsigned long count, __le32 *first,
+                            __le32 *last)
 {
        __le32 *p;
-       int     flags = EXT4_FREE_BLOCKS_FORGET;
+       int     flags = EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_VALIDATED;
 
        if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
                flags |= EXT4_FREE_BLOCKS_METADATA;
 
+       if (!ext4_data_block_valid(EXT4_SB(inode->i_sb), block_to_free,
+                                  count)) {
+               ext4_error(inode->i_sb, __func__, "inode #%lu: "
+                          "attempt to clear blocks %llu len %lu, invalid",
+                          inode->i_ino, (unsigned long long) block_to_free,
+                          count);
+               return 1;
+       }
+
        if (try_to_extend_transaction(handle, inode)) {
                if (bh) {
                        BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
                *p = 0;
 
        ext4_free_blocks(handle, inode, 0, block_to_free, count, flags);
+       return 0;
 }
 
 /**
                        } else if (nr == block_to_free + count) {
                                count++;
                        } else {
-                               ext4_clear_blocks(handle, inode, this_bh,
-                                                 block_to_free,
-                                                 count, block_to_free_p, p);
+                               if (ext4_clear_blocks(handle, inode, this_bh,
+                                                     block_to_free, count,
+                                                     block_to_free_p, p))
+                                       break;
                                block_to_free = nr;
                                block_to_free_p = p;
                                count = 1;
                        if (!nr)
                                continue;               /* A hole */
 
+                       if (!ext4_data_block_valid(EXT4_SB(inode->i_sb),
+                                                  nr, 1)) {
+                               ext4_error(inode->i_sb, __func__,
+                                          "indirect mapped block in inode "
+                                          "#%lu invalid (level %d, blk #%lu)",
+                                          inode->i_ino, depth,
+                                          (unsigned long) nr);
+                               break;
+                       }
+
                        /* Go read the buffer for the next level down */
                        bh = sb_bread(inode->i_sb, nr);
 
 
 
        sbi = EXT4_SB(sb);
        es = EXT4_SB(sb)->s_es;
-       if (!ext4_data_block_valid(sbi, block, count)) {
+       if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) &&
+           !ext4_data_block_valid(sbi, block, count)) {
                ext4_error(sb, __func__,
-                           "Freeing blocks not in datazone - "
-                           "block = %llu, count = %lu", block, count);
+                          "Freeing blocks not in datazone - "
+                          "block = %llu, count = %lu", block, count);
                goto error_return;
        }