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>
This commit is contained in:
Jan Kara 2009-06-17 16:26:24 -07:00 committed by Linus Torvalds
parent 6f3f1cb21f
commit ef43618a47

View file

@ -2374,7 +2374,7 @@ void ext3_truncate(struct inode *inode)
struct page *page; struct page *page;
if (!ext3_can_truncate(inode)) if (!ext3_can_truncate(inode))
return; goto out_notrans;
if (inode->i_size == 0 && ext3_should_writeback_data(inode)) if (inode->i_size == 0 && ext3_should_writeback_data(inode))
ei->i_state |= EXT3_STATE_FLUSH_ON_CLOSE; ei->i_state |= EXT3_STATE_FLUSH_ON_CLOSE;
@ -2390,7 +2390,7 @@ void ext3_truncate(struct inode *inode)
page = grab_cache_page(mapping, page = grab_cache_page(mapping,
inode->i_size >> PAGE_CACHE_SHIFT); inode->i_size >> PAGE_CACHE_SHIFT);
if (!page) if (!page)
return; goto out_notrans;
} }
handle = start_transaction(inode); handle = start_transaction(inode);
@ -2401,7 +2401,7 @@ void ext3_truncate(struct inode *inode)
unlock_page(page); unlock_page(page);
page_cache_release(page); page_cache_release(page);
} }
return; /* AKPM: return what? */ goto out_notrans;
} }
last_block = (inode->i_size + blocksize-1) last_block = (inode->i_size + blocksize-1)
@ -2525,6 +2525,14 @@ out_stop:
ext3_orphan_del(handle, inode); ext3_orphan_del(handle, inode);
ext3_journal_stop(handle); 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, 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); 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)) if (!rc && (ia_valid & ATTR_MODE))
rc = ext3_acl_chmod(inode); rc = ext3_acl_chmod(inode);