* - A bitmap of which inodes have bad fields. (inode_bad_map)
* - A bitmap of which inodes are in bad blocks. (inode_bb_map)
* - A bitmap of which inodes are imagic inodes. (inode_imagic_map)
+ * - A bitmap of which inodes are casefolded. (inode_casefold_map)
* - A bitmap of which blocks are in use. (block_found_map)
* - A bitmap of which blocks are in use by two inodes (block_dup_map)
* - The data blocks of the directory inodes. (dir_map)
+ * - Ref counts for ea_inodes. (ea_inode_refs)
+ * - The encryption policy ID of each encrypted inode. (encrypted_files)
*
* Pass 1 is designed to stash away enough information so that the
* other passes should not need to read in the inode information
- * during the normal course of a filesystem check. (Althogh if an
+ * during the normal course of a filesystem check. (Although if an
* inconsistency is detected, other passes may need to read in an
* inode to fix it.)
*
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
+#include <assert.h>
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
#include "e2fsck.h"
#include <ext2fs/ext2_ext_attr.h>
+/* todo remove this finally */
+#include <ext2fs/ext2fsP.h>
+#include <e2p/e2p.h>
#include "problem.h"
#undef DEBUG
+struct ea_quota {
+ blk64_t blocks;
+ __u64 inodes;
+};
+
static int process_block(ext2_filsys fs, blk64_t *blocknr,
e2_blkcnt_t blockcnt, blk64_t ref_blk,
int ref_offset, void *priv_data);
e2_blkcnt_t blockcnt, blk64_t ref_blk,
int ref_offset, void *priv_data);
static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf);
+ char *block_buf,
+ const struct ea_quota *ea_ibody_quota);
static void mark_table_blocks(e2fsck_t ctx);
static void alloc_bb_map(e2fsck_t ctx);
static void alloc_imagic_map(e2fsck_t ctx);
static void mark_inode_bad(e2fsck_t ctx, ino_t ino);
-static void add_encrypted_dir(e2fsck_t ctx, ino_t ino);
+static void add_casefolded_dir(e2fsck_t ctx, ino_t ino);
static void handle_fs_bad_blocks(e2fsck_t ctx);
-static void process_inodes(e2fsck_t ctx, char *block_buf);
static EXT2_QSORT_TYPE process_inode_cmp(const void *a, const void *b);
static errcode_t scan_callback(ext2_filsys fs, ext2_inode_scan scan,
dgrp_t group, void * priv_data);
struct problem_context *pctx;
ext2fs_block_bitmap fs_meta_blocks;
e2fsck_t ctx;
- region_t region;
+ blk64_t next_lblock;
struct extent_tree_info eti;
};
struct process_inode_block {
ext2_ino_t ino;
+ struct ea_quota ea_ibody_quota;
struct ext2_inode_large inode;
};
struct scan_callback_struct {
- e2fsck_t ctx;
- char *block_buf;
+ e2fsck_t ctx;
+ char *block_buf;
+ struct process_inode_block *inodes_to_process;
+ int *process_inode_count;
};
-/*
- * For the inodes to process list.
- */
-static struct process_inode_block *inodes_to_process;
-static int process_inode_count;
+static void process_inodes(e2fsck_t ctx, char *block_buf,
+ struct process_inode_block *inodes_to_process,
+ int *process_inode_count);
static __u64 ext2_max_sizes[EXT2_MAX_BLOCK_LOG_SIZE -
EXT2_MIN_BLOCK_LOG_SIZE + 1];
/*
- * Free all memory allocated by pass1 in preparation for restarting
- * things.
- */
-static void unwind_pass1(ext2_filsys fs EXT2FS_ATTR((unused)))
-{
- ext2fs_free_mem(&inodes_to_process);
- inodes_to_process = 0;
-}
-
-/*
* Check to make sure a device inode is real. Returns 1 if the device
* checks out, 0 if not.
*
int i;
/*
- * If the index flag is set, then this is a bogus
+ * If the index or extents flag is set, then this is a bogus
* device/fifo/socket
*/
- if (inode->i_flags & EXT2_INDEX_FL)
+ if (inode->i_flags & (EXT2_INDEX_FL | EXT4_EXTENTS_FL))
return 0;
/*
int e2fsck_pass1_check_symlink(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode, char *buf)
{
+ unsigned int buflen;
unsigned int len;
- int i;
- blk64_t blocks;
- ext2_extent_handle_t handle;
- struct ext2_extent_info info;
- struct ext2fs_extent extent;
if ((inode->i_size_high || inode->i_size == 0) ||
(inode->i_flags & EXT2_INDEX_FL))
return 0;
- if (inode->i_flags & EXT4_EXTENTS_FL) {
- if (inode->i_flags & EXT4_INLINE_DATA_FL)
- return 0;
- if (inode->i_size > fs->blocksize)
- return 0;
- if (ext2fs_extent_open2(fs, ino, inode, &handle))
- return 0;
- i = 0;
- if (ext2fs_extent_get_info(handle, &info) ||
- (info.num_entries != 1) ||
- (info.max_depth != 0))
- goto exit_extent;
- if (ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent) ||
- (extent.e_lblk != 0) ||
- (extent.e_len != 1) ||
- (extent.e_pblk < fs->super->s_first_data_block) ||
- (extent.e_pblk >= ext2fs_blocks_count(fs->super)))
- goto exit_extent;
- i = 1;
- exit_extent:
- ext2fs_extent_free(handle);
- return i;
- }
-
if (inode->i_flags & EXT4_INLINE_DATA_FL) {
size_t inline_size;
+ if (inode->i_flags & EXT4_EXTENTS_FL)
+ return 0;
if (ext2fs_inline_data_size(fs, ino, &inline_size))
return 0;
if (inode->i_size != inline_size)
return 1;
}
- blocks = ext2fs_inode_data_blocks2(fs, inode);
- if (blocks) {
- if (inode->i_flags & EXT4_INLINE_DATA_FL)
- return 0;
- if ((inode->i_size >= fs->blocksize) ||
- (blocks != fs->blocksize >> 9) ||
- (inode->i_block[0] < fs->super->s_first_data_block) ||
- (inode->i_block[0] >= ext2fs_blocks_count(fs->super)))
+ if (ext2fs_is_fast_symlink(inode)) {
+ if (inode->i_flags & EXT4_EXTENTS_FL)
return 0;
+ buf = (char *)inode->i_block;
+ buflen = sizeof(inode->i_block);
+ } else {
+ ext2_extent_handle_t handle;
+ struct ext2_extent_info info;
+ struct ext2fs_extent extent;
+ blk64_t blk;
+ int i;
- for (i = 1; i < EXT2_N_BLOCKS; i++)
- if (inode->i_block[i])
+ if (inode->i_flags & EXT4_EXTENTS_FL) {
+ if (ext2fs_extent_open2(fs, ino, inode, &handle))
return 0;
-
- if (io_channel_read_blk64(fs->io, inode->i_block[0], 1, buf))
- return 0;
-
- if (inode->i_flags & EXT4_ENCRYPT_FL) {
- len = ext2fs_le32_to_cpu(*((__u32 *)buf)) + 4;
+ if (ext2fs_extent_get_info(handle, &info) ||
+ (info.num_entries != 1) ||
+ (info.max_depth != 0)) {
+ ext2fs_extent_free(handle);
+ return 0;
+ }
+ if (ext2fs_extent_get(handle, EXT2_EXTENT_ROOT,
+ &extent) ||
+ (extent.e_lblk != 0) ||
+ (extent.e_len != 1)) {
+ ext2fs_extent_free(handle);
+ return 0;
+ }
+ blk = extent.e_pblk;
+ ext2fs_extent_free(handle);
} else {
- len = strnlen(buf, fs->blocksize);
+ blk = inode->i_block[0];
+
+ for (i = 1; i < EXT2_N_BLOCKS; i++)
+ if (inode->i_block[i])
+ return 0;
}
- if (len == fs->blocksize)
- return 0;
- } else if (inode->i_flags & EXT4_INLINE_DATA_FL) {
- char *inline_buf = NULL;
- size_t inline_sz = 0;
- if (ext2fs_inline_data_size(fs, ino, &inline_sz))
- return 0;
- if (inode->i_size != inline_sz)
- return 0;
- if (ext2fs_get_mem(inline_sz + 1, &inline_buf))
- return 0;
- i = 0;
- if (ext2fs_inline_data_get(fs, ino, inode, inline_buf, NULL))
- goto exit_inline;
- inline_buf[inline_sz] = 0;
- len = strnlen(inline_buf, inline_sz);
- if (len != inline_sz)
- goto exit_inline;
- i = 1;
-exit_inline:
- ext2fs_free_mem(&inline_buf);
- return i;
- } else {
- if (inode->i_size >= sizeof(inode->i_block))
+ if (blk < fs->super->s_first_data_block ||
+ blk >= ext2fs_blocks_count(fs->super))
return 0;
- len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
- if (len == sizeof(inode->i_block))
+ if (io_channel_read_blk64(fs->io, blk, 1, buf))
return 0;
+
+ buflen = fs->blocksize;
}
+
+ if (inode->i_flags & EXT4_ENCRYPT_FL)
+ len = ext2fs_le16_to_cpu(*(__u16 *)buf) + 2;
+ else
+ len = strnlen(buf, buflen);
+
+ if (len >= buflen)
+ return 0;
+
if (len != inode->i_size)
- if ((inode->i_flags & EXT4_ENCRYPT_FL) == 0)
- return 0;
+ return 0;
return 1;
}
e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
}
-static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
+/*
+ * For a given size, calculate how many blocks would be charged towards quota.
+ */
+static blk64_t size_to_quota_blocks(ext2_filsys fs, size_t size)
+{
+ blk64_t clusters;
+
+ clusters = DIV_ROUND_UP(size, fs->blocksize << fs->cluster_ratio_bits);
+ return EXT2FS_C2B(fs, clusters);
+}
+
+/*
+ * Check validity of EA inode. Return 0 if EA inode is valid, otherwise return
+ * the problem code.
+ */
+static problem_t check_large_ea_inode(e2fsck_t ctx,
+ struct ext2_ext_attr_entry *entry,
+ struct problem_context *pctx,
+ blk64_t *quota_blocks)
+{
+ struct ext2_inode inode;
+ __u32 hash;
+ errcode_t retval;
+
+ /* Check if inode is within valid range */
+ if ((entry->e_value_inum < EXT2_FIRST_INODE(ctx->fs->super)) ||
+ (entry->e_value_inum > ctx->fs->super->s_inodes_count)) {
+ pctx->num = entry->e_value_inum;
+ return PR_1_ATTR_VALUE_EA_INODE;
+ }
+
+ e2fsck_read_inode(ctx, entry->e_value_inum, &inode, "pass1");
+
+ retval = ext2fs_ext_attr_hash_entry2(ctx->fs, entry, NULL, &hash);
+ if (retval) {
+ com_err("check_large_ea_inode", retval,
+ _("while hashing entry with e_value_inum = %u"),
+ entry->e_value_inum);
+ fatal_error(ctx, 0);
+ }
+
+ if (hash == entry->e_hash) {
+ *quota_blocks = size_to_quota_blocks(ctx->fs,
+ entry->e_value_size);
+ } else {
+ /* This might be an old Lustre-style ea_inode reference. */
+ if (inode.i_mtime == pctx->ino &&
+ inode.i_generation == pctx->inode->i_generation) {
+ *quota_blocks = 0;
+ } else {
+ /* If target inode is also missing EA_INODE flag,
+ * this is likely to be a bad reference.
+ */
+ if (!(inode.i_flags & EXT4_EA_INODE_FL)) {
+ pctx->num = entry->e_value_inum;
+ return PR_1_ATTR_VALUE_EA_INODE;
+ } else {
+ pctx->num = entry->e_hash;
+ return PR_1_ATTR_HASH;
+ }
+ }
+ }
+
+ if (!(inode.i_flags & EXT4_EA_INODE_FL)) {
+ pctx->num = entry->e_value_inum;
+ if (fix_problem(ctx, PR_1_ATTR_SET_EA_INODE_FL, pctx)) {
+ inode.i_flags |= EXT4_EA_INODE_FL;
+ ext2fs_write_inode(ctx->fs, entry->e_value_inum,
+ &inode);
+ } else {
+ return PR_1_ATTR_NO_EA_INODE_FL;
+ }
+ }
+ return 0;
+}
+
+static void inc_ea_inode_refs(e2fsck_t ctx, struct problem_context *pctx,
+ struct ext2_ext_attr_entry *first, void *end)
+{
+ struct ext2_ext_attr_entry *entry = first;
+ struct ext2_ext_attr_entry *np = EXT2_EXT_ATTR_NEXT(entry);
+
+ while ((void *) entry < end && (void *) np < end &&
+ !EXT2_EXT_IS_LAST_ENTRY(entry)) {
+ if (!entry->e_value_inum)
+ goto next;
+ if (!ctx->ea_inode_refs) {
+ pctx->errcode = ea_refcount_create(0,
+ &ctx->ea_inode_refs);
+ if (pctx->errcode) {
+ pctx->num = 4;
+ fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
+ ea_refcount_increment(ctx->ea_inode_refs, entry->e_value_inum,
+ 0);
+ next:
+ entry = np;
+ np = EXT2_EXT_ATTR_NEXT(entry);
+ }
+}
+
+static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx,
+ struct ea_quota *ea_ibody_quota)
{
struct ext2_super_block *sb = ctx->fs->super;
struct ext2_inode_large *inode;
struct ext2_ext_attr_entry *entry;
- char *start, *header;
+ char *start, *header, *end;
unsigned int storage_size, remain;
problem_t problem = 0;
region_t region = 0;
+ ea_ibody_quota->blocks = 0;
+ ea_ibody_quota->inodes = 0;
+
inode = (struct ext2_inode_large *) pctx->inode;
storage_size = EXT2_INODE_SIZE(ctx->fs->super) - EXT2_GOOD_OLD_INODE_SIZE -
inode->i_extra_isize;
header = ((char *) inode) + EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize;
+ end = header + storage_size;
start = header + sizeof(__u32);
entry = (struct ext2_ext_attr_entry *) start;
/* attribute len eats this space */
remain -= EXT2_EXT_ATTR_SIZE(entry->e_name_len);
- /* check value size */
- if (entry->e_value_size > remain) {
- pctx->num = entry->e_value_size;
- problem = PR_1_ATTR_VALUE_SIZE;
- goto fix;
- }
+ if (entry->e_value_inum == 0) {
+ /* check value size */
+ if (entry->e_value_size > remain) {
+ pctx->num = entry->e_value_size;
+ problem = PR_1_ATTR_VALUE_SIZE;
+ goto fix;
+ }
- /* e_value_block must be 0 in inode's ea */
- if (entry->e_value_block != 0) {
- pctx->num = entry->e_value_block;
- problem = PR_1_ATTR_VALUE_BLOCK;
- goto fix;
- }
+ if (entry->e_value_size &&
+ region_allocate(region,
+ sizeof(__u32) + entry->e_value_offs,
+ EXT2_EXT_ATTR_SIZE(
+ entry->e_value_size))) {
+ problem = PR_1_INODE_EA_ALLOC_COLLISION;
+ goto fix;
+ }
- if (entry->e_value_size &&
- region_allocate(region, sizeof(__u32) + entry->e_value_offs,
- EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
- problem = PR_1_INODE_EA_ALLOC_COLLISION;
- goto fix;
- }
+ hash = ext2fs_ext_attr_hash_entry(entry,
+ start + entry->e_value_offs);
- hash = ext2fs_ext_attr_hash_entry(entry,
- start + entry->e_value_offs);
+ /* e_hash may be 0 in older inode's ea */
+ if (entry->e_hash != 0 && entry->e_hash != hash) {
+ pctx->num = entry->e_hash;
+ problem = PR_1_ATTR_HASH;
+ goto fix;
+ }
+ } else {
+ blk64_t quota_blocks;
- /* e_hash may be 0 in older inode's ea */
- if (entry->e_hash != 0 && entry->e_hash != hash) {
- pctx->num = entry->e_hash;
- problem = PR_1_ATTR_HASH;
- goto fix;
+ problem = check_large_ea_inode(ctx, entry, pctx,
+ "a_blocks);
+ if (problem != 0)
+ goto fix;
+
+ ea_ibody_quota->blocks += quota_blocks;
+ ea_ibody_quota->inodes++;
}
- remain -= entry->e_value_size;
+ /* If EA value is stored in external inode then it does not
+ * consume space here */
+ if (entry->e_value_inum == 0)
+ remain -= entry->e_value_size;
entry = EXT2_EXT_ATTR_NEXT(entry);
}
* it seems like a corruption. it's very unlikely we could repair
* EA(s) in automatic fashion -bzzz
*/
- if (problem == 0 || !fix_problem(ctx, problem, pctx))
+ if (problem == 0 || !fix_problem(ctx, problem, pctx)) {
+ inc_ea_inode_refs(ctx, pctx,
+ (struct ext2_ext_attr_entry *)start, end);
return;
+ }
/* simply remove all possible EA(s) */
*((__u32 *)header) = 0UL;
e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
EXT2_INODE_SIZE(sb), "pass1");
+ ea_ibody_quota->blocks = 0;
+ ea_ibody_quota->inodes = 0;
}
static int check_inode_extra_negative_epoch(__u32 xtime, __u32 extra) {
- return (xtime & (1 << 31)) != 0 &&
+ return (xtime & (1U << 31)) != 0 &&
(extra & EXT4_EPOCH_MASK) == EXT4_EPOCH_MASK;
}
*/
#define EXT4_EXTRA_NEGATIVE_DATE_CUTOFF 2 * (1LL << 32)
-static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx)
+static void check_inode_extra_space(e2fsck_t ctx, struct problem_context *pctx,
+ struct ea_quota *ea_ibody_quota)
{
struct ext2_super_block *sb = ctx->fs->super;
struct ext2_inode_large *inode;
__u32 *eamagic;
int min, max;
+ ea_ibody_quota->blocks = 0;
+ ea_ibody_quota->inodes = 0;
+
inode = (struct ext2_inode_large *) pctx->inode;
if (EXT2_INODE_SIZE(sb) == EXT2_GOOD_OLD_INODE_SIZE) {
/* this isn't large inode. so, nothing to check */
inode->i_extra_isize = (inode->i_extra_isize + 3) & ~3;
e2fsck_write_inode_full(ctx, pctx->ino, pctx->inode,
EXT2_INODE_SIZE(sb), "pass1");
- return;
}
/* check if there is no place for an EA header */
inode->i_extra_isize);
if (*eamagic == EXT2_EXT_ATTR_MAGIC) {
/* it seems inode has an extended attribute(s) in body */
- check_ea_in_inode(ctx, pctx);
+ check_ea_in_inode(ctx, pctx, ea_ibody_quota);
}
/*
}
e2fsck_set_bitmap_type(ctx->fs, EXT2FS_BMAP64_RBTREE, icount_name,
&save_type);
+ if (ctx->options & E2F_OPT_ICOUNT_FULLMAP)
+ flags |= EXT2_ICOUNT_OPT_FULLMAP;
retval = ext2fs_create_icount2(ctx->fs, flags, 0, hint, ret);
ctx->fs->default_bitmap_type = save_type;
return retval;
return 0;
}
-void e2fsck_pass1(e2fsck_t ctx)
+static int e2fsck_should_abort(e2fsck_t ctx)
+{
+ e2fsck_t global_ctx;
+
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return 1;
+
+ if (ctx->global_ctx) {
+ global_ctx = ctx->global_ctx;
+ if (global_ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return 1;
+ }
+ return 0;
+}
+
+void e2fsck_pass1_run(e2fsck_t ctx)
{
int i;
- __u64 max_sizes;
ext2_filsys fs = ctx->fs;
ext2_ino_t ino = 0;
struct ext2_inode *inode = NULL;
struct scan_callback_struct scan_struct;
struct ext2_super_block *sb = ctx->fs->super;
const char *old_op;
- int imagic_fs, extent_fs, inlinedata_fs;
+ const char *eop_next_inode = _("getting next inode from scan");
+ int imagic_fs, extent_fs, inlinedata_fs, casefold_fs;
int low_dtime_check = 1;
- int inode_size = EXT2_INODE_SIZE(fs->super);
- int bufsize;
+ unsigned int inode_size = EXT2_INODE_SIZE(fs->super);
+ unsigned int bufsize;
int failed_csum = 0;
ext2_ino_t ino_threshold = 0;
dgrp_t ra_group = 0;
+ struct ea_quota ea_ibody_quota;
+ struct process_inode_block *inodes_to_process;
+ int process_inode_count;
init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&pctx);
mtrace_print("Pass 1");
#endif
-#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
-
- for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
- max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
- max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
- max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
- max_sizes = (max_sizes * (1UL << i));
- ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
- }
-#undef EXT2_BPP
-
imagic_fs = ext2fs_has_feature_imagic_inodes(sb);
extent_fs = ext2fs_has_feature_extents(sb);
inlinedata_fs = ext2fs_has_feature_inline_data(sb);
+ casefold_fs = ext2fs_has_feature_casefold(sb);
/*
* Allocate bitmaps structures
}
pctx.errcode = e2fsck_allocate_inode_bitmap(fs,
_("directory inode map"),
+ ctx->global_ctx ? EXT2FS_BMAP64_RBTREE :
EXT2FS_BMAP64_AUTODIR,
"inode_dir_map", &ctx->inode_dir_map);
if (pctx.errcode) {
ctx->flags |= E2F_FLAG_ABORT;
return;
}
+ if (casefold_fs) {
+ pctx.errcode =
+ e2fsck_allocate_inode_bitmap(fs,
+ _("inode casefold map"),
+ EXT2FS_BMAP64_RBTREE,
+ "inode_casefold_map",
+ &ctx->inode_casefold_map);
+ if (pctx.errcode) {
+ pctx.num = 1;
+ fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return;
+ }
+ }
pctx.errcode = e2fsck_setup_icount(ctx, "inode_link_info", 0, NULL,
&ctx->inode_link_info);
if (pctx.errcode) {
ctx->stashed_inode = inode;
scan_struct.ctx = ctx;
scan_struct.block_buf = block_buf;
+ scan_struct.inodes_to_process = inodes_to_process;
+ scan_struct.process_inode_count = &process_inode_count;
ext2fs_set_inode_callback(scan, scan_callback, &scan_struct);
if (ctx->progress && ((ctx->progress)(ctx, 1, 0,
ctx->fs->group_desc_count)))
goto endit;
- if ((fs->super->s_wtime < fs->super->s_inodes_count) ||
- (fs->super->s_mtime < fs->super->s_inodes_count) ||
+ if ((fs->super->s_wtime &&
+ fs->super->s_wtime < fs->super->s_inodes_count) ||
+ (fs->super->s_mtime &&
+ fs->super->s_mtime < fs->super->s_inodes_count) ||
(fs->super->s_mkfs_time &&
fs->super->s_mkfs_time < fs->super->s_inodes_count))
low_dtime_check = 0;
/* Set up ctx->lost_and_found if possible */
(void) e2fsck_get_lost_and_found(ctx, 0);
+#ifdef HAVE_PTHREAD
+ if (ctx->global_ctx) {
+ if (ctx->options & E2F_OPT_DEBUG &&
+ ctx->options & E2F_OPT_MULTITHREAD)
+ fprintf(stderr, "thread %d jumping to group %d\n",
+ ctx->thread_info.et_thread_index,
+ ctx->thread_info.et_group_start);
+ pctx.errcode = ext2fs_inode_scan_goto_blockgroup(scan,
+ ctx->thread_info.et_group_start);
+ if (pctx.errcode) {
+ fix_problem(ctx, PR_1_PASS_HEADER, &pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ goto endit;
+ }
+ }
+#endif
+
while (1) {
if (ino % (fs->super->s_inodes_per_group * 4) == 1) {
if (e2fsck_mmp_update(fs))
fatal_error(ctx, 0);
}
- old_op = ehandler_operation(_("getting next inode from scan"));
+ old_op = ehandler_operation(eop_next_inode);
pctx.errcode = ext2fs_get_next_inode_full(scan, &ino,
inode, inode_size);
if (ino > ino_threshold)
pass1_readahead(ctx, &ra_group, &ino_threshold);
ehandler_operation(old_op);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
goto endit;
if (pctx.errcode == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) {
/*
fix_problem(ctx, PR_1_ISCAN_ERROR,
&pctx);
ctx->flags |= E2F_FLAG_ABORT;
- goto endit;
- }
- err = ext2fs_inode_scan_goto_blockgroup(scan,
- 0);
- if (err) {
- fix_problem(ctx, PR_1_ISCAN_ERROR,
- &pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- goto endit;
- }
- continue;
+ } else
+ ctx->flags |= E2F_FLAG_RESTART;
+ goto endit;
}
if (!ctx->inode_bb_map)
alloc_bb_map(ctx);
ext2fs_mark_inode_bitmap2(ctx->inode_used_map, ino);
continue;
}
+ if (pctx.errcode == EXT2_ET_SCAN_FINISHED)
+ break;
if (pctx.errcode &&
pctx.errcode != EXT2_ET_INODE_CSUM_INVALID &&
pctx.errcode != EXT2_ET_INODE_IS_GARBAGE) {
}
if (!ino)
break;
+#ifdef HAVE_PTHREAD
+ if (ctx->global_ctx)
+ ctx->thread_info.et_inode_number++;
+#endif
pctx.ino = ino;
pctx.inode = inode;
ctx->stashed_ino = ino;
}
failed_csum = pctx.errcode != 0;
+ /*
+ * Check for inodes who might have been part of the
+ * orphaned list linked list. They should have gotten
+ * dealt with by now, unless the list had somehow been
+ * corrupted.
+ *
+ * FIXME: In the future, inodes which are still in use
+ * (and which are therefore) pending truncation should
+ * be handled specially. Right now we just clear the
+ * dtime field, and the normal e2fsck handling of
+ * inodes where i_size and the inode blocks are
+ * inconsistent is to fix i_size, instead of releasing
+ * the extra blocks. This won't catch the inodes that
+ * was at the end of the orphan list, but it's better
+ * than nothing. The right answer is that there
+ * shouldn't be any bugs in the orphan list handling. :-)
+ */
+ if (inode->i_dtime && low_dtime_check &&
+ inode->i_dtime < ctx->fs->super->s_inodes_count) {
+ if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
+ inode->i_dtime = inode->i_links_count ?
+ 0 : ctx->now;
+ e2fsck_write_inode(ctx, ino, inode,
+ "pass1");
+ failed_csum = 0;
+ }
+ }
+
if (inode->i_links_count) {
pctx.errcode = ext2fs_icount_store(ctx->inode_link_info,
ino, inode->i_links_count);
ctx->flags |= E2F_FLAG_ABORT;
goto endit;
}
+ } else if ((ino >= EXT2_FIRST_INODE(fs->super)) &&
+ !quota_inum_is_reserved(fs, ino)) {
+ if (!inode->i_dtime && inode->i_mode) {
+ if (fix_problem(ctx,
+ PR_1_ZERO_DTIME, &pctx)) {
+ inode->i_dtime = ctx->now;
+ e2fsck_write_inode(ctx, ino, inode,
+ "pass1");
+ failed_csum = 0;
+ }
+ }
+ FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
+ continue;
+ }
+
+ if ((inode->i_flags & EXT4_CASEFOLD_FL) &&
+ ((!LINUX_S_ISDIR(inode->i_mode) &&
+ fix_problem(ctx, PR_1_CASEFOLD_NONDIR, &pctx)) ||
+ (!casefold_fs &&
+ fix_problem(ctx, PR_1_CASEFOLD_FEATURE, &pctx)))) {
+ inode->i_flags &= ~EXT4_CASEFOLD_FL;
+ e2fsck_write_inode(ctx, ino, inode, "pass1");
}
/* Conflicting inlinedata/extents inode flags? */
(ino >= EXT2_FIRST_INODE(fs->super))) {
size_t size = 0;
- pctx.errcode = ext2fs_inline_data_size(fs, ino, &size);
- if (!pctx.errcode && size &&
+ pctx.errcode = get_inline_data_ea_size(fs, ino, &size);
+ if (!pctx.errcode &&
fix_problem(ctx, PR_1_INLINE_DATA_FEATURE, &pctx)) {
ext2fs_set_feature_inline_data(sb);
ext2fs_mark_super_dirty(fs);
/* Test for inline data flag but no attr */
if ((inode->i_flags & EXT4_INLINE_DATA_FL) && inlinedata_fs &&
- EXT2_I_SIZE(inode) > EXT4_MIN_INLINE_DATA_SIZE &&
(ino >= EXT2_FIRST_INODE(fs->super))) {
size_t size = 0;
errcode_t err;
case EXT2_ET_NO_INLINE_DATA:
case EXT2_ET_EXT_ATTR_CSUM_INVALID:
case EXT2_ET_EA_BAD_VALUE_OFFSET:
+ case EXT2_ET_EA_INODE_CORRUPTED:
/* broken EA or no system.data EA; truncate */
if (fix_problem(ctx, PR_1_INLINE_DATA_NO_ATTR,
&pctx)) {
- err = ext2fs_inode_size_set(fs, inode,
- sizeof(inode->i_block));
+ err = ext2fs_inode_size_set(fs, inode, 0);
if (err) {
pctx.errcode = err;
ctx->flags |= E2F_FLAG_ABORT;
goto endit;
}
- if (LINUX_S_ISLNK(inode->i_mode))
- inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+ inode->i_flags &= ~EXT4_INLINE_DATA_FL;
+ memset(&inode->i_block, 0,
+ sizeof(inode->i_block));
e2fsck_write_inode(ctx, ino, inode,
"pass1");
failed_csum = 0;
/*
* Make sure the root inode is a directory; if
* not, offer to clear it. It will be
- * regnerated in pass #3.
+ * regenerated in pass #3.
*/
if (!LINUX_S_ISDIR(inode->i_mode)) {
if (fix_problem(ctx, PR_1_ROOT_NO_DIR, &pctx))
"pass1");
failed_csum = 0;
}
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
"pass1");
failed_csum = 0;
}
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
failed_csum = 0;
}
}
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf, NULL);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
- /*
- * Check for inodes who might have been part of the
- * orphaned list linked list. They should have gotten
- * dealt with by now, unless the list had somehow been
- * corrupted.
- *
- * FIXME: In the future, inodes which are still in use
- * (and which are therefore) pending truncation should
- * be handled specially. Right now we just clear the
- * dtime field, and the normal e2fsck handling of
- * inodes where i_size and the inode blocks are
- * inconsistent is to fix i_size, instead of releasing
- * the extra blocks. This won't catch the inodes that
- * was at the end of the orphan list, but it's better
- * than nothing. The right answer is that there
- * shouldn't be any bugs in the orphan list handling. :-)
- */
- if (inode->i_dtime && low_dtime_check &&
- inode->i_dtime < ctx->fs->super->s_inodes_count) {
- if (fix_problem(ctx, PR_1_LOW_DTIME, &pctx)) {
- inode->i_dtime = inode->i_links_count ?
- 0 : ctx->now;
- e2fsck_write_inode(ctx, ino, inode,
- "pass1");
- failed_csum = 0;
- }
- }
-
- /*
- * This code assumes that deleted inodes have
- * i_links_count set to 0.
- */
if (!inode->i_links_count) {
- if (!inode->i_dtime && inode->i_mode) {
- if (fix_problem(ctx,
- PR_1_ZERO_DTIME, &pctx)) {
- inode->i_dtime = ctx->now;
- e2fsck_write_inode(ctx, ino, inode,
- "pass1");
- failed_csum = 0;
- }
- }
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
}
if (inode->i_faddr || frag || fsize ||
- (LINUX_S_ISDIR(inode->i_mode) && inode->i_dir_acl))
+ (!ext2fs_has_feature_largedir(fs->super) &&
+ (LINUX_S_ISDIR(inode->i_mode) && inode->i_size_high)))
mark_inode_bad(ctx, ino);
if ((fs->super->s_creator_os != EXT2_OS_HURD) &&
!ext2fs_has_feature_64bit(fs->super) &&
}
}
- check_inode_extra_space(ctx, &pctx);
+ check_inode_extra_space(ctx, &pctx, &ea_ibody_quota);
check_is_really_dir(ctx, &pctx, block_buf);
/*
failed_csum = 0;
}
+ if ((inode->i_flags & EXT4_ENCRYPT_FL) &&
+ add_encrypted_file(ctx, &pctx) < 0)
+ goto clear_inode;
+
+ if (casefold_fs && inode->i_flags & EXT4_CASEFOLD_FL)
+ ext2fs_mark_inode_bitmap2(ctx->inode_casefold_map, ino);
+
if (LINUX_S_ISDIR(inode->i_mode)) {
ext2fs_mark_inode_bitmap2(ctx->inode_dir_map, ino);
e2fsck_add_dir_info(ctx, ino, 0);
ctx->fs_directory_count++;
- if (inode->i_flags & EXT4_ENCRYPT_FL)
- add_encrypted_dir(ctx, ino);
+ if (inode->i_flags & EXT4_CASEFOLD_FL)
+ add_casefolded_dir(ctx, ino);
} else if (LINUX_S_ISREG (inode->i_mode)) {
ext2fs_mark_inode_bitmap2(ctx->inode_reg_map, ino);
ctx->fs_regular_count++;
if (inode->i_flags & EXT4_INLINE_DATA_FL) {
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
- } else if (ext2fs_inode_data_blocks(fs, inode) == 0) {
+ } else if (ext2fs_is_fast_symlink(inode)) {
ctx->fs_fast_symlinks_count++;
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf,
+ &ea_ibody_quota);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
continue;
}
inode->i_block[EXT2_DIND_BLOCK] ||
inode->i_block[EXT2_TIND_BLOCK] ||
ext2fs_file_acl_block(fs, inode))) {
- inodes_to_process[process_inode_count].ino = ino;
- inodes_to_process[process_inode_count].inode =
- *(struct ext2_inode_large *)inode;
+ struct process_inode_block *itp;
+
+ itp = &inodes_to_process[process_inode_count];
+ itp->ino = ino;
+ itp->ea_ibody_quota = ea_ibody_quota;
+ if (inode_size < sizeof(struct ext2_inode_large))
+ memcpy(&itp->inode, inode, inode_size);
+ else
+ memcpy(&itp->inode, inode, sizeof(itp->inode));
process_inode_count++;
} else
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf, &ea_ibody_quota);
FINISH_INODE_LOOP(ctx, ino, &pctx, failed_csum);
goto endit;
if (process_inode_count >= ctx->process_inode_size) {
- process_inodes(ctx, block_buf);
+ process_inodes(ctx, block_buf, inodes_to_process,
+ &process_inode_count);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
goto endit;
}
}
- process_inodes(ctx, block_buf);
+ process_inodes(ctx, block_buf, inodes_to_process,
+ &process_inode_count);
ext2fs_close_inode_scan(scan);
scan = NULL;
ctx->refcount_extra = 0;
}
+ if (ctx->ea_block_quota_blocks) {
+ ea_refcount_free(ctx->ea_block_quota_blocks);
+ ctx->ea_block_quota_blocks = 0;
+ }
+
+ if (ctx->ea_block_quota_inodes) {
+ ea_refcount_free(ctx->ea_block_quota_inodes);
+ ctx->ea_block_quota_inodes = 0;
+ }
+
if (ctx->invalid_bitmaps)
handle_fs_bad_blocks(ctx);
ctx->block_ea_map = 0;
}
+ /* We don't need the encryption policy => ID map any more */
+ destroy_encryption_policy_map(ctx);
+
if (ctx->flags & E2F_FLAG_RESIZE_INODE) {
clear_problem_context(&pctx);
pctx.errcode = ext2fs_create_resize_inode(fs);
* master superblock.
*/
ctx->use_superblock = 0;
- unwind_pass1(fs);
goto endit;
}
+ if (ctx->large_dirs && !ext2fs_has_feature_largedir(fs->super)) {
+ if (fix_problem(ctx, PR_2_FEATURE_LARGE_DIRS, &pctx)) {
+ ext2fs_set_feature_largedir(fs->super);
+ fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
+ ext2fs_mark_super_dirty(fs);
+ }
+ if (fs->super->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);
+ }
+ }
+
if (ctx->block_dup_map) {
if (ctx->options & E2F_OPT_PREEN) {
clear_problem_context(&pctx);
e2fsck_pass1_dupblocks(ctx, block_buf);
}
ctx->flags |= E2F_FLAG_ALLOC_OK;
- ext2fs_free_mem(&inodes_to_process);
endit:
e2fsck_use_inode_shortcuts(ctx, 0);
+ ext2fs_free_mem(&inodes_to_process);
+ inodes_to_process = 0;
if (scan)
ext2fs_close_inode_scan(scan);
else
ctx->invalid_bitmaps++;
}
-#undef FINISH_INODE_LOOP
+
+static void init_ext2_max_sizes()
+{
+ int i;
+ __u64 max_sizes;
+
+ /*
+ * Init ext2_max_sizes which will be immutable and shared between
+ * threads
+ */
+#define EXT2_BPP(bits) (1ULL << ((bits) - 2))
+
+ for (i = EXT2_MIN_BLOCK_LOG_SIZE; i <= EXT2_MAX_BLOCK_LOG_SIZE; i++) {
+ max_sizes = EXT2_NDIR_BLOCKS + EXT2_BPP(i);
+ max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i);
+ max_sizes = max_sizes + EXT2_BPP(i) * EXT2_BPP(i) * EXT2_BPP(i);
+ max_sizes = (max_sizes * (1UL << i));
+ ext2_max_sizes[i - EXT2_MIN_BLOCK_LOG_SIZE] = max_sizes;
+ }
+#undef EXT2_BPP
+}
+
+#ifdef HAVE_PTHREAD
+static errcode_t e2fsck_pass1_copy_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src,
+ ext2fs_generic_bitmap *dest)
+{
+ errcode_t ret;
+
+ ret = ext2fs_copy_bitmap(*src, dest);
+ if (ret)
+ return ret;
+
+ (*dest)->fs = fs;
+
+ return 0;
+}
+
+static void e2fsck_pass1_free_bitmap(ext2fs_generic_bitmap *bitmap)
+{
+ if (*bitmap) {
+ ext2fs_free_generic_bmap(*bitmap);
+ *bitmap = NULL;
+ }
+
+}
+
+static errcode_t e2fsck_pass1_merge_bitmap(ext2_filsys fs, ext2fs_generic_bitmap *src,
+ ext2fs_generic_bitmap *dest)
+{
+ errcode_t ret = 0;
+
+ if (*src) {
+ if (*dest == NULL) {
+ *dest = *src;
+ *src = NULL;
+ } else {
+ ret = ext2fs_merge_bitmap(*src, *dest, NULL, NULL);
+ if (ret)
+ return ret;
+ }
+ (*dest)->fs = fs;
+ }
+
+ return 0;
+}
+
+static errcode_t e2fsck_pass1_copy_fs(ext2_filsys dest, e2fsck_t src_context,
+ ext2_filsys src)
+{
+ errcode_t retval;
+
+ memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+ dest->inode_map = NULL;
+ dest->block_map = NULL;
+ dest->badblocks = NULL;
+ if (dest->dblist)
+ dest->dblist->fs = dest;
+ if (src->block_map) {
+ retval = e2fsck_pass1_copy_bitmap(dest, &src->block_map,
+ &dest->block_map);
+ if (retval)
+ return retval;
+ }
+ if (src->inode_map) {
+ retval = e2fsck_pass1_copy_bitmap(dest, &src->inode_map,
+ &dest->inode_map);
+ if (retval)
+ return retval;
+ }
+
+ if (src->badblocks) {
+ retval = ext2fs_badblocks_copy(src->badblocks,
+ &dest->badblocks);
+ if (retval)
+ return retval;
+ }
+
+ /* disable it for now */
+ src_context->openfs_flags &= ~EXT2_FLAG_EXCLUSIVE;
+ retval = ext2fs_open_channel(dest, src_context->io_options,
+ src_context->io_manager,
+ src_context->openfs_flags,
+ src->io->block_size);
+ if (retval)
+ return retval;
+
+ /* Block size might not be default */
+ io_channel_set_blksize(dest->io, src->io->block_size);
+ ehandler_init(dest->io);
+
+ assert(dest->io->magic == src->io->magic);
+ assert(dest->io->manager == src->io->manager);
+ assert(strcmp(dest->io->name, src->io->name) == 0);
+ assert(dest->io->block_size == src->io->block_size);
+ assert(dest->io->read_error == src->io->read_error);
+ assert(dest->io->write_error == src->io->write_error);
+ assert(dest->io->refcount == src->io->refcount);
+ assert(dest->io->flags == src->io->flags);
+ assert(dest->io->app_data == dest);
+ assert(src->io->app_data == src);
+ assert(dest->io->align == src->io->align);
+
+ /* The data should be written to disk immediately */
+ dest->io->flags |= CHANNEL_FLAGS_WRITETHROUGH;
+ /* icache will be rebuilt if needed, so do not copy from @src */
+ src->icache = NULL;
+ return 0;
+}
+
+static int e2fsck_pass1_merge_fs(ext2_filsys dest, ext2_filsys src)
+{
+ struct ext2_inode_cache *icache = dest->icache;
+ errcode_t retval = 0;
+ io_channel dest_io;
+ io_channel dest_image_io;
+ ext2fs_inode_bitmap inode_map;
+ ext2fs_block_bitmap block_map;
+ ext2_badblocks_list badblocks;
+ ext2_dblist dblist;
+
+ dest_io = dest->io;
+ dest_image_io = dest->image_io;
+ inode_map = dest->inode_map;
+ block_map = dest->block_map;
+ badblocks = dest->badblocks;
+ dblist = dest->dblist;
+
+ memcpy(dest, src, sizeof(struct struct_ext2_filsys));
+ dest->io = dest_io;
+ dest->image_io = dest_image_io;
+ dest->icache = icache;
+ dest->inode_map = inode_map;
+ dest->block_map = block_map;
+ dest->badblocks = badblocks;
+ dest->dblist = dblist;
+ if (dest->dblist)
+ dest->dblist->fs = dest;
+
+ if (src->icache) {
+ ext2fs_free_inode_cache(src->icache);
+ src->icache = NULL;
+ }
+
+ retval = e2fsck_pass1_merge_bitmap(dest, &src->inode_map,
+ &dest->inode_map);
+ if (retval)
+ goto out;
+
+ retval = e2fsck_pass1_merge_bitmap(dest, &src->block_map,
+ &dest->block_map);
+ if (retval)
+ goto out;
+
+ if (src->dblist) {
+ if (dest->dblist) {
+ retval = ext2fs_merge_dblist(src->dblist,
+ dest->dblist);
+ if (retval)
+ goto out;
+ } else {
+ dest->dblist = src->dblist;
+ dest->dblist->fs = dest;
+ src->dblist = NULL;
+ }
+ }
+
+ if (src->badblocks) {
+ if (dest->badblocks == NULL)
+ retval = ext2fs_badblocks_copy(src->badblocks,
+ &dest->badblocks);
+ else
+ retval = ext2fs_badblocks_merge(src->badblocks,
+ dest->badblocks);
+ }
+out:
+ io_channel_close(src->io);
+ if (src->inode_map)
+ ext2fs_free_generic_bmap(src->inode_map);
+ if (src->block_map)
+ ext2fs_free_generic_bmap(src->block_map);
+ if (src->badblocks)
+ ext2fs_badblocks_list_free(src->badblocks);
+ if (src->dblist)
+ ext2fs_free_dblist(src->dblist);
+
+ return retval;
+}
+
+static errcode_t e2fsck_pass1_thread_prepare(e2fsck_t global_ctx, e2fsck_t *thread_ctx,
+ int thread_index, int num_threads)
+{
+ errcode_t retval;
+ e2fsck_t thread_context;
+ ext2_filsys thread_fs;
+ ext2_filsys global_fs = global_ctx->fs;
+ struct e2fsck_thread *tinfo;
+ dgrp_t average_group;
+
+ assert(global_ctx->inode_used_map == NULL);
+ assert(global_ctx->inode_dir_map == NULL);
+ assert(global_ctx->inode_bb_map == NULL);
+ assert(global_ctx->inode_imagic_map == NULL);
+ assert(global_ctx->inode_reg_map == NULL);
+ assert(global_ctx->inodes_to_rebuild == NULL);
+
+ assert(global_ctx->block_found_map == NULL);
+ assert(global_ctx->block_dup_map == NULL);
+ assert(global_ctx->block_ea_map == NULL);
+ assert(global_ctx->block_metadata_map == NULL);
+ assert(global_ctx->fs->dblist == NULL);
+
+ retval = ext2fs_get_mem(sizeof(struct e2fsck_struct), &thread_context);
+ if (retval) {
+ com_err(global_ctx->program_name, retval, "while allocating memory");
+ return retval;
+ }
+ memcpy(thread_context, global_ctx, sizeof(struct e2fsck_struct));
+ thread_context->global_ctx = global_ctx;
+
+ retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &thread_fs);
+ if (retval) {
+ com_err(global_ctx->program_name, retval, "while allocating memory");
+ goto out_context;
+ }
+
+ io_channel_flush_cleanup(global_fs->io);
+ retval = e2fsck_pass1_copy_fs(thread_fs, global_ctx, global_fs);
+ if (retval) {
+ com_err(global_ctx->program_name, retval, "while copying fs");
+ goto out_fs;
+ }
+ thread_fs->priv_data = thread_context;
+
+ thread_context->thread_info.et_thread_index = thread_index;
+ set_up_logging(thread_context);
+
+ /*
+ * Distribute work to multiple threads:
+ * Each thread work on fs->group_desc_count / nthread groups.
+ */
+ tinfo = &thread_context->thread_info;
+ average_group = thread_fs->group_desc_count / num_threads;
+ if (average_group == 0)
+ average_group = 1;
+ tinfo->et_group_start = average_group * thread_index;
+ if (thread_index == num_threads - 1)
+ tinfo->et_group_end = thread_fs->group_desc_count;
+ else
+ tinfo->et_group_end = average_group * (thread_index + 1);
+ tinfo->et_group_next = tinfo->et_group_start;
+ tinfo->et_inode_number = 0;
+ tinfo->et_log_buf[0] = '\0';
+ tinfo->et_log_length = 0;
+ if (thread_context->options & E2F_OPT_MULTITHREAD)
+ log_out(thread_context, _("Scan group range [%d, %d)\n"),
+ tinfo->et_group_start, tinfo->et_group_end);
+ thread_context->fs = thread_fs;
+ *thread_ctx = thread_context;
+ return 0;
+out_fs:
+ ext2fs_free_mem(&thread_fs);
+out_context:
+ ext2fs_free_mem(&thread_context);
+ return retval;
+}
+
+static void e2fsck_pass1_merge_dir_info(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+ if (thread_ctx->dir_info == NULL)
+ return;
+
+ if (global_ctx->dir_info == NULL) {
+ global_ctx->dir_info = thread_ctx->dir_info;
+ thread_ctx->dir_info = NULL;
+ return;
+ }
+
+ e2fsck_merge_dir_info(global_ctx, thread_ctx->dir_info,
+ global_ctx->dir_info);
+}
+
+static inline errcode_t
+e2fsck_pass1_merge_icount(ext2_icount_t *dest_icount,
+ ext2_icount_t *src_icount)
+{
+ if (*src_icount) {
+ if (*dest_icount == NULL) {
+ *dest_icount = *src_icount;
+ *src_icount = NULL;
+ } else {
+ errcode_t ret;
+
+ ret = ext2fs_icount_merge(*src_icount,
+ *dest_icount);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static errcode_t e2fsck_pass1_merge_icounts(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+ errcode_t ret;
+
+ ret = e2fsck_pass1_merge_icount(&global_ctx->inode_count,
+ &thread_ctx->inode_count);
+ if (ret)
+ return ret;
+ ret = e2fsck_pass1_merge_icount(&global_ctx->inode_link_info,
+ &thread_ctx->inode_link_info);
+
+ return ret;
+}
+
+static int e2fsck_pass1_thread_join_one(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+ errcode_t retval;
+ int flags = global_ctx->flags;
+ ext2_filsys thread_fs = thread_ctx->fs;
+ ext2_filsys global_fs = global_ctx->fs;
+ FILE *global_logf = global_ctx->logf;
+ FILE *global_problem_logf = global_ctx->problem_logf;
+ ext2fs_inode_bitmap inode_bad_map = global_ctx->inode_bad_map;
+ struct dir_info_db *dir_info = global_ctx->dir_info;
+ ext2fs_inode_bitmap inode_used_map = global_ctx->inode_used_map;
+ ext2fs_inode_bitmap inode_dir_map = global_ctx->inode_dir_map;
+ ext2fs_inode_bitmap inode_bb_map = global_ctx->inode_bb_map;
+ ext2fs_inode_bitmap inode_imagic_map = global_ctx->inode_imagic_map;
+ ext2fs_inode_bitmap inode_reg_map = global_ctx->inode_reg_map;
+ ext2fs_block_bitmap block_found_map = global_ctx->block_found_map;
+ ext2fs_block_bitmap block_dup_map = global_ctx->block_dup_map;
+ ext2fs_block_bitmap block_ea_map = global_ctx->block_ea_map;
+ ext2fs_block_bitmap block_metadata_map = global_ctx->block_metadata_map;
+ ext2fs_block_bitmap inodes_to_rebuild = global_ctx->inodes_to_rebuild;
+ ext2_icount_t inode_count = global_ctx->inode_count;
+ ext2_icount_t inode_link_info = global_ctx->inode_link_info;
+ __u32 fs_directory_count = global_ctx->fs_directory_count;
+ __u32 fs_regular_count = global_ctx->fs_regular_count;
+ __u32 fs_blockdev_count = global_ctx->fs_blockdev_count;
+ __u32 fs_chardev_count = global_ctx->fs_chardev_count;
+ __u32 fs_links_count = global_ctx->fs_links_count;
+ __u32 fs_symlinks_count = global_ctx->fs_symlinks_count;
+ __u32 fs_fast_symlinks_count = global_ctx->fs_fast_symlinks_count;
+ __u32 fs_fifo_count = global_ctx->fs_fifo_count;
+ __u32 fs_total_count = global_ctx->fs_total_count;
+ __u32 fs_badblocks_count = global_ctx->fs_badblocks_count;
+ __u32 fs_sockets_count = global_ctx->fs_sockets_count;
+ __u32 fs_ind_count = global_ctx->fs_ind_count;
+ __u32 fs_dind_count = global_ctx->fs_dind_count;
+ __u32 fs_tind_count = global_ctx->fs_tind_count;
+ __u32 fs_fragmented = global_ctx->fs_fragmented;
+ __u32 fs_fragmented_dir = global_ctx->fs_fragmented_dir;
+ __u32 large_files = global_ctx->large_files;
+
+#ifdef HAVE_SETJMP_H
+ jmp_buf old_jmp;
+
+ memcpy(old_jmp, global_ctx->abort_loc, sizeof(jmp_buf));
+#endif
+ memcpy(global_ctx, thread_ctx, sizeof(struct e2fsck_struct));
+#ifdef HAVE_SETJMP_H
+ memcpy(global_ctx->abort_loc, old_jmp, sizeof(jmp_buf));
+#endif
+
+ global_ctx->inode_used_map = inode_used_map;
+ global_ctx->inode_bad_map = inode_bad_map;
+ global_ctx->inode_dir_map = inode_dir_map;
+ global_ctx->inode_bb_map = inode_bb_map;
+ global_ctx->inode_imagic_map = inode_imagic_map;
+ global_ctx->inodes_to_rebuild = inodes_to_rebuild;
+ global_ctx->inode_reg_map = inode_reg_map;
+ global_ctx->block_found_map = block_found_map;
+ global_ctx->block_dup_map = block_dup_map;
+ global_ctx->block_ea_map = block_ea_map;
+ global_ctx->block_metadata_map = block_metadata_map;
+ global_ctx->dir_info = dir_info;
+ e2fsck_pass1_merge_dir_info(global_ctx, thread_ctx);
+ global_ctx->inode_count = inode_count;
+ global_ctx->inode_link_info = inode_link_info;
+ global_ctx->fs_directory_count += fs_directory_count;
+ global_ctx->fs_regular_count += fs_regular_count;
+ global_ctx->fs_blockdev_count += fs_blockdev_count;
+ global_ctx->fs_chardev_count += fs_chardev_count;
+ global_ctx->fs_links_count += fs_links_count;
+ global_ctx->fs_symlinks_count += fs_symlinks_count;
+ global_ctx->fs_fast_symlinks_count += fs_fast_symlinks_count;
+ global_ctx->fs_fifo_count += fs_fifo_count;
+ global_ctx->fs_total_count += fs_total_count;
+ global_ctx->fs_badblocks_count += fs_badblocks_count;
+ global_ctx->fs_sockets_count += fs_sockets_count;
+ global_ctx->fs_ind_count += fs_ind_count;
+ global_ctx->fs_dind_count += fs_dind_count;
+ global_ctx->fs_tind_count += fs_tind_count;
+ global_ctx->fs_fragmented += fs_fragmented;
+ global_ctx->fs_fragmented_dir += fs_fragmented_dir;
+ global_ctx->large_files += large_files;
+
+ /* Keep the global singal flags*/
+ global_ctx->flags |= (flags & E2F_FLAG_SIGNAL_MASK) |
+ (global_ctx->flags & E2F_FLAG_SIGNAL_MASK);
+
+ retval = e2fsck_pass1_merge_fs(global_fs, thread_fs);
+ if (retval) {
+ com_err(global_ctx->program_name, 0, _("while merging fs\n"));
+ return retval;
+ }
+ global_fs->priv_data = global_ctx;
+ global_ctx->fs = global_fs;
+ global_ctx->logf = global_logf;
+ global_ctx->problem_logf = global_problem_logf;
+ global_ctx->global_ctx = NULL;
+ retval = e2fsck_pass1_merge_icounts(global_ctx, thread_ctx);
+ if (retval) {
+ com_err(global_ctx->program_name, 0,
+ _("while merging icounts\n"));
+ return retval;
+ }
+
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->inode_used_map,
+ &global_ctx->inode_used_map);
+ if (retval)
+ return retval;
+
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->inode_bad_map,
+ &global_ctx->inode_bad_map);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->inode_dir_map,
+ &global_ctx->inode_dir_map);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->inode_bb_map,
+ &global_ctx->inode_bb_map);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->inode_imagic_map,
+ &global_ctx->inode_imagic_map);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->inode_reg_map,
+ &global_ctx->inode_reg_map);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->inodes_to_rebuild,
+ &global_ctx->inodes_to_rebuild);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->block_found_map,
+ &global_ctx->block_found_map);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->block_dup_map,
+ &global_ctx->block_dup_map);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->block_ea_map,
+ &global_ctx->block_ea_map);
+ if (retval)
+ return retval;
+ retval = e2fsck_pass1_merge_bitmap(global_fs,
+ &thread_ctx->block_metadata_map,
+ &global_ctx->block_metadata_map);
+ if (retval)
+ return retval;
+
+ return 0;
+}
+
+static int e2fsck_pass1_thread_join(e2fsck_t global_ctx, e2fsck_t thread_ctx)
+{
+ errcode_t retval;
+
+ retval = e2fsck_pass1_thread_join_one(global_ctx, thread_ctx);
+ ext2fs_free_mem(&thread_ctx->fs);
+ if (thread_ctx->logf)
+ fclose(thread_ctx->logf);
+ if (thread_ctx->problem_logf) {
+ fputs("</problem_log>\n", thread_ctx->problem_logf);
+ fclose(thread_ctx->problem_logf);
+ }
+ e2fsck_pass1_free_bitmap(&thread_ctx->inode_used_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->inode_bad_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->inode_dir_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->inode_bb_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->inode_imagic_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->inode_reg_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->inodes_to_rebuild);
+ e2fsck_pass1_free_bitmap(&thread_ctx->block_found_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->block_dup_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->block_ea_map);
+ e2fsck_pass1_free_bitmap(&thread_ctx->block_metadata_map);
+ e2fsck_free_dir_info(thread_ctx);
+ ext2fs_free_icount(thread_ctx->inode_count);
+ ext2fs_free_icount(thread_ctx->inode_link_info);
+ ext2fs_free_mem(&thread_ctx);
+
+ return retval;
+}
+
+static int e2fsck_pass1_threads_join(struct e2fsck_thread_info *infos,
+ int num_threads, e2fsck_t global_ctx)
+{
+ errcode_t rc;
+ errcode_t ret = 0;
+ int i;
+ struct e2fsck_thread_info *pinfo;
+
+ for (i = 0; i < num_threads; i++) {
+ pinfo = &infos[i];
+
+ if (!pinfo->eti_started)
+ continue;
+
+ rc = pthread_join(pinfo->eti_thread_id, NULL);
+ if (rc) {
+ com_err(global_ctx->program_name, rc,
+ _("while joining thread\n"));
+ if (ret == 0)
+ ret = rc;
+ }
+ rc = e2fsck_pass1_thread_join(global_ctx, infos[i].eti_thread_ctx);
+ if (rc) {
+ com_err(global_ctx->program_name, rc,
+ _("while joining pass1 thread\n"));
+ if (ret == 0)
+ ret = rc;
+ }
+ }
+ free(infos);
+
+ return ret;
+}
+
+static void *e2fsck_pass1_thread(void *arg)
+{
+ struct e2fsck_thread_info *info = arg;
+ e2fsck_t thread_ctx = info->eti_thread_ctx;
+#ifdef DEBUG_THREADS
+ struct e2fsck_thread_debug *thread_debug = info->eti_debug;
+#endif
+
+#ifdef DEBUG_THREADS
+ pthread_mutex_lock(&thread_debug->etd_mutex);
+ while (info->eti_thread_index > thread_debug->etd_finished_threads) {
+ pthread_cond_wait(&thread_debug->etd_cond,
+ &thread_debug->etd_mutex);
+ }
+ pthread_mutex_unlock(&thread_debug->etd_mutex);
+#endif
+
+#ifdef HAVE_SETJMP_H
+ /*
+ * When fatal_error() happens, jump to here. The thread
+ * context's flags will be saved, but its abort_loc will
+ * be overwritten by original jump buffer for the later
+ * tests.
+ */
+ if (setjmp(thread_ctx->abort_loc)) {
+ thread_ctx->flags &= ~E2F_FLAG_SETJMP_OK;
+ goto out;
+ }
+ thread_ctx->flags |= E2F_FLAG_SETJMP_OK;
+#endif
+
+ e2fsck_pass1_run(thread_ctx);
+
+out:
+ if (thread_ctx->options & E2F_OPT_MULTITHREAD)
+ log_out(thread_ctx,
+ _("Scanned group range [%lu, %lu), inodes %lu\n"),
+ thread_ctx->thread_info.et_group_start,
+ thread_ctx->thread_info.et_group_end,
+ thread_ctx->thread_info.et_inode_number);
+
+#ifdef DEBUG_THREADS
+ pthread_mutex_lock(&thread_debug->etd_mutex);
+ thread_debug->etd_finished_threads++;
+ pthread_cond_broadcast(&thread_debug->etd_cond);
+ pthread_mutex_unlock(&thread_debug->etd_mutex);
+#endif
+
+ return NULL;
+}
+
+static int e2fsck_pass1_threads_start(struct e2fsck_thread_info **pinfo,
+ int num_threads, e2fsck_t global_ctx)
+{
+ struct e2fsck_thread_info *infos;
+ pthread_attr_t attr;
+ errcode_t retval;
+ errcode_t ret;
+ struct e2fsck_thread_info *tmp_pinfo;
+ int i;
+ e2fsck_t thread_ctx;
+#ifdef DEBUG_THREADS
+ struct e2fsck_thread_debug thread_debug =
+ {PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER, 0};
+
+ thread_debug.etd_finished_threads = 0;
+#endif
+
+ retval = pthread_attr_init(&attr);
+ if (retval) {
+ com_err(global_ctx->program_name, retval,
+ _("while setting pthread attribute\n"));
+ return retval;
+ }
+
+ infos = calloc(num_threads, sizeof(struct e2fsck_thread_info));
+ if (infos == NULL) {
+ retval = -ENOMEM;
+ com_err(global_ctx->program_name, retval,
+ _("while allocating memory for threads\n"));
+ pthread_attr_destroy(&attr);
+ return retval;
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ tmp_pinfo = &infos[i];
+ tmp_pinfo->eti_thread_index = i;
+#ifdef DEBUG_THREADS
+ tmp_pinfo->eti_debug = &thread_debug;
+#endif
+ retval = e2fsck_pass1_thread_prepare(global_ctx, &thread_ctx,
+ i, num_threads);
+ if (retval) {
+ com_err(global_ctx->program_name, retval,
+ _("while preparing pass1 thread\n"));
+ break;
+ }
+ tmp_pinfo->eti_thread_ctx = thread_ctx;
+
+ retval = pthread_create(&tmp_pinfo->eti_thread_id, &attr,
+ &e2fsck_pass1_thread, tmp_pinfo);
+ if (retval) {
+ com_err(global_ctx->program_name, retval,
+ _("while creating thread\n"));
+ e2fsck_pass1_thread_join(global_ctx, thread_ctx);
+ break;
+ }
+
+ tmp_pinfo->eti_started = 1;
+ }
+
+ /* destroy the thread attribute object, since it is no longer needed */
+ ret = pthread_attr_destroy(&attr);
+ if (ret) {
+ com_err(global_ctx->program_name, ret,
+ _("while destroying thread attribute\n"));
+ if (retval == 0)
+ retval = ret;
+ }
+
+ if (retval) {
+ e2fsck_pass1_threads_join(infos, num_threads, global_ctx);
+ return retval;
+ }
+ *pinfo = infos;
+ return 0;
+}
+
+static void e2fsck_pass1_multithread(e2fsck_t global_ctx)
+{
+ struct e2fsck_thread_info *infos = NULL;
+ int num_threads = 1;
+ errcode_t retval;
+
+ retval = e2fsck_pass1_threads_start(&infos, num_threads, global_ctx);
+ if (retval) {
+ com_err(global_ctx->program_name, retval,
+ _("while starting pass1 threads\n"));
+ goto out_abort;
+ }
+
+ retval = e2fsck_pass1_threads_join(infos, num_threads, global_ctx);
+ if (retval) {
+ com_err(global_ctx->program_name, retval,
+ _("while joining pass1 threads\n"));
+ goto out_abort;
+ }
+ return;
+out_abort:
+ global_ctx->flags |= E2F_FLAG_ABORT;
+ return;
+}
+#endif
+
+/* TODO: tdb needs to be handled properly for multiple threads*/
+static int multiple_threads_supported(e2fsck_t ctx)
+{
+#ifdef CONFIG_TDB
+ unsigned int threshold;
+ ext2_ino_t num_dirs;
+ errcode_t retval;
+ char *tdb_dir;
+ int enable;
+
+ profile_get_string(ctx->profile, "scratch_files", "directory", 0, 0,
+ &tdb_dir);
+ profile_get_uint(ctx->profile, "scratch_files",
+ "numdirs_threshold", 0, 0, &threshold);
+ profile_get_boolean(ctx->profile, "scratch_files",
+ "icount", 0, 1, &enable);
+
+ retval = ext2fs_get_num_dirs(ctx->fs, &num_dirs);
+ if (retval)
+ num_dirs = 1024; /* Guess */
+
+ /* tdb is unsupported now */
+ if (enable && tdb_dir && !access(tdb_dir, W_OK) &&
+ (!threshold || num_dirs > threshold))
+ return 0;
+ #endif
+ return 1;
+}
+
+void e2fsck_pass1(e2fsck_t ctx)
+{
+
+ init_ext2_max_sizes();
+#ifdef HAVE_PTHREAD
+ if (multiple_threads_supported(ctx)) {
+ e2fsck_pass1_multithread(ctx);
+ return;
+ }
+ fprintf(stderr, "Fall through single thread for pass1 "
+ "because tdb could not handle properly\n");
+#endif
+ e2fsck_pass1_run(ctx);
+}
+
+#undef FINISH_INODE_LOOP
/*
* When the inode_scan routines call this callback at the end of the
{
struct scan_callback_struct *scan_struct;
e2fsck_t ctx;
+ struct e2fsck_thread *tinfo;
scan_struct = (struct scan_callback_struct *) priv_data;
ctx = scan_struct->ctx;
- process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf);
+ process_inodes((e2fsck_t) fs->priv_data, scan_struct->block_buf,
+ scan_struct->inodes_to_process,
+ scan_struct->process_inode_count);
if (ctx->progress)
if ((ctx->progress)(ctx, 1, group+1,
ctx->fs->group_desc_count))
return EXT2_ET_CANCEL_REQUESTED;
+#ifdef HAVE_PTHREAD
+ if (ctx->global_ctx) {
+ tinfo = &ctx->thread_info;
+ tinfo->et_group_next++;
+ if (ctx->options & E2F_OPT_DEBUG &&
+ ctx->options & E2F_OPT_MULTITHREAD)
+ log_out(ctx, _("group %d finished\n"),
+ tinfo->et_group_next);
+ if (tinfo->et_group_next >= tinfo->et_group_end)
+ return EXT2_ET_SCAN_FINISHED;
+ }
+#endif
+
return 0;
}
/*
* Process the inodes in the "inodes to process" list.
*/
-static void process_inodes(e2fsck_t ctx, char *block_buf)
+static void process_inodes(e2fsck_t ctx, char *block_buf,
+ struct process_inode_block *inodes_to_process,
+ int *process_inode_count)
{
int i;
struct ext2_inode *old_stashed_inode;
#if 0
printf("begin process_inodes: ");
#endif
- if (process_inode_count == 0)
+ if (*process_inode_count == 0)
return;
old_operation = ehandler_operation(0);
old_stashed_inode = ctx->stashed_inode;
old_stashed_ino = ctx->stashed_ino;
- qsort(inodes_to_process, process_inode_count,
+ qsort(inodes_to_process, *process_inode_count,
sizeof(struct process_inode_block), process_inode_cmp);
clear_problem_context(&pctx);
- for (i=0; i < process_inode_count; i++) {
+ for (i=0; i < *process_inode_count; i++) {
pctx.inode = ctx->stashed_inode =
(struct ext2_inode *) &inodes_to_process[i].inode;
pctx.ino = ctx->stashed_ino = inodes_to_process[i].ino;
sprintf(buf, _("reading indirect blocks of inode %u"),
pctx.ino);
ehandler_operation(buf);
- check_blocks(ctx, &pctx, block_buf);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ check_blocks(ctx, &pctx, block_buf,
+ &inodes_to_process[i].ea_ibody_quota);
+ if (e2fsck_should_abort(ctx))
break;
}
ctx->stashed_inode = old_stashed_inode;
ctx->stashed_ino = old_stashed_ino;
- process_inode_count = 0;
+ *process_inode_count = 0;
#if 0
printf("end process inodes\n");
#endif
ext2fs_mark_inode_bitmap2(ctx->inode_bad_map, ino);
}
-static void add_encrypted_dir(e2fsck_t ctx, ino_t ino)
+static void add_casefolded_dir(e2fsck_t ctx, ino_t ino)
{
struct problem_context pctx;
- if (!ctx->encrypted_dirs) {
- pctx.errcode = ext2fs_u32_list_create(&ctx->encrypted_dirs, 0);
+ if (!ctx->casefolded_dirs) {
+ pctx.errcode = ext2fs_u32_list_create(&ctx->casefolded_dirs, 0);
if (pctx.errcode)
goto error;
}
- pctx.errcode = ext2fs_u32_list_add(ctx->encrypted_dirs, ino);
+ pctx.errcode = ext2fs_u32_list_add(ctx->casefolded_dirs, ino);
if (pctx.errcode == 0)
return;
error:
- fix_problem(ctx, PR_1_ALLOCATE_ENCRYPTED_DIRLIST, &pctx);
+ fix_problem(ctx, PR_1_ALLOCATE_CASEFOLDED_DIRLIST, &pctx);
/* Should never get here */
ctx->flags |= E2F_FLAG_ABORT;
}
clear_problem_context(&pctx);
if (ext2fs_fast_test_block_bitmap2(ctx->block_found_map, block)) {
+ if (ext2fs_has_feature_shared_blocks(ctx->fs->super) &&
+ !(ctx->options & E2F_OPT_UNSHARE_BLOCKS)) {
+ return;
+ }
if (!ctx->block_dup_map) {
pctx.errcode = e2fsck_allocate_block_bitmap(ctx->fs,
_("multiply claimed block map"),
}
}
+/*
+ * When cluster size is greater than one block, it is caller's responsibility
+ * to make sure block parameter starts at a cluster boundary.
+ */
static _INLINE_ void mark_blocks_used(e2fsck_t ctx, blk64_t block,
unsigned int num)
{
if (ext2fs_test_block_bitmap_range2(ctx->block_found_map, block, num))
ext2fs_mark_block_bitmap_range2(ctx->block_found_map, block, num);
- else
- while (num--)
- mark_block_used(ctx, block++);
+ else {
+ unsigned int i;
+
+ for (i = 0; i < num; i += EXT2FS_CLUSTER_RATIO(ctx->fs))
+ mark_block_used(ctx, block + i);
+ }
}
/*
ext2_filsys fs = ctx->fs;
blk64_t blk;
__u32 should_be;
- int count;
+ ea_value_t count;
clear_problem_context(&pctx);
}
header = (struct ext2_ext_attr_header *) block_buf;
pctx.blkcount = header->h_refcount;
- should_be = header->h_refcount + adjust_sign * count;
+ should_be = header->h_refcount + adjust_sign * (int)count;
pctx.num = should_be;
if (fix_problem(ctx, PR_1_EXTATTR_REFCOUNT, &pctx)) {
header->h_refcount = should_be;
* Handle processing the extended attribute blocks
*/
static int check_ext_attr(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf)
+ char *block_buf, struct ea_quota *ea_block_quota)
{
ext2_filsys fs = ctx->fs;
ext2_ino_t ino = pctx->ino;
blk64_t blk;
char * end;
struct ext2_ext_attr_header *header;
- struct ext2_ext_attr_entry *entry;
- int count;
+ struct ext2_ext_attr_entry *first, *entry;
+ blk64_t quota_blocks = EXT2FS_C2B(fs, 1);
+ __u64 quota_inodes = 0;
region_t region = 0;
int failed_csum = 0;
+ ea_block_quota->blocks = 0;
+ ea_block_quota->inodes = 0;
+
blk = ext2fs_file_acl_block(fs, inode);
if (blk == 0)
return 0;
/* Have we seen this EA block before? */
if (ext2fs_fast_test_block_bitmap2(ctx->block_ea_map, blk)) {
+ ea_block_quota->blocks = EXT2FS_C2B(fs, 1);
+ ea_block_quota->inodes = 0;
+
+ if (ctx->ea_block_quota_blocks) {
+ ea_refcount_fetch(ctx->ea_block_quota_blocks, blk,
+ "a_blocks);
+ if (quota_blocks)
+ ea_block_quota->blocks = quota_blocks;
+ }
+
+ if (ctx->ea_block_quota_inodes)
+ ea_refcount_fetch(ctx->ea_block_quota_inodes, blk,
+ &ea_block_quota->inodes);
+
if (ea_refcount_decrement(ctx->refcount, blk, 0) == 0)
return 1;
/* Ooops, this EA was referenced more than it stated */
goto clear_extattr;
}
- entry = (struct ext2_ext_attr_entry *)(header+1);
+ first = (struct ext2_ext_attr_entry *)(header+1);
end = block_buf + fs->blocksize;
+ entry = first;
while ((char *)entry < end && *(__u32 *)entry) {
__u32 hash;
goto clear_extattr;
break;
}
- if (entry->e_value_block != 0) {
- if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
- goto clear_extattr;
- }
- if (entry->e_value_offs + entry->e_value_size > fs->blocksize) {
- if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
- goto clear_extattr;
- break;
- }
- if (entry->e_value_size &&
- region_allocate(region, entry->e_value_offs,
- EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
- if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION, pctx))
- goto clear_extattr;
- }
+ if (entry->e_value_inum == 0) {
+ if (entry->e_value_size > EXT2_XATTR_SIZE_MAX ||
+ (entry->e_value_offs + entry->e_value_size >
+ fs->blocksize)) {
+ if (fix_problem(ctx, PR_1_EA_BAD_VALUE, pctx))
+ goto clear_extattr;
+ break;
+ }
+ if (entry->e_value_size &&
+ region_allocate(region, entry->e_value_offs,
+ EXT2_EXT_ATTR_SIZE(entry->e_value_size))) {
+ if (fix_problem(ctx, PR_1_EA_ALLOC_COLLISION,
+ pctx))
+ goto clear_extattr;
+ }
- hash = ext2fs_ext_attr_hash_entry(entry, block_buf +
- entry->e_value_offs);
+ hash = ext2fs_ext_attr_hash_entry(entry, block_buf +
+ entry->e_value_offs);
- if (entry->e_hash != hash) {
- pctx->num = entry->e_hash;
- if (fix_problem(ctx, PR_1_ATTR_HASH, pctx))
+ if (entry->e_hash != hash) {
+ pctx->num = entry->e_hash;
+ if (fix_problem(ctx, PR_1_ATTR_HASH, pctx))
+ goto clear_extattr;
+ entry->e_hash = hash;
+ }
+ } else {
+ problem_t problem;
+ blk64_t entry_quota_blocks;
+
+ problem = check_large_ea_inode(ctx, entry, pctx,
+ &entry_quota_blocks);
+ if (problem && fix_problem(ctx, problem, pctx))
goto clear_extattr;
- entry->e_hash = hash;
+
+ quota_blocks += entry_quota_blocks;
+ quota_inodes++;
}
entry = EXT2_EXT_ATTR_NEXT(entry);
return 0;
}
- count = header->h_refcount - 1;
- if (count)
- ea_refcount_store(ctx->refcount, blk, count);
+ if (quota_blocks != EXT2FS_C2B(fs, 1U)) {
+ if (!ctx->ea_block_quota_blocks) {
+ pctx->errcode = ea_refcount_create(0,
+ &ctx->ea_block_quota_blocks);
+ if (pctx->errcode) {
+ pctx->num = 3;
+ goto refcount_fail;
+ }
+ }
+ ea_refcount_store(ctx->ea_block_quota_blocks, blk,
+ quota_blocks);
+ }
+
+ if (quota_inodes) {
+ if (!ctx->ea_block_quota_inodes) {
+ pctx->errcode = ea_refcount_create(0,
+ &ctx->ea_block_quota_inodes);
+ if (pctx->errcode) {
+ pctx->num = 4;
+refcount_fail:
+ fix_problem(ctx, PR_1_ALLOCATE_REFCOUNT, pctx);
+ ctx->flags |= E2F_FLAG_ABORT;
+ return 0;
+ }
+ }
+
+ ea_refcount_store(ctx->ea_block_quota_inodes, blk,
+ quota_inodes);
+ }
+ ea_block_quota->blocks = quota_blocks;
+ ea_block_quota->inodes = quota_inodes;
+
+ inc_ea_inode_refs(ctx, pctx, first, end);
+ ea_refcount_store(ctx->refcount, blk, header->h_refcount - 1);
mark_block_used(ctx, blk);
ext2fs_fast_mark_block_bitmap2(ctx->block_ea_map, blk);
return 1;
if ((root->hash_version != EXT2_HASH_LEGACY) &&
(root->hash_version != EXT2_HASH_HALF_MD4) &&
(root->hash_version != EXT2_HASH_TEA) &&
+ (root->hash_version != EXT2_HASH_SIPHASH) &&
fix_problem(ctx, PR_1_HTREE_HASHV, pctx))
return 1;
+ if (ext4_hash_in_dirent(inode)) {
+ if (root->hash_version != EXT2_HASH_SIPHASH &&
+ fix_problem(ctx, PR_1_HTREE_NEEDS_SIPHASH, pctx))
+ return 1;
+ } else {
+ if (root->hash_version == EXT2_HASH_SIPHASH &&
+ fix_problem(ctx, PR_1_HTREE_CANNOT_SIPHASH, pctx))
+ return 1;
+ }
+
if ((root->unused_flags & EXT2_HASH_FLAG_INCOMPAT) &&
fix_problem(ctx, PR_1_HTREE_INCOMPAT, pctx))
return 1;
pctx->num = root->indirect_levels;
- if ((root->indirect_levels > 1) &&
+ /* if htree level is clearly too high, consider it to be broken */
+ if (root->indirect_levels > EXT4_HTREE_LEVEL &&
fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
return 1;
+ /* if level is only maybe too high, LARGE_DIR feature could be unset */
+ if (root->indirect_levels > ext2_dir_htree_level(fs) &&
+ !ext2fs_has_feature_largedir(fs->super)) {
+ int blockbits = EXT2_BLOCK_SIZE_BITS(fs->super) + 10;
+ unsigned idx_pb = 1 << (blockbits - 3);
+
+ /* compare inode size/blocks vs. max-sized 2-level htree */
+ if (EXT2_I_SIZE(pctx->inode) <
+ (idx_pb - 1) * (idx_pb - 2) << blockbits &&
+ pctx->inode->i_blocks <
+ (idx_pb - 1) * (idx_pb - 2) << (blockbits - 9) &&
+ fix_problem(ctx, PR_1_HTREE_DEPTH, pctx))
+ return 1;
+ }
+
+ if (root->indirect_levels > EXT4_HTREE_LEVEL_COMPAT ||
+ ext2fs_needs_large_file_feature(EXT2_I_SIZE(inode)))
+ ctx->large_dirs++;
+
return 0;
}
{
struct ext2fs_extent extent;
blk64_t blk, last_lblk;
- e2_blkcnt_t blockcnt;
- unsigned int i;
+ unsigned int i, n;
int is_dir, is_leaf;
problem_t problem;
struct ext2_extent_info info;
if (pctx->errcode)
return;
if (!(ctx->options & E2F_OPT_FIXES_ONLY) &&
- !pb->eti.force_rebuild) {
+ !pb->eti.force_rebuild &&
+ info.curr_level < MAX_EXTENT_DEPTH_COUNT) {
struct extent_tree_level *etl;
etl = pb->eti.ext_info + info.curr_level;
else if (extent.e_lblk < start_block)
problem = PR_1_OUT_OF_ORDER_EXTENTS;
else if ((end_block && last_lblk > end_block) &&
- (!(extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT &&
- last_lblk > eof_block)))
+ !(last_lblk > eof_block &&
+ ((extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) ||
+ (pctx->inode->i_flags & EXT4_VERITY_FL))))
problem = PR_1_EXTENT_END_OUT_OF_BOUNDS;
else if (is_leaf && extent.e_len == 0)
problem = PR_1_EXTENT_LENGTH_ZERO;
(extent.e_pblk + extent.e_len) >
ext2fs_blocks_count(ctx->fs->super))
problem = PR_1_EXTENT_ENDS_BEYOND;
- else if (is_leaf && is_dir &&
+ else if (is_leaf && is_dir && !pctx->inode->i_size_high &&
+ !ext2fs_has_feature_largedir(ctx->fs->super) &&
((extent.e_lblk + extent.e_len) >
(1U << (21 - ctx->fs->super->s_log_block_size))))
problem = PR_1_TOOBIG_DIR;
- if (is_leaf && problem == 0 && extent.e_len > 0 &&
- region_allocate(pb->region, extent.e_lblk, extent.e_len))
- problem = PR_1_EXTENT_COLLISION;
+ if (is_leaf && problem == 0 && extent.e_len > 0) {
+#if 0
+ printf("extent_region(ino=%u, expect=%llu, "
+ "lblk=%llu, len=%u)\n", pb->ino,
+ (unsigned long long) pb->next_lblock,
+ (unsigned long long) extent.e_lblk,
+ extent.e_len);
+#endif
+ if (extent.e_lblk < pb->next_lblock)
+ problem = PR_1_EXTENT_COLLISION;
+ else if (extent.e_lblk + extent.e_len > pb->next_lblock)
+ pb->next_lblock = extent.e_lblk + extent.e_len;
+ }
/*
* Uninitialized blocks in a directory? Clear the flag and
return;
failed_csum = 0;
}
-
+#ifdef CONFIG_DEVELOPER_FEATURES
+ if (try_repairs && !is_dir && problem == 0 &&
+ (ctx->options & E2F_OPT_CLEAR_UNINIT) &&
+ (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) &&
+ fix_problem(ctx, PR_1_CLEAR_UNINIT_EXTENT, pctx)) {
+ extent.e_flags &= ~EXT2_EXTENT_FLAGS_UNINIT;
+ pb->inode_modified = 1;
+ pctx->errcode = ext2fs_extent_replace(ehandle, 0,
+ &extent);
+ if (pctx->errcode)
+ return;
+ failed_csum = 0;
+ }
+#endif
if (try_repairs && problem) {
report_problem:
if (fix_problem(ctx, problem, pctx)) {
* will reallocate the block; then we can try again.
*/
if (pb->ino != EXT2_RESIZE_INO &&
+ extent.e_pblk < ctx->fs->super->s_blocks_count &&
ext2fs_test_block_bitmap2(ctx->block_metadata_map,
extent.e_pblk)) {
next_try_repairs = 0;
fix_problem(ctx,
PR_1_CRITICAL_METADATA_COLLISION,
pctx);
- ctx->flags |= E2F_FLAG_RESTART_LATER;
+ if ((ctx->options & E2F_OPT_NO) == 0)
+ ctx->flags |= E2F_FLAG_RESTART_LATER;
}
pctx->errcode = ext2fs_extent_get(ehandle,
EXT2_EXTENT_DOWN, &extent);
if (extent.e_lblk != lblk) {
struct ext2_extent_info e_info;
- ext2fs_extent_get_info(ehandle, &e_info);
+ pctx->errcode = ext2fs_extent_get_info(ehandle,
+ &e_info);
+ if (pctx->errcode) {
+ pctx->str = "ext2fs_extent_get_info";
+ return;
+ }
pctx->blk = lblk;
pctx->blk2 = extent.e_lblk;
pctx->num = e_info.curr_level - 1;
}
}
alloc_later:
- while (is_dir && (++pb->last_db_block <
- (e2_blkcnt_t) extent.e_lblk)) {
- pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist,
- pb->ino, 0,
- pb->last_db_block);
- if (pctx->errcode) {
- pctx->blk = 0;
- pctx->num = pb->last_db_block;
- goto failed_add_dir_block;
- }
- }
- if (!ctx->fs->cluster_ratio_bits) {
- mark_blocks_used(ctx, extent.e_pblk, extent.e_len);
- pb->num_blocks += extent.e_len;
- }
- for (blk = extent.e_pblk, blockcnt = extent.e_lblk, i = 0;
- i < extent.e_len;
- blk++, blockcnt++, i++) {
- if (ctx->fs->cluster_ratio_bits &&
- !(pb->previous_block &&
- (EXT2FS_B2C(ctx->fs, blk) ==
- EXT2FS_B2C(ctx->fs, pb->previous_block)) &&
- (blk & EXT2FS_CLUSTER_MASK(ctx->fs)) ==
- ((unsigned) blockcnt & EXT2FS_CLUSTER_MASK(ctx->fs)))) {
- mark_block_used(ctx, blk);
- pb->num_blocks++;
- }
- if (has_unaligned_cluster_map(ctx, pb->previous_block,
- pb->last_block, blk,
- blockcnt)) {
- pctx->blk = blockcnt;
- pctx->blk2 = blk;
- fix_problem(ctx, PR_1_MISALIGNED_CLUSTER, pctx);
- mark_block_used(ctx, blk);
- mark_block_used(ctx, blk);
+ if (is_dir) {
+ while (++pb->last_db_block <
+ (e2_blkcnt_t) extent.e_lblk) {
+ pctx->errcode = ext2fs_add_dir_block2(
+ ctx->fs->dblist,
+ pb->ino, 0,
+ pb->last_db_block);
+ if (pctx->errcode) {
+ pctx->blk = 0;
+ pctx->num = pb->last_db_block;
+ goto failed_add_dir_block;
+ }
}
- pb->last_block = blockcnt;
- pb->previous_block = blk;
- if (is_dir) {
- pctx->errcode = ext2fs_add_dir_block2(ctx->fs->dblist, pctx->ino, blk, blockcnt);
+ for (i = 0; i < extent.e_len; i++) {
+ pctx->errcode = ext2fs_add_dir_block2(
+ ctx->fs->dblist,
+ pctx->ino,
+ extent.e_pblk + i,
+ extent.e_lblk + i);
if (pctx->errcode) {
- pctx->blk = blk;
- pctx->num = blockcnt;
+ pctx->blk = extent.e_pblk + i;
+ pctx->num = extent.e_lblk + i;
failed_add_dir_block:
fix_problem(ctx, PR_1_ADD_DBLOCK, pctx);
/* Should never get here */
return;
}
}
+ if (extent.e_len > 0)
+ pb->last_db_block = extent.e_lblk + extent.e_len - 1;
+ }
+ if (has_unaligned_cluster_map(ctx, pb->previous_block,
+ pb->last_block,
+ extent.e_pblk,
+ extent.e_lblk)) {
+ for (i = 0; i < extent.e_len; i++) {
+ pctx->blk = extent.e_lblk + i;
+ pctx->blk2 = extent.e_pblk + i;
+ fix_problem(ctx, PR_1_MISALIGNED_CLUSTER, pctx);
+ mark_block_used(ctx, extent.e_pblk + i);
+ mark_block_used(ctx, extent.e_pblk + i);
+ }
}
- if (is_dir && extent.e_len > 0)
- pb->last_db_block = blockcnt - 1;
+
+ /*
+ * Check whether first cluster got marked in previous iteration.
+ */
+ if (ctx->fs->cluster_ratio_bits &&
+ pb->previous_block &&
+ (EXT2FS_B2C(ctx->fs, extent.e_pblk) ==
+ EXT2FS_B2C(ctx->fs, pb->previous_block)))
+ /* Set blk to the beginning of next cluster. */
+ blk = EXT2FS_C2B(
+ ctx->fs,
+ EXT2FS_B2C(ctx->fs, extent.e_pblk) + 1);
+ else
+ /* Set blk to the beginning of current cluster. */
+ blk = EXT2FS_C2B(ctx->fs,
+ EXT2FS_B2C(ctx->fs, extent.e_pblk));
+
+ if (blk < extent.e_pblk + extent.e_len) {
+ mark_blocks_used(ctx, blk,
+ extent.e_pblk + extent.e_len - blk);
+ n = DIV_ROUND_UP(extent.e_pblk + extent.e_len - blk,
+ EXT2FS_CLUSTER_RATIO(ctx->fs));
+ pb->num_blocks += n;
+ }
+ pb->last_block = extent.e_lblk + extent.e_len - 1;
pb->previous_block = extent.e_pblk + extent.e_len - 1;
start_block = pb->last_block = last_lblk;
if (is_leaf && !is_dir &&
memset(pb->eti.ext_info, 0, sizeof(pb->eti.ext_info));
pb->eti.ino = pb->ino;
- pb->region = region_create(0, info.max_lblk);
- if (!pb->region) {
- ext2fs_extent_free(ehandle);
- fix_problem(ctx, PR_1_EXTENT_ALLOC_REGION_ABORT, pctx);
- ctx->flags |= E2F_FLAG_ABORT;
- return;
- }
+ pb->next_lblock = 0;
eof_lblk = ((EXT2_I_SIZE(inode) + fs->blocksize - 1) >>
EXT2_BLOCK_SIZE_BITS(fs->super)) - 1;
"check_blocks_extents");
pctx->errcode = 0;
}
- region_free(pb->region);
- pb->region = NULL;
ext2fs_extent_free(ehandle);
/* Rebuild unless it's a dir and we're rehashing it */
* blocks used by that inode.
*/
static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
- char *block_buf)
+ char *block_buf, const struct ea_quota *ea_ibody_quota)
{
ext2_filsys fs = ctx->fs;
struct process_block_struct pb;
int extent_fs;
int inlinedata_fs;
__u64 size;
+ struct ea_quota ea_block_quota;
pb.ino = ino;
- pb.num_blocks = 0;
+ pb.num_blocks = EXT2FS_B2C(ctx->fs,
+ ea_ibody_quota ? ea_ibody_quota->blocks : 0);
pb.last_block = ~0;
pb.last_init_lblock = -1;
pb.last_db_block = -1;
pb.previous_block = 0;
pb.is_dir = LINUX_S_ISDIR(inode->i_mode);
pb.is_reg = LINUX_S_ISREG(inode->i_mode);
- pb.max_blocks = 1 << (31 - fs->super->s_log_block_size);
+ pb.max_blocks = 1U << (31 - fs->super->s_log_block_size);
pb.inode = inode;
pb.pctx = pctx;
pb.ctx = ctx;
extent_fs = ext2fs_has_feature_extents(ctx->fs->super);
inlinedata_fs = ext2fs_has_feature_inline_data(ctx->fs->super);
- if (check_ext_attr(ctx, pctx, block_buf)) {
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (check_ext_attr(ctx, pctx, block_buf, &ea_block_quota)) {
+ if (e2fsck_should_abort(ctx))
goto out;
- pb.num_blocks++;
+ pb.num_blocks += EXT2FS_B2C(ctx->fs, ea_block_quota.blocks);
}
if (inlinedata_fs && (inode->i_flags & EXT4_INLINE_DATA_FL))
}
end_problem_latch(ctx, PR_LATCH_BLOCK);
end_problem_latch(ctx, PR_LATCH_TOOBIG);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
goto out;
if (pctx->errcode)
fix_problem(ctx, PR_1_BLOCK_ITERATE, pctx);
inode->i_flags &= ~EXT2_INDEX_FL;
dirty_inode++;
} else {
- e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
+ e2fsck_add_dx_dir(ctx, ino, inode, pb.last_block+1);
}
}
}
if (ino != quota_type2inum(PRJQUOTA, fs->super) &&
- (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super))) {
+ (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
+ !(inode->i_flags & EXT4_EA_INODE_FL)) {
quota_data_add(ctx->qctx, (struct ext2_inode_large *) inode,
- ino, pb.num_blocks * fs->blocksize);
+ ino,
+ pb.num_blocks * EXT2_CLUSTER_SIZE(fs->super));
quota_data_inodes(ctx->qctx, (struct ext2_inode_large *) inode,
- ino, +1);
+ ino, (ea_ibody_quota ?
+ ea_ibody_quota->inodes : 0) +
+ ea_block_quota.inodes + 1);
}
if (!ext2fs_has_feature_huge_file(fs->super) ||
pb.num_blocks *= EXT2FS_CLUSTER_RATIO(fs);
#if 0
printf("inode %u, i_size = %u, last_block = %llu, i_blocks=%llu, num_blocks = %llu\n",
- ino, inode->i_size, pb.last_block, ext2fs_inode_i_blocks(fs, inode),
- pb.num_blocks);
+ ino, inode->i_size, (unsigned long long) pb.last_block,
+ (unsigned long long) ext2fs_inode_i_blocks(fs, inode),
+ (unsigned long long) pb.num_blocks);
#endif
+ size = EXT2_I_SIZE(inode);
if (pb.is_dir) {
- unsigned nblock = inode->i_size >> EXT2_BLOCK_SIZE_BITS(fs->super);
+ unsigned nblock = size >> EXT2_BLOCK_SIZE_BITS(fs->super);
if (inode->i_flags & EXT4_INLINE_DATA_FL) {
int flags;
size_t sz = 0;
EXT2_FLAG_IGNORE_CSUM_ERRORS) |
(ctx->fs->flags &
~EXT2_FLAG_IGNORE_CSUM_ERRORS);
- if (err || sz != inode->i_size) {
+ if (err || sz != size) {
bad_size = 7;
pctx->num = sz;
}
- } else if (inode->i_size & (fs->blocksize - 1))
+ } else if (size & (fs->blocksize - 1))
bad_size = 5;
else if (nblock > (pb.last_block + 1))
bad_size = 1;
bad_size = 2;
}
} else {
- e2_blkcnt_t blkpg = ctx->blocks_per_page;
-
- size = EXT2_I_SIZE(inode);
if ((pb.last_init_lblock >= 0) &&
- /* allow allocated blocks to end of PAGE_SIZE */
+ /* Do not allow initialized allocated blocks past i_size*/
(size < (__u64)pb.last_init_lblock * fs->blocksize) &&
- (pb.last_init_lblock / blkpg * blkpg != pb.last_init_lblock ||
- size < (__u64)(pb.last_init_lblock & ~(blkpg-1)) *
- fs->blocksize))
+ !(inode->i_flags & EXT4_VERITY_FL))
bad_size = 3;
else if (!(extent_fs && (inode->i_flags & EXT4_EXTENTS_FL)) &&
size > ext2_max_sizes[fs->super->s_log_block_size])
pctx->num = (pb.last_block + 1) * fs->blocksize;
pctx->group = bad_size;
if (fix_problem(ctx, PR_1_BAD_I_SIZE, pctx)) {
- if (LINUX_S_ISDIR(inode->i_mode))
- pctx->num &= 0xFFFFFFFFULL;
ext2fs_inode_size_set(fs, inode, pctx->num);
if (EXT2_I_SIZE(inode) == 0 &&
(inode->i_flags & EXT4_INLINE_DATA_FL)) {
(unsigned long) pctx->ino, type,
(unsigned long) p->previous_block+1,
(unsigned long) blk,
- blockcnt);
+ (long long) blockcnt);
}
p->fragmented = 1;
}
}
- if (p->is_dir && blockcnt > (1 << (21 - fs->super->s_log_block_size)))
+ if (p->is_dir && !ext2fs_has_feature_largedir(fs->super) &&
+ !pctx->inode->i_size_high &&
+ blockcnt > (1 << (21 - fs->super->s_log_block_size)))
problem = PR_1_TOOBIG_DIR;
- if (p->is_reg && p->num_blocks+1 >= p->max_blocks)
+ if (p->is_dir && p->num_blocks + 1 >= p->max_blocks)
+ problem = PR_1_TOOBIG_DIR;
+ if (p->is_reg && p->num_blocks + 1 >= p->max_blocks)
problem = PR_1_TOOBIG_REG;
if (!p->is_dir && !p->is_reg && blockcnt > 0)
problem = PR_1_TOOBIG_SYMLINK;
*/
if (blockcnt < 0 &&
p->ino != EXT2_RESIZE_INO &&
+ blk < ctx->fs->super->s_blocks_count &&
ext2fs_test_block_bitmap2(ctx->block_metadata_map, blk)) {
pctx->blk = blk;
fix_problem(ctx, PR_1_CRITICAL_METADATA_COLLISION, pctx);
- ctx->flags |= E2F_FLAG_RESTART_LATER;
+ if ((ctx->options & E2F_OPT_NO) == 0)
+ ctx->flags |= E2F_FLAG_RESTART_LATER;
}
if (problem) {
*block_nr = 0;
return BLOCK_CHANGED;
}
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
return BLOCK_ABORT;
} else
mark_block_used(ctx, blk);
*block_nr = 0;
return BLOCK_CHANGED;
}
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ if (e2fsck_should_abort(ctx))
return BLOCK_ABORT;
return 0;
}
*/
is_flexbg = ext2fs_has_feature_flex_bg(fs->super);
if (is_flexbg) {
- flexbg_size = 1 << fs->super->s_log_groups_per_flex;
+ flexbg_size = 1U << fs->super->s_log_groups_per_flex;
flexbg = group / flexbg_size;
first_block = ext2fs_group_first_block2(fs,
flexbg_size * flexbg);
}
/*
- * Thes subroutines short circuits ext2fs_get_blocks and
+ * These subroutines short circuits ext2fs_get_blocks and
* ext2fs_check_directory; we use them since we already have the inode
* structure, so there's no point in letting the ext2fs library read
* the inode again.