diff options
author | Jan Kara <jack@suse.cz> | 2009-06-17 16:26:24 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-18 13:03:45 -0700 |
commit | ef43618a47179b41e7203a624f2c7445e7da488c (patch) | |
tree | 64d42f459e5086768e57d8e8e9bec8304b17c150 /fs/ext3/inode.c | |
parent | 6f3f1cb21f08fbf757bbbbb0709ee515a7a7c6ad (diff) | |
download | linux-ef43618a47179b41e7203a624f2c7445e7da488c.tar.gz linux-ef43618a47179b41e7203a624f2c7445e7da488c.tar.bz2 linux-ef43618a47179b41e7203a624f2c7445e7da488c.zip |
ext3: make sure inode is deleted from orphan list after truncate
As Ted pointed out, it can happen that ext3_truncate() returns without
removing inode from orphan list. This way we could in some rare cases
(like when we get ENOMEM from an allocation in ext3_truncate called
because of failed ext3_write_begin) leave the inode on orphan list and
that triggers assertion failure on umount.
So make ext3_truncate() always remove inode from in-memory orphan list.
Cc: Theodore Ts'o <tytso@mit.edu>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ext3/inode.c')
-rw-r--r-- | fs/ext3/inode.c | 20 |
1 files changed, 11 insertions, 9 deletions
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 253c2cdc8d04..05dea8132fc0 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c @@ -2374,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; @@ -2390,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); @@ -2401,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) @@ -2525,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, @@ -3122,12 +3130,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); |