]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blobdiff - e2fsck/pass1.c
Merge branch 'maint' into next
[thirdparty/e2fsprogs.git] / e2fsck / pass1.c
index a8237af9edbeb9b32b18f9a4c104b353ea1e4cfe..821239e59c6aa724ce4504e659a06b326e6a8602 100644 (file)
@@ -473,7 +473,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
 
        /* read the first block */
        ehandler_operation(_("reading directory block"));
-       retval = ext2fs_read_dir_block3(ctx->fs, blk, buf, 0);
+       retval = ext2fs_read_dir_block4(ctx->fs, blk, buf, 0, pctx->ino);
        ehandler_operation(0);
        if (retval)
                return;
@@ -482,7 +482,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
        retval = ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
        if (retval)
                return;
-       if (((dirent->name_len & 0xFF) != 1) ||
+       if ((ext2fs_dirent_name_len(dirent) != 1) ||
            (dirent->name[0] != '.') ||
            (dirent->inode != pctx->ino) ||
            (rec_len < 12) ||
@@ -494,7 +494,7 @@ static void check_is_really_dir(e2fsck_t ctx, struct problem_context *pctx,
        retval = ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
        if (retval)
                return;
-       if (((dirent->name_len & 0xFF) != 2) ||
+       if ((ext2fs_dirent_name_len(dirent) != 2) ||
            (dirent->name[0] != '.') ||
            (dirent->name[1] != '.') ||
            (rec_len < 12) ||
@@ -540,6 +540,40 @@ extern void e2fsck_setup_tdb_icount(e2fsck_t ctx, int flags,
                *ret = 0;
 }
 
+static errcode_t recheck_bad_inode_checksum(ext2_filsys fs, ext2_ino_t ino,
+                                           e2fsck_t ctx,
+                                           struct problem_context *pctx)
+{
+       errcode_t retval;
+       struct ext2_inode_large inode;
+
+       /*
+        * Reread inode.  If we don't see checksum error, then this inode
+        * has been fixed elsewhere.
+        */
+       retval = ext2fs_read_inode_full(fs, ino, (struct ext2_inode *)&inode,
+                                       sizeof(inode));
+       if (retval && retval != EXT2_ET_INODE_CSUM_INVALID)
+               return retval;
+       if (!retval)
+               return 0;
+
+       /*
+        * Checksum still doesn't match.  That implies that the inode passes
+        * all the sanity checks, so maybe the checksum is simply corrupt.
+        * See if the user will go for fixing that.
+        */
+       if (!fix_problem(ctx, PR_1_INODE_ONLY_CSUM_INVALID, pctx))
+               return 0;
+
+       retval = ext2fs_write_inode_full(fs, ino, (struct ext2_inode *)&inode,
+                                        sizeof(inode));
+       if (retval)
+               return retval;
+
+       return 0;
+}
+
 void e2fsck_pass1(e2fsck_t ctx)
 {
        int     i;
@@ -561,6 +595,7 @@ void e2fsck_pass1(e2fsck_t ctx)
        int             imagic_fs, extent_fs;
        int             busted_fs_time = 0;
        int             inode_size;
+       int             failed_csum = 0;
 
        init_resource_track(&rtrack, ctx->fs->io);
        clear_problem_context(&pctx);
@@ -691,7 +726,8 @@ void e2fsck_pass1(e2fsck_t ctx)
        }
        block_buf = (char *) e2fsck_allocate_memory(ctx, fs->blocksize * 3,
                                                    "block interate buffer");
-       e2fsck_use_inode_shortcuts(ctx, 1);
+       if (EXT2_INODE_SIZE(fs->super) == EXT2_GOOD_OLD_INODE_SIZE)
+               e2fsck_use_inode_shortcuts(ctx, 1);
        old_op = ehandler_operation(_("opening inode scan"));
        pctx.errcode = ext2fs_open_inode_scan(fs, ctx->inode_buffer_blocks,
                                              &scan);
@@ -721,6 +757,9 @@ void e2fsck_pass1(e2fsck_t ctx)
                ext2fs_mark_block_bitmap2(ctx->block_found_map,
                                          fs->super->s_mmp_block);
 
+       /* Set up ctx->lost_and_found if possible */
+       (void) e2fsck_get_lost_and_found(ctx, 0);
+
        while (1) {
                if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
                        if (e2fsck_mmp_update(fs))
@@ -739,7 +778,8 @@ void e2fsck_pass1(e2fsck_t ctx)
                        ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
                        continue;
                }
-               if (pctx.errcode) {
+               if (pctx.errcode &&
+                   pctx.errcode != EXT2_ET_INODE_CSUM_INVALID) {
                        fix_problem(ctx, PR_1_ISCAN_ERROR, &pctx);
                        ctx->flags |= E2F_FLAG_ABORT;
                        return;
@@ -749,6 +789,14 @@ void e2fsck_pass1(e2fsck_t ctx)
                pctx.ino = ino;
                pctx.inode = inode;
                ctx->stashed_ino = ino;
+
+               /* Clear corrupt inode? */
+               if (pctx.errcode == EXT2_ET_INODE_CSUM_INVALID) {
+                       if (fix_problem(ctx, PR_1_INODE_CSUM_INVALID, &pctx))
+                               goto clear_inode;
+                       failed_csum = 1;
+               }
+
                if (inode->i_links_count) {
                        pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
                                           ino, inode->i_links_count);
@@ -1145,6 +1193,20 @@ void e2fsck_pass1(e2fsck_t ctx)
                } else
                        check_blocks(ctx, &pctx, block_buf);
 
+               /*
+                * If the inode failed the checksum and the user didn't
+                * clear the inode, test the checksum again -- if it still
+                * fails, ask the user if the checksum should be corrected.
+                */
+               if (failed_csum) {
+                       pctx.errcode = recheck_bad_inode_checksum(fs, ino, ctx,
+                                                                 &pctx);
+                       if (pctx.errcode) {
+                               ctx->flags |= E2F_FLAG_ABORT;
+                               return;
+                       }
+               }
+
                if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                        return;
 
@@ -1236,6 +1298,12 @@ endit:
        ext2fs_free_mem(&block_buf);
        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;
+
        print_resource_track(ctx, _("Pass 1"), &rtrack, ctx->fs->io);
 }
 
@@ -1465,7 +1533,8 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
                if ((blk = ea_refcount_intr_next(refcount, &count)) == 0)
                        break;
                pctx.blk = blk;
-               pctx.errcode = ext2fs_read_ext_attr2(fs, blk, block_buf);
+               pctx.errcode = ext2fs_read_ext_attr3(fs, blk, block_buf,
+                                                    pctx.ino);
                if (pctx.errcode) {
                        fix_problem(ctx, PR_1_EXTATTR_READ_ABORT, &pctx);
                        return;
@@ -1476,8 +1545,9 @@ static void adjust_extattr_refcount(e2fsck_t ctx, ext2_refcount_t refcount,
                pctx.num = should_be;
                if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
                        header->h_refcount = should_be;
-                       pctx.errcode = ext2fs_write_ext_attr2(fs, blk,
-                                                            block_buf);
+                       pctx.errcode = ext2fs_write_ext_attr3(fs, blk,
+                                                            block_buf,
+                                                            pctx.ino);
                        if (pctx.errcode) {
                                fix_problem(ctx, PR_1_EXTATTR_WRITE_ABORT,
                                            &pctx);
@@ -1502,6 +1572,7 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
        struct ext2_ext_attr_entry *entry;
        int             count;
        region_t        region = 0;
+       int             failed_csum = 0;
 
        blk = ext2fs_file_acl_block(fs, inode);
        if (blk == 0)
@@ -1575,7 +1646,12 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
         * validate it
         */
        pctx->blk = blk;
-       pctx->errcode = ext2fs_read_ext_attr2(fs, blk, block_buf);
+       pctx->errcode = ext2fs_read_ext_attr3(fs, blk, block_buf, pctx->ino);
+       if (pctx->errcode == EXT2_ET_EXT_ATTR_CSUM_INVALID) {
+               if (fix_problem(ctx, PR_1_EA_BLOCK_CSUM_INVALID, pctx))
+                       goto clear_extattr;
+               failed_csum = 1;
+       }
        if (pctx->errcode && fix_problem(ctx, PR_1_READ_EA_BLOCK, pctx))
                goto clear_extattr;
        header = (struct ext2_ext_attr_header *) block_buf;
@@ -1657,6 +1733,18 @@ static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
        }
        region_free(region);
 
+       /*
+        * We only get here if there was no other errors that were fixed.
+        * If there was a checksum fail, ask to correct it.
+        */
+       if (failed_csum &&
+           fix_problem(ctx, PR_1_EA_BLOCK_ONLY_CSUM_INVALID, pctx)) {
+               pctx->errcode = ext2fs_write_ext_attr3(fs, blk, block_buf,
+                                                      pctx->ino);
+               if (pctx->errcode)
+                       return 0;
+       }
+
        count = header->h_refcount - 1;
        if (count)
                ea_refcount_store(ctx->refcount, blk, count);
@@ -1771,6 +1859,7 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
        int                     is_dir, is_leaf;
        problem_t               problem;
        struct ext2_extent_info info;
+       int                     failed_csum;
 
        pctx->errcode = ext2fs_extent_get_info(ehandle, &info);
        if (pctx->errcode)
@@ -1778,12 +1867,26 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
 
        pctx->errcode = ext2fs_extent_get(ehandle, EXT2_EXTENT_FIRST_SIB,
                                          &extent);
-       while (!pctx->errcode && info.num_entries-- > 0) {
+       while ((pctx->errcode == 0 ||
+               pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) &&
+              info.num_entries-- > 0) {
+               failed_csum = 0;
                is_leaf = extent.e_flags & EXT2_EXTENT_FLAGS_LEAF;
                is_dir = LINUX_S_ISDIR(pctx->inode->i_mode);
                last_lblk = extent.e_lblk + extent.e_len - 1;
 
                problem = 0;
+               /* Ask to clear a corrupt extent block */
+               if (pctx->errcode == EXT2_ET_EXTENT_CSUM_INVALID) {
+                       pctx->blk = extent.e_pblk;
+                       pctx->blk2 = extent.e_lblk;
+                       pctx->num = extent.e_len;
+                       problem = PR_1_EXTENT_CSUM_INVALID;
+                       if (fix_problem(ctx, problem, pctx))
+                               goto fix_problem_now;
+                       failed_csum = 1;
+               }
+
                if (extent.e_pblk == 0 ||
                    extent.e_pblk < ctx->fs->super->s_first_data_block ||
                    extent.e_pblk >= ext2fs_blocks_count(ctx->fs->super))
@@ -1805,6 +1908,17 @@ static void scan_extent_node(e2fsck_t ctx, struct problem_context *pctx,
                          (1 << (21 - ctx->fs->super->s_log_block_size))))
                        problem = PR_1_TOOBIG_DIR;
 
+               /* Corrupt but passes checks?  Ask to fix checksum. */
+               if (failed_csum) {
+                       pctx->blk = extent.e_pblk;
+                       pctx->blk2 = extent.e_lblk;
+                       pctx->num = extent.e_len;
+                       problem = 0;
+                       if (fix_problem(ctx, PR_1_EXTENT_ONLY_CSUM_INVALID,
+                                       pctx))
+                               ext2fs_extent_replace(ehandle, 0, &extent);
+               }
+
                if (problem) {
 report_problem:
                        pctx->blk = extent.e_pblk;
@@ -1812,6 +1926,7 @@ report_problem:
                        pctx->num = extent.e_len;
                        pctx->blkcount = extent.e_lblk + extent.e_len;
                        if (fix_problem(ctx, problem, pctx)) {
+fix_problem_now:
                                e2fsck_read_bitmaps(ctx);
                                pctx->errcode =
                                        ext2fs_extent_delete(ehandle, 0);
@@ -1841,7 +1956,10 @@ report_problem:
                        if (pctx->errcode) {
                                pctx->str = "EXT2_EXTENT_DOWN";
                                problem = PR_1_EXTENT_HEADER_INVALID;
-                               if (pctx->errcode == EXT2_ET_EXTENT_HEADER_BAD)
+                               if (pctx->errcode ==
+                                       EXT2_ET_EXTENT_HEADER_BAD ||
+                                   pctx->errcode ==
+                                       EXT2_ET_EXTENT_CSUM_INVALID)
                                        goto report_problem;
                                return;
                        }
@@ -2181,9 +2299,10 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
        }
 
        if (ctx->dirs_to_hash && pb.is_dir &&
+           !(ctx->lost_and_found && ctx->lost_and_found == ino) &&
            !(inode->i_flags & EXT2_INDEX_FL) &&
            ((inode->i_size / fs->blocksize) >= 3))
-               ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
+               e2fsck_rehash_dir_later(ctx, ino);
 
 out:
        if (dirty_inode)