diff options
author | Duane Griffin <duaneg@dghda.com> | 2008-07-11 19:27:31 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2008-07-11 19:27:31 -0400 |
commit | 91ef4caf800030fa6e5224b8a41f8c74787b303d (patch) | |
tree | f0c058b50ac2eb9058aa8b077b2da733f8c3ee02 | |
parent | bce7f793daec3e65ec5c5705d2457b81fe7b5725 (diff) |
ext4: handle corrupted orphan list at mount
If the orphan node list includes valid, untruncatable nodes with nlink > 0
the ext4_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
ext4_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.
Signed-off-by: Duane Griffin <duaneg@dghda.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r-- | fs/ext4/ext4.h | 1 | ||||
-rw-r--r-- | fs/ext4/ialloc.c | 9 | ||||
-rw-r--r-- | fs/ext4/inode.c | 20 |
3 files changed, 24 insertions, 6 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 8158083f7ac0..f7a0758f4689 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -1039,6 +1039,7 @@ extern void ext4_discard_reservation (struct inode *); | |||
1039 | extern void ext4_dirty_inode(struct inode *); | 1039 | extern void ext4_dirty_inode(struct inode *); |
1040 | extern int ext4_change_inode_journal_flag(struct inode *, int); | 1040 | extern int ext4_change_inode_journal_flag(struct inode *, int); |
1041 | extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); | 1041 | extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); |
1042 | extern int ext4_can_truncate(struct inode *inode); | ||
1042 | extern void ext4_truncate (struct inode *); | 1043 | extern void ext4_truncate (struct inode *); |
1043 | extern void ext4_set_inode_flags(struct inode *); | 1044 | extern void ext4_set_inode_flags(struct inode *); |
1044 | extern void ext4_get_inode_flags(struct ext4_inode_info *); | 1045 | extern void ext4_get_inode_flags(struct ext4_inode_info *); |
diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index c6efbab0c801..11cafe14aa27 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c | |||
@@ -817,6 +817,14 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino) | |||
817 | if (IS_ERR(inode)) | 817 | if (IS_ERR(inode)) |
818 | goto iget_failed; | 818 | goto iget_failed; |
819 | 819 | ||
820 | /* | ||
821 | * If the orphans has i_nlinks > 0 then it should be able to be | ||
822 | * truncated, otherwise it won't be removed from the orphan list | ||
823 | * during processing and an infinite loop will result. | ||
824 | */ | ||
825 | if (inode->i_nlink && !ext4_can_truncate(inode)) | ||
826 | goto bad_orphan; | ||
827 | |||
820 | if (NEXT_ORPHAN(inode) > max_ino) | 828 | if (NEXT_ORPHAN(inode) > max_ino) |
821 | goto bad_orphan; | 829 | goto bad_orphan; |
822 | brelse(bitmap_bh); | 830 | brelse(bitmap_bh); |
@@ -838,6 +846,7 @@ bad_orphan: | |||
838 | printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", | 846 | printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n", |
839 | NEXT_ORPHAN(inode)); | 847 | NEXT_ORPHAN(inode)); |
840 | printk(KERN_NOTICE "max_ino=%lu\n", max_ino); | 848 | printk(KERN_NOTICE "max_ino=%lu\n", max_ino); |
849 | printk(KERN_NOTICE "i_nlink=%u\n", inode->i_nlink); | ||
841 | /* Avoid freeing blocks if we got a bad deleted inode */ | 850 | /* Avoid freeing blocks if we got a bad deleted inode */ |
842 | if (inode->i_nlink == 0) | 851 | if (inode->i_nlink == 0) |
843 | inode->i_blocks = 0; | 852 | inode->i_blocks = 0; |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 8d9707746413..269763b66361 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -2305,6 +2305,19 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode, | |||
2305 | } | 2305 | } |
2306 | } | 2306 | } |
2307 | 2307 | ||
2308 | int ext4_can_truncate(struct inode *inode) | ||
2309 | { | ||
2310 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
2311 | return 0; | ||
2312 | if (S_ISREG(inode->i_mode)) | ||
2313 | return 1; | ||
2314 | if (S_ISDIR(inode->i_mode)) | ||
2315 | return 1; | ||
2316 | if (S_ISLNK(inode->i_mode)) | ||
2317 | return !ext4_inode_is_fast_symlink(inode); | ||
2318 | return 0; | ||
2319 | } | ||
2320 | |||
2308 | /* | 2321 | /* |
2309 | * ext4_truncate() | 2322 | * ext4_truncate() |
2310 | * | 2323 | * |
@@ -2349,12 +2362,7 @@ void ext4_truncate(struct inode *inode) | |||
2349 | unsigned blocksize = inode->i_sb->s_blocksize; | 2362 | unsigned blocksize = inode->i_sb->s_blocksize; |
2350 | struct page *page; | 2363 | struct page *page; |
2351 | 2364 | ||
2352 | if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || | 2365 | if (!ext4_can_truncate(inode)) |
2353 | S_ISLNK(inode->i_mode))) | ||
2354 | return; | ||
2355 | if (ext4_inode_is_fast_symlink(inode)) | ||
2356 | return; | ||
2357 | if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) | ||
2358 | return; | 2366 | return; |
2359 | 2367 | ||
2360 | /* | 2368 | /* |