typedef void (*pass_t)(e2fsck_t ctx);
static pass_t e2fsck_passes[] = {
- e2fsck_pass1, e2fsck_pass2, e2fsck_pass3, e2fsck_pass4,
- e2fsck_pass5, 0 };
+ e2fsck_pass1, e2fsck_pass1e, e2fsck_pass2, e2fsck_pass3,
+ e2fsck_pass4, e2fsck_pass5, 0 };
- #define E2F_FLAG_RUN_RETURN (E2F_FLAG_SIGNAL_MASK|E2F_FLAG_RESTART)
-
int e2fsck_run(e2fsck_t ctx)
{
int i;
old_op = ehandler_operation(_("getting next inode from scan"));
pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
inode, inode_size);
+ if (ino > ino_threshold)
+ pass1_readahead(ctx, &ra_group, &ino_threshold);
ehandler_operation(old_op);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
+ goto endit;
if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
+ /*
+ * If badblocks says badblocks is bad, offer to clear
+ * the list, update the in-core bb list, and restart
+ * the inode scan.
+ */
+ if (ino == EXT2_BAD_INO &&
+ fix_problem(ctx, PR_1_BADBLOCKS_IN_BADBLOCKS,
+ &pctx)) {
+ errcode_t err;
+
+ e2fsck_clear_inode(ctx, ino, inode, 0, "pass1");
+ ext2fs_badblocks_list_free(ctx->fs->badblocks);
+ ctx->fs->badblocks = NULL;
+ err = ext2fs_read_bb_inode(ctx->fs,
+ &ctx->fs->badblocks);
+ if (err) {
+ fix_problem(ctx, PR_1_ISCAN_ERROR,
+ &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ goto endit;
+ }
+ err = ext2fs_inode_scan_goto_blockgroup(scan,
+ 0);
+ if (err) {
+ fix_problem(ctx, PR_1_ISCAN_ERROR,
+ &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ goto endit;
+ }
+ continue;
+ }
if (!ctx->inode_bb_map)
alloc_bb_map(ctx);
ext2fs_mark_inode_bitmap2(ctx->inode_bb_map, ino);
if (inode)
ext2fs_free_mem(&inode);
+ /*
+ * The l+f inode may have been cleared, so zap it now and
+ * later passes will recalculate it if necessary
+ */
+ ctx->lost_and_found = 0;
+
if ((ctx->flags & E2F_FLAG_SIGNAL_MASK) == 0)
print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
+ else
+ ctx->invalid_bitmaps++;
}
+#undef FINISH_INODE_LOOP
/*
* When the inode_scan routines call this callback at the end of the
if (ctx->progress)
(void) (ctx->progress)(ctx, 2, 0, cd.max);
- if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
+ if (ext2fs_has_feature_dir_index(fs->super))
ext2fs_dblist_sort2(fs->dblist, special_dir_block_cmp);
- cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_block,
+ check_dir_func = cd.ra_entries ? check_dir_block2 : check_dir_block;
+ cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_func,
&cd);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART)
- return;
-
if (ctx->flags & E2F_FLAG_RESTART_LATER) {
ctx->flags |= E2F_FLAG_RESTART;
- return;
+ ctx->flags &= ~E2F_FLAG_RESTART_LATER;
}
+ if (ctx->flags & E2F_FLAG_RUN_RETURN)
+ return;
+
if (cd.pctx.errcode) {
fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
ctx->flags |= E2F_FLAG_ABORT;
struct problem_context pctx;
int dups_found = 0;
int ret;
+ int dx_csum_size = 0, de_csum_size = 0;
+ int failed_csum = 0;
+ int is_leaf = 1;
+ size_t inline_data_size = 0;
+ int filetype = 0;
+ int encrypted = 0;
+ size_t max_block_size;
cd = (struct check_dir_struct *) priv_data;
- buf = cd->buf;
+ ibuf = buf = cd->buf;
ctx = cd->ctx;
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART)
+ if (ctx->flags & E2F_FLAG_RUN_RETURN)
return DIRENT_ABORT;
if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
N_("@i %i logical @b %b (physical @b %c) violates cluster allocation rules.\nWill fix in pass 1B.\n"),
PROMPT_NONE, 0 },
+ /* Inode has INLINE_DATA_FL flag but extended attribute not found */
+ { PR_1_INLINE_DATA_NO_ATTR,
+ N_("@i %i has INLINE_DATA_FL flag but @a not found. "),
+ PROMPT_TRUNCATE, 0 },
+
+ /* Extents/inlinedata flag set on a device or socket inode */
+ { PR_1_SPECIAL_EXTENTS_IDATA,
+ N_("Special (@v/socket/fifo) file (@i %i) has extents\n"
+ "or inline-data flag set. "),
+ PROMPT_CLEAR, PR_PREEN_OK | PR_PREEN_NO | PR_NO_OK },
+
+ /* Inode has extent header but inline data flag is set */
+ { PR_1_CLEAR_INLINE_DATA_FOR_EXTENT,
+ N_("@i %i has @x header but inline data flag is set.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Inode seems to have inline data but extent flag is set */
+ { PR_1_CLEAR_EXTENT_FOR_INLINE_DATA,
+ N_("@i %i seems to have inline data but @x flag is set.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Inode seems to have block map but inline data and extent flags set */
+ { PR_1_CLEAR_EXTENT_INLINE_DATA_FLAGS,
+ N_("@i %i seems to have @b map but inline data and @x flags set.\n"),
+ PROMPT_FIX, 0 },
+
+ /* Inode has inline data and extent flags but i_block contains junk */
+ { PR_1_CLEAR_EXTENT_INLINE_DATA_INODE,
+ N_("@i %i has inline data and @x flags set but i_block contains junk.\n"),
+ PROMPT_CLEAR_INODE, 0 },
+
+ /* Bad block list says the bad block list inode is bad */
+ { PR_1_BADBLOCKS_IN_BADBLOCKS,
+ N_("Bad block list says the bad block list @i is bad. "),
+ PROMPT_CLEAR_INODE, 0 },
+
+ /* Error allocating extent region allocation structure */
+ { PR_1_EXTENT_ALLOC_REGION_ABORT,
+ N_("@A @x region allocation structure. "),
+ PROMPT_NONE, PR_FATAL},
+
+ /* Inode has a duplicate extent mapping */
+ { PR_1_EXTENT_COLLISION,
+ N_("@i %i has a duplicate @x mapping\n\t(logical @b %c, @n physical @b %b, len %N)\n"),
+ PROMPT_CLEAR, 0 },
+
+ /* Error allocating memory for encrypted directory list */
+ { PR_1_ALLOCATE_ENCRYPTED_DIRLIST,
+ N_("@A memory for encrypted @d list\n"),
+ PROMPT_NONE, PR_FATAL },
+
+ /* Inode extent tree could be more shallow */
+ { PR_1_EXTENT_BAD_MAX_DEPTH,
+ N_("@i %i @x tree could be more shallow (%b; could be <= %c)\n"),
+ PROMPT_FIX, PR_NO_OK | PR_PREEN_NO | PR_PREEN_OK },
+
+ /* Inode extent tree could be more shallow */
+ { PR_1_NO_BIGALLOC_BLOCKMAP_FILES,
+ N_("@i %i on bigalloc @f cannot be @b mapped. "),
+ PROMPT_FIX, 0 },
+
+ /* Inode has corrupt extent header */
+ { PR_1_MISSING_EXTENT_HEADER,
+ N_("@i %i has corrupt @x header. "),
+ PROMPT_CLEAR_INODE, 0 },
+
/* Pass 1b errors */
/* Pass 1B: Rescan for duplicate/bad blocks */
#include "e2fsck.h"
#include "problem.h"
+/* Schedule a dir to be rebuilt during pass 3A. */
+void e2fsck_rehash_dir_later(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (!ctx->dirs_to_hash)
+ ext2fs_u32_list_create(&ctx->dirs_to_hash, 50);
+ if (ctx->dirs_to_hash)
+ ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+}
+
+/* Ask if a dir will be rebuilt during pass 3A. */
+int e2fsck_dir_will_be_rehashed(e2fsck_t ctx, ext2_ino_t ino)
+{
+ if (ctx->options & E2F_OPT_COMPRESS_DIRS)
+ return 1;
+ if (!ctx->dirs_to_hash)
+ return 0;
+ return ext2fs_u32_list_test(ctx->dirs_to_hash, ino);
+}
+
+ #undef REHASH_DEBUG
+
struct fill_dir_struct {
char *buf;
struct ext2_inode *inode;
struct write_dir_struct {
struct out_dir *outdir;
errcode_t err;
+ ext2_ino_t ino;
e2fsck_t ctx;
- blk64_t cleared;
+ ext2_ino_t dir;
};
/*
{
struct write_dir_struct *wd = (struct write_dir_struct *) priv_data;
blk64_t blk;
- char *dir;
+ char *dir, *buf = 0;
- if (*block_nr == 0)
- return 0;
- if (blockcnt < 0)
+ #ifdef REHASH_DEBUG
+ printf("%u: write_dir_block %lld:%lld", wd->ino, blockcnt, *block_nr);
+ #endif
- if (*block_nr == 0) {
++ if ((*block_nr == 0) || (blockcnt < 0)) {
+ #ifdef REHASH_DEBUG
+ printf(" - skip\n");
+ #endif
return 0;
- /* Don't free blocks at the end of the directory, they will be
- * truncated by the caller. */
- if (blockcnt >= wd->outdir->num) {
+ }
- /* We don't need this block, so release it */
- e2fsck_read_bitmaps(wd->ctx);
- blk = *block_nr;
- /*
- * In theory, we only release blocks from the end of the
- * directory file, so it's fine to clobber a whole cluster at
- * once.
- */
- if (blk % EXT2FS_CLUSTER_RATIO(fs) == 0) {
- ext2fs_block_alloc_stats2(fs, blk, -1);
- wd->cleared++;
- }
- *block_nr = 0;
- return BLOCK_CHANGED;
+ if (blockcnt < wd->outdir->num)
+ dir = wd->outdir->buf + (blockcnt * fs->blocksize);
+ else if (wd->ctx->lost_and_found == wd->dir) {
+ /* Don't release any extra directory blocks for lost+found */
+ wd->err = ext2fs_new_dir_block(fs, 0, 0, &buf);
+ if (wd->err)
+ return BLOCK_ABORT;
+ dir = buf;
+ wd->outdir->num++;
+ } else {
++ /* Don't free blocks at the end of the directory, they
++ * will be truncated by the caller. */
+ #ifdef REHASH_DEBUG
+ printf(" - not freed\n");
+ #endif
+ return 0;
}
-
- if (blockcnt < 0) {
-#ifdef REHASH_DEBUG
- printf(" - skip\n");
-#endif
- return 0;
- }
+ wd->err = ext2fs_write_dir_block4(fs, *block_nr, dir, 0, wd->dir);
+ if (buf)
+ ext2fs_free_mem(&buf);
- dir = wd->outdir->buf + (blockcnt * fs->blocksize);
- wd->err = ext2fs_write_dir_block3(fs, *block_nr, dir, 0);
+ #ifdef REHASH_DEBUG
+ printf(" - write (%d)\n", wd->err);
+ #endif
if (wd->err)
return BLOCK_ABORT;
return 0;
wd.outdir = outdir;
wd.err = 0;
+ wd.ino = ino;
wd.ctx = ctx;
- wd.cleared = 0;
+ wd.dir = ino;
- retval = ext2fs_block_iterate3(fs, ino, 0, 0,
+ retval = ext2fs_block_iterate3(fs, ino, 0, NULL,
write_dir_block, &wd);
if (retval)
return retval;
if (wd.err)
return wd.err;
- e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+ e2fsck_read_inode(ctx, ino, inode, "rehash_dir");
if (compress)
- inode.i_flags &= ~EXT2_INDEX_FL;
+ inode->i_flags &= ~EXT2_INDEX_FL;
else
- inode.i_flags |= EXT2_INDEX_FL;
+ inode->i_flags |= EXT2_INDEX_FL;
- retval = ext2fs_inode_size_set(fs, inode,
- outdir->num * fs->blocksize);
+ #ifdef REHASH_DEBUG
+ printf("%u: set inode size to %u blocks = %u bytes\n",
+ ino, outdir->num, outdir->num * fs->blocksize);
+ #endif
- retval = ext2fs_inode_size_set(fs, &inode, (ext2_off64_t)outdir->num *
++ retval = ext2fs_inode_size_set(fs, inode, (ext2_off64_t)outdir->num *
+ fs->blocksize);
if (retval)
return retval;
- ext2fs_iblk_sub_blocks(fs, inode, wd.cleared);
- e2fsck_write_inode(ctx, ino, inode, "rehash_dir");
- return 0;
+ /* ext2fs_punch() calls ext2fs_write_inode() which writes the size */
- return ext2fs_punch(fs, ino, &inode, NULL, outdir->num, ~0ULL);
++ return ext2fs_punch(fs, ino, inode, NULL, outdir->num, ~0ULL);
}
-errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino)
+errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino,
+ struct problem_context *pctx)
{
ext2_filsys fs = ctx->fs;
errcode_t retval;
struct ext2_inode inode;
char *dir_buf = 0;
- struct fill_dir_struct fd;
- struct out_dir outdir;
+ struct fill_dir_struct fd = { NULL };
+ struct out_dir outdir = { 0 };
- outdir.max = outdir.num = 0;
- outdir.buf = 0;
- outdir.hashes = 0;
e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+ if (ext2fs_has_feature_inline_data(fs->super) &&
+ (inode.i_flags & EXT4_INLINE_DATA_FL))
+ return 0;
+
retval = ENOMEM;
- fd.harray = 0;
dir_buf = malloc(inode.i_size);
if (!dir_buf)
goto errout;
fd.ctx = ctx;
fd.buf = dir_buf;
fd.inode = &inode;
- fd.ino = ino;
- fd.err = 0;
- fd.dir_size = 0;
- fd.compress = 0;
- if (!(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) ||
+ fd.dir = ino;
+ if (!ext2fs_has_feature_dir_index(fs->super) ||
(inode.i_size / fs->blocksize) < 2)
fd.compress = 1;
fd.parent = 0;
ext2fs_extent_free(handle);
return retval;
}
+
+static errcode_t ext2fs_punch_inline_data(ext2_filsys fs, ext2_ino_t ino,
+ struct ext2_inode *inode,
+ blk64_t start,
+ blk64_t end EXT2FS_ATTR((unused)))
+{
+ errcode_t retval;
+
+ /*
+ * In libext2fs ext2fs_punch is based on block unit. So that
+ * means that if start > 0 we don't need to do nothing. Due
+ * to this we will remove all inline data in ext2fs_punch()
+ * now.
+ */
+ if (start > 0)
+ return 0;
+
+ memset((char *)inode->i_block, 0, EXT4_MIN_INLINE_DATA_SIZE);
+ inode->i_size = 0;
+ retval = ext2fs_write_inode(fs, ino, inode);
+ if (retval)
+ return retval;
+
+ return ext2fs_inline_data_ea_remove(fs, ino);
+}
/*
- * Deallocate all logical blocks starting at start to end, inclusive.
- * If end is ~0, then this is effectively truncate.
+ * Deallocate all logical _blocks_ starting at start to end, inclusive.
+ * If end is ~0ULL, then this is effectively truncate.
*/
errcode_t ext2fs_punch(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode,
return retval;
inode = &inode_buf;
}
- if (inode->i_flags & EXT4_EXTENTS_FL)
+ if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ return ext2fs_punch_inline_data(fs, ino, inode, start, end);
+ else if (inode->i_flags & EXT4_EXTENTS_FL)
retval = ext2fs_punch_extent(fs, ino, inode, start, end);
- else {
- blk_t count;
-
- if (start > ~0U)
- return 0;
- if (end > ~0U)
- end = ~0U;
- count = ((end - start + 1) < ~0U) ? (end - start + 1) : ~0U;
- retval = ext2fs_punch_ind(fs, inode, block_buf,
- (blk_t) start, count);
- }
+ else
+ retval = ext2fs_punch_ind(fs, inode, block_buf, start, end);
if (retval)
return retval;
(logical block 0, invalid physical block 22011707397135, len 15)
Clear? yes
+Inode 17 extent tree (at level 1) could be shorter. Fix? yes
+
Inode 17, i_blocks is 32, should be 0. Fix? yes
- Error while reading over extent tree in inode 18: Corrupt extent header
- Clear inode? yes
+ Inode 18 has corrupt extent header. Clear inode? yes
Inode 18, i_blocks is 2, should be 0. Fix? yes