Whamcloud - gitweb
e2fsck.8: minor man page fixes
[tools/e2fsprogs.git] / e2fsck / pass2.c
index 2873609..08ab40f 100644 (file)
@@ -1341,57 +1341,68 @@ skip_checksum:
                            (rec_len < min_dir_len) ||
                            ((rec_len % 4) != 0) ||
                            ((ext2fs_dir_rec_len(ext2fs_dirent_name_len(dirent),
-                                                extended)) > rec_len)) {
-                               if (fix_problem(ctx, PR_2_DIR_CORRUPTED,
-                                               &cd->pctx)) {
+                                                extended)) > rec_len))
+                               problem = PR_2_DIR_CORRUPTED;
+                       if (problem) {
+                               if ((offset == 0) &&
+                                   (rec_len == fs->blocksize) &&
+                                   (dirent->inode == 0) &&
+                                   e2fsck_dir_will_be_rehashed(ctx, ino)) {
+                                       problem = 0;
+                                       max_block_size = fs->blocksize;
+                               }
+                       }
+                       if (problem) {
 #ifdef WORDS_BIGENDIAN
-                                       /*
-                                        * On big-endian systems, if the dirent
-                                        * swap routine finds a rec_len that it
-                                        * doesn't like, it continues
-                                        * processing the block as if rec_len
-                                        * == EXT2_DIR_ENTRY_HEADER_LEN.  This means that the name
-                                        * field gets byte swapped, which means
-                                        * that salvage will not detect the
-                                        * correct name length (unless the name
-                                        * has a length that's an exact
-                                        * multiple of four bytes), and it'll
-                                        * discard the entry (unnecessarily)
-                                        * and the rest of the dirent block.
-                                        * Therefore, swap the rest of the
-                                        * block back to disk order, run
-                                        * salvage, and re-swap anything after
-                                        * the salvaged dirent.
-                                        */
-                                       int need_reswab = 0;
-                                       if (rec_len < EXT2_DIR_ENTRY_HEADER_LEN || rec_len % 4) {
-                                               need_reswab = 1;
-                                               ext2fs_dirent_swab_in2(fs,
-                                                       ((char *)dirent) + EXT2_DIR_ENTRY_HEADER_LEN,
-                                                       max_block_size - offset - EXT2_DIR_ENTRY_HEADER_LEN,
-                                                       0);
-                                       }
+                               int need_reswab = 0;
 #endif
-                                       salvage_directory(fs, dirent, prev,
-                                                         &offset,
-                                                         max_block_size,
-                                                         hash_in_dirent);
+
+                               if (!fix_problem(ctx, PR_2_DIR_CORRUPTED,
+                                               &cd->pctx))
+                                       goto abort_free_dict;
 #ifdef WORDS_BIGENDIAN
-                                       if (need_reswab) {
-                                               unsigned int len;
-
-                                               (void) ext2fs_get_rec_len(fs,
-                                                       dirent, &len);
-                                               len += offset;
-                                               if (max_block_size > len)
-                                                       ext2fs_dirent_swab_in2(fs,
-                               ((char *)dirent) + len, max_block_size - len, 0);
-                                       }
+                               /*
+                                * On big-endian systems, if the dirent
+                                * swap routine finds a rec_len that it
+                                * doesn't like, it continues processing
+                                * the block as if rec_len ==
+                                * EXT2_DIR_ENTRY_HEADER_LEN.  This means
+                                * that the name field gets byte swapped,
+                                * which means that salvage will not detect
+                                * the correct name length (unless the name
+                                * has a length that's an exact multiple of
+                                * four bytes), and it'll discard the entry
+                                * (unnecessarily) and the rest of the
+                                * dirent block.  Therefore, swap the rest
+                                * of the block back to disk order, run
+                                * salvage, and re-swap anything after the
+                                * salvaged dirent.
+                                */
+                               if (rec_len < EXT2_DIR_ENTRY_HEADER_LEN ||
+                                   rec_len % 4) {
+                                       need_reswab = 1;
+                                       ext2fs_dirent_swab_in2(fs,
+                       ((char *)dirent) + EXT2_DIR_ENTRY_HEADER_LEN,
+                       max_block_size - offset - EXT2_DIR_ENTRY_HEADER_LEN, 0);
+                               }
 #endif
-                                       dir_modified++;
-                                       continue;
-                               } else
-                                       goto abort_free_dict;
+                               salvage_directory(fs, dirent, prev, &offset,
+                                                 max_block_size,
+                                                 hash_in_dirent);
+#ifdef WORDS_BIGENDIAN
+                               if (need_reswab) {
+                                       unsigned int len;
+
+                                       (void) ext2fs_get_rec_len(fs, dirent,
+                                                                 &len);
+                                       len += offset;
+                                       if (max_block_size > len)
+                                               ext2fs_dirent_swab_in2(fs,
+                       ((char *)dirent) + len, max_block_size - len, 0);
+                               }
+#endif
+                               dir_modified++;
+                               continue;
                        }
                } else {
                        if (dot_state == 0) {
@@ -1446,7 +1457,8 @@ skip_checksum:
                    (dirent->inode > fs->super->s_inodes_count) ||
                    (dirent->inode == fs->super->s_usr_quota_inum) ||
                    (dirent->inode == fs->super->s_grp_quota_inum) ||
-                   (dirent->inode == fs->super->s_prj_quota_inum)) {
+                   (dirent->inode == fs->super->s_prj_quota_inum) ||
+                   (dirent->inode == fs->super->s_orphan_file_inum)) {
                        problem = PR_2_BAD_INO;
                } else if (ctx->inode_bb_map &&
                           (ext2fs_test_inode_bitmap2(ctx->inode_bb_map,
@@ -1512,7 +1524,7 @@ skip_checksum:
                                             dirent->inode)) {
                        if (e2fsck_process_bad_inode(ctx, ino,
                                                     dirent->inode,
-                                                    buf + fs->blocksize)) {
+                                                    cd->buf + fs->blocksize)) {
                                dirent->inode = 0;
                                dir_modified++;
                                goto next;
@@ -1573,7 +1585,8 @@ skip_checksum:
                 */
                if (!(ctx->flags & E2F_FLAG_RESTART_LATER) &&
                    !(ext2fs_test_inode_bitmap2(ctx->inode_used_map,
-                                               dirent->inode)))
+                                               dirent->inode))
+                       )
                        problem = PR_2_UNUSED_INODE;
 
                if (problem) {
@@ -1841,17 +1854,26 @@ static int deallocate_inode_block(ext2_filsys fs,
 }
 
 /*
- * This function deallocates an inode
+ * This function reverts various counters and bitmaps incremented in
+ * pass1 for the inode, blocks, and quotas before it was decided the
+ * inode was corrupt and needed to be cleared.  This avoids the need
+ * to run e2fsck a second time (or have it restart itself) to repair
+ * these counters.
+ *
+ * It does not modify any on-disk state, so even if the inode is bad
+ * it _should_ reset in-memory state to before the inode was first
+ * processed.
  */
 static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
 {
        ext2_filsys fs = ctx->fs;
-       struct ext2_inode       inode;
+       struct ext2_inode_large inode;
        struct problem_context  pctx;
        __u32                   count;
        struct del_block        del_block;
 
-       e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
+       e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode),
+                              sizeof(inode), "deallocate_inode");
        clear_problem_context(&pctx);
        pctx.ino = ino;
 
