Commit bd39597c authored by Eric Sandeen's avatar Eric Sandeen Committed by Linus Torvalds

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: default avatarEric Sandeen <sandeen@redhat.com>
Signed-off-by: default avatar"Theodore Ts'o" <tytso@mit.edu>
Cc: Eugene Teo <eugeneteo@kernel.sg>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent d707d31c
...@@ -103,7 +103,7 @@ static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len) ...@@ -103,7 +103,7 @@ static int ext2_commit_chunk(struct page *page, loff_t pos, unsigned len)
return err; return err;
} }
static void ext2_check_page(struct page *page) static void ext2_check_page(struct page *page, int quiet)
{ {
struct inode *dir = page->mapping->host; struct inode *dir = page->mapping->host;
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
...@@ -146,10 +146,10 @@ static void ext2_check_page(struct page *page) ...@@ -146,10 +146,10 @@ static void ext2_check_page(struct page *page)
/* Too bad, we had an error */ /* Too bad, we had an error */
Ebadsize: Ebadsize:
ext2_error(sb, "ext2_check_page", if (!quiet)
"size of directory #%lu is not a multiple of chunk size", ext2_error(sb, __func__,
dir->i_ino "size of directory #%lu is not a multiple "
); "of chunk size", dir->i_ino);
goto fail; goto fail;
Eshort: Eshort:
error = "rec_len is smaller than minimal"; error = "rec_len is smaller than minimal";
...@@ -166,32 +166,36 @@ static void ext2_check_page(struct page *page) ...@@ -166,32 +166,36 @@ static void ext2_check_page(struct page *page)
Einumber: Einumber:
error = "inode out of bounds"; error = "inode out of bounds";
bad_entry: bad_entry:
ext2_error (sb, "ext2_check_page", "bad entry in directory #%lu: %s - " if (!quiet)
"offset=%lu, inode=%lu, rec_len=%d, name_len=%d", ext2_error(sb, __func__, "bad entry in directory #%lu: : %s - "
dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs, "offset=%lu, inode=%lu, rec_len=%d, name_len=%d",
(unsigned long) le32_to_cpu(p->inode), dir->i_ino, error, (page->index<<PAGE_CACHE_SHIFT)+offs,
rec_len, p->name_len); (unsigned long) le32_to_cpu(p->inode),
rec_len, p->name_len);
goto fail; goto fail;
Eend: Eend:
p = (ext2_dirent *)(kaddr + offs); if (!quiet) {
ext2_error (sb, "ext2_check_page", p = (ext2_dirent *)(kaddr + offs);
"entry in directory #%lu spans the page boundary" ext2_error(sb, "ext2_check_page",
"offset=%lu, inode=%lu", "entry in directory #%lu spans the page boundary"
dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs, "offset=%lu, inode=%lu",
(unsigned long) le32_to_cpu(p->inode)); dir->i_ino, (page->index<<PAGE_CACHE_SHIFT)+offs,
(unsigned long) le32_to_cpu(p->inode));
}
fail: fail:
SetPageChecked(page); SetPageChecked(page);
SetPageError(page); SetPageError(page);
} }
static struct page * ext2_get_page(struct inode *dir, unsigned long n) static struct page * ext2_get_page(struct inode *dir, unsigned long n,
int quiet)
{ {
struct address_space *mapping = dir->i_mapping; struct address_space *mapping = dir->i_mapping;
struct page *page = read_mapping_page(mapping, n, NULL); struct page *page = read_mapping_page(mapping, n, NULL);
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
kmap(page); kmap(page);
if (!PageChecked(page)) if (!PageChecked(page))
ext2_check_page(page); ext2_check_page(page, quiet);
if (PageError(page)) if (PageError(page))
goto fail; goto fail;
} }
...@@ -292,7 +296,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir) ...@@ -292,7 +296,7 @@ ext2_readdir (struct file * filp, void * dirent, filldir_t filldir)
for ( ; n < npages; n++, offset = 0) { for ( ; n < npages; n++, offset = 0) {
char *kaddr, *limit; char *kaddr, *limit;
ext2_dirent *de; ext2_dirent *de;
struct page *page = ext2_get_page(inode, n); struct page *page = ext2_get_page(inode, n, 0);
if (IS_ERR(page)) { if (IS_ERR(page)) {
ext2_error(sb, __func__, ext2_error(sb, __func__,
...@@ -361,6 +365,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, ...@@ -361,6 +365,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
struct page *page = NULL; struct page *page = NULL;
struct ext2_inode_info *ei = EXT2_I(dir); struct ext2_inode_info *ei = EXT2_I(dir);
ext2_dirent * de; ext2_dirent * de;
int dir_has_error = 0;
if (npages == 0) if (npages == 0)
goto out; goto out;
...@@ -374,7 +379,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, ...@@ -374,7 +379,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
n = start; n = start;
do { do {
char *kaddr; char *kaddr;
page = ext2_get_page(dir, n); page = ext2_get_page(dir, n, dir_has_error);
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
kaddr = page_address(page); kaddr = page_address(page);
de = (ext2_dirent *) kaddr; de = (ext2_dirent *) kaddr;
...@@ -391,7 +396,9 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, ...@@ -391,7 +396,9 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
de = ext2_next_entry(de); de = ext2_next_entry(de);
} }
ext2_put_page(page); ext2_put_page(page);
} } else
dir_has_error = 1;
if (++n >= npages) if (++n >= npages)
n = 0; n = 0;
/* next page is past the blocks we've got */ /* next page is past the blocks we've got */
...@@ -414,7 +421,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir, ...@@ -414,7 +421,7 @@ struct ext2_dir_entry_2 * ext2_find_entry (struct inode * dir,
struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p) struct ext2_dir_entry_2 * ext2_dotdot (struct inode *dir, struct page **p)
{ {
struct page *page = ext2_get_page(dir, 0); struct page *page = ext2_get_page(dir, 0, 0);
ext2_dirent *de = NULL; ext2_dirent *de = NULL;
if (!IS_ERR(page)) { if (!IS_ERR(page)) {
...@@ -487,7 +494,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode) ...@@ -487,7 +494,7 @@ int ext2_add_link (struct dentry *dentry, struct inode *inode)
for (n = 0; n <= npages; n++) { for (n = 0; n <= npages; n++) {
char *dir_end; char *dir_end;
page = ext2_get_page(dir, n); page = ext2_get_page(dir, n, 0);
err = PTR_ERR(page); err = PTR_ERR(page);
if (IS_ERR(page)) if (IS_ERR(page))
goto out; goto out;
...@@ -655,14 +662,17 @@ int ext2_empty_dir (struct inode * inode) ...@@ -655,14 +662,17 @@ int ext2_empty_dir (struct inode * inode)
{ {
struct page *page = NULL; struct page *page = NULL;
unsigned long i, npages = dir_pages(inode); unsigned long i, npages = dir_pages(inode);
int dir_has_error = 0;
for (i = 0; i < npages; i++) { for (i = 0; i < npages; i++) {
char *kaddr; char *kaddr;
ext2_dirent * de; ext2_dirent * de;
page = ext2_get_page(inode, i); page = ext2_get_page(inode, i, dir_has_error);
if (IS_ERR(page)) if (IS_ERR(page)) {
dir_has_error = 1;
continue; continue;
}
kaddr = page_address(page); kaddr = page_address(page);
de = (ext2_dirent *)kaddr; de = (ext2_dirent *)kaddr;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment