summaryrefslogtreecommitdiffstats
path: root/e2fsck/pass2.c
diff options
context:
space:
mode:
Diffstat (limited to 'e2fsck/pass2.c')
-rw-r--r--e2fsck/pass2.c168
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");
}
/*