switch ext3 to inode->i_acl
[safe/jmp/linux-2.6] / fs / ext3 / inode.c
index 0f5bca0..5f51fed 100644 (file)
@@ -820,7 +820,7 @@ int ext3_get_blocks_handle(handle_t *handle, struct inode *inode,
                while (count < maxblocks && count <= blocks_to_boundary) {
                        ext3_fsblk_t blk;
 
-                       if (!verify_chain(chain, partial)) {
+                       if (!verify_chain(chain, chain + depth - 1)) {
                                /*
                                 * Indirect block might be removed by
                                 * truncate while we were reading it.
@@ -1149,12 +1149,15 @@ static int ext3_write_begin(struct file *file, struct address_space *mapping,
                                struct page **pagep, void **fsdata)
 {
        struct inode *inode = mapping->host;
-       int ret, needed_blocks = ext3_writepage_trans_blocks(inode);
+       int ret;
        handle_t *handle;
        int retries = 0;
        struct page *page;
        pgoff_t index;
        unsigned from, to;
+       /* Reserve one block more for addition to orphan list in case
+        * we allocate blocks but write fails for some reason */
+       int needed_blocks = ext3_writepage_trans_blocks(inode) + 1;
 
        index = pos >> PAGE_CACHE_SHIFT;
        from = pos & (PAGE_CACHE_SIZE - 1);
@@ -1184,15 +1187,20 @@ retry:
        }
 write_begin_failed:
        if (ret) {
-               ext3_journal_stop(handle);
-               unlock_page(page);
-               page_cache_release(page);
                /*
                 * block_write_begin may have instantiated a few blocks
                 * outside i_size.  Trim these off again. Don't need
                 * i_size_read because we hold i_mutex.
+                *
+                * Add inode to orphan list in case we crash before truncate
+                * finishes.
                 */
                if (pos + len > inode->i_size)
+                       ext3_orphan_add(handle, inode);
+               ext3_journal_stop(handle);
+               unlock_page(page);
+               page_cache_release(page);
+               if (pos + len > inode->i_size)
                        vmtruncate(inode, inode->i_size);
        }
        if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))
@@ -1211,6 +1219,18 @@ int ext3_journal_dirty_data(handle_t *handle, struct buffer_head *bh)
        return err;
 }
 
+/* For ordered writepage and write_end functions */
+static int journal_dirty_data_fn(handle_t *handle, struct buffer_head *bh)
+{
+       /*
+        * Write could have mapped the buffer but it didn't copy the data in
+        * yet. So avoid filing such buffer into a transaction.
+        */
+       if (buffer_mapped(bh) && buffer_uptodate(bh))
+               return ext3_journal_dirty_data(handle, bh);
+       return 0;
+}
+
 /* For write_end() in data=journal mode */
 static int write_end_fn(handle_t *handle, struct buffer_head *bh)
 {
@@ -1221,26 +1241,20 @@ static int write_end_fn(handle_t *handle, struct buffer_head *bh)
 }
 
 /*
- * Generic write_end handler for ordered and writeback ext3 journal modes.
- * We can't use generic_write_end, because that unlocks the page and we need to
- * unlock the page after ext3_journal_stop, but ext3_journal_stop must run
- * after block_write_end.
+ * This is nasty and subtle: ext3_write_begin() could have allocated blocks
+ * for the whole page but later we failed to copy the data in. Update inode
+ * size according to what we managed to copy. The rest is going to be
+ * truncated in write_end function.
  */
-static int ext3_generic_write_end(struct file *file,
-                               struct address_space *mapping,
-                               loff_t pos, unsigned len, unsigned copied,
-                               struct page *page, void *fsdata)
+static void update_file_sizes(struct inode *inode, loff_t pos, unsigned copied)
 {
-       struct inode *inode = file->f_mapping->host;
-
-       copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
-
-       if (pos+copied > inode->i_size) {
-               i_size_write(inode, pos+copied);
+       /* What matters to us is i_disksize. We don't write i_size anywhere */
+       if (pos + copied > inode->i_size)
+               i_size_write(inode, pos + copied);
+       if (pos + copied > EXT3_I(inode)->i_disksize) {
+               EXT3_I(inode)->i_disksize = pos + copied;
                mark_inode_dirty(inode);
        }
-
-       return copied;
 }
 
 /*
@@ -1260,35 +1274,29 @@ static int ext3_ordered_write_end(struct file *file,
        unsigned from, to;
        int ret = 0, ret2;
 
-       from = pos & (PAGE_CACHE_SIZE - 1);
-       to = from + len;
+       copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
 
+       from = pos & (PAGE_CACHE_SIZE - 1);
+       to = from + copied;
        ret = walk_page_buffers(handle, page_buffers(page),
-               from, to, NULL, ext3_journal_dirty_data);
+               from, to, NULL, journal_dirty_data_fn);
 
-       if (ret == 0) {
-               /*
-                * generic_write_end() will run mark_inode_dirty() if i_size
-                * changes.  So let's piggyback the i_disksize mark_inode_dirty
-                * into that.
-                */
-               loff_t new_i_size;
-
-               new_i_size = pos + copied;
-               if (new_i_size > EXT3_I(inode)->i_disksize)
-                       EXT3_I(inode)->i_disksize = new_i_size;
-               ret2 = ext3_generic_write_end(file, mapping, pos, len, copied,
-                                                       page, fsdata);
-               copied = ret2;
-               if (ret2 < 0)
-                       ret = ret2;
-       }
+       if (ret == 0)
+               update_file_sizes(inode, pos, copied);
+       /*
+        * There may be allocated blocks outside of i_size because
+        * we failed to copy some data. Prepare for truncate.
+        */
+       if (pos + len > inode->i_size)
+               ext3_orphan_add(handle, inode);
        ret2 = ext3_journal_stop(handle);
        if (!ret)
                ret = ret2;
        unlock_page(page);
        page_cache_release(page);
 
+       if (pos + len > inode->i_size)
+               vmtruncate(inode, inode->i_size);
        return ret ? ret : copied;
 }
 
