diff options
author | Jan Kara <jack@suse.cz> | 2009-06-17 19:26:24 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-18 16:03:45 -0400 |
commit | ef43618a47179b41e7203a624f2c7445e7da488c (patch) | |
tree | 64d42f459e5086768e57d8e8e9bec8304b17c150 | |
parent | 6f3f1cb21f08fbf757bbbbb0709ee515a7a7c6ad (diff) |
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>
-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) | |||
2374 | struct page *page; | 2374 | struct page *page; |
2375 | 2375 | ||
2376 | if (!ext3_can_truncate(inode)) | 2376 | if (!ext3_can_truncate(inode)) |
2377 | return; | 2377 | goto out_notrans; |
2378 | 2378 | ||
2379 | if (inode->i_size == 0 && ext3_should_writeback_data(inode)) | 2379 | if (inode->i_size == 0 && ext3_should_writeback_data(inode)) |
2380 | ei->i_state |= EXT3_STATE_FLUSH_ON_CLOSE; | 2380 | ei->i_state |= EXT3_STATE_FLUSH_ON_CLOSE; |
@@ -2390,7 +2390,7 @@ void ext3_truncate(struct inode *inode) | |||
2390 | page = grab_cache_page(mapping, | 2390 | page = grab_cache_page(mapping, |
2391 | inode->i_size >> PAGE_CACHE_SHIFT); | 2391 | inode->i_size >> PAGE_CACHE_SHIFT); |
2392 | if (!page) | 2392 | if (!page) |
2393 | return; | 2393 | goto out_notrans; |
2394 | } | 2394 | } |
2395 | 2395 | ||
2396 | handle = start_transaction(inode); | 2396 | handle = start_transaction(inode); |
@@ -2401,7 +2401,7 @@ void ext3_truncate(struct inode *inode) | |||
2401 | unlock_page(page); | 2401 | unlock_page(page); |
2402 | page_cache_release(page); | 2402 | page_cache_release(page); |
2403 | } | 2403 | } |
2404 | return; /* AKPM: return what? */ | 2404 | goto out_notrans; |
2405 | } | 2405 | } |
2406 | 2406 | ||
2407 | last_block = (inode->i_size + blocksize-1) | 2407 | last_block = (inode->i_size + blocksize-1) |
@@ -2525,6 +2525,14 @@ out_stop: | |||
2525 | ext3_orphan_del(handle, inode); | 2525 | ext3_orphan_del(handle, inode); |
2526 | 2526 | ||
2527 | ext3_journal_stop(handle); | 2527 | ext3_journal_stop(handle); |
2528 | return; | ||
2529 | out_notrans: | ||
2530 | /* | ||
2531 | * Delete the inode from orphan list so that it doesn't stay there | ||
2532 | * forever and trigger assertion on umount. | ||
2533 | */ | ||
2534 | if (inode->i_nlink) | ||
2535 | ext3_orphan_del(NULL, inode); | ||
2528 | } | 2536 | } |
2529 | 2537 | ||
2530 | static ext3_fsblk_t ext3_get_inode_block(struct super_block *sb, | 2538 | 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) | |||
3122 | 3130 | ||
3123 | rc = inode_setattr(inode, attr); | 3131 | rc = inode_setattr(inode, attr); |
3124 | 3132 | ||
3125 | /* If inode_setattr's call to ext3_truncate failed to get a | ||
3126 | * transaction handle at all, we need to clean up the in-core | ||
3127 | * orphan list manually. */ | ||
3128 | if (inode->i_nlink) | ||
3129 | ext3_orphan_del(NULL, inode); | ||
3130 | |||
3131 | if (!rc && (ia_valid & ATTR_MODE)) | 3133 | if (!rc && (ia_valid & ATTR_MODE)) |
3132 | rc = ext3_acl_chmod(inode); | 3134 | rc = ext3_acl_chmod(inode); |
3133 | 3135 | ||