Merge branch 'urgent' of git://git.kernel.org/pub/scm/linux/kernel/git/brodo/pcmcia-2.6
[pandora-kernel.git] / fs / nilfs2 / inode.c
index 39e038a..eccb2f2 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/writeback.h>
 #include <linux/uio.h>
 #include "nilfs.h"
+#include "btnode.h"
 #include "segment.h"
 #include "page.h"
 #include "mdt.h"
@@ -197,11 +198,15 @@ static int nilfs_write_begin(struct file *file, struct address_space *mapping,
        if (unlikely(err))
                return err;
 
-       *pagep = NULL;
-       err = block_write_begin(file, mapping, pos, len, flags, pagep,
-                               fsdata, nilfs_get_block);
-       if (unlikely(err))
+       err = block_write_begin(mapping, pos, len, flags, pagep,
+                               nilfs_get_block);
+       if (unlikely(err)) {
+               loff_t isize = mapping->host->i_size;
+               if (pos + len > isize)
+                       vmtruncate(mapping->host, isize);
+
                nilfs_transaction_abort(inode->i_sb);
+       }
        return err;
 }
 
@@ -237,6 +242,19 @@ nilfs_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
        /* Needs synchronization with the cleaner */
        size = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
                                  offset, nr_segs, nilfs_get_block, NULL);
+
+       /*
+        * In case of error extending write may have instantiated a few
+        * blocks outside i_size. Trim these off again.
+        */
+       if (unlikely((rw & WRITE) && size < 0)) {
+               loff_t isize = i_size_read(inode);
+               loff_t end = offset + iov_length(iov, nr_segs);
+
+               if (end > isize)
+                       vmtruncate(inode, isize);
+       }
+
        return size;
 }
 
@@ -337,7 +355,6 @@ void nilfs_free_inode(struct inode *inode)
        struct super_block *sb = inode->i_sb;
        struct nilfs_sb_info *sbi = NILFS_SB(sb);
 
-       clear_inode(inode);
        /* XXX: check error code? Is there any thing I can do? */
        (void) nilfs_ifile_delete_inode(sbi->s_ifile, inode->i_ino);
        atomic_dec(&sbi->s_inodes_count);
@@ -597,16 +614,34 @@ void nilfs_truncate(struct inode *inode)
           But truncate has no return value. */
 }
 
-void nilfs_delete_inode(struct inode *inode)
+static void nilfs_clear_inode(struct inode *inode)
+{
+       struct nilfs_inode_info *ii = NILFS_I(inode);
+
+       /*
+        * Free resources allocated in nilfs_read_inode(), here.
+        */
+       BUG_ON(!list_empty(&ii->i_dirty));
+       brelse(ii->i_bh);
+       ii->i_bh = NULL;
+
+       if (test_bit(NILFS_I_BMAP, &ii->i_state))
+               nilfs_bmap_clear(ii->i_bmap);
+
+       nilfs_btnode_cache_clear(&ii->i_btnode_cache);
+}
+
+void nilfs_evict_inode(struct inode *inode)
 {
        struct nilfs_transaction_info ti;
        struct super_block *sb = inode->i_sb;
        struct nilfs_inode_info *ii = NILFS_I(inode);
 
-       if (unlikely(is_bad_inode(inode))) {
+       if (inode->i_nlink || unlikely(is_bad_inode(inode))) {
                if (inode->i_data.nrpages)
                        truncate_inode_pages(&inode->i_data, 0);
-               clear_inode(inode);
+               end_writeback(inode);
+               nilfs_clear_inode(inode);
                return;
        }
        nilfs_transaction_begin(sb, &ti, 0); /* never fails */
@@ -616,6 +651,8 @@ void nilfs_delete_inode(struct inode *inode)
 
        nilfs_truncate_bmap(ii, 0);
        nilfs_mark_inode_dirty(inode);
+       end_writeback(inode);
+       nilfs_clear_inode(inode);
        nilfs_free_inode(inode);
        /* nilfs_free_inode() marks inode buffer dirty */
        if (IS_SYNC(inode))
@@ -639,14 +676,27 @@ int nilfs_setattr(struct dentry *dentry, struct iattr *iattr)
        err = nilfs_transaction_begin(sb, &ti, 0);
        if (unlikely(err))
                return err;
-       err = inode_setattr(inode, iattr);
-       if (!err && (iattr->ia_valid & ATTR_MODE))
+
+       if ((iattr->ia_valid & ATTR_SIZE) &&
+           iattr->ia_size != i_size_read(inode)) {
+               err = vmtruncate(inode, iattr->ia_size);
+               if (unlikely(err))
+                       goto out_err;
+       }
+
+       setattr_copy(inode, iattr);
+       mark_inode_dirty(inode);
+
+       if (iattr->ia_valid & ATTR_MODE) {
                err = nilfs_acl_chmod(inode);
-       if (likely(!err))
-               err = nilfs_transaction_commit(sb);
-       else
-               nilfs_transaction_abort(sb);
+               if (unlikely(err))
+                       goto out_err;
+       }
+
+       return nilfs_transaction_commit(sb);
 
+out_err:
+       nilfs_transaction_abort(sb);
        return err;
 }