Check for inodes which are too big (either too many blocks, or
would cause i_size to be too big), and offer to truncate the inode.
Remove old bogus i_size checks.
Add test case which tests e2fsck's handling of large sparse files.
Older e2fsck with the old(er) bogus i_size checks didn't handle
this correctly.
diff --git a/e2fsck/ChangeLog b/e2fsck/ChangeLog
index 5c1df96..15b5d48 100644
--- a/e2fsck/ChangeLog
+++ b/e2fsck/ChangeLog
@@ -1,3 +1,15 @@
+2002-05-21 Theodore Ts'o <tytso@mit.edu>
+
+ * pass1.c (process_block): If an inode has too many blocks or
+ is too big, then offer to truncate the inode.
+ (check_blocks): Don't bother checking the size to see if
+ it's too big, since that's just a symptom, not the disease
+ (which we're now appropriately checking in process_block).
+
+ * problem.c, problem.h: Add new problem codes PR_1_INODE_TOOBIG,
+ PR_1_TOOBIG_DIR, PR_1_TOOBIG_REG, PR_1_TOOBIG_SYMLINK, and
+ add the latch code PR_LATCH_TOOBIG.
+
2002-05-20 Theodore Ts'o <tytso@mit.edu>
* e2fsck.h, pass1.c (e2fsck_pass1_check_symlink), pass2.c
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index 5828b13..c8eee89 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -76,9 +76,10 @@
struct process_block_struct {
ext2_ino_t ino;
- int is_dir:1, clear:1, suppress:1,
+ int is_dir:1, is_reg:1, clear:1, suppress:1,
fragmented:1, compressed:1;
blk_t num_blocks;
+ blk_t max_blocks;
e2_blkcnt_t last_block;
int num_illegal_blocks;
blk_t previous_block;
@@ -410,7 +411,7 @@
pb.num_blocks = pb.last_block = 0;
pb.num_illegal_blocks = 0;
pb.suppress = 0; pb.clear = 0; pb.is_dir = 0;
- pb.fragmented = 0;
+ pb.is_reg = 0; pb.fragmented = 0;
pb.inode = &inode;
pb.pctx = &pctx;
pb.ctx = ctx;
@@ -1150,6 +1151,8 @@
pb.compressed = 0;
pb.previous_block = 0;
pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
+ pb.is_reg = LINUX_S_ISREG(inode->i_mode);
+ pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
pb.inode = inode;
pb.pctx = pctx;
pb.ctx = ctx;
@@ -1174,6 +1177,7 @@
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;
end_problem_latch(ctx, PR_LATCH_BLOCK);
+ end_problem_latch(ctx, PR_LATCH_TOOBIG);
if (pctx->errcode)
fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
@@ -1221,25 +1225,21 @@
}
if (pb.is_dir) {
int nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
- /* We don't let a directory become larger than 2GB */
- if (nblock > (pb.last_block + 1) ||
- (inode->i_size & ((fs->blocksize-1) | 0x80000000UL)) != 0)
+ if (nblock > (pb.last_block + 1))
bad_size = 1;
else if (nblock < (pb.last_block + 1)) {
if (((pb.last_block + 1) - nblock) >
fs->super->s_prealloc_dir_blocks)
bad_size = 2;
}
- } else if (!LINUX_S_ISREG(inode->i_mode)) {
- if (inode->i_size_high)
- bad_size = 5;
} else {
+ if (!LINUX_S_ISREG(inode->i_mode) && inode->i_size_high)
+ bad_size = 5;
size = inode->i_size | ((__u64) inode->i_size_high << 32);
if ((size < pb.last_block * fs->blocksize))
bad_size = 3;
else if (size > ext2_max_sizes[fs->super->s_log_block_size])
bad_size = 4;
- /* FIXME: need to ensure pb.num_blocks < 2^32 */
}
if (bad_size) {
pctx->num = (pb.last_block+1) * fs->blocksize;
@@ -1400,6 +1400,13 @@
}
p->previous_block = blk;
+ if (p->is_dir && blockcnt > 2*1024*1024/fs->blocksize)
+ problem = PR_1_TOOBIG_DIR;
+ if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
+ problem = PR_1_TOOBIG_REG;
+ if (!p->is_dir && !p->is_reg && blockcnt > 0)
+ problem = PR_1_TOOBIG_SYMLINK;
+
if (blk < fs->super->s_first_data_block ||
blk >= fs->super->s_blocks_count)
problem = PR_1_ILLEGAL_BLOCK_NUM;
diff --git a/e2fsck/problem.c b/e2fsck/problem.c
index aaaa270..e786fa2 100644
--- a/e2fsck/problem.c
+++ b/e2fsck/problem.c
@@ -89,6 +89,7 @@
N_("FILE DELETED"), /* 15 */
N_("SUPPRESSED"), /* 16 */
N_("UNLINKED"), /* 17 */
+ "", /* 18 */
};
static const struct e2fsck_problem problem_table[] = {
@@ -645,6 +646,25 @@
N_("@a @b %b is corrupt (invalid value). "),
PROMPT_CLEAR, 0},
+ /* Inode too big (latch question) */
+ { PR_1_INODE_TOOBIG,
+ N_("@i %i is too big. "), PROMPT_TRUNCATE, 0 },
+
+ /* Directory too big */
+ { PR_1_TOOBIG_DIR,
+ N_("@b #%B (%b) causes @d to be too big. "),
+ PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
+ /* Regular file too big */
+ { PR_1_TOOBIG_REG,
+ N_("@b #%B (%b) causes file to be too big. "),
+ PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
+ /* Symlink too big */
+ { PR_1_TOOBIG_SYMLINK,
+ N_("@b #%B (%b) causes symlink to be too big. "),
+ PROMPT_CLEAR, PR_LATCH_TOOBIG },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
@@ -1255,6 +1275,7 @@
{ PR_LATCH_RELOC, PR_0_RELOCATE_HINT, 0 },
{ PR_LATCH_DBLOCK, PR_1B_DUP_BLOCK_HEADER, PR_1B_DUP_BLOCK_END },
{ PR_LATCH_LOW_DTIME, PR_1_ORPHAN_LIST_REFUGEES, 0 },
+ { PR_LATCH_TOOBIG, PR_1_INODE_TOOBIG, 0 },
{ -1, 0, 0 },
};
diff --git a/e2fsck/problem.h b/e2fsck/problem.h
index f5eae27..3c4b162 100644
--- a/e2fsck/problem.h
+++ b/e2fsck/problem.h
@@ -36,6 +36,7 @@
#define PR_LATCH_RELOC 0x0050 /* Latch for superblock relocate hint */
#define PR_LATCH_DBLOCK 0x0060 /* Latch for pass 1b dup block headers */
#define PR_LATCH_LOW_DTIME 0x0070 /* Latch for pass1 orphaned list refugees */
+#define PR_LATCH_TOOBIG 0x0080 /* Latch for file to big errors */
#define PR_LATCH(x) ((((x) & PR_LATCH_MASK) >> 4) - 1)
@@ -372,6 +373,18 @@
/* Bad extended attribute value */
#define PR_1_EA_BAD_VALUE 0x010042
+/* Inode too big (latch question) */
+#define PR_1_INODE_TOOBIG 0x010043
+
+/* Directory too big */
+#define PR_1_TOOBIG_DIR 0x010044
+
+/* Regular file too big */
+#define PR_1_TOOBIG_REG 0x010045
+
+/* Symlink too big */
+#define PR_1_TOOBIG_SYMLINK 0x010046
+
/*
* Pass 1b errors
*/