diff options
author | Eric Sandeen <sandeen@redhat.com> | 2008-10-16 01:04:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-16 14:21:46 -0400 |
commit | bd39597cbd42a784105a04010100e27267481c67 (patch) | |
tree | afe3e40d871f4581146f5679ea546670fe93f4f2 /fs | |
parent | d707d31c972b657dfc2efefd0b99cc4e14223dab (diff) |
ext2: avoid printk floods in the face of directory corruption
A very large directory with many read failures (either due to storage
problems, or due to invalid size & blocks from corruption) will generate a
printk storm as the filesystem continues to try to read all the blocks.
This flood of messages can tie up the box until it is complete - which may
be a very long time, especially for very large corrupted values.
This is fixed by only reporting the corruption once each time we try to
read the directory.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: Eugene Teo <eugeneteo@kernel.sg>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/ext2/dir.c | 60 |
1 files changed, 35 insertions, 25 deletions
diff --git a/fs/ext2/dir.c b/fs/ext2/dir.c index a78c6b4af060..11a49ce84392 100644 --- a/fs/ext2/dir.c +++ b/fs/ext2/dir.c | |||
@@ -103,7 +103,7 @@ static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len) | |||
103 | return err; | 103 | return err; |
104 | } | 104 | } |
105 | 105 | ||
106 | static void ext2_check_page(struct page *page) | 106 | static void ext2_check_page(struct page *page, int quiet) |
107 | { | 107 | { |
108 | struct inode *dir = page->mapping->host; | 108 | struct inode *dir = page->mapping->host; |
109 | struct super_block *sb = dir->i_sb; | 109 | struct super_block *sb = dir->i_sb; |
@@ -146,10 +146,10 @@ out: | |||
146 | /* Too bad, we had an error */ | 146 | /* Too bad, we had an error */ |
147 | 147 | ||
148 | Ebadsize: | 148 | Ebadsize: |
149 | ext2_error(sb, "ext2_check_page", | 149 | if (!quiet) |
150 | "size of directory #%lu is not a multiple of chunk size", | 150 | ext2_error(sb, __func__, |
151 | dir->i_ino | 151 | "size of directory #%lu is not a multiple " |
152 | ); | 152 | "of chunk size", dir->i_ino); |
153 | goto fail; | 153 | goto fail; |
154 | Eshort: | 154 | Eshort: |
155 | error = "rec_len is smaller than minimal"; | 155 | error = "rec_len is smaller than minimal"; |
@@ -166,32 +166,36 @@ Espan: | |||
166 | Einumber: | 166 | Einumber: |
167 | error = "inode out of bounds"; | 167 | error = "inode out of bounds"; |
168 | bad_entry: | 168 | bad_entry: |
169 | ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - " | 169 | if (!quiet) |
170 | "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", | 170 | ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - " |
171 | dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, | 171 | "offset=%lu, inode=%lu, rec_len=%d, name_len=%d", |
172 | (unsigned long) le32_to_cpu(p->inode), | 172 | dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, |
173 | rec_len, p->name_len); | 173 | (unsigned long) le32_to_cpu(p->inode), |
174 | rec_len, p->name_len); | ||
174 | goto fail; | 175 | goto fail; |
175 | Eend: | 176 | Eend: |
176 | p = (ext2_dirent *)(kaddr + offs); | 177 | if (!quiet) { |
177 | ext2_error (sb, "ext2_check_page", | 178 | p = (ext2_dirent *)(kaddr + offs); |
178 | "entry in directory #%lu spans the page boundary" | 179 | ext2_error(sb, "ext2_check_page", |
179 | "offset=%lu, inode=%lu", | 180 | "entry in directory #%lu spans the page boundary" |
180 | dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, | 181 | "offset=%lu, inode=%lu", |
181 | (unsigned long) le32_to_cpu(p->inode)); | 182 | dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, |
183 | (unsigned long) le32_to_cpu(p->inode)); | ||
184 | } | ||
182 | fail: | 185 | fail: |
183 | SetPageChecked(page); | 186 | SetPageChecked(page); |
184 | SetPageError(page); | 187 | SetPageError(page); |
185 | } | 188 | } |
186 | 189 | ||
187 | static struct page * ext2_get_page(struct inode *dir, unsigned long n) | 190 | static struct page * ext2_get_page(struct inode *dir, unsigned long n, |
191 | int quiet) | ||
188 | { | 192 | { |
189 | struct address_space *mapping = dir->i_mapping; | 193 | struct address_space *mapping = dir->i_mapping; |
190 | struct page *page = read_mapping_page(mapping, n, NULL); | 194 | struct page *page = read_mapping_page(mapping, n, NULL); |
191 | if (!IS_ERR(page)) { | 195 | if (!IS_ERR(page)) { |
192 | kmap(page); | 196 | kmap(page); |
193 | if (!PageChecked(page)) | 197 | if (!PageChecked(page)) |
194 | ext2_check_page(page); | 198 | ext2_check_page(page, quiet); |
195 | if (PageError(page)) | 199 | if (PageError(page)) |
196 | goto fail; | 200 | goto fail; |
197 | } | 201 | } |
@@ -292,7 +296,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) | |||
292 | for ( ; n < npages; n++, offset = 0) { | 296 | for ( ; n < npages; n++, offset = 0) { |
293 | char *kaddr, *limit; | 297 | char *kaddr, *limit; |
294 | ext2_dirent *de; | 298 | ext2_dirent *de; |
295 | struct page *page = ext2_get_page(inode, n); | 299 | struct page *page = ext2_get_page(inode, n, 0); |
296 | 300 | ||
297 | if (IS_ERR(page)) { | 301 | if (IS_ERR(page)) { |
298 | ext2_error(sb, __func__, | 302 | ext2_error(sb, __func__, |
@@ -361,6 +365,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, | |||
361 | struct page *page = NULL; | 365 | struct page *page = NULL; |
362 | struct ext2_inode_info *ei = EXT2_I(dir); | 366 | struct ext2_inode_info *ei = EXT2_I(dir); |
363 | ext2_dirent * de; | 367 | ext2_dirent * de; |
368 | int dir_has_error = 0; | ||
364 | 369 | ||
365 | if (npages == 0) | 370 | if (npages == 0) |
366 | goto out; | 371 | goto out; |
@@ -374,7 +379,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, | |||
374 | n = start; | 379 | n = start; |
375 | do { | 380 | do { |
376 | char *kaddr; | 381 | char *kaddr; |
377 | page = ext2_get_page(dir, n); | 382 | page = ext2_get_page(dir, n, dir_has_error); |
378 | if (!IS_ERR(page)) { | 383 | if (!IS_ERR(page)) { |
379 | kaddr = page_address(page); | 384 | kaddr = page_address(page); |
380 | de = (ext2_dirent *) kaddr; | 385 | de = (ext2_dirent *) kaddr; |
@@ -391,7 +396,9 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, | |||
391 | de = ext2_next_entry(de); | 396 | de = ext2_next_entry(de); |
392 | } | 397 | } |
393 | ext2_put_page(page); | 398 | ext2_put_page(page); |
394 | } | 399 | } else |
400 | dir_has_error = 1; | ||
401 | |||
395 | if (++n >= npages) | 402 | if (++n >= npages) |
396 | n = 0; | 403 | n = 0; |
397 | /* next page is past the blocks we've got */ | 404 | /* next page is past the blocks we've got */ |
@@ -414,7 +421,7 @@ found: | |||
414 | 421 | ||
415 | struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) | 422 | struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) |
416 | { | 423 | { |
417 | struct page *page = ext2_get_page(dir, 0); | 424 | struct page *page = ext2_get_page(dir, 0, 0); |
418 | ext2_dirent *de = NULL; | 425 | ext2_dirent *de = NULL; |
419 | 426 | ||
420 | if (!IS_ERR(page)) { | 427 | if (!IS_ERR(page)) { |
@@ -487,7 +494,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) | |||
487 | for (n = 0; n <= npages; n++) { | 494 | for (n = 0; n <= npages; n++) { |
488 | char *dir_end; | 495 | char *dir_end; |
489 | 496 | ||
490 | page = ext2_get_page(dir, n); | 497 | page = ext2_get_page(dir, n, 0); |
491 | err = PTR_ERR(page); | 498 | err = PTR_ERR(page); |
492 | if (IS_ERR(page)) | 499 | if (IS_ERR(page)) |
493 | goto out; | 500 | goto out; |
@@ -655,14 +662,17 @@ int ext2_empty_dir (struct inode * inode) | |||
655 | { | 662 | { |
656 | struct page *page = NULL; | 663 | struct page *page = NULL; |
657 | unsigned long i, npages = dir_pages(inode); | 664 | unsigned long i, npages = dir_pages(inode); |
665 | int dir_has_error = 0; | ||
658 | 666 | ||
659 | for (i = 0; i < npages; i++) { | 667 | for (i = 0; i < npages; i++) { |
660 | char *kaddr; | 668 | char *kaddr; |
661 | ext2_dirent * de; | 669 | ext2_dirent * de; |
662 | page = ext2_get_page(inode, i); | 670 | page = ext2_get_page(inode, i, dir_has_error); |
663 | 671 | ||
664 | if (IS_ERR(page)) | 672 | if (IS_ERR(page)) { |
673 | dir_has_error = 1; | ||
665 | continue; | 674 | continue; |
675 | } | ||
666 | 676 | ||
667 | kaddr = page_address(page); | 677 | kaddr = page_address(page); |
668 | de = (ext2_dirent *)kaddr; | 678 | de = (ext2_dirent *)kaddr; |