e2fsprogs/e2fsck/pass2.c

1925 lines
52 KiB
C
Raw Permalink Normal View History

1997-04-26 17:21:57 +04:00
/*
* pass2.c --- check directory structure
*
1997-04-29 20:15:03 +04:00
* Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o
*
* %Begin-Header%
* This file may be redistributed under the terms of the GNU Public
* License.
* %End-Header%
*
1997-04-26 17:21:57 +04:00
* Pass 2 of e2fsck iterates through all active directory inodes, and
* applies to following tests to each directory entry in the directory
* blocks in the inodes:
*
* - The length of the directory entry (rec_len) should be at
* least 8 bytes, and no more than the remaining space
* left in the directory block.
* - The length of the name in the directory entry (name_len)
* should be less than (rec_len - 8).
1997-04-26 17:21:57 +04:00
* - The inode number in the directory entry should be within
* legal bounds.
* - The inode number should refer to a in-use inode.
* - The first entry should be '.', and its inode should be
* the inode of the directory.
* - The second entry should be '..'.
*
* To minimize disk seek time, the directory blocks are processed in
* sorted order of block numbers.
*
* Pass 2 also collects the following information:
* - The inode numbers of the subdirectories for each directory.
*
* Pass 2 relies on the following information from previous passes:
* - The directory information collected in pass 1.
* - The inode_used_map bitmap
* - The inode_bad_map bitmap
* - The inode_dir_map bitmap
*
* Pass 2 frees the following data structures
* - The inode_bad_map bitmap
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
* - The inode_reg_map bitmap
1997-04-26 17:21:57 +04:00
*/
2003-12-28 15:04:35 +03:00
#define _GNU_SOURCE 1 /* get strnlen() */
#include "config.h"
2003-07-06 08:36:48 +04:00
#include <string.h>
1997-04-26 17:21:57 +04:00
#include "e2fsck.h"
1997-04-29 20:15:03 +04:00
#include "problem.h"
#include "support/dict.h"
1997-04-26 17:21:57 +04:00
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
#ifdef NO_INLINE_FUNCS
#define _INLINE_
#else
#define _INLINE_ inline
#endif
/* #define DX_DEBUG */
1997-04-26 17:21:57 +04:00
/*
* Keeps track of how many times an inode is referenced.
*/
static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
e2fsck: read-ahead metadata during passes 1, 2, and 4 e2fsck pass1 is modified to use the block group data prefetch function to try to fetch the inode tables into the pagecache before it is needed. We iterate through the blockgroups until we have enough inode tables that need reading such that we can issue readahead; then we sit and wait until the last inode table block read of the last group to start fetching the next bunch. pass2 is modified to use the dirblock prefetching function to prefetch the list of directory blocks that are assembled in pass1. We use the "iterate a subset of a dblist" and avoid copying the dblist. Directory blocks are fetched incrementally as we walk through the directory block list. In previous iterations of this patch we would free the directory blocks after processing, but the performance hit to e2fsck itself wasn't worth it. Furthermore, it is anticipated that most users will then mount the FS and start using the directories, so they may as well remain in the page cache. pass4 is modified to prefetch the block and inode bitmaps in anticipation of pass 5, because pass4 is entirely CPU bound. In general, these mechanisms can decrease fsck time by 10-40%, if the host system has sufficient memory and the storage system can provide a lot of IOPs. Pretty much any storage system capable of handling multiple IOs in-flight at any time will see a fairly large performance boost. (Single-issue USB mass storage disks seem to suffer badly.) By default, the readahead buffer size will be set to the size of a block group's inode table (which is 2MiB for a regular ext4 FS). The -E readahead_kb= option can be given to specify the amount of memory to use for readahead or zero to disable it entirely; or an option can be given in e2fsck.conf. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-04-21 05:27:19 +03:00
static int check_dir_block2(ext2_filsys fs,
struct ext2_db_entry2 *dir_blocks_info,
void *priv_data);
1997-04-26 17:21:57 +04:00
static int check_dir_block(ext2_filsys fs,
struct ext2_db_entry2 *dir_blocks_info,
void *priv_data);
static int allocate_dir_block(e2fsck_t ctx,
struct ext2_db_entry2 *dir_blocks_info,
1997-04-29 20:15:03 +04:00
char *buf, struct problem_context *pctx);
static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
static int htree_depth(struct dx_dir_info *dx_dir,
struct dx_dirblock_info *dx_db);
static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b);
1997-04-26 17:21:57 +04:00
1997-04-29 20:15:03 +04:00
struct check_dir_struct {
char *buf;
struct problem_context pctx;
int count, max;
e2fsck_t ctx;
e2fsck: read-ahead metadata during passes 1, 2, and 4 e2fsck pass1 is modified to use the block group data prefetch function to try to fetch the inode tables into the pagecache before it is needed. We iterate through the blockgroups until we have enough inode tables that need reading such that we can issue readahead; then we sit and wait until the last inode table block read of the last group to start fetching the next bunch. pass2 is modified to use the dirblock prefetching function to prefetch the list of directory blocks that are assembled in pass1. We use the "iterate a subset of a dblist" and avoid copying the dblist. Directory blocks are fetched incrementally as we walk through the directory block list. In previous iterations of this patch we would free the directory blocks after processing, but the performance hit to e2fsck itself wasn't worth it. Furthermore, it is anticipated that most users will then mount the FS and start using the directories, so they may as well remain in the page cache. pass4 is modified to prefetch the block and inode bitmaps in anticipation of pass 5, because pass4 is entirely CPU bound. In general, these mechanisms can decrease fsck time by 10-40%, if the host system has sufficient memory and the storage system can provide a lot of IOPs. Pretty much any storage system capable of handling multiple IOs in-flight at any time will see a fairly large performance boost. (Single-issue USB mass storage disks seem to suffer badly.) By default, the readahead buffer size will be set to the size of a block group's inode table (which is 2MiB for a regular ext4 FS). The -E readahead_kb= option can be given to specify the amount of memory to use for readahead or zero to disable it entirely; or an option can be given in e2fsck.conf. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-04-21 05:27:19 +03:00
unsigned long long list_offset;
unsigned long long ra_entries;
unsigned long long next_ra_off;
};
1997-04-29 20:15:03 +04:00
void e2fsck_pass2(e2fsck_t ctx)
1997-04-26 17:21:57 +04:00
{
struct ext2_super_block *sb = ctx->fs->super;
struct problem_context pctx;
ext2_filsys fs = ctx->fs;
char *buf;
#ifdef RESOURCE_TRACK
1997-04-26 17:21:57 +04:00
struct resource_track rtrack;
#endif
1997-04-29 20:15:03 +04:00
struct check_dir_struct cd;
struct dx_dir_info *dx_dir;
struct dx_dirblock_info *dx_db, *dx_parent;
2003-12-07 09:28:50 +03:00
int b;
int i, depth;
problem_t code;
int bad_dir;
e2fsck: read-ahead metadata during passes 1, 2, and 4 e2fsck pass1 is modified to use the block group data prefetch function to try to fetch the inode tables into the pagecache before it is needed. We iterate through the blockgroups until we have enough inode tables that need reading such that we can issue readahead; then we sit and wait until the last inode table block read of the last group to start fetching the next bunch. pass2 is modified to use the dirblock prefetching function to prefetch the list of directory blocks that are assembled in pass1. We use the "iterate a subset of a dblist" and avoid copying the dblist. Directory blocks are fetched incrementally as we walk through the directory block list. In previous iterations of this patch we would free the directory blocks after processing, but the performance hit to e2fsck itself wasn't worth it. Furthermore, it is anticipated that most users will then mount the FS and start using the directories, so they may as well remain in the page cache. pass4 is modified to prefetch the block and inode bitmaps in anticipation of pass 5, because pass4 is entirely CPU bound. In general, these mechanisms can decrease fsck time by 10-40%, if the host system has sufficient memory and the storage system can provide a lot of IOPs. Pretty much any storage system capable of handling multiple IOs in-flight at any time will see a fairly large performance boost. (Single-issue USB mass storage disks seem to suffer badly.) By default, the readahead buffer size will be set to the size of a block group's inode table (which is 2MiB for a regular ext4 FS). The -E readahead_kb= option can be given to specify the amount of memory to use for readahead or zero to disable it entirely; or an option can be given in e2fsck.conf. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-04-21 05:27:19 +03:00
int (*check_dir_func)(ext2_filsys fs,
struct ext2_db_entry2 *dir_blocks_info,
void *priv_data);
init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&cd.pctx);
1997-04-26 17:21:57 +04:00
#ifdef MTRACE
mtrace_print("Pass 2");
#endif
if (!(ctx->options & E2F_OPT_PREEN))
fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
cd.pctx.errcode = e2fsck_setup_icount(ctx, "inode_count",
EXT2_ICOUNT_OPT_INCREMENT,
ctx->inode_link_info, &ctx->inode_count);
if (cd.pctx.errcode) {
fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
ctx->flags |= E2F_FLAG_ABORT;
return;
1997-04-29 20:15:03 +04:00
}
buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
"directory scan buffer");
1997-04-26 17:21:57 +04:00
1997-04-29 20:15:03 +04:00
/*
* Set up the parent pointer for the root directory, if
* present. (If the root directory is not present, we will
* create it in pass 3.)
*/
(void) e2fsck_dir_info_set_parent(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO);
1997-04-29 20:15:03 +04:00
cd.buf = buf;
cd.ctx = ctx;
cd.count = 1;
cd.max = ext2fs_dblist_count2(fs->dblist);
e2fsck: read-ahead metadata during passes 1, 2, and 4 e2fsck pass1 is modified to use the block group data prefetch function to try to fetch the inode tables into the pagecache before it is needed. We iterate through the blockgroups until we have enough inode tables that need reading such that we can issue readahead; then we sit and wait until the last inode table block read of the last group to start fetching the next bunch. pass2 is modified to use the dirblock prefetching function to prefetch the list of directory blocks that are assembled in pass1. We use the "iterate a subset of a dblist" and avoid copying the dblist. Directory blocks are fetched incrementally as we walk through the directory block list. In previous iterations of this patch we would free the directory blocks after processing, but the performance hit to e2fsck itself wasn't worth it. Furthermore, it is anticipated that most users will then mount the FS and start using the directories, so they may as well remain in the page cache. pass4 is modified to prefetch the block and inode bitmaps in anticipation of pass 5, because pass4 is entirely CPU bound. In general, these mechanisms can decrease fsck time by 10-40%, if the host system has sufficient memory and the storage system can provide a lot of IOPs. Pretty much any storage system capable of handling multiple IOs in-flight at any time will see a fairly large performance boost. (Single-issue USB mass storage disks seem to suffer badly.) By default, the readahead buffer size will be set to the size of a block group's inode table (which is 2MiB for a regular ext4 FS). The -E readahead_kb= option can be given to specify the amount of memory to use for readahead or zero to disable it entirely; or an option can be given in e2fsck.conf. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-04-21 05:27:19 +03:00
cd.list_offset = 0;
cd.ra_entries = ctx->readahead_kb * 1024 / ctx->fs->blocksize;
cd.next_ra_off = 0;
if (ctx->progress)
(void) (ctx->progress)(ctx, 2, 0, cd.max);
if (ext2fs_has_feature_dir_index(fs->super))
ext2fs_dblist_sort2(fs->dblist, special_dir_block_cmp);
e2fsck: read-ahead metadata during passes 1, 2, and 4 e2fsck pass1 is modified to use the block group data prefetch function to try to fetch the inode tables into the pagecache before it is needed. We iterate through the blockgroups until we have enough inode tables that need reading such that we can issue readahead; then we sit and wait until the last inode table block read of the last group to start fetching the next bunch. pass2 is modified to use the dirblock prefetching function to prefetch the list of directory blocks that are assembled in pass1. We use the "iterate a subset of a dblist" and avoid copying the dblist. Directory blocks are fetched incrementally as we walk through the directory block list. In previous iterations of this patch we would free the directory blocks after processing, but the performance hit to e2fsck itself wasn't worth it. Furthermore, it is anticipated that most users will then mount the FS and start using the directories, so they may as well remain in the page cache. pass4 is modified to prefetch the block and inode bitmaps in anticipation of pass 5, because pass4 is entirely CPU bound. In general, these mechanisms can decrease fsck time by 10-40%, if the host system has sufficient memory and the storage system can provide a lot of IOPs. Pretty much any storage system capable of handling multiple IOs in-flight at any time will see a fairly large performance boost. (Single-issue USB mass storage disks seem to suffer badly.) By default, the readahead buffer size will be set to the size of a block group's inode table (which is 2MiB for a regular ext4 FS). The -E readahead_kb= option can be given to specify the amount of memory to use for readahead or zero to disable it entirely; or an option can be given in e2fsck.conf. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-04-21 05:27:19 +03:00
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_RESTART_LATER) {
ctx->flags |= E2F_FLAG_RESTART;
e2fsck: skip quota update when interrupted There is a bug in how e2fsck handles being interrupted by CTRL-C. If CTRL-C is pressed to kill e2fsck rather than e.g. kill -9, then the interrupt handler sets E2F_FLAG_CANCEL in the context but doesn't actually kill the process. Instead, e2fsck_pass1() checks this flag before processing the next inode. If a filesystem is running in fix mode (e2fsck -fy) is interrupted, and the quota feature is enabled, then the quota file will still be written to disk even though the inode scan was not complete and the quota information is totally inaccurate. Even worse, if the Pass 1 inode and block scan was not finished, then the in-memory block bitmaps (which are used for block allocation during e2fsck) are also invalid, so any blocks allocated to the quota files may corrupt other files if those blocks were actually used. e2fsck 1.42.13.wc3 (28-Aug-2015) Pass 1: Checking inodes, blocks, and sizes ^C[QUOTA WARNING] Usage inconsistent for ID 0: actual (6455296, 168) != expected (8568832, 231) [QUOTA WARNING] Usage inconsistent for ID 695: actual (614932320256, 63981) != expected (2102405386240, 176432) Update quota info for quota type 0? yes [QUOTA WARNING] Usage inconsistent for ID 0: actual (6455296, 168) != expected (8568832, 231) [QUOTA WARNING] Usage inconsistent for ID 538: actual (614932320256, 63981) != expected (2102405386240, 176432) Update quota info for quota type 1? yes myth-OST0001: e2fsck canceled. myth-OST0001: ***** FILE SYSTEM WAS MODIFIED ***** There may be a desire to flush out modified inodes and such that have been repaired, so that restarting an interrupted e2fsck will make progress, but the quota file update is plain wrong unless at least pass1 has finished, and the journal recreation is also dangerous if the block bitmaps have not been fully updated. Signed-off-by: Andreas Dilger <andreas.dilger@intel.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-11-14 04:10:27 +03:00
ctx->flags &= ~E2F_FLAG_RESTART_LATER;
}
e2fsck: skip quota update when interrupted There is a bug in how e2fsck handles being interrupted by CTRL-C. If CTRL-C is pressed to kill e2fsck rather than e.g. kill -9, then the interrupt handler sets E2F_FLAG_CANCEL in the context but doesn't actually kill the process. Instead, e2fsck_pass1() checks this flag before processing the next inode. If a filesystem is running in fix mode (e2fsck -fy) is interrupted, and the quota feature is enabled, then the quota file will still be written to disk even though the inode scan was not complete and the quota information is totally inaccurate. Even worse, if the Pass 1 inode and block scan was not finished, then the in-memory block bitmaps (which are used for block allocation during e2fsck) are also invalid, so any blocks allocated to the quota files may corrupt other files if those blocks were actually used. e2fsck 1.42.13.wc3 (28-Aug-2015) Pass 1: Checking inodes, blocks, and sizes ^C[QUOTA WARNING] Usage inconsistent for ID 0: actual (6455296, 168) != expected (8568832, 231) [QUOTA WARNING] Usage inconsistent for ID 695: actual (614932320256, 63981) != expected (2102405386240, 176432) Update quota info for quota type 0? yes [QUOTA WARNING] Usage inconsistent for ID 0: actual (6455296, 168) != expected (8568832, 231) [QUOTA WARNING] Usage inconsistent for ID 538: actual (614932320256, 63981) != expected (2102405386240, 176432) Update quota info for quota type 1? yes myth-OST0001: e2fsck canceled. myth-OST0001: ***** FILE SYSTEM WAS MODIFIED ***** There may be a desire to flush out modified inodes and such that have been repaired, so that restarting an interrupted e2fsck will make progress, but the quota file update is plain wrong unless at least pass1 has finished, and the journal recreation is also dangerous if the block bitmaps have not been fully updated. Signed-off-by: Andreas Dilger <andreas.dilger@intel.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-11-14 04:10:27 +03:00
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;
return;
}
for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return;
if (e2fsck_dir_will_be_rehashed(ctx, dx_dir->ino) ||
dx_dir->numblocks == 0)
continue;
clear_problem_context(&pctx);
bad_dir = 0;
pctx.dir = dx_dir->ino;
dx_db = dx_dir->dx_block;
if (dx_db->flags & DX_FLAG_REFERENCED)
dx_db->flags |= DX_FLAG_DUP_REF;
else
dx_db->flags |= DX_FLAG_REFERENCED;
/*
* Find all of the first and last leaf blocks, and
* update their parent's min and max hash values
*/
for (b=0, dx_db = dx_dir->dx_block;
b < dx_dir->numblocks;
b++, dx_db++) {
if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
!(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
continue;
dx_parent = &dx_dir->dx_block[dx_db->parent];
/*
* XXX Make sure dx_parent->min_hash > dx_db->min_hash
*/
if (dx_db->flags & DX_FLAG_FIRST)
dx_parent->min_hash = dx_db->min_hash;
/*
* XXX Make sure dx_parent->max_hash < dx_db->max_hash
*/
if (dx_db->flags & DX_FLAG_LAST)
dx_parent->max_hash = dx_db->max_hash;
}
for (b=0, dx_db = dx_dir->dx_block;
b < dx_dir->numblocks;
b++, dx_db++) {
pctx.blkcount = b;
pctx.group = dx_db->parent;
code = 0;
if (!(dx_db->flags & DX_FLAG_FIRST) &&
(dx_db->min_hash < dx_db->node_min_hash)) {
pctx.blk = dx_db->min_hash;
pctx.blk2 = dx_db->node_min_hash;
code = PR_2_HTREE_MIN_HASH;
fix_problem(ctx, code, &pctx);
bad_dir++;
}
if (dx_db->type == DX_DIRBLOCK_LEAF) {
depth = htree_depth(dx_dir, dx_db);
if (depth != dx_dir->depth) {
pctx.num = dx_dir->depth;
code = PR_2_HTREE_BAD_DEPTH;
fix_problem(ctx, code, &pctx);
bad_dir++;
}
}
/*
* This test doesn't apply for the root block
* at block #0
*/
if (b &&
(dx_db->max_hash > dx_db->node_max_hash)) {
pctx.blk = dx_db->max_hash;
pctx.blk2 = dx_db->node_max_hash;
code = PR_2_HTREE_MAX_HASH;
fix_problem(ctx, code, &pctx);
bad_dir++;
}
if (!(dx_db->flags & DX_FLAG_REFERENCED)) {
code = PR_2_HTREE_NOTREF;
fix_problem(ctx, code, &pctx);
bad_dir++;
} else if (dx_db->flags & DX_FLAG_DUP_REF) {
code = PR_2_HTREE_DUPREF;
fix_problem(ctx, code, &pctx);
bad_dir++;
}
}
if (bad_dir && fix_problem(ctx, PR_2_HTREE_CLEAR, &pctx)) {
clear_htree(ctx, dx_dir->ino);
dx_dir->numblocks = 0;
}
}
e2fsck_free_dx_dir_info(ctx);
ext2fs_free_mem(&buf);
1997-04-29 20:15:03 +04:00
ext2fs_free_dblist(fs->dblist);
if (ctx->inode_bad_map) {
ext2fs_free_inode_bitmap(ctx->inode_bad_map);
ctx->inode_bad_map = 0;
1997-04-26 17:21:57 +04:00
}
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
if (ctx->inode_reg_map) {
ext2fs_free_inode_bitmap(ctx->inode_reg_map);
ctx->inode_reg_map = 0;
}
if (ctx->encrypted_dirs) {
ext2fs_u32_list_free(ctx->encrypted_dirs);
ctx->encrypted_dirs = 0;
}
clear_problem_context(&pctx);
if (ctx->large_files) {
if (!ext2fs_has_feature_large_file(sb) &&
fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
ext2fs_set_feature_large_file(sb);
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
ext2fs_mark_super_dirty(fs);
}
if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
fix_problem(ctx, PR_1_FS_REV_LEVEL, &pctx)) {
ext2fs_update_dynamic_rev(fs);
ext2fs_mark_super_dirty(fs);
}
}
print_resource_track(ctx, _("Pass 2"), &rtrack, fs->io);
1997-04-26 17:21:57 +04:00
}
#define MAX_DEPTH 32000
static int htree_depth(struct dx_dir_info *dx_dir,
struct dx_dirblock_info *dx_db)
{
int depth = 0;
while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
dx_db = &dx_dir->dx_block[dx_db->parent];
depth++;
}
return depth;
}
static int dict_de_cmp(const void *a, const void *b)
{
2003-04-19 21:48:27 +04:00
const struct ext2_dir_entry *de_a, *de_b;
int a_len, b_len;
2003-04-19 21:48:27 +04:00
de_a = (const struct ext2_dir_entry *) a;
a_len = ext2fs_dirent_name_len(de_a);
2003-04-19 21:48:27 +04:00
de_b = (const struct ext2_dir_entry *) b;
b_len = ext2fs_dirent_name_len(de_b);
if (a_len != b_len)
return (a_len - b_len);
return memcmp(de_a->name, de_b->name, a_len);
}
/*
* This is special sort function that makes sure that directory blocks
* with a dirblock of zero are sorted to the beginning of the list.
* This guarantees that the root node of the htree directories are
* processed first, so we know what hash version to use.
*/
static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b)
{
const struct ext2_db_entry2 *db_a =
(const struct ext2_db_entry2 *) a;
const struct ext2_db_entry2 *db_b =
(const struct ext2_db_entry2 *) b;
if (db_a->blockcnt && !db_b->blockcnt)
return 1;
if (!db_a->blockcnt && db_b->blockcnt)
return -1;
if (db_a->blk != db_b->blk)
return (int) (db_a->blk - db_b->blk);
if (db_a->ino != db_b->ino)
return (int) (db_a->ino - db_b->ino);
return (int) (db_a->blockcnt - db_b->blockcnt);
}
1997-04-26 17:21:57 +04:00
/*
* Make sure the first entry in the directory is '.', and that the
* directory entry is sane.
*/
static int check_dot(e2fsck_t ctx,
1997-04-26 17:21:57 +04:00
struct ext2_dir_entry *dirent,
ext2_ino_t ino, struct problem_context *pctx)
1997-04-26 17:21:57 +04:00
{
struct ext2_dir_entry *nextdir;
unsigned int rec_len, new_len;
int status = 0;
int created = 0;
problem_t problem = 0;
1997-04-29 20:15:03 +04:00
if (!dirent->inode)
problem = PR_2_MISSING_DOT;
else if ((ext2fs_dirent_name_len(dirent) != 1) ||
1997-04-29 20:15:03 +04:00
(dirent->name[0] != '.'))
problem = PR_2_1ST_NOT_DOT;
else if (dirent->name[1] != '\0')
problem = PR_2_DOT_NULL_TERM;
(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
1997-04-29 20:15:03 +04:00
if (problem) {
if (fix_problem(ctx, problem, pctx)) {
if (rec_len < 12)
rec_len = dirent->rec_len = 12;
1997-04-26 17:21:57 +04:00
dirent->inode = ino;
ext2fs_dirent_set_name_len(dirent, 1);
ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
1997-04-26 17:21:57 +04:00
dirent->name[0] = '.';
1997-04-29 20:15:03 +04:00
dirent->name[1] = '\0';
1997-04-26 17:21:57 +04:00
status = 1;
created = 1;
}
}
if (dirent->inode != ino) {
if (fix_problem(ctx, PR_2_BAD_INODE_DOT, pctx)) {
1997-04-26 17:21:57 +04:00
dirent->inode = ino;
status = 1;
1997-04-29 20:15:03 +04:00
}
1997-04-26 17:21:57 +04:00
}
if (rec_len > 12) {
new_len = rec_len - 12;
1997-04-26 17:21:57 +04:00
if (new_len > 12) {
if (created ||
fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
1997-04-26 17:21:57 +04:00
nextdir = (struct ext2_dir_entry *)
((char *) dirent + 12);
dirent->rec_len = 12;
(void) ext2fs_set_rec_len(ctx->fs, new_len,
nextdir);
1997-04-26 17:21:57 +04:00
nextdir->inode = 0;
ext2fs_dirent_set_name_len(nextdir, 0);
ext2fs_dirent_set_file_type(nextdir,
EXT2_FT_UNKNOWN);
1997-04-26 17:21:57 +04:00
status = 1;
}
}
}
return status;
}
/*
* Make sure the second entry in the directory is '..', and that the
* directory entry is sane. We do not check the inode number of '..'
* here; this gets done in pass 3.
*/
static int check_dotdot(e2fsck_t ctx,
1997-04-26 17:21:57 +04:00
struct ext2_dir_entry *dirent,
ext2_ino_t ino, struct problem_context *pctx)
1997-04-26 17:21:57 +04:00
{
problem_t problem = 0;
unsigned int rec_len;
1997-04-29 20:15:03 +04:00
if (!dirent->inode)
problem = PR_2_MISSING_DOT_DOT;
else if ((ext2fs_dirent_name_len(dirent) != 2) ||
1997-04-29 20:15:03 +04:00
(dirent->name[0] != '.') ||
(dirent->name[1] != '.'))
problem = PR_2_2ND_NOT_DOT_DOT;
else if (dirent->name[2] != '\0')
problem = PR_2_DOT_DOT_NULL_TERM;
(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
1997-04-29 20:15:03 +04:00
if (problem) {
if (fix_problem(ctx, problem, pctx)) {
if (rec_len < 12)
1997-04-29 20:15:03 +04:00
dirent->rec_len = 12;
1997-04-26 17:21:57 +04:00
/*
* Note: we don't have the parent inode just
* yet, so we will fill it in with the root
* inode. This will get fixed in pass 3.
*/
dirent->inode = EXT2_ROOT_INO;
ext2fs_dirent_set_name_len(dirent, 2);
ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
1997-04-26 17:21:57 +04:00
dirent->name[0] = '.';
dirent->name[1] = '.';
1997-04-29 20:15:03 +04:00
dirent->name[2] = '\0';
1997-04-26 17:21:57 +04:00
return 1;
}
1997-04-26 17:21:57 +04:00
return 0;
}
if (e2fsck_dir_info_set_dotdot(ctx, ino, dirent->inode)) {
fix_problem(ctx, PR_2_NO_DIRINFO, pctx);
return -1;
}
1997-04-26 17:21:57 +04:00
return 0;
}
/*
* Check to make sure a directory entry doesn't contain any illegal
* characters.
*/
static int check_name(e2fsck_t ctx,
1997-04-26 17:21:57 +04:00
struct ext2_dir_entry *dirent,
2003-12-07 09:28:50 +03:00
struct problem_context *pctx)
1997-04-26 17:21:57 +04:00
{
int i;
int fixup = -1;
int ret = 0;
for ( i = 0; i < ext2fs_dirent_name_len(dirent); i++) {
if (dirent->name[i] != '/' && dirent->name[i] != '\0')
continue;
if (fixup < 0)
fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
if (fixup == 0)
return 0;
dirent->name[i] = '.';
ret = 1;
1997-04-26 17:21:57 +04:00
}
return ret;
}
static int encrypted_check_name(e2fsck_t ctx,
struct ext2_dir_entry *dirent,
struct problem_context *pctx)
{
if (ext2fs_dirent_name_len(dirent) < EXT4_CRYPTO_BLOCK_SIZE) {
if (fix_problem(ctx, PR_2_BAD_ENCRYPTED_NAME, pctx)) {
dirent->inode = 0;
return 1;
}
ext2fs_unmark_valid(ctx->fs);
}
return 0;
}
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
/*
* Check the directory filetype (if present)
*/
static _INLINE_ int check_filetype(e2fsck_t ctx,
2003-12-07 09:28:50 +03:00
struct ext2_dir_entry *dirent,
ext2_ino_t dir_ino EXT2FS_ATTR((unused)),
struct problem_context *pctx)
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
{
int filetype = ext2fs_dirent_file_type(dirent);
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
int should_be = EXT2_FT_UNKNOWN;
struct ext2_inode inode;
if (!ext2fs_has_feature_filetype(ctx->fs->super)) {
if (filetype == 0 ||
!fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
return 0;
ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
return 1;
}
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode)) {
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
should_be = EXT2_FT_DIR;
} else if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map,
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
dirent->inode)) {
should_be = EXT2_FT_REG_FILE;
} else if (ctx->inode_bad_map &&
ext2fs_test_inode_bitmap2(ctx->inode_bad_map,
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
dirent->inode))
should_be = 0;
else {
e2fsck_read_inode(ctx, dirent->inode, &inode,
"check_filetype");
should_be = ext2_file_type(inode.i_mode);
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
}
if (filetype == should_be)
return 0;
pctx->num = should_be;
if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
pctx) == 0)
return 0;
ext2fs_dirent_set_file_type(dirent, should_be);
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
return 1;
}
static void parse_int_node(ext2_filsys fs,
struct ext2_db_entry2 *db,
struct check_dir_struct *cd,
struct dx_dir_info *dx_dir,
char *block_buf, int failed_csum)
{
struct ext2_dx_root_info *root;
struct ext2_dx_entry *ent;
struct ext2_dx_countlimit *limit;
struct dx_dirblock_info *dx_db;
int i, expect_limit, count;
blk_t blk;
ext2_dirhash_t min_hash = 0xffffffff;
ext2_dirhash_t max_hash = 0;
ext2_dirhash_t hash = 0, prev_hash;
int csum_size = 0;
if (db->blockcnt == 0) {
root = (struct ext2_dx_root_info *) (block_buf + 24);
#ifdef DX_DEBUG
printf("Root node dump:\n");
printf("\t Reserved zero: %u\n", root->reserved_zero);
printf("\t Hash Version: %d\n", root->hash_version);
printf("\t Info length: %d\n", root->info_length);
printf("\t Indirect levels: %d\n", root->indirect_levels);
printf("\t Flags: %d\n", root->unused_flags);
#endif
ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
if (failed_csum &&
(e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
fix_problem(cd->ctx, PR_2_HTREE_ROOT_CSUM_INVALID,
&cd->pctx)))
goto clear_and_exit;
} else {
ent = (struct ext2_dx_entry *) (block_buf+8);
if (failed_csum &&
(e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
fix_problem(cd->ctx, PR_2_HTREE_NODE_CSUM_INVALID,
&cd->pctx)))
goto clear_and_exit;
}
limit = (struct ext2_dx_countlimit *) ent;
#ifdef DX_DEBUG
printf("Number of entries (count): %d\n",
ext2fs_le16_to_cpu(limit->count));
printf("Number of entries (limit): %d\n",
ext2fs_le16_to_cpu(limit->limit));
#endif
count = ext2fs_le16_to_cpu(limit->count);
if (ext2fs_has_feature_metadata_csum(fs->super))
csum_size = sizeof(struct ext2_dx_tail);
expect_limit = (fs->blocksize -
(csum_size + ((char *) ent - block_buf))) /
sizeof(struct ext2_dx_entry);
if (ext2fs_le16_to_cpu(limit->limit) != expect_limit) {
cd->pctx.num = ext2fs_le16_to_cpu(limit->limit);
if (fix_problem(cd->ctx, PR_2_HTREE_BAD_LIMIT, &cd->pctx))
goto clear_and_exit;
}
if (count > expect_limit) {
cd->pctx.num = count;
if (fix_problem(cd->ctx, PR_2_HTREE_BAD_COUNT, &cd->pctx))
goto clear_and_exit;
count = expect_limit;
}
for (i=0; i < count; i++) {
prev_hash = hash;
hash = i ? (ext2fs_le32_to_cpu(ent[i].hash) & ~1) : 0;
#ifdef DX_DEBUG
printf("Entry #%d: Hash 0x%08x, block %u\n", i,
hash, ext2fs_le32_to_cpu(ent[i].block));
#endif
blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
/* Check to make sure the block is valid */
if (blk >= (blk_t) dx_dir->numblocks) {
cd->pctx.blk = blk;
if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
&cd->pctx))
goto clear_and_exit;
continue;
}
if (hash < prev_hash &&
fix_problem(cd->ctx, PR_2_HTREE_HASH_ORDER, &cd->pctx))
goto clear_and_exit;
dx_db = &dx_dir->dx_block[blk];
if (dx_db->flags & DX_FLAG_REFERENCED) {
dx_db->flags |= DX_FLAG_DUP_REF;
} else {
dx_db->flags |= DX_FLAG_REFERENCED;
dx_db->parent = db->blockcnt;
}
if (hash < min_hash)
min_hash = hash;
if (hash > max_hash)
max_hash = hash;
dx_db->node_min_hash = hash;
if ((i+1) < count)
dx_db->node_max_hash =
ext2fs_le32_to_cpu(ent[i+1].hash) & ~1;
else {
dx_db->node_max_hash = 0xfffffffe;
dx_db->flags |= DX_FLAG_LAST;
}
if (i == 0)
dx_db->flags |= DX_FLAG_FIRST;
}
#ifdef DX_DEBUG
printf("Blockcnt = %d, min hash 0x%08x, max hash 0x%08x\n",
db->blockcnt, min_hash, max_hash);
#endif
dx_db = &dx_dir->dx_block[db->blockcnt];
dx_db->min_hash = min_hash;
dx_db->max_hash = max_hash;
return;
clear_and_exit:
clear_htree(cd->ctx, cd->pctx.ino);
dx_dir->numblocks = 0;
e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino);
}
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
/*
* Given a busted directory, try to salvage it somehow.
*
*/
static void salvage_directory(ext2_filsys fs,
struct ext2_dir_entry *dirent,
struct ext2_dir_entry *prev,
unsigned int *offset,
unsigned int block_len)
{
char *cp = (char *) dirent;
int left;
unsigned int rec_len, prev_rec_len;
unsigned int name_len;
/*
* If the space left for the entry is too small to be an entry,
* we can't access dirent's fields, so plumb in the values needed
* so that the previous entry absorbs this one.
*/
if (block_len - *offset < EXT2_DIR_ENTRY_HEADER_LEN) {
name_len = 0;
rec_len = block_len - *offset;
} else {
name_len = ext2fs_dirent_name_len(dirent);
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
}
left = block_len - *offset - rec_len;
/*
* Special case of directory entry of size 8: copy what's left
* of the directory block up to cover up the invalid hole.
*/
if ((left >= 12) && (rec_len == EXT2_DIR_ENTRY_HEADER_LEN)) {
memmove(cp, cp+EXT2_DIR_ENTRY_HEADER_LEN, left);
memset(cp + left, 0, EXT2_DIR_ENTRY_HEADER_LEN);
return;
}
/*
* If the directory entry overruns the end of the directory
* block, and the name is small enough to fit, then adjust the
* record length.
*/
if ((left < 0) &&
((int) rec_len + left > EXT2_DIR_ENTRY_HEADER_LEN) &&
((int) name_len + EXT2_DIR_ENTRY_HEADER_LEN <= (int) rec_len + left) &&
dirent->inode <= fs->super->s_inodes_count &&
strnlen(dirent->name, name_len) == name_len) {
(void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent);
return;
}
/*
* If the record length of the directory entry is a multiple
* of four, and not too big, such that it is valid, let the
* previous directory entry absorb the invalid one.
*/
if (prev && rec_len && (rec_len % 4) == 0 &&
(*offset + rec_len <= block_len)) {
(void) ext2fs_get_rec_len(fs, prev, &prev_rec_len);
prev_rec_len += rec_len;
(void) ext2fs_set_rec_len(fs, prev_rec_len, prev);
*offset += rec_len;
return;
}
/*
* Default salvage method --- kill all of the directory
* entries for the rest of the block. We will either try to
* absorb it into the previous directory entry, or create a
* new empty directory entry the rest of the directory block.
*/
if (prev) {
(void) ext2fs_get_rec_len(fs, prev, &prev_rec_len);
prev_rec_len += block_len - *offset;
(void) ext2fs_set_rec_len(fs, prev_rec_len, prev);
*offset = fs->blocksize;
} else {
rec_len = block_len - *offset;
(void) ext2fs_set_rec_len(fs, rec_len, dirent);
ext2fs_dirent_set_name_len(dirent, 0);
ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
dirent->inode = 0;
}
}
#define NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len))
static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf)
{
struct ext2_dir_entry *d;
void *top;
struct ext2_dir_entry_tail *t;
d = dirbuf;
top = EXT2_DIRENT_TAIL(dirbuf, fs->blocksize);
while (d->rec_len && !(d->rec_len & 0x3) && NEXT_DIRENT(d) <= top)
d = NEXT_DIRENT(d);
if (d != top) {
unsigned int min_size = EXT2_DIR_REC_LEN(
ext2fs_dirent_name_len(dirbuf));
if (min_size > (char *)top - (char *)d)
return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
d->rec_len = (char *)top - (char *)d;
}
t = (struct ext2_dir_entry_tail *)top;
if (t->det_reserved_zero1 ||
t->det_rec_len != sizeof(struct ext2_dir_entry_tail) ||
t->det_reserved_name_len != EXT2_DIR_NAME_LEN_CSUM)
ext2fs_initialize_dirent_tail(fs, t);
return 0;
}
#undef NEXT_DIRENT
static errcode_t fix_inline_dir_size(e2fsck_t ctx, ext2_ino_t ino,
size_t *inline_data_size,
struct problem_context *pctx,
char *buf)
{
ext2_filsys fs = ctx->fs;
struct ext2_inode inode;
size_t new_size, old_size;
errcode_t retval;
old_size = *inline_data_size;
/*
* If there's not enough bytes to start the "second" dir block
* (in the EA space) then truncate everything to the first block.
*/
if (old_size > EXT4_MIN_INLINE_DATA_SIZE &&
old_size < EXT4_MIN_INLINE_DATA_SIZE +
EXT2_DIR_REC_LEN(1)) {
old_size = EXT4_MIN_INLINE_DATA_SIZE;
new_size = old_size;
} else
/* Increase to the next four-byte boundary for salvaging */
new_size = old_size + (4 - (old_size & 3));
memset(buf + old_size, 0, new_size - old_size);
retval = ext2fs_inline_data_set(fs, ino, 0, buf, new_size);
if (retval == EXT2_ET_INLINE_DATA_NO_SPACE) {
/* Or we can't, so truncate. */
new_size -= 4;
retval = ext2fs_inline_data_set(fs, ino, 0, buf, new_size);
if (retval) {
if (fix_problem(ctx, PR_2_FIX_INLINE_DIR_FAILED,
pctx)) {
new_size = 0;
goto write_inode;
}
goto err;
}
} else if (retval) {
if (fix_problem(ctx, PR_2_FIX_INLINE_DIR_FAILED,
pctx)) {
new_size = 0;
goto write_inode;
}
goto err;
}
write_inode:
retval = ext2fs_read_inode(fs, ino, &inode);
if (retval)
goto err;
retval = ext2fs_inode_size_set(fs, &inode, new_size);
if (retval)
goto err;
if (new_size == 0)
inode.i_flags &= ~EXT4_INLINE_DATA_FL;
retval = ext2fs_write_inode(fs, ino, &inode);
if (retval)
goto err;
*inline_data_size = new_size;
err:
return retval;
}
e2fsck: read-ahead metadata during passes 1, 2, and 4 e2fsck pass1 is modified to use the block group data prefetch function to try to fetch the inode tables into the pagecache before it is needed. We iterate through the blockgroups until we have enough inode tables that need reading such that we can issue readahead; then we sit and wait until the last inode table block read of the last group to start fetching the next bunch. pass2 is modified to use the dirblock prefetching function to prefetch the list of directory blocks that are assembled in pass1. We use the "iterate a subset of a dblist" and avoid copying the dblist. Directory blocks are fetched incrementally as we walk through the directory block list. In previous iterations of this patch we would free the directory blocks after processing, but the performance hit to e2fsck itself wasn't worth it. Furthermore, it is anticipated that most users will then mount the FS and start using the directories, so they may as well remain in the page cache. pass4 is modified to prefetch the block and inode bitmaps in anticipation of pass 5, because pass4 is entirely CPU bound. In general, these mechanisms can decrease fsck time by 10-40%, if the host system has sufficient memory and the storage system can provide a lot of IOPs. Pretty much any storage system capable of handling multiple IOs in-flight at any time will see a fairly large performance boost. (Single-issue USB mass storage disks seem to suffer badly.) By default, the readahead buffer size will be set to the size of a block group's inode table (which is 2MiB for a regular ext4 FS). The -E readahead_kb= option can be given to specify the amount of memory to use for readahead or zero to disable it entirely; or an option can be given in e2fsck.conf. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-04-21 05:27:19 +03:00
static int check_dir_block2(ext2_filsys fs,
struct ext2_db_entry2 *db,
void *priv_data)
{
int err;
struct check_dir_struct *cd = priv_data;
if (cd->ra_entries && cd->list_offset >= cd->next_ra_off) {
err = e2fsck_readahead_dblist(fs,
E2FSCK_RA_DBLIST_IGNORE_BLOCKCNT,
fs->dblist,
cd->list_offset + cd->ra_entries / 8,
cd->ra_entries);
if (err)
cd->ra_entries = 0;
cd->next_ra_off = cd->list_offset + (cd->ra_entries * 7 / 8);
}
err = check_dir_block(fs, db, priv_data);
cd->list_offset++;
return err;
}
1997-04-26 17:21:57 +04:00
static int check_dir_block(ext2_filsys fs,
struct ext2_db_entry2 *db,
void *priv_data)
1997-04-26 17:21:57 +04:00
{
struct dx_dir_info *dx_dir;
struct dx_dirblock_info *dx_db = 0;
struct ext2_dir_entry *dirent, *prev, dot, dotdot;
ext2_dirhash_t hash;
2003-12-07 09:28:50 +03:00
unsigned int offset = 0;
1997-04-26 17:21:57 +04:00
int dir_modified = 0;
1997-04-29 20:15:03 +04:00
int dot_state;
unsigned int rec_len;
blk64_t block_nr = db->blk;
ext2_ino_t ino = db->ino;
ext2_ino_t subdir_parent;
1997-04-29 20:15:03 +04:00
__u16 links;
struct check_dir_struct *cd;
char *buf, *ibuf;
e2fsck_t ctx;
problem_t problem;
struct ext2_dx_root_info *root;
struct ext2_dx_countlimit *limit;
static dict_t de_dict;
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;
ibuf = buf = cd->buf;
ctx = cd->ctx;
e2fsck: skip quota update when interrupted There is a bug in how e2fsck handles being interrupted by CTRL-C. If CTRL-C is pressed to kill e2fsck rather than e.g. kill -9, then the interrupt handler sets E2F_FLAG_CANCEL in the context but doesn't actually kill the process. Instead, e2fsck_pass1() checks this flag before processing the next inode. If a filesystem is running in fix mode (e2fsck -fy) is interrupted, and the quota feature is enabled, then the quota file will still be written to disk even though the inode scan was not complete and the quota information is totally inaccurate. Even worse, if the Pass 1 inode and block scan was not finished, then the in-memory block bitmaps (which are used for block allocation during e2fsck) are also invalid, so any blocks allocated to the quota files may corrupt other files if those blocks were actually used. e2fsck 1.42.13.wc3 (28-Aug-2015) Pass 1: Checking inodes, blocks, and sizes ^C[QUOTA WARNING] Usage inconsistent for ID 0: actual (6455296, 168) != expected (8568832, 231) [QUOTA WARNING] Usage inconsistent for ID 695: actual (614932320256, 63981) != expected (2102405386240, 176432) Update quota info for quota type 0? yes [QUOTA WARNING] Usage inconsistent for ID 0: actual (6455296, 168) != expected (8568832, 231) [QUOTA WARNING] Usage inconsistent for ID 538: actual (614932320256, 63981) != expected (2102405386240, 176432) Update quota info for quota type 1? yes myth-OST0001: e2fsck canceled. myth-OST0001: ***** FILE SYSTEM WAS MODIFIED ***** There may be a desire to flush out modified inodes and such that have been repaired, so that restarting an interrupted e2fsck will make progress, but the quota file update is plain wrong unless at least pass1 has finished, and the journal recreation is also dangerous if the block bitmaps have not been fully updated. Signed-off-by: Andreas Dilger <andreas.dilger@intel.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2015-11-14 04:10:27 +03:00
if (ctx->flags & E2F_FLAG_RUN_RETURN)
return DIRENT_ABORT;
if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
return DIRENT_ABORT;
if (ext2fs_has_feature_metadata_csum(fs->super)) {
dx_csum_size = sizeof(struct ext2_dx_tail);
de_csum_size = sizeof(struct ext2_dir_entry_tail);
}
if (ext2fs_has_feature_filetype(fs->super))
filetype = EXT2_FT_DIR << 8;
1997-04-26 17:21:57 +04:00
/*
* Make sure the inode is still in use (could have been
1997-04-26 17:21:57 +04:00
* deleted in the duplicate/bad blocks pass.
*/
if (!(ext2fs_test_inode_bitmap2(ctx->inode_used_map, ino)))
1997-04-26 17:21:57 +04:00
return 0;
1997-04-26 17:58:21 +04:00
1997-04-29 20:15:03 +04:00
cd->pctx.ino = ino;
cd->pctx.blk = block_nr;
cd->pctx.blkcount = db->blockcnt;
cd->pctx.ino2 = 0;
cd->pctx.dirent = 0;
cd->pctx.num = 0;
if (ext2fs_has_feature_inline_data(fs->super)) {
errcode_t ec;
ec = ext2fs_inline_data_size(fs, ino, &inline_data_size);
if (ec && ec != EXT2_ET_NO_INLINE_DATA)
return DIRENT_ABORT;
}
if (db->blk == 0 && !inline_data_size) {
if (allocate_dir_block(ctx, db, buf, &cd->pctx))
1997-04-26 17:58:21 +04:00
return 0;
block_nr = db->blk;
}
1997-04-26 17:21:57 +04:00
if (db->blockcnt)
dot_state = 2;
else
dot_state = 0;
if (ctx->dirs_to_hash &&
ext2fs_u32_list_test(ctx->dirs_to_hash, ino))
dups_found++;
1997-04-26 17:21:57 +04:00
#if 0
1997-04-26 17:34:30 +04:00
printf("In process_dir_block block %lu, #%d, inode %lu\n", block_nr,
1997-04-26 17:21:57 +04:00
db->blockcnt, ino);
#endif
ehandler_operation(_("reading directory block"));
if (inline_data_size) {
memset(buf, 0, fs->blocksize - inline_data_size);
cd->pctx.errcode = ext2fs_inline_data_get(fs, ino, 0, buf, 0);
if (cd->pctx.errcode)
goto inline_read_fail;
#ifdef WORDS_BIGENDIAN
if (db->blockcnt)
goto skip_first_read_swab;
*((__u32 *)buf) = ext2fs_le32_to_cpu(*((__u32 *)buf));
cd->pctx.errcode = ext2fs_dirent_swab_in2(fs,
buf + EXT4_INLINE_DATA_DOTDOT_SIZE,
EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE,
0);
if (cd->pctx.errcode)
goto inline_read_fail;
skip_first_read_swab:
if (inline_data_size <= EXT4_MIN_INLINE_DATA_SIZE ||
!db->blockcnt)
goto inline_read_fail;
cd->pctx.errcode = ext2fs_dirent_swab_in2(fs,
buf + EXT4_MIN_INLINE_DATA_SIZE,
inline_data_size - EXT4_MIN_INLINE_DATA_SIZE,
0);
#endif
} else
cd->pctx.errcode = ext2fs_read_dir_block4(fs, block_nr,
buf, 0, ino);
inline_read_fail:
pctx.ino = ino;
pctx.num = inline_data_size;
if (((inline_data_size & 3) ||
(inline_data_size > EXT4_MIN_INLINE_DATA_SIZE &&
inline_data_size < EXT4_MIN_INLINE_DATA_SIZE +
EXT2_DIR_REC_LEN(1))) &&
fix_problem(ctx, PR_2_BAD_INLINE_DIR_SIZE, &pctx)) {
errcode_t err = fix_inline_dir_size(ctx, ino,
&inline_data_size, &pctx,
buf);
if (err)
return DIRENT_ABORT;
}
ehandler_operation(0);
if (cd->pctx.errcode == EXT2_ET_DIR_CORRUPTED)
cd->pctx.errcode = 0; /* We'll handle this ourselves */
else if (cd->pctx.errcode == EXT2_ET_DIR_CSUM_INVALID) {
cd->pctx.errcode = 0; /* We'll handle this ourselves */
failed_csum = 1;
}
if (cd->pctx.errcode) {
char *buf2;
if (!fix_problem(ctx, PR_2_READ_DIRBLOCK, &cd->pctx)) {
ctx->flags |= E2F_FLAG_ABORT;
return DIRENT_ABORT;
}
ext2fs_new_dir_block(fs, db->blockcnt == 0 ? ino : 0,
EXT2_ROOT_INO, &buf2);
memcpy(buf, buf2, fs->blocksize);
ext2fs_free_mem(&buf2);
1997-04-26 17:21:57 +04:00
}
dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
if (dx_dir && dx_dir->numblocks) {
if (db->blockcnt >= dx_dir->numblocks) {
pctx.dir = ino;
if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK,
&pctx)) {
clear_htree(ctx, ino);
dx_dir->numblocks = 0;
dx_db = 0;
goto out_htree;
}
fatal_error(ctx, _("Can not continue."));
}
dx_db = &dx_dir->dx_block[db->blockcnt];
dx_db->type = DX_DIRBLOCK_LEAF;
dx_db->phys = block_nr;
dx_db->min_hash = ~0;
dx_db->max_hash = 0;
dirent = (struct ext2_dir_entry *) buf;
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
limit = (struct ext2_dx_countlimit *) (buf+8);
if (db->blockcnt == 0) {
root = (struct ext2_dx_root_info *) (buf + 24);
dx_db->type = DX_DIRBLOCK_ROOT;
dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
if ((root->reserved_zero ||
root->info_length < 8 ||
root->indirect_levels > 1) &&
fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
clear_htree(ctx, ino);
dx_dir->numblocks = 0;
dx_db = 0;
}
dx_dir->hashversion = root->hash_version;
if ((dx_dir->hashversion <= EXT2_HASH_TEA) &&
(fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
dx_dir->hashversion += 3;
dx_dir->depth = root->indirect_levels + 1;
} else if ((dirent->inode == 0) &&
(rec_len == fs->blocksize) &&
(ext2fs_dirent_name_len(dirent) == 0) &&
(ext2fs_le16_to_cpu(limit->limit) ==
((fs->blocksize - (8 + dx_csum_size)) /
sizeof(struct ext2_dx_entry))))
dx_db->type = DX_DIRBLOCK_NODE;
is_leaf = (dx_db->type == DX_DIRBLOCK_LEAF);
}
out_htree:
1997-04-26 17:21:57 +04:00
/* Leaf node with no space for csum? Rebuild dirs in pass 3A. */
if (is_leaf && !inline_data_size && failed_csum &&
!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
de_csum_size = 0;
if (e2fsck_dir_will_be_rehashed(ctx, ino)) {
failed_csum = 0;
goto skip_checksum;
}
if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
&cd->pctx))
goto skip_checksum;
e2fsck_rehash_dir_later(ctx, ino);
failed_csum = 0;
goto skip_checksum;
}
/* htree nodes don't use fake dirents to store checksums */
if (!is_leaf)
de_csum_size = 0;
skip_checksum:
if (inline_data_size) {
if (db->blockcnt) {
buf += EXT4_MIN_INLINE_DATA_SIZE;
max_block_size = inline_data_size - EXT4_MIN_INLINE_DATA_SIZE;
/* Zero-length second block, just exit */
if (max_block_size == 0)
return 0;
} else {
max_block_size = EXT4_MIN_INLINE_DATA_SIZE;
}
} else
max_block_size = fs->blocksize - de_csum_size;
if (ctx->encrypted_dirs)
encrypted = ext2fs_u32_list_test(ctx->encrypted_dirs, ino);
dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
prev = 0;
1997-04-26 17:21:57 +04:00
do {
dgrp_t group;
ext2_ino_t first_unused_inode;
unsigned int name_len;
problem = 0;
if (!inline_data_size || dot_state > 1) {
dirent = (struct ext2_dir_entry *) (buf + offset);
/*
* If there's not even space for the entry header,
* force salvaging this dir.
*/
if (max_block_size - offset < EXT2_DIR_ENTRY_HEADER_LEN)
rec_len = EXT2_DIR_REC_LEN(1);
else
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
cd->pctx.dirent = dirent;
cd->pctx.num = offset;
if ((offset + rec_len > max_block_size) ||
(rec_len < 12) ||
((rec_len % 4) != 0) ||
(((unsigned) ext2fs_dirent_name_len(dirent) + EXT2_DIR_ENTRY_HEADER_LEN) > rec_len)) {
if (fix_problem(ctx, PR_2_DIR_CORRUPTED,
&cd->pctx)) {
#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);
}
#endif
salvage_directory(fs, dirent, prev,
&offset,
max_block_size);
#ifdef WORDS_BIGENDIAN
if (need_reswab) {
(void) ext2fs_get_rec_len(fs,
dirent, &rec_len);
ext2fs_dirent_swab_in2(fs,
((char *)dirent) + offset + rec_len,
max_block_size - offset - rec_len,
0);
}
#endif
dir_modified++;
continue;
} else
goto abort_free_dict;
}
} else {
if (dot_state == 0) {
memset(&dot, 0, sizeof(dot));
dirent = &dot;
dirent->inode = ino;
dirent->rec_len = EXT2_DIR_REC_LEN(1);
dirent->name_len = 1 | filetype;
dirent->name[0] = '.';
} else if (dot_state == 1) {
memset(&dotdot, 0, sizeof(dotdot));
dirent = &dotdot;
dirent->inode =
((struct ext2_dir_entry *)buf)->inode;
dirent->rec_len = EXT2_DIR_REC_LEN(2);
dirent->name_len = 2 | filetype;
dirent->name[0] = '.';
dirent->name[1] = '.';
} else {
fatal_error(ctx, _("Can not continue."));
}
cd->pctx.dirent = dirent;
cd->pctx.num = offset;
1997-04-26 17:21:57 +04:00
}
1997-04-26 17:58:21 +04:00
if (dot_state == 0) {
if (check_dot(ctx, dirent, ino, &cd->pctx))
1997-04-26 17:21:57 +04:00
dir_modified++;
} else if (dot_state == 1) {
ret = check_dotdot(ctx, dirent, ino, &cd->pctx);
if (ret < 0)
goto abort_free_dict;
if (ret)
1997-04-26 17:21:57 +04:00
dir_modified++;
} else if (dirent->inode == ino) {
problem = PR_2_LINK_DOT;
if (fix_problem(ctx, PR_2_LINK_DOT, &cd->pctx)) {
1997-04-26 17:21:57 +04:00
dirent->inode = 0;
dir_modified++;
1997-04-29 20:15:03 +04:00
goto next;
1997-04-26 17:21:57 +04:00
}
}
if (!dirent->inode)
1997-04-26 17:21:57 +04:00
goto next;
1997-04-26 17:21:57 +04:00
/*
* Make sure the inode listed is a legal one.
*/
name_len = ext2fs_dirent_name_len(dirent);
1997-04-26 17:21:57 +04:00
if (((dirent->inode != EXT2_ROOT_INO) &&
1997-04-26 18:48:50 +04:00
(dirent->inode < EXT2_FIRST_INODE(fs->super))) ||
1997-04-26 17:21:57 +04:00
(dirent->inode > fs->super->s_inodes_count)) {
problem = PR_2_BAD_INO;
} else if (ctx->inode_bb_map &&
(ext2fs_test_inode_bitmap2(ctx->inode_bb_map,
dirent->inode))) {
/*
* If the inode is in a bad block, offer to
* clear it.
*/
problem = PR_2_BB_INODE;
} else if ((dot_state > 1) && (name_len == 1) &&
(dirent->name[0] == '.')) {
/*
* If there's a '.' entry in anything other
* than the first directory entry, it's a
* duplicate entry that should be removed.
*/
problem = PR_2_DUP_DOT;
} else if ((dot_state > 1) && (name_len == 2) &&
(dirent->name[0] == '.') &&
(dirent->name[1] == '.')) {
/*
* If there's a '..' entry in anything other
* than the second directory entry, it's a
* duplicate entry that should be removed.
*/
problem = PR_2_DUP_DOT_DOT;
} else if ((dot_state > 1) &&
(dirent->inode == EXT2_ROOT_INO)) {
/*
* Don't allow links to the root directory.
* We check this specially to make sure we
* catch this error case even if the root
* directory hasn't been created yet.
*/
problem = PR_2_LINK_ROOT;
} else if ((dot_state > 1) && (name_len == 0)) {
/*
* Don't allow zero-length directory names.
*/
problem = PR_2_NULL_NAME;
1997-04-29 20:15:03 +04:00
}
if (problem) {
if (fix_problem(ctx, problem, &cd->pctx)) {
1997-04-29 20:15:03 +04:00
dirent->inode = 0;
dir_modified++;
goto next;
} else {
ext2fs_unmark_valid(fs);
if (problem == PR_2_BAD_INO)
goto next;
1997-04-29 20:15:03 +04:00
}
1997-04-26 17:21:57 +04:00
}
/*
* If the inode was marked as having bad fields in
* pass1, process it and offer to fix/clear it.
* (We wait until now so that we can display the
* pathname to the user.)
*/
if (ctx->inode_bad_map &&
ext2fs_test_inode_bitmap2(ctx->inode_bad_map,
1997-04-26 17:21:57 +04:00
dirent->inode)) {
if (e2fsck_process_bad_inode(ctx, ino,
dirent->inode,
buf + fs->blocksize)) {
1997-04-26 17:21:57 +04:00
dirent->inode = 0;
dir_modified++;
goto next;
}
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return DIRENT_ABORT;
1997-04-26 17:21:57 +04:00
}
group = ext2fs_group_of_ino(fs, dirent->inode);
first_unused_inode = group * fs->super->s_inodes_per_group +
1 + fs->super->s_inodes_per_group -
ext2fs_bg_itable_unused(fs, group);
cd->pctx.group = group;
/*
* Check if the inode was missed out because
* _INODE_UNINIT flag was set or bg_itable_unused was
* incorrect. If so, clear the _INODE_UNINIT flag and
* restart e2fsck. In the future it would be nice if
* we could call a function in pass1.c that checks the
* newly visible inodes.
*/
if (ext2fs_bg_flags_test(fs, group, EXT2_BG_INODE_UNINIT)) {
pctx.num = dirent->inode;
if (fix_problem(ctx, PR_2_INOREF_BG_INO_UNINIT,
&cd->pctx)){
ext2fs_bg_flags_clear(fs, group,
EXT2_BG_INODE_UNINIT);
ext2fs_mark_super_dirty(fs);
ctx->flags |= E2F_FLAG_RESTART_LATER;
} else {
ext2fs_unmark_valid(fs);
if (problem == PR_2_BAD_INO)
goto next;
}
} else if (dirent->inode >= first_unused_inode) {
pctx.num = dirent->inode;
if (fix_problem(ctx, PR_2_INOREF_IN_UNUSED, &cd->pctx)){
ext2fs_bg_itable_unused_set(fs, group, 0);
ext2fs_mark_super_dirty(fs);
ctx->flags |= E2F_FLAG_RESTART_LATER;
} else {
ext2fs_unmark_valid(fs);
if (problem == PR_2_BAD_INO)
goto next;
}
}
/*
* Offer to clear unused inodes; if we are going to be
* restarting the scan due to bg_itable_unused being
* wrong, then don't clear any inodes to avoid zapping
* inodes that were skipped during pass1 due to an
* incorrect bg_itable_unused; we'll get any real
* problems after we restart.
*/
if (!(ctx->flags & E2F_FLAG_RESTART_LATER) &&
!(ext2fs_test_inode_bitmap2(ctx->inode_used_map,
dirent->inode)))
problem = PR_2_UNUSED_INODE;
if (problem) {
if (fix_problem(ctx, problem, &cd->pctx)) {
dirent->inode = 0;
dir_modified++;
goto next;
} else {
ext2fs_unmark_valid(fs);
if (problem == PR_2_BAD_INO)
goto next;
}
}
if (!encrypted && check_name(ctx, dirent, &cd->pctx))
dir_modified++;
if (encrypted && (dot_state) > 1 &&
encrypted_check_name(ctx, dirent, &cd->pctx)) {
dir_modified++;
goto next;
}
Many files: pass4.c (e2fsck_pass4): If an inode is set in the inode_imagic_map bitmap, don't check to see if it is disconnected from the inode tree (because it almost certainly will be). Free inode_imagic_map at the end of pass 4. pass2.c (check_dir_block, check_filetype): If the FILETYPE feature is set, check the directory entry's filetype information field, and fix/set it if necessary. (e2fsck_pass2): Free the inode_reg_map bitmap at the end of pass 2. pass1.c (e2fsck_pass1, alloc_imagic_map): Allocate and fill in information for inode_reg_map and inode_imagic_map, which indicates which inodes are regular files and AFS inodes, respectively. Since only the master superblock is written during a restart, force that superblock to be used after a restart; otherwise changes to the block group descriptors end up getting ignored. problem.c, problemP.h: If e2fsck is run -n, make def_yn variable be 0 for "no". Add support for a new flag, PR_NO_NOMSG, which supresses the problem message if e2fsck is run with the -n option. problem.c, problem.h (PR_2_SET_FILETYPE, PR_2_BAD_FILETYPE): Add new problem codes. message.c (expand_dirent_expression): Add support for %dt which prints the dirent type information. e2fsck.c (e2fsck_reset_context): Free new bitmaps (inode_reg_map and inode_imagic_map). e2fsck.h (e2fsck_t): Add new inode_reg_map and inode_magic_map to the context structure. ChangeLog, nt_io.c: nt_io.c: New file which supports I/O under Windows NT. ChangeLog, gen_uuid_nt.c: gen_uuid_nt.c: New file which creates a UUID under Windows NT. Many files: Add support for non-Unix compiles
1999-10-21 23:33:18 +04:00
if (check_filetype(ctx, dirent, ino, &cd->pctx))
dir_modified++;
if (dx_db) {
ext2fs_dirhash(dx_dir->hashversion, dirent->name,
ext2fs_dirent_name_len(dirent),
fs->super->s_hash_seed, &hash, 0);
if (hash < dx_db->min_hash)
dx_db->min_hash = hash;
if (hash > dx_db->max_hash)
dx_db->max_hash = hash;
}
1997-04-26 17:21:57 +04:00
/*
* If this is a directory, then mark its parent in its
* dir_info structure. If the parent field is already
* filled in, then this directory has more than one
* hard link. We assume the first link is correct,
* and ask the user if he/she wants to clear this one.
*/
if ((dot_state > 1) &&
(ext2fs_test_inode_bitmap2(ctx->inode_dir_map,
1997-04-26 17:21:57 +04:00
dirent->inode))) {
if (e2fsck_dir_info_get_parent(ctx, dirent->inode,
&subdir_parent)) {
cd->pctx.ino = dirent->inode;
fix_problem(ctx, PR_2_NO_DIRINFO, &cd->pctx);
goto abort_free_dict;
1997-04-26 17:21:57 +04:00
}
if (subdir_parent) {
cd->pctx.ino2 = subdir_parent;
if (fix_problem(ctx, PR_2_LINK_DIR,
1997-04-29 20:15:03 +04:00
&cd->pctx)) {
1997-04-26 17:21:57 +04:00
dirent->inode = 0;
dir_modified++;
goto next;
1997-04-29 20:15:03 +04:00
}
cd->pctx.ino2 = 0;
} else {
(void) e2fsck_dir_info_set_parent(ctx,
dirent->inode, ino);
}
1997-04-26 17:21:57 +04:00
}
if (dups_found) {
;
} else if (dict_lookup(&de_dict, dirent)) {
clear_problem_context(&pctx);
pctx.ino = ino;
pctx.dirent = dirent;
fix_problem(ctx, PR_2_REPORT_DUP_DIRENT, &pctx);
e2fsck_rehash_dir_later(ctx, ino);
dups_found++;
} else
dict_alloc_insert(&de_dict, dirent, dirent);
ext2fs_icount_increment(ctx->inode_count, dirent->inode,
&links);
1997-04-29 20:15:03 +04:00
if (links > 1)
ctx->fs_links_count++;
ctx->fs_total_count++;
1997-04-26 17:21:57 +04:00
next:
prev = dirent;
if (dir_modified)
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
if (!inline_data_size || dot_state > 1) {
offset += rec_len;
} else {
if (dot_state == 1) {
offset = 4;
/*
* If we get here, we're checking an inline
* directory and we've just checked a (fake)
* dotdot entry that we created on the stack.
* Therefore set 'prev' to NULL so that if we
* call salvage_directory on the next entry,
* it won't try to absorb the next entry into
* the on-stack dotdot entry.
*/
prev = NULL;
}
}
dot_state++;
} while (offset < max_block_size);
1997-04-26 17:21:57 +04:00
#if 0
printf("\n");
#endif
if (dx_db) {
#ifdef DX_DEBUG
printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n",
db->blockcnt, dx_db->type,
dx_db->min_hash, dx_db->max_hash);
#endif
cd->pctx.dir = cd->pctx.ino;
if ((dx_db->type == DX_DIRBLOCK_ROOT) ||
(dx_db->type == DX_DIRBLOCK_NODE))
parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
}
if (offset != max_block_size) {
cd->pctx.num = rec_len + offset - max_block_size;
if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
dirent->rec_len = cd->pctx.num;
dir_modified++;
}
1997-04-26 17:21:57 +04:00
}
if (dir_modified) {
e2fsck: correctly preserve fs flags when modifying ignore-csum-error flag When we need to modify the "ignore checksum error" behavior flag to get us past a library call, it's possible that the library call can result in other flag bits being changed. Therefore, it is not correct to restore unconditionally the previous flags value, since this will have unintended side effects on the other fs->flags; nor is it correct to assume that we can unconditionally set (or clear) the "ignore csum error" flag bit. Therefore, we must merge the previous value of the "ignore csum error" flag with the value of flags after the call. Note that we want to leave checksum verification on as much as possible because doing so exposes e2fsck bugs where two metadata blocks are "sharing" the same disk block, and attempting to fix one before relocating the other causes major filesystem damage. The damage is much more obvious when a previously checked piece of metadata suddenly fails in a subsequent pass. The modifications to the pass 2, 3, and 3A code are justified as follows: When e2fsck encounters a block of directory entries and cannot find the placeholder entry at the end that contains the checksum, it will try to insert the placeholder. If that fails, it will schedule the directory for a pass 3A reconstruction. Until that happens, we don't want directory block writing (pass 2), block iteration (pass 3), or block reading (pass 3A) to fail due to checksum errors, because failing to find the placeholder is itself a checksum verification error, which causes e2fsck to abort without fixing anything. The e2fsck call to ext2fs_read_bitmaps must never fail due to a checksum error because e2fsck subsequently (a) verifies the bitmaps itself; or (b) decides that they don't match what has been observed, and rewrites them. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-08-03 06:48:21 +04:00
int flags, will_rehash;
/* leaf block with no tail? Rehash dirs later. */
if (ext2fs_has_feature_metadata_csum(fs->super) &&
is_leaf &&
!inline_data_size &&
!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
if (insert_dirent_tail(fs, buf) == 0)
goto write_and_fix;
e2fsck_rehash_dir_later(ctx, ino);
}
write_and_fix:
e2fsck: correctly preserve fs flags when modifying ignore-csum-error flag When we need to modify the "ignore checksum error" behavior flag to get us past a library call, it's possible that the library call can result in other flag bits being changed. Therefore, it is not correct to restore unconditionally the previous flags value, since this will have unintended side effects on the other fs->flags; nor is it correct to assume that we can unconditionally set (or clear) the "ignore csum error" flag bit. Therefore, we must merge the previous value of the "ignore csum error" flag with the value of flags after the call. Note that we want to leave checksum verification on as much as possible because doing so exposes e2fsck bugs where two metadata blocks are "sharing" the same disk block, and attempting to fix one before relocating the other causes major filesystem damage. The damage is much more obvious when a previously checked piece of metadata suddenly fails in a subsequent pass. The modifications to the pass 2, 3, and 3A code are justified as follows: When e2fsck encounters a block of directory entries and cannot find the placeholder entry at the end that contains the checksum, it will try to insert the placeholder. If that fails, it will schedule the directory for a pass 3A reconstruction. Until that happens, we don't want directory block writing (pass 2), block iteration (pass 3), or block reading (pass 3A) to fail due to checksum errors, because failing to find the placeholder is itself a checksum verification error, which causes e2fsck to abort without fixing anything. The e2fsck call to ext2fs_read_bitmaps must never fail due to a checksum error because e2fsck subsequently (a) verifies the bitmaps itself; or (b) decides that they don't match what has been observed, and rewrites them. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-08-03 06:48:21 +04:00
will_rehash = e2fsck_dir_will_be_rehashed(ctx, ino);
if (will_rehash) {
flags = ctx->fs->flags;
ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
e2fsck: correctly preserve fs flags when modifying ignore-csum-error flag When we need to modify the "ignore checksum error" behavior flag to get us past a library call, it's possible that the library call can result in other flag bits being changed. Therefore, it is not correct to restore unconditionally the previous flags value, since this will have unintended side effects on the other fs->flags; nor is it correct to assume that we can unconditionally set (or clear) the "ignore csum error" flag bit. Therefore, we must merge the previous value of the "ignore csum error" flag with the value of flags after the call. Note that we want to leave checksum verification on as much as possible because doing so exposes e2fsck bugs where two metadata blocks are "sharing" the same disk block, and attempting to fix one before relocating the other causes major filesystem damage. The damage is much more obvious when a previously checked piece of metadata suddenly fails in a subsequent pass. The modifications to the pass 2, 3, and 3A code are justified as follows: When e2fsck encounters a block of directory entries and cannot find the placeholder entry at the end that contains the checksum, it will try to insert the placeholder. If that fails, it will schedule the directory for a pass 3A reconstruction. Until that happens, we don't want directory block writing (pass 2), block iteration (pass 3), or block reading (pass 3A) to fail due to checksum errors, because failing to find the placeholder is itself a checksum verification error, which causes e2fsck to abort without fixing anything. The e2fsck call to ext2fs_read_bitmaps must never fail due to a checksum error because e2fsck subsequently (a) verifies the bitmaps itself; or (b) decides that they don't match what has been observed, and rewrites them. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-08-03 06:48:21 +04:00
}
if (inline_data_size) {
buf = ibuf;
#ifdef WORDS_BIGENDIAN
if (db->blockcnt)
goto skip_first_write_swab;
*((__u32 *)buf) = ext2fs_le32_to_cpu(*((__u32 *)buf));
cd->pctx.errcode = ext2fs_dirent_swab_out2(fs,
buf + EXT4_INLINE_DATA_DOTDOT_SIZE,
EXT4_MIN_INLINE_DATA_SIZE -
EXT4_INLINE_DATA_DOTDOT_SIZE,
0);
if (cd->pctx.errcode)
goto skip_second_write_swab;
skip_first_write_swab:
if (inline_data_size <= EXT4_MIN_INLINE_DATA_SIZE ||
!db->blockcnt)
goto skip_second_write_swab;
cd->pctx.errcode = ext2fs_dirent_swab_out2(fs,
buf + EXT4_MIN_INLINE_DATA_SIZE,
inline_data_size -
EXT4_MIN_INLINE_DATA_SIZE,
0);
skip_second_write_swab:
if (cd->pctx.errcode &&
!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx))
goto abort_free_dict;
#endif
cd->pctx.errcode =
ext2fs_inline_data_set(fs, ino, 0, buf,
inline_data_size);
} else
cd->pctx.errcode = ext2fs_write_dir_block4(fs, block_nr,
buf, 0, ino);
e2fsck: correctly preserve fs flags when modifying ignore-csum-error flag When we need to modify the "ignore checksum error" behavior flag to get us past a library call, it's possible that the library call can result in other flag bits being changed. Therefore, it is not correct to restore unconditionally the previous flags value, since this will have unintended side effects on the other fs->flags; nor is it correct to assume that we can unconditionally set (or clear) the "ignore csum error" flag bit. Therefore, we must merge the previous value of the "ignore csum error" flag with the value of flags after the call. Note that we want to leave checksum verification on as much as possible because doing so exposes e2fsck bugs where two metadata blocks are "sharing" the same disk block, and attempting to fix one before relocating the other causes major filesystem damage. The damage is much more obvious when a previously checked piece of metadata suddenly fails in a subsequent pass. The modifications to the pass 2, 3, and 3A code are justified as follows: When e2fsck encounters a block of directory entries and cannot find the placeholder entry at the end that contains the checksum, it will try to insert the placeholder. If that fails, it will schedule the directory for a pass 3A reconstruction. Until that happens, we don't want directory block writing (pass 2), block iteration (pass 3), or block reading (pass 3A) to fail due to checksum errors, because failing to find the placeholder is itself a checksum verification error, which causes e2fsck to abort without fixing anything. The e2fsck call to ext2fs_read_bitmaps must never fail due to a checksum error because e2fsck subsequently (a) verifies the bitmaps itself; or (b) decides that they don't match what has been observed, and rewrites them. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
2014-08-03 06:48:21 +04:00
if (will_rehash)
ctx->fs->flags = (flags &
EXT2_FLAG_IGNORE_CSUM_ERRORS) |
(ctx->fs->flags &
~EXT2_FLAG_IGNORE_CSUM_ERRORS);
if (cd->pctx.errcode) {
if (!fix_problem(ctx, PR_2_WRITE_DIRBLOCK,
&cd->pctx))
goto abort_free_dict;
1997-04-26 17:21:57 +04:00
}
ext2fs_mark_changed(fs);
} else if (is_leaf && failed_csum && !dir_modified) {
/*
* If a leaf node that fails csum makes it this far without
* alteration, ask the user if the checksum should be fixed.
*/
if (fix_problem(ctx, PR_2_LEAF_NODE_ONLY_CSUM_INVALID,
&cd->pctx))
goto write_and_fix;
1997-04-26 17:21:57 +04:00
}
dict_free_nodes(&de_dict);
1997-04-26 17:21:57 +04:00
return 0;
abort_free_dict:
ctx->flags |= E2F_FLAG_ABORT;
dict_free_nodes(&de_dict);
return DIRENT_ABORT;
1997-04-26 17:21:57 +04:00
}
struct del_block {
e2fsck_t ctx;
e2_blkcnt_t num;
};
1997-04-26 17:21:57 +04:00
/*
* This function is called to deallocate a block, and is an interator
* functioned called by deallocate inode via ext2fs_iterate_block().
*/
static int deallocate_inode_block(ext2_filsys fs,
blk64_t *block_nr,
2003-12-07 09:28:50 +03:00
e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
blk64_t ref_block EXT2FS_ATTR((unused)),
2003-12-07 09:28:50 +03:00
int ref_offset EXT2FS_ATTR((unused)),
void *priv_data)
1997-04-26 17:21:57 +04:00
{
struct del_block *p = priv_data;
if (*block_nr == 0)
1997-04-26 17:21:57 +04:00
return 0;
if ((*block_nr < fs->super->s_first_data_block) ||
(*block_nr >= ext2fs_blocks_count(fs->super)))
return 0;
if ((*block_nr % EXT2FS_CLUSTER_RATIO(fs)) == 0)
ext2fs_block_alloc_stats2(fs, *block_nr, -1);
p->num++;
1997-04-26 17:21:57 +04:00
return 0;
}
1997-04-26 17:21:57 +04:00
/*
* This fuction deallocates an inode
*/
static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
1997-04-26 17:21:57 +04:00
{
ext2_filsys fs = ctx->fs;
1997-04-26 17:21:57 +04:00
struct ext2_inode inode;
struct problem_context pctx;
__u32 count;
struct del_block del_block;
e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
clear_problem_context(&pctx);
pctx.ino = ino;
1997-04-26 17:34:30 +04:00
1997-04-26 17:21:57 +04:00
/*
* Fix up the bitmaps...
*/
e2fsck_read_bitmaps(ctx);
ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
if (ext2fs_file_acl_block(fs, &inode) &&
ext2fs_has_feature_xattr(fs->super)) {
pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
ext2fs_file_acl_block(fs, &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);
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_set(fs, &inode, 0);
}
1997-04-26 17:21:57 +04:00
if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
goto clear_inode;
/* Inline data inodes don't have blocks to iterate */
if (inode.i_flags & EXT4_INLINE_DATA_FL)
goto clear_inode;
if (LINUX_S_ISREG(inode.i_mode) &&
ext2fs_needs_large_file_feature(EXT2_I_SIZE(&inode)))
ctx->large_files--;
del_block.ctx = ctx;
del_block.num = 0;
pctx.errcode = ext2fs_block_iterate3(fs, ino, 0, block_buf,
deallocate_inode_block,
&del_block);
if (pctx.errcode) {
fix_problem(ctx, PR_2_DEALLOC_INODE, &pctx);
ctx->flags |= E2F_FLAG_ABORT;
return;
}
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");
1997-04-26 17:21:57 +04:00
}
/*
* This fuction clears the htree flag on an inode
*/
static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
{
struct ext2_inode inode;
e2fsck_read_inode(ctx, ino, &inode, "clear_htree");
inode.i_flags = inode.i_flags & ~EXT2_INDEX_FL;
e2fsck_write_inode(ctx, ino, &inode, "clear_htree");
if (ctx->dirs_to_hash)
ext2fs_u32_list_add(ctx->dirs_to_hash, ino);
}
int e2fsck_process_bad_inode(e2fsck_t ctx, ext2_ino_t dir,
ext2_ino_t ino, char *buf)
1997-04-26 17:21:57 +04:00
{
ext2_filsys fs = ctx->fs;
1997-04-26 17:21:57 +04:00
struct ext2_inode inode;
int inode_modified = 0;
int not_fixed = 0;
1997-04-29 18:53:37 +04:00
unsigned char *frag, *fsize;
1997-04-29 20:15:03 +04:00
struct problem_context pctx;
problem_t problem = 0;
1997-04-26 17:21:57 +04:00
e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
1997-04-29 20:15:03 +04:00
clear_problem_context(&pctx);
pctx.ino = ino;
pctx.dir = dir;
pctx.inode = &inode;
if (ext2fs_file_acl_block(fs, &inode) &&
!ext2fs_has_feature_xattr(fs->super)) {
if (fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
ext2fs_file_acl_block_set(fs, &inode, 0);
inode_modified++;
} else
not_fixed++;
}
1997-04-26 17:58:21 +04:00
if (!LINUX_S_ISDIR(inode.i_mode) && !LINUX_S_ISREG(inode.i_mode) &&
!LINUX_S_ISCHR(inode.i_mode) && !LINUX_S_ISBLK(inode.i_mode) &&
!LINUX_S_ISLNK(inode.i_mode) && !LINUX_S_ISFIFO(inode.i_mode) &&
!(LINUX_S_ISSOCK(inode.i_mode)))
problem = PR_2_BAD_MODE;
else if (LINUX_S_ISCHR(inode.i_mode)
&& !e2fsck_pass1_check_device_inode(fs, &inode))
problem = PR_2_BAD_CHAR_DEV;
else if (LINUX_S_ISBLK(inode.i_mode)
&& !e2fsck_pass1_check_device_inode(fs, &inode))
problem = PR_2_BAD_BLOCK_DEV;
else if (LINUX_S_ISFIFO(inode.i_mode)
&& !e2fsck_pass1_check_device_inode(fs, &inode))
problem = PR_2_BAD_FIFO;
else if (LINUX_S_ISSOCK(inode.i_mode)
&& !e2fsck_pass1_check_device_inode(fs, &inode))
problem = PR_2_BAD_SOCKET;
else if (LINUX_S_ISLNK(inode.i_mode)
&& !e2fsck_pass1_check_symlink(fs, ino, &inode, buf)) {
problem = PR_2_INVALID_SYMLINK;
}
if (problem) {
if (fix_problem(ctx, problem, &pctx)) {
deallocate_inode(ctx, ino, 0);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
return 0;
return 1;
} else
not_fixed++;
problem = 0;
}
if (inode.i_faddr) {
if (fix_problem(ctx, PR_2_FADDR_ZERO, &pctx)) {
inode.i_faddr = 0;
inode_modified++;
} else
not_fixed++;
1997-04-26 17:21:57 +04:00
}
1997-04-29 18:53:37 +04:00
switch (fs->super->s_creator_os) {
case EXT2_OS_HURD:
frag = &inode.osd2.hurd2.h_i_frag;
fsize = &inode.osd2.hurd2.h_i_fsize;
break;
default:
frag = fsize = 0;
}
1997-04-29 20:15:03 +04:00
if (frag && *frag) {
pctx.num = *frag;
if (fix_problem(ctx, PR_2_FRAG_ZERO, &pctx)) {
1997-04-29 20:15:03 +04:00
*frag = 0;
inode_modified++;
} else
not_fixed++;
1997-04-29 20:15:03 +04:00
pctx.num = 0;
}
if (fsize && *fsize) {
pctx.num = *fsize;
if (fix_problem(ctx, PR_2_FSIZE_ZERO, &pctx)) {
1997-04-29 20:15:03 +04:00
*fsize = 0;
inode_modified++;
} else
not_fixed++;
1997-04-29 20:15:03 +04:00
pctx.num = 0;
}
if ((fs->super->s_creator_os == EXT2_OS_LINUX) &&
!ext2fs_has_feature_huge_file(fs->super) &&
(inode.osd2.linux2.l_i_blocks_hi != 0)) {
pctx.num = inode.osd2.linux2.l_i_blocks_hi;
if (fix_problem(ctx, PR_2_BLOCKS_HI_ZERO, &pctx)) {
inode.osd2.linux2.l_i_blocks_hi = 0;
inode_modified++;
}
}
if ((fs->super->s_creator_os == EXT2_OS_LINUX) &&
!ext2fs_has_feature_64bit(fs->super) &&
inode.osd2.linux2.l_i_file_acl_high != 0) {
pctx.num = inode.osd2.linux2.l_i_file_acl_high;
if (fix_problem(ctx, PR_2_I_FILE_ACL_HI_ZERO, &pctx)) {
inode.osd2.linux2.l_i_file_acl_high = 0;
inode_modified++;
} else
not_fixed++;
}
if (ext2fs_file_acl_block(fs, &inode) &&
((ext2fs_file_acl_block(fs, &inode) < fs->super->s_first_data_block) ||
(ext2fs_file_acl_block(fs, &inode) >= ext2fs_blocks_count(fs->super)))) {
if (fix_problem(ctx, PR_2_FILE_ACL_BAD, &pctx)) {
ext2fs_file_acl_block_set(fs, &inode, 0);
inode_modified++;
} else
not_fixed++;
}
1997-04-29 20:15:03 +04:00
if (inode.i_dir_acl &&
LINUX_S_ISDIR(inode.i_mode)) {
if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
inode.i_dir_acl = 0;
inode_modified++;
} else
not_fixed++;
1997-04-29 20:15:03 +04:00
}
1997-04-26 17:34:30 +04:00
if (inode_modified)
e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
if (!not_fixed && ctx->inode_bad_map)
ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino);
1997-04-26 17:21:57 +04:00
return 0;
}
1997-04-26 17:58:21 +04:00
/*
* allocate_dir_block --- this function allocates a new directory
* block for a particular inode; this is done if a directory has
* a "hole" in it, or if a directory has a illegal block number
* that was zeroed out and now needs to be replaced.
*/
static int allocate_dir_block(e2fsck_t ctx,
struct ext2_db_entry2 *db,
char *buf EXT2FS_ATTR((unused)),
2003-12-07 09:28:50 +03:00
struct problem_context *pctx)
1997-04-26 17:58:21 +04:00
{
ext2_filsys fs = ctx->fs;
blk64_t blk = 0;
1997-04-26 17:58:21 +04:00
char *block;
struct ext2_inode inode;
if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
1997-04-26 17:58:21 +04:00
return 1;
/*
* Read the inode and block bitmaps in; we'll be messing with
* them.
*/
e2fsck_read_bitmaps(ctx);
1997-04-26 17:58:21 +04:00
/*
* First, find a free block
*/
e2fsck_read_inode(ctx, db->ino, &inode, "allocate_dir_block");
pctx->errcode = ext2fs_map_cluster_block(fs, db->ino, &inode,
db->blockcnt, &blk);
if (pctx->errcode || blk == 0) {
blk = ext2fs_find_inode_goal(fs, db->ino, &inode, db->blockcnt);
pctx->errcode = ext2fs_new_block2(fs, blk,
ctx->block_found_map, &blk);
if (pctx->errcode) {
pctx->str = "ext2fs_new_block";
fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
return 1;
}
1997-04-26 17:58:21 +04:00
}
ext2fs_mark_block_bitmap2(ctx->block_found_map, blk);
ext2fs_mark_block_bitmap2(fs->block_map, blk);
1997-04-26 17:58:21 +04:00
ext2fs_mark_bb_dirty(fs);
/*
* Now let's create the actual data block for the inode
*/
if (db->blockcnt)
pctx->errcode = ext2fs_new_dir_block(fs, 0, 0, &block);
1997-04-26 17:58:21 +04:00
else
pctx->errcode = ext2fs_new_dir_block(fs, db->ino,
EXT2_ROOT_INO, &block);
1997-04-26 17:58:21 +04:00
if (pctx->errcode) {
pctx->str = "ext2fs_new_dir_block";
fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
1997-04-26 17:58:21 +04:00
return 1;
}
pctx->errcode = ext2fs_write_dir_block4(fs, blk, block, 0, db->ino);
ext2fs_free_mem(&block);
if (pctx->errcode) {
pctx->str = "ext2fs_write_dir_block";
fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
1997-04-26 17:58:21 +04:00
return 1;
}
/*
* Update the inode block count
*/
ext2fs_iblk_add_blocks(fs, &inode, 1);
if (EXT2_I_SIZE(&inode) < ((__u64) db->blockcnt+1) * fs->blocksize) {
pctx->errcode = ext2fs_inode_size_set(fs, &inode,
(db->blockcnt+1) * fs->blocksize);
if (pctx->errcode) {
pctx->str = "ext2fs_inode_size_set";
fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
return 1;
}
}
e2fsck_write_inode(ctx, db->ino, &inode, "allocate_dir_block");
1997-04-26 17:58:21 +04:00
/*
* Finally, update the block pointers for the inode
*/
db->blk = blk;
pctx->errcode = ext2fs_bmap2(fs, db->ino, &inode, 0, BMAP_SET,
db->blockcnt, 0, &blk);
if (pctx->errcode) {
pctx->str = "ext2fs_block_iterate";
fix_problem(ctx, PR_2_ALLOC_DIRBOCK, pctx);
1997-04-26 17:58:21 +04:00
return 1;
}
return 0;
}