diff options
Diffstat (limited to 'fs/ext3/inode.c')
| -rw-r--r-- | fs/ext3/inode.c | 63 | 
1 files changed, 49 insertions, 14 deletions
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 001eb0e2d48..5e0faf4cda7 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -190,18 +190,28 @@ static int truncate_restart_transaction(handle_t *handle, struct inode *inode)  }  /* - * Called at the last iput() if i_nlink is zero. + * Called at inode eviction from icache   */ -void ext3_delete_inode (struct inode * inode) +void ext3_evict_inode (struct inode *inode)  { +	struct ext3_block_alloc_info *rsv;  	handle_t *handle; +	int want_delete = 0; -	if (!is_bad_inode(inode)) +	if (!inode->i_nlink && !is_bad_inode(inode)) {  		dquot_initialize(inode); +		want_delete = 1; +	}  	truncate_inode_pages(&inode->i_data, 0); -	if (is_bad_inode(inode)) +	ext3_discard_reservation(inode); +	rsv = EXT3_I(inode)->i_block_alloc_info; +	EXT3_I(inode)->i_block_alloc_info = NULL; +	if (unlikely(rsv)) +		kfree(rsv); + +	if (!want_delete)  		goto no_delete;  	handle = start_transaction(inode); @@ -238,15 +248,22 @@ void ext3_delete_inode (struct inode * inode)  	 * having errors), but we can't free the inode if the mark_dirty  	 * fails.  	 */ -	if (ext3_mark_inode_dirty(handle, inode)) -		/* If that failed, just do the required in-core inode clear. */ -		clear_inode(inode); -	else +	if (ext3_mark_inode_dirty(handle, inode)) { +		/* If that failed, just dquot_drop() and be done with that */ +		dquot_drop(inode); +		end_writeback(inode); +	} else { +		ext3_xattr_delete_inode(handle, inode); +		dquot_free_inode(inode); +		dquot_drop(inode); +		end_writeback(inode);  		ext3_free_inode(handle, inode); +	}  	ext3_journal_stop(handle);  	return;  no_delete: -	clear_inode(inode);	/* We must guarantee clearing of inode... */ +	end_writeback(inode); +	dquot_drop(inode);  }  typedef struct { @@ -1212,8 +1229,7 @@ retry:  		ret = PTR_ERR(handle);  		goto out;  	} -	ret = block_write_begin(file, mapping, pos, len, flags, pagep, fsdata, -							ext3_get_block); +	ret = __block_write_begin(page, pos, len, ext3_get_block);  	if (ret)  		goto write_begin_failed; @@ -1798,6 +1814,17 @@ retry:  	ret = blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,  				 offset, nr_segs,  				 ext3_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) && ret < 0)) { +		loff_t isize = i_size_read(inode); +		loff_t end = offset + iov_length(iov, nr_segs); + +		if (end > isize) +			vmtruncate(inode, isize); +	}  	if (ret == -ENOSPC && ext3_should_retry_alloc(inode->i_sb, &retries))  		goto retry; @@ -2560,7 +2587,7 @@ out_stop:  	 * If this was a simple ftruncate(), and the file will remain alive  	 * then we need to clear up the orphan record which we created above.  	 * However, if this was a real unlink then we were called by -	 * ext3_delete_inode(), and we allow that function to clean up the +	 * ext3_evict_inode(), and we allow that function to clean up the  	 * orphan info for us.  	 */  	if (inode->i_nlink) @@ -3204,9 +3231,17 @@ int ext3_setattr(struct dentry *dentry, struct iattr *attr)  		ext3_journal_stop(handle);  	} -	rc = inode_setattr(inode, attr); +	if ((attr->ia_valid & ATTR_SIZE) && +	    attr->ia_size != i_size_read(inode)) { +		rc = vmtruncate(inode, attr->ia_size); +		if (rc) +			goto err_out; +	} + +	setattr_copy(inode, attr); +	mark_inode_dirty(inode); -	if (!rc && (ia_valid & ATTR_MODE)) +	if (ia_valid & ATTR_MODE)  		rc = ext3_acl_chmod(inode);  err_out:  |