@@ -1861,29 +1883,29 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
        e2fsck_read_bitmaps(ctx);
        ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
 
-       if (ext2fs_file_acl_block(fs, &inode) &&
+       if (ext2fs_file_acl_block(fs, EXT2_INODE(&inode)) &&
            ext2fs_has_feature_xattr(fs->super)) {
                pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
-                               ext2fs_file_acl_block(fs, &inode),
+                               ext2fs_file_acl_block(fs, EXT2_INODE(&inode)),
                                block_buf, -1, &count, ino);
                if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
                        pctx.errcode = 0;
                        count = 1;
                }
                if (pctx.errcode) {
-                       pctx.blk = ext2fs_file_acl_block(fs, &inode);
+                       pctx.blk = ext2fs_file_acl_block(fs, EXT2_INODE(&inode));
                        fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
                        ctx->flags |= E2F_FLAG_ABORT;
                        return;
                }
                if (count == 0) {
                        ext2fs_block_alloc_stats2(fs,
-                                 ext2fs_file_acl_block(fs, &inode), -1);
+                                 ext2fs_file_acl_block(fs, EXT2_INODE(&inode)), -1);
                }
-               ext2fs_file_acl_block_set(fs, &inode, 0);
+               ext2fs_file_acl_block_set(fs, EXT2_INODE(&inode), 0);
        }
 
-       if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
+       if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(&inode)))
                goto clear_inode;
 
        /* Inline data inodes don't have blocks to iterate */
@@ -1908,10 +1930,22 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
                ctx->flags |= E2F_FLAG_ABORT;
                return;
        }
+
+       if ((ino != quota_type2inum(PRJQUOTA, fs->super)) &&
+           (ino != fs->super->s_orphan_file_inum) &&
+           (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
+           !(inode.i_flags & EXT4_EA_INODE_FL)) {
+               if (del_block.num > 0)
+                       quota_data_sub(ctx->qctx, &inode, ino,
+                                      del_block.num * EXT2_CLUSTER_SIZE(fs->super));
+               quota_data_inodes(ctx->qctx, (struct ext2_inode_large *)&inode,
+                                 ino, -1);
+       }
+
 clear_inode:
        /* Inode may have changed by block_iterate, so reread it */
-       e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
-       e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode");
+       e2fsck_read_inode(ctx, ino, EXT2_INODE(&inode), "deallocate_inode");
+       e2fsck_clear_inode(ctx, ino, EXT2_INODE(&inode), 0, "deallocate_inode");
 }
 
 /*