diff options
author | Duane Griffin <duaneg@dghda.com> | 2008-07-25 04:46:23 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-25 13:53:32 -0400 |
commit | ae76dd9a6b5bbe5315fb7028e03f68f75b8538f3 (patch) | |
tree | 3b877057e84fda45782dd25a3c8be91173324922 | |
parent | ef1afd39519b74fbe1f63c9ab5a14490effec0e3 (diff) |
ext3: handle corrupted orphan list at mount
If the orphan node list includes valid, untruncatable nodes with nlink > 0
the ext3_orphan_cleanup loop which attempts to delete them will not do so,
causing it to loop forever. Fix by checking for such nodes in the
ext3_orphan_get function.
This patch fixes the second case (image hdb.20000009.softlockup.gz)
reported in http://bugzilla.kernel.org/show_bug.cgi?id=10882.
[akpm@linux-foundation.org: coding-style fixes]
[akpm@linux-foundation.org: printk warning fix]
Signed-off-by: Duane Griffin <duaneg@dghda.com>
Cc: <linux-ext4@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/ext3/ialloc.c | 9 | ||||
-rw-r--r-- | fs/ext3/inode.c | 20 | ||||
-rw-r--r-- | include/linux/ext3_fs.h | 1 |
3 files changed, 24 insertions, 6 deletions
diff --git a/fs/ext3/ialloc.c b/fs/ext3/ialloc.c index 77126821b2e9..47b678d73e7a 100644 --- a/fs/ext3/ialloc.c +++ b/fs/ext3/ialloc.c | |||
@@ -669,6 +669,14 @@ struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino) | |||
669 | if (IS_ERR(inode)) | 669 | if (IS_ERR(inode)) |
670 | goto iget_failed; | 670 | goto iget_failed; |
671 | 671 | ||
672 | /* | ||
673 | * If the orphans has i_nlinks > 0 then it should be able to be | ||
674 | * truncated, otherwise it won't be removed from the orphan list | ||
675 | * during processing and an infinite loop will result. | ||
676 | */ | ||
677 | if (inode->i_nlink && !ext3_can_truncate(inode)) | ||
678 | goto bad_orphan; | ||
679 | |||
672 | if (NEXT_ORPHAN(inode) > max_ino) | 680 | if (NEXT_ORPHAN(inode) > max_ino) |
673 | goto bad_orphan; | 681 | goto bad_orphan; |
674 | brelse(bitmap_bh); | 682 | brelse(bitmap_bh); |
@@ -690,6 +698,7 @@ bad_orphan: | |||
690 | printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", | 698 | printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", |
691 | NEXT_ORPHAN(inode)); | 699 | NEXT_ORPHAN(inode)); |
692 | printk(KERN_NOTICE "max_ino=%lu\n", max_ino); | 700 | printk(KERN_NOTICE "max_ino=%lu\n", max_ino); |
701 | printk(KERN_NOTICE "i_nlink=%u\n", inode->i_nlink); | ||
693 | /* Avoid freeing blocks if we got a bad deleted inode */ | 702 | /* Avoid freeing blocks if we got a bad deleted inode */ |
694 | if (inode->i_nlink == 0) | 703 | if (inode->i_nlink == 0) |
695 | inode->i_blocks = 0; | 704 | inode->i_blocks = 0; |
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c index 6ae4ecf3ce40..74b432fa166b 100644 --- a/fs/ext3/inode.c +++ b/fs/ext3/inode.c | |||
@@ -2253,6 +2253,19 @@ static void ext3_free_branches(handle_t *handle, struct inode *inode, | |||
2253 | } | 2253 | } |
2254 | } | 2254 | } |
2255 | 2255 | ||
2256 | int ext3_can_truncate(struct inode *inode) | ||
2257 | { | ||
2258 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
2259 | return 0; | ||
2260 | if (S_ISREG(inode->i_mode)) | ||
2261 | return 1; | ||
2262 | if (S_ISDIR(inode->i_mode)) | ||
2263 | return 1; | ||
2264 | if (S_ISLNK(inode->i_mode)) | ||
2265 | return !ext3_inode_is_fast_symlink(inode); | ||
2266 | return 0; | ||
2267 | } | ||
2268 | |||
2256 | /* | 2269 | /* |
2257 | * ext3_truncate() | 2270 | * ext3_truncate() |
2258 | * | 2271 | * |
@@ -2297,12 +2310,7 @@ void ext3_truncate(struct inode *inode) | |||
2297 | unsigned blocksize = inode->i_sb->s_blocksize; | 2310 | unsigned blocksize = inode->i_sb->s_blocksize; |
2298 | struct page *page; | 2311 | struct page *page; |
2299 | 2312 | ||
2300 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || | 2313 | if (!ext3_can_truncate(inode)) |
2301 | S_ISLNK(inode->i_mode))) | ||
2302 | return; | ||
2303 | if (ext3_inode_is_fast_symlink(inode)) | ||
2304 | return; | ||
2305 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
2306 | return; | 2314 | return; |
2307 | 2315 | ||
2308 | /* | 2316 | /* |
diff --git a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h index 36c540396377..80171ee89a22 100644 --- a/include/linux/ext3_fs.h +++ b/include/linux/ext3_fs.h | |||
@@ -832,6 +832,7 @@ extern void ext3_discard_reservation (struct inode *); | |||
832 | extern void ext3_dirty_inode(struct inode *); | 832 | extern void ext3_dirty_inode(struct inode *); |
833 | extern int ext3_change_inode_journal_flag(struct inode *, int); | 833 | extern int ext3_change_inode_journal_flag(struct inode *, int); |
834 | extern int ext3_get_inode_loc(struct inode *, struct ext3_iloc *); | 834 | extern int ext3_get_inode_loc(struct inode *, struct ext3_iloc *); |
835 | extern int ext3_can_truncate(struct inode *inode); | ||
835 | extern void ext3_truncate (struct inode *); | 836 | extern void ext3_truncate (struct inode *); |
836 | extern void ext3_set_inode_flags(struct inode *); | 837 | extern void ext3_set_inode_flags(struct inode *); |
837 | extern void ext3_get_inode_flags(struct ext3_inode_info *); | 838 | extern void ext3_get_inode_flags(struct ext3_inode_info *); |