Pull platform-drivers into test branch
[pandora-kernel.git] / fs / ext4 / inode.c
index c05dc57..a127cc0 100644 (file)
@@ -1147,37 +1147,102 @@ static int do_journal_get_write_access(handle_t *handle,
        return ext4_journal_get_write_access(handle, bh);
 }
 
+/*
+ * The idea of this helper function is following:
+ * if prepare_write has allocated some blocks, but not all of them, the
+ * transaction must include the content of the newly allocated blocks.
+ * This content is expected to be set to zeroes by block_prepare_write().
+ * 2006/10/14  SAW
+ */
+static int ext4_prepare_failure(struct file *file, struct page *page,
+                               unsigned from, unsigned to)
+{
+       struct address_space *mapping;
+       struct buffer_head *bh, *head, *next;
+       unsigned block_start, block_end;
+       unsigned blocksize;
+       int ret;
+       handle_t *handle = ext4_journal_current_handle();
+
+       mapping = page->mapping;
+       if (ext4_should_writeback_data(mapping->host)) {
+               /* optimization: no constraints about data */
+skip:
+               return ext4_journal_stop(handle);
+       }
+
+       head = page_buffers(page);
+       blocksize = head->b_size;
+       for (   bh = head, block_start = 0;
+               bh != head || !block_start;
+               block_start = block_end, bh = next)
+       {
+               next = bh->b_this_page;
+               block_end = block_start + blocksize;
+               if (block_end <= from)
+                       continue;
+               if (block_start >= to) {
+                       block_start = to;
+                       break;
+               }
+               if (!buffer_mapped(bh))
+               /* prepare_write failed on this bh */
+                       break;
+               if (ext4_should_journal_data(mapping->host)) {
+                       ret = do_journal_get_write_access(handle, bh);
+                       if (ret) {
+                               ext4_journal_stop(handle);
+                               return ret;
+                       }
+               }
+       /*
+        * block_start here becomes the first block where the current iteration
+        * of prepare_write failed.
+        */
+       }
+       if (block_start <= from)
+               goto skip;
+
+       /* commit allocated and zeroed buffers */
+       return mapping->a_ops->commit_write(file, page, from, block_start);
+}
+
 static int ext4_prepare_write(struct file *file, struct page *page,
                              unsigned from, unsigned to)
 {
        struct inode *inode = page->mapping->host;
-       int ret, needed_blocks = ext4_writepage_trans_blocks(inode);
+       int ret, ret2;
+       int needed_blocks = ext4_writepage_trans_blocks(inode);
        handle_t *handle;
        int retries = 0;
 
 retry:
        handle = ext4_journal_start(inode, needed_blocks);
-       if (IS_ERR(handle)) {
-               ret = PTR_ERR(handle);
-               goto out;
-       }
+       if (IS_ERR(handle))
+               return PTR_ERR(handle);
        if (test_opt(inode->i_sb, NOBH) && ext4_should_writeback_data(inode))
                ret = nobh_prepare_write(page, from, to, ext4_get_block);
        else
                ret = block_prepare_write(page, from, to, ext4_get_block);
        if (ret)
-               goto prepare_write_failed;
+               goto failure;
 
        if (ext4_should_journal_data(inode)) {
                ret = walk_page_buffers(handle, page_buffers(page),
                                from, to, NULL, do_journal_get_write_access);
+               if (ret)
+                       /* fatal error, just put the handle and return */
+                       ext4_journal_stop(handle);
        }
-prepare_write_failed:
-       if (ret)
-               ext4_journal_stop(handle);
+       return ret;
+
+failure:
+       ret2 = ext4_prepare_failure(file, page, from, to);
+       if (ret2 < 0)
+               return ret2;
        if (ret == -ENOSPC && ext4_should_retry_alloc(inode->i_sb, &retries))
                goto retry;
-out:
+       /* retry number exceeded, or other error like -EDQUOT */
        return ret;
 }
 
@@ -2432,14 +2497,15 @@ static ext4_fsblk_t ext4_get_inode_block(struct super_block *sb,
                return 0;
        }
 
-       gdp = (struct ext4_group_desc *)bh->b_data;
+       gdp = (struct ext4_group_desc *)((__u8 *)bh->b_data +
+               desc * EXT4_DESC_SIZE(sb));
        /*
         * Figure out the offset within the block group inode table
         */
        offset = ((ino - 1) % EXT4_INODES_PER_GROUP(sb)) *
                EXT4_INODE_SIZE(sb);
-       block = ext4_inode_table(gdp + desc) +
-                       (offset >> EXT4_BLOCK_SIZE_BITS(sb));
+       block = ext4_inode_table(sb, gdp) +
+               (offset >> EXT4_BLOCK_SIZE_BITS(sb));
 
        iloc->block_group = block_group;
        iloc->offset = offset & (EXT4_BLOCK_SIZE(sb) - 1);
@@ -2506,7 +2572,7 @@ static int __ext4_get_inode_loc(struct inode *inode,
                                goto make_io;
 
                        bitmap_bh = sb_getblk(inode->i_sb,
-                               ext4_inode_bitmap(desc));
+                               ext4_inode_bitmap(inode->i_sb, desc));
                        if (!bitmap_bh)
                                goto make_io;