* - 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)
- * - A bitmap of EA inodes. (inode_ea_map)
+ * - Ref counts for ea_inodes. (ea_inode_refs)
*
* 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.)
*
#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);
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;
};
{
unsigned int len;
int i;
- blk64_t blocks;
ext2_extent_handle_t handle;
struct ext2_extent_info info;
struct ext2fs_extent extent;
return 1;
}
- blocks = ext2fs_inode_data_blocks2(fs, inode);
- if (blocks) {
- if (inode->i_flags & EXT4_INLINE_DATA_FL)
+ if (ext2fs_is_fast_symlink(inode)) {
+ if (inode->i_size >= sizeof(inode->i_block))
return 0;
+
+ len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
+ if (len == sizeof(inode->i_block))
+ return 0;
+ } else {
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)))
return 0;
return 0;
if (inode->i_flags & EXT4_ENCRYPT_FL) {
- len = ext2fs_le32_to_cpu(*((__u32 *)buf)) + 4;
+ len = ext2fs_le16_to_cpu(*((__u16 *)buf)) + 2;
} else {
len = strnlen(buf, fs->blocksize);
}
- 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))
- return 0;
-
- len = strnlen((char *)inode->i_block, sizeof(inode->i_block));
- if (len == sizeof(inode->i_block))
+ if (len >= fs->blocksize)
return 0;
}
if (len != inode->i_size)
e2fsck_write_inode(ctx, pctx->ino, pctx->inode, "pass1");
}
-static void mark_inode_ea_map(e2fsck_t ctx, struct problem_context *pctx,
- ext2_ino_t ino)
+/*
+ * 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)
{
- if (!ctx->inode_ea_map) {
- pctx->errcode = ext2fs_allocate_inode_bitmap(ctx->fs,
- _("EA inode map"),
- &ctx->inode_ea_map);
- if (pctx->errcode) {
- fix_problem(ctx, PR_1_ALLOCATE_IBITMAP_ERROR,
- pctx);
- exit(1);
- }
- }
+ blk64_t clusters;
- ext2fs_mark_inode_bitmap2(ctx->inode_ea_map, ino);
+ clusters = DIV_ROUND_UP(size, fs->blocksize << fs->cluster_ratio_bits);
+ return EXT2FS_C2B(fs, clusters);
}
/*
*/
static problem_t check_large_ea_inode(e2fsck_t ctx,
struct ext2_ext_attr_entry *entry,
- struct problem_context *pctx)
+ struct problem_context *pctx,
+ blk64_t *quota_blocks)
{
struct ext2_inode inode;
__u32 hash;
fatal_error(ctx, 0);
}
- if (hash != entry->e_hash) {
+ 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) {
-
+ 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.
*/
return 0;
}
-static void check_ea_in_inode(e2fsck_t ctx, struct problem_context *pctx)
+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;
+
+ for (entry = first;
+ (void *)entry < end && !EXT2_EXT_IS_LAST_ENTRY(entry);
+ entry = EXT2_EXT_ATTR_NEXT(entry)) {
+ if (!entry->e_value_inum)
+ continue;
+ 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);
+ }
+}
+
+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;
goto fix;
}
} else {
- problem = check_large_ea_inode(ctx, entry, pctx);
+ blk64_t quota_blocks;
+
+ problem = check_large_ea_inode(ctx, entry, pctx,
+ "a_blocks);
if (problem != 0)
goto fix;
- mark_inode_ea_map(ctx, pctx, entry->e_value_inum);
+ ea_ibody_quota->blocks += quota_blocks;
+ ea_ibody_quota->inodes++;
}
/* If EA value is stored in external inode then it does not
* 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) {
*/
#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);
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);
}
/*
errcode_t retval;
char *tdb_dir;
int enable;
+ int full_map;
*ret = 0;
}
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;
int failed_csum = 0;
ext2_ino_t ino_threshold = 0;
dgrp_t ra_group = 0;
+ struct ea_quota ea_ibody_quota;
init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&pctx);
/*
* 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_inode_extra_space(ctx, &pctx);
+ check_inode_extra_space(ctx, &pctx, &ea_ibody_quota);
check_is_really_dir(ctx, &pctx, block_buf);
/*
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))) {
- struct ext2_inode_large *ip;
+ struct process_inode_block *itp;
- inodes_to_process[process_inode_count].ino = ino;
- ip = &inodes_to_process[process_inode_count].inode;
+ 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(ip, inode, inode_size);
+ memcpy(&itp->inode, inode, inode_size);
else
- memcpy(ip, inode, sizeof(*ip));
+ 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);
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);
sprintf(buf, _("reading indirect blocks of inode %u"),
pctx.ino);
ehandler_operation(buf);
- check_blocks(ctx, &pctx, block_buf);
+ check_blocks(ctx, &pctx, block_buf,
+ &inodes_to_process[i].ea_ibody_quota);
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
break;
}
* 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;
}
} else {
problem_t problem;
+ blk64_t entry_quota_blocks;
- problem = check_large_ea_inode(ctx, entry, pctx);
- if (problem == 0)
- mark_inode_ea_map(ctx, pctx,
- entry->e_value_inum);
- else if (fix_problem(ctx, problem, pctx))
+ problem = check_large_ea_inode(ctx, entry, pctx,
+ &entry_quota_blocks);
+ if (problem && fix_problem(ctx, problem, pctx))
goto clear_extattr;
+
+ 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, 1)) {
+ 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;
(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, pb->next_lblock,
+ 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
* 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);
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;
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 (check_ext_attr(ctx, pctx, block_buf, &ea_block_quota)) {
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
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))
}
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 * 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) ||
*/
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) {
}
/*
- * 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.