aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nilfs2/inode.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nilfs2/inode.c')
-rw-r--r--fs/nilfs2/inode.c78
1 files changed, 64 insertions, 14 deletions
diff --git a/fs/nilfs2/inode.c b/fs/nilfs2/inode.c
index 39e038a..eccb2f2 100644
--- a/fs/nilfs2/inode.c
+++ b/fs/nilfs2/inode.c
@@ -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;
}