@@ -1299,25 +1307,22 @@ static int ext3_writeback_write_end(struct file *file,
 {
        handle_t *handle = ext3_journal_current_handle();
        struct inode *inode = file->f_mapping->host;
-       int ret = 0, ret2;
-       loff_t new_i_size;
-
-       new_i_size = pos + copied;
-       if (new_i_size > EXT3_I(inode)->i_disksize)
-               EXT3_I(inode)->i_disksize = new_i_size;
-
-       ret2 = ext3_generic_write_end(file, mapping, pos, len, copied,
-                                                       page, fsdata);
-       copied = ret2;
-       if (ret2 < 0)
-               ret = ret2;
+       int ret;
 
-       ret2 = ext3_journal_stop(handle);
-       if (!ret)
-               ret = ret2;
+       copied = block_write_end(file, mapping, pos, len, copied, page, fsdata);
+       update_file_sizes(inode, pos, copied);
+       /*
+        * There may be allocated blocks outside of i_size because
+        * we failed to copy some data. Prepare for truncate.
+        */
+       if (pos + len > inode->i_size)
+               ext3_orphan_add(handle, inode);
+       ret = ext3_journal_stop(handle);
        unlock_page(page);
        page_cache_release(page);
 
+       if (pos + len > inode->i_size)
+               vmtruncate(inode, inode->i_size);
        return ret ? ret : copied;
 }
 
@@ -1338,15 +1343,23 @@ static int ext3_journalled_write_end(struct file *file,
        if (copied < len) {
                if (!PageUptodate(page))
                        copied = 0;
-               page_zero_new_buffers(page, from+copied, to);
+               page_zero_new_buffers(page, from + copied, to);
+               to = from + copied;
        }
 
        ret = walk_page_buffers(handle, page_buffers(page), from,
                                to, &partial, write_end_fn);
        if (!partial)
                SetPageUptodate(page);
-       if (pos+copied > inode->i_size)
-               i_size_write(inode, pos+copied);
+
+       if (pos + copied > inode->i_size)
+               i_size_write(inode, pos + copied);
+       /*
+        * There may be allocated blocks outside of i_size because
+        * we failed to copy some data. Prepare for truncate.
+        */
+       if (pos + len > inode->i_size)
+               ext3_orphan_add(handle, inode);
        EXT3_I(inode)->i_state |= EXT3_STATE_JDATA;
        if (inode->i_size > EXT3_I(inode)->i_disksize) {
                EXT3_I(inode)->i_disksize = inode->i_size;
@@ -1361,6 +1374,8 @@ static int ext3_journalled_write_end(struct file *file,
        unlock_page(page);
        page_cache_release(page);
 
+       if (pos + len > inode->i_size)
+               vmtruncate(inode, inode->i_size);
        return ret ? ret : copied;
 }
 
@@ -1428,11 +1443,9 @@ static int bput_one(handle_t *handle, struct buffer_head *bh)
        return 0;
 }
 
-static int journal_dirty_data_fn(handle_t *handle, struct buffer_head *bh)
+static int buffer_unmapped(handle_t *handle, struct buffer_head *bh)
 {
-       if (buffer_mapped(bh))
-               return ext3_journal_dirty_data(handle, bh);
-       return 0;
+       return !buffer_mapped(bh);
 }
 
 /*
@@ -1505,6 +1518,19 @@ static int ext3_ordered_writepage(struct page *page,
        if (ext3_journal_current_handle())
                goto out_fail;
 
+       if (!page_has_buffers(page)) {
+               create_empty_buffers(page, inode->i_sb->s_blocksize,
+                               (1 << BH_Dirty)|(1 << BH_Uptodate));
+               page_bufs = page_buffers(page);
+       } else {
+               page_bufs = page_buffers(page);
+               if (!walk_page_buffers(NULL, page_bufs, 0, PAGE_CACHE_SIZE,
+                                      NULL, buffer_unmapped)) {
+                       /* Provide NULL get_block() to catch bugs if buffers
+                        * weren't really mapped */
+                       return block_write_full_page(page, NULL, wbc);
+               }
+       }
        handle = ext3_journal_start(inode, ext3_writepage_trans_blocks(inode));
 
        if (IS_ERR(handle)) {
@@ -1512,11 +1538,6 @@ static int ext3_ordered_writepage(struct page *page,
                goto out_fail;
        }
 
-       if (!page_has_buffers(page)) {
-               create_empty_buffers(page, inode->i_sb->s_blocksize,
-                               (1 << BH_Dirty)|(1 << BH_Uptodate));
-       }
-       page_bufs = page_buffers(page);
        walk_page_buffers(handle, page_bufs, 0,
                        PAGE_CACHE_SIZE, NULL, bget_one);
 
@@ -1564,6 +1585,15 @@ static int ext3_writeback_writepage(struct page *page,
        if (ext3_journal_current_handle())
                goto out_fail;
 
+       if (page_has_buffers(page)) {
+               if (!walk_page_buffers(NULL, page_buffers(page), 0,
+                                     PAGE_CACHE_SIZE, NULL, buffer_unmapped)) {
+                       /* Provide NULL get_block() to catch bugs if buffers
+                        * weren't really mapped */
+                       return block_write_full_page(page, NULL, wbc);
+               }
+       }
+
        handle = ext3_journal_start(inode, ext3_writepage_trans_blocks(inode));
        if (IS_ERR(handle)) {
                ret = PTR_ERR(handle);
@@ -2344,7 +2374,7 @@ void ext3_truncate(struct inode *inode)
        struct page *page;
 
        if (!ext3_can_truncate(inode))
-               return;
+               goto out_notrans;
 
        if (inode->i_size == 0 && ext3_should_writeback_data(inode))
                ei->i_state |= EXT3_STATE_FLUSH_ON_CLOSE;
@@ -2360,7 +2390,7 @@ void ext3_truncate(struct inode *inode)
                page = grab_cache_page(mapping,
                                inode->i_size >> PAGE_CACHE_SHIFT);
                if (!page)
-                       return;
+                       goto out_notrans;
        }
 
        handle = start_transaction(inode);
@@ -2371,7 +2401,7 @@ void ext3_truncate(struct inode *inode)
                        unlock_page(page);
                        page_cache_release(page);
                }
-               return;         /* AKPM: return what? */
+               goto out_notrans;
        }
 
        last_block = (inode->i_size + blocksize-1)
@@ -2495,6 +2525,14 @@ out_stop:
                ext3_orphan_del(handle, inode);
 
        ext3_journal_stop(handle);
+       return;
+out_notrans:
+       /*
+        * Delete the inode from orphan list so that it doesn't stay there
+        * forever and trigger assertion on umount.
+        */
+       if (inode->i_nlink)
+               ext3_orphan_del(NULL, inode);
 }
 
 static ext3_fsblk_t ext3_get_inode_block(struct super_block *sb,
@@ -2714,10 +2752,6 @@ struct inode *ext3_iget(struct super_block *sb, unsigned long ino)
                return inode;
 
        ei = EXT3_I(inode);
-#ifdef CONFIG_EXT3_FS_POSIX_ACL
-       ei->i_acl = EXT3_ACL_NOT_CACHED;
-       ei->i_default_acl = EXT3_ACL_NOT_CACHED;
-#endif
        ei->i_block_alloc_info = NULL;
 
        ret = __ext3_get_inode_loc(inode, &iloc, 0);
@@ -2930,7 +2964,6 @@ static int ext3_do_update_inode(handle_t *handle,
                                ext3_update_dynamic_rev(sb);
                                EXT3_SET_RO_COMPAT_FEATURE(sb,
                                        EXT3_FEATURE_RO_COMPAT_LARGE_FILE);
-                               sb->s_dirt = 1;
                                handle->h_sync = 1;
                                err = ext3_journal_dirty_metadata(handle,
                                                EXT3_SB(sb)->s_sbh);
@@ -3058,7 +3091,7 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
                        error = PTR_ERR(handle);
                        goto err_out;
                }
-               error = DQUOT_TRANSFER(inode, attr) ? -EDQUOT : 0;
+               error = vfs_dq_transfer(inode, attr) ? -EDQUOT : 0;
                if (error) {
                        ext3_journal_stop(handle);
                        return error;
@@ -3093,12 +3126,6 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)
 
        rc = inode_setattr(inode, attr);
 
-       /* If inode_setattr's call to ext3_truncate failed to get a
-        * transaction handle at all, we need to clean up the in-core
-        * orphan list manually. */
-       if (inode->i_nlink)
-               ext3_orphan_del(NULL, inode);
-
        if (!rc && (ia_valid & ATTR_MODE))
                rc = ext3_acl_chmod(inode);
 
@@ -3149,7 +3176,7 @@ static int ext3_writepage_trans_blocks(struct inode *inode)
                ret = 2 * (bpp + indirects) + 2;
 
 #ifdef CONFIG_QUOTA
-       /* We know that structure was already allocated during DQUOT_INIT so
+       /* We know that structure was already allocated during vfs_dq_init so
         * we will be updating only the data blocks + inodes */
        ret += 2*EXT3_QUOTA_TRANS_BLOCKS(inode->i_sb);
 #endif
@@ -3240,7 +3267,7 @@ int ext3_mark_inode_dirty(handle_t *handle, struct inode *inode)
  * i_size has been changed by generic_commit_write() and we thus need
  * to include the updated inode in the current transaction.
  *
- * Also, DQUOT_ALLOC_SPACE() will always dirty the inode when blocks
+ * Also, vfs_dq_alloc_space() will always dirty the inode when blocks
  * are allocated to the file.
  *
  * If the inode is marked synchronous, we don't honour that here - doing