diff options
Diffstat (limited to 'e2fsck/pass2.c')
-rw-r--r-- | e2fsck/pass2.c | 168 |
1 files changed, 108 insertions, 60 deletions
diff --git a/e2fsck/pass2.c b/e2fsck/pass2.c index 410edd1..036c002 100644 --- a/e2fsck/pass2.c +++ b/e2fsck/pass2.c @@ -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) { @@ -1490,6 +1501,21 @@ skip_checksum: problem = PR_2_NULL_NAME; } + /* + * Check if inode was tracked as EA inode and has actual + * references from xattrs. In that case dir entry is likely + * bogus and we want to clear it. The case of EA inode without + * references from xattrs will be handled in pass 4. + */ + if (!problem && ctx->ea_inode_refs) { + ea_value_t refs; + + ea_refcount_fetch(ctx->ea_inode_refs, dirent->inode, + &refs); + if (refs && refs != EA_INODE_NO_REFS) + problem = PR_2_EA_INODE_DIR_LINK; + } + if (problem) { if (fix_problem(ctx, problem, &cd->pctx)) { dirent->inode = 0; @@ -1513,7 +1539,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; @@ -1574,7 +1600,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) { @@ -1842,17 +1869,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; @@ -1862,29 +1898,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 */ @@ -1909,10 +1945,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"); } /* |