* - The inode_used_map bitmap
* - The inode_bad_map bitmap
* - The inode_dir_map bitmap
+ * - The encrypted_file_info
*
* Pass 2 frees the following data structures
* - The inode_bad_map bitmap
* - The inode_reg_map bitmap
+ * - The encrypted_file_info
*/
#define _GNU_SOURCE 1 /* get strnlen() */
#include "e2fsck.h"
#include "problem.h"
-#include "dict.h"
+#include "support/dict.h"
#ifdef NO_INLINE_FUNCS
#define _INLINE_
* Keeps track of how many times an inode is referenced.
*/
static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf);
+static int check_dir_block2(ext2_filsys fs,
+ struct ext2_db_entry2 *dir_blocks_info,
+ void *priv_data);
static int check_dir_block(ext2_filsys fs,
struct ext2_db_entry2 *dir_blocks_info,
void *priv_data);
struct problem_context pctx;
int count, max;
e2fsck_t ctx;
+ unsigned long long list_offset;
+ unsigned long long ra_entries;
+ unsigned long long next_ra_off;
};
+static void update_parents(struct dx_dir_info *dx_dir, int type)
+{
+ struct dx_dirblock_info *dx_db, *dx_parent, *dx_previous;
+ int b;
+
+ for (b = 0, dx_db = dx_dir->dx_block;
+ b < dx_dir->numblocks;
+ b++, dx_db++) {
+ dx_parent = &dx_dir->dx_block[dx_db->parent];
+ if (dx_db->type != type)
+ continue;
+
+ /*
+ * XXX Make sure dx_parent->min_hash > dx_db->min_hash
+ */
+ if (dx_db->flags & DX_FLAG_FIRST) {
+ dx_parent->min_hash = dx_db->min_hash;
+ if (dx_parent->previous) {
+ dx_previous =
+ &dx_dir->dx_block[dx_parent->previous];
+ dx_previous->node_max_hash =
+ dx_parent->min_hash;
+ }
+ }
+ /*
+ * XXX Make sure dx_parent->max_hash < dx_db->max_hash
+ */
+ if (dx_db->flags & DX_FLAG_LAST) {
+ dx_parent->max_hash = dx_db->max_hash;
+ }
+ }
+}
+
void e2fsck_pass2(e2fsck_t ctx)
{
struct ext2_super_block *sb = ctx->fs->super;
struct problem_context pctx;
ext2_filsys fs = ctx->fs;
- char *buf;
+ char *buf = NULL;
#ifdef RESOURCE_TRACK
struct resource_track rtrack;
#endif
struct check_dir_struct cd;
struct dx_dir_info *dx_dir;
- struct dx_dirblock_info *dx_db, *dx_parent;
- unsigned int save_type;
+ struct dx_dirblock_info *dx_db;
int b;
int i, depth;
problem_t code;
int bad_dir;
+ int (*check_dir_func)(ext2_filsys fs,
+ struct ext2_db_entry2 *dir_blocks_info,
+ void *priv_data);
init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&cd.pctx);
if (!(ctx->options & E2F_OPT_PREEN))
fix_problem(ctx, PR_2_PASS_HEADER, &cd.pctx);
- e2fsck_setup_tdb_icount(ctx, EXT2_ICOUNT_OPT_INCREMENT,
- &ctx->inode_count);
- if (ctx->inode_count)
- cd.pctx.errcode = 0;
- else {
- e2fsck_set_bitmap_type(fs, EXT2FS_BMAP64_RBTREE,
- "inode_count", &save_type);
- cd.pctx.errcode = ext2fs_create_icount2(fs,
- EXT2_ICOUNT_OPT_INCREMENT,
- 0, ctx->inode_link_info,
- &ctx->inode_count);
- fs->default_bitmap_type = save_type;
- }
+ cd.pctx.errcode = e2fsck_setup_icount(ctx, "inode_count",
+ EXT2_ICOUNT_OPT_INCREMENT,
+ ctx->inode_link_info, &ctx->inode_count);
if (cd.pctx.errcode) {
fix_problem(ctx, PR_2_ALLOCATE_ICOUNT, &cd.pctx);
ctx->flags |= E2F_FLAG_ABORT;
- return;
+ goto cleanup;
}
buf = (char *) e2fsck_allocate_memory(ctx, 2*fs->blocksize,
"directory scan buffer");
cd.ctx = ctx;
cd.count = 1;
cd.max = ext2fs_dblist_count2(fs->dblist);
+ cd.list_offset = 0;
+ cd.ra_entries = ctx->readahead_kb * 1024 / ctx->fs->blocksize;
+ cd.next_ra_off = 0;
if (ctx->progress)
(void) (ctx->progress)(ctx, 2, 0, cd.max);
- if (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_DIR_INDEX)
+ if (ext2fs_has_feature_dir_index(fs->super))
ext2fs_dblist_sort2(fs->dblist, special_dir_block_cmp);
- cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_block,
+ check_dir_func = cd.ra_entries ? check_dir_block2 : check_dir_block;
+ cd.pctx.errcode = ext2fs_dblist_iterate2(fs->dblist, check_dir_func,
&cd);
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART)
- return;
-
if (ctx->flags & E2F_FLAG_RESTART_LATER) {
ctx->flags |= E2F_FLAG_RESTART;
- return;
+ ctx->flags &= ~E2F_FLAG_RESTART_LATER;
}
+ if (ctx->flags & E2F_FLAG_RUN_RETURN)
+ goto cleanup;
+
if (cd.pctx.errcode) {
fix_problem(ctx, PR_2_DBLIST_ITERATE, &cd.pctx);
ctx->flags |= E2F_FLAG_ABORT;
- return;
+ goto cleanup;
}
-#ifdef ENABLE_HTREE
for (i=0; (dx_dir = e2fsck_dx_dir_info_iter(ctx, &i)) != 0;) {
if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return;
- if (dx_dir->numblocks == 0)
+ goto cleanup;
+ if (e2fsck_dir_will_be_rehashed(ctx, dx_dir->ino) ||
+ dx_dir->numblocks == 0)
continue;
clear_problem_context(&pctx);
bad_dir = 0;
* Find all of the first and last leaf blocks, and
* update their parent's min and max hash values
*/
- for (b=0, dx_db = dx_dir->dx_block;
- b < dx_dir->numblocks;
- b++, dx_db++) {
- if ((dx_db->type != DX_DIRBLOCK_LEAF) ||
- !(dx_db->flags & (DX_FLAG_FIRST | DX_FLAG_LAST)))
- continue;
- dx_parent = &dx_dir->dx_block[dx_db->parent];
- /*
- * XXX Make sure dx_parent->min_hash > dx_db->min_hash
- */
- if (dx_db->flags & DX_FLAG_FIRST)
- dx_parent->min_hash = dx_db->min_hash;
- /*
- * XXX Make sure dx_parent->max_hash < dx_db->max_hash
- */
- if (dx_db->flags & DX_FLAG_LAST)
- dx_parent->max_hash = dx_db->max_hash;
- }
+ update_parents(dx_dir, DX_DIRBLOCK_LEAF);
+
+ /* for 3 level htree: update 2 level parent's min
+ * and max hash values */
+ update_parents(dx_dir, DX_DIRBLOCK_NODE);
for (b=0, dx_db = dx_dir->dx_block;
b < dx_dir->numblocks;
}
}
e2fsck_free_dx_dir_info(ctx);
-#endif
+
ext2fs_free_mem(&buf);
ext2fs_free_dblist(fs->dblist);
ext2fs_free_inode_bitmap(ctx->inode_reg_map);
ctx->inode_reg_map = 0;
}
+ destroy_encrypted_file_info(ctx);
clear_problem_context(&pctx);
if (ctx->large_files) {
- if (!(sb->s_feature_ro_compat &
- EXT2_FEATURE_RO_COMPAT_LARGE_FILE) &&
+ if (!ext2fs_has_feature_large_file(sb) &&
fix_problem(ctx, PR_2_FEATURE_LARGE_FILES, &pctx)) {
- sb->s_feature_ro_compat |=
- EXT2_FEATURE_RO_COMPAT_LARGE_FILE;
+ ext2fs_set_feature_large_file(sb);
fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
ext2fs_mark_super_dirty(fs);
}
}
print_resource_track(ctx, _("Pass 2"), &rtrack, fs->io);
+cleanup:
+ ext2fs_free_mem(&buf);
}
#define MAX_DEPTH 32000
if (a_len != b_len)
return (a_len - b_len);
- return strncmp(de_a->name, de_b->name, a_len);
+ return memcmp(de_a->name, de_b->name, a_len);
}
/*
*/
static int check_name(e2fsck_t ctx,
struct ext2_dir_entry *dirent,
- ext2_ino_t dir_ino EXT2FS_ATTR((unused)),
struct problem_context *pctx)
{
int i;
int ret = 0;
for ( i = 0; i < ext2fs_dirent_name_len(dirent); i++) {
- if (dirent->name[i] == '/' || dirent->name[i] == '\0') {
- if (fixup < 0) {
- fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
- }
- if (fixup) {
- dirent->name[i] = '.';
- ret = 1;
- }
- }
+ if (dirent->name[i] != '/' && dirent->name[i] != '\0')
+ continue;
+ if (fixup < 0)
+ fixup = fix_problem(ctx, PR_2_BAD_NAME, pctx);
+ if (fixup == 0)
+ return 0;
+ dirent->name[i] = '.';
+ ret = 1;
}
return ret;
}
+static int encrypted_check_name(e2fsck_t ctx,
+ const struct ext2_dir_entry *dirent,
+ struct problem_context *pctx)
+{
+ if (ext2fs_dirent_name_len(dirent) < EXT4_CRYPTO_BLOCK_SIZE) {
+ if (fix_problem(ctx, PR_2_BAD_ENCRYPTED_NAME, pctx))
+ return 1;
+ ext2fs_unmark_valid(ctx->fs);
+ }
+ return 0;
+}
+
/*
* Check the directory filetype (if present)
*/
int should_be = EXT2_FT_UNKNOWN;
struct ext2_inode inode;
- if (!(ctx->fs->super->s_feature_incompat &
- EXT2_FEATURE_INCOMPAT_FILETYPE)) {
+ if (!ext2fs_has_feature_filetype(ctx->fs->super)) {
if (filetype == 0 ||
!fix_problem(ctx, PR_2_CLEAR_FILETYPE, pctx))
return 0;
return 1;
}
-#ifdef ENABLE_HTREE
static void parse_int_node(ext2_filsys fs,
struct ext2_db_entry2 *db,
struct check_dir_struct *cd,
#endif
count = ext2fs_le16_to_cpu(limit->count);
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+ if (ext2fs_has_feature_metadata_csum(fs->super))
csum_size = sizeof(struct ext2_dx_tail);
expect_limit = (fs->blocksize -
(csum_size + ((char *) ent - block_buf))) /
printf("Entry #%d: Hash 0x%08x, block %u\n", i,
hash, ext2fs_le32_to_cpu(ent[i].block));
#endif
- blk = ext2fs_le32_to_cpu(ent[i].block) & 0x0ffffff;
+ blk = ext2fs_le32_to_cpu(ent[i].block) & EXT4_DX_BLOCK_MASK;
/* Check to make sure the block is valid */
if (blk >= (blk_t) dx_dir->numblocks) {
cd->pctx.blk = blk;
dx_db->flags |= DX_FLAG_REFERENCED;
dx_db->parent = db->blockcnt;
}
+
+ dx_db->previous =
+ i ? (ext2fs_le32_to_cpu(ent[i-1].block) &
+ EXT4_DX_BLOCK_MASK) : 0;
+
if (hash < min_hash)
min_hash = hash;
if (hash > max_hash)
dx_dir->numblocks = 0;
e2fsck_rehash_dir_later(cd->ctx, cd->pctx.ino);
}
-#endif /* ENABLE_HTREE */
/*
* Given a busted directory, try to salvage it somehow.
char *cp = (char *) dirent;
int left;
unsigned int rec_len, prev_rec_len;
- unsigned int name_len = ext2fs_dirent_name_len(dirent);
+ unsigned int name_len;
- (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
+ /*
+ * If the space left for the entry is too small to be an entry,
+ * we can't access dirent's fields, so plumb in the values needed
+ * so that the previous entry absorbs this one.
+ */
+ if (block_len - *offset < EXT2_DIR_ENTRY_HEADER_LEN) {
+ name_len = 0;
+ rec_len = block_len - *offset;
+ } else {
+ name_len = ext2fs_dirent_name_len(dirent);
+ (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
+ }
left = block_len - *offset - rec_len;
/*
* Special case of directory entry of size 8: copy what's left
* of the directory block up to cover up the invalid hole.
*/
- if ((left >= 12) && (rec_len == 8)) {
- memmove(cp, cp+8, left);
- memset(cp + left, 0, 8);
+ if ((left >= 12) && (rec_len == EXT2_DIR_ENTRY_HEADER_LEN)) {
+ memmove(cp, cp+EXT2_DIR_ENTRY_HEADER_LEN, left);
+ memset(cp + left, 0, EXT2_DIR_ENTRY_HEADER_LEN);
return;
}
/*
* record length.
*/
if ((left < 0) &&
- ((int) rec_len + left > 8) &&
- ((int) name_len + 8 <= (int) rec_len + left) &&
+ ((int) rec_len + left > EXT2_DIR_ENTRY_HEADER_LEN) &&
+ ((int) name_len + EXT2_DIR_ENTRY_HEADER_LEN <= (int) rec_len + left) &&
dirent->inode <= fs->super->s_inodes_count &&
strnlen(dirent->name, name_len) == name_len) {
(void) ext2fs_set_rec_len(fs, (int) rec_len + left, dirent);
}
}
-static int is_last_entry(ext2_filsys fs, int inline_data_size,
- unsigned int offset, int csum_size)
-{
- if (inline_data_size)
- return (offset < inline_data_size);
- else
- return (offset < fs->blocksize - csum_size);
-}
-
#define NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len))
static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf)
{
d = NEXT_DIRENT(d);
if (d != top) {
- size_t min_size = EXT2_DIR_REC_LEN(
+ unsigned int min_size = EXT2_DIR_REC_LEN(
ext2fs_dirent_name_len(dirbuf));
- if (min_size > top - (void *)d)
+ if (min_size > (char *)top - (char *)d)
return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
- d->rec_len = top - (void *)d;
+ d->rec_len = (char *)top - (char *)d;
}
t = (struct ext2_dir_entry_tail *)top;
errcode_t retval;
old_size = *inline_data_size;
- new_size = old_size + (4 - (old_size & 3));
+ /*
+ * If there's not enough bytes to start the "second" dir block
+ * (in the EA space) then truncate everything to the first block.
+ */
+ if (old_size > EXT4_MIN_INLINE_DATA_SIZE &&
+ old_size < EXT4_MIN_INLINE_DATA_SIZE +
+ EXT2_DIR_REC_LEN(1)) {
+ old_size = EXT4_MIN_INLINE_DATA_SIZE;
+ new_size = old_size;
+ } else
+ /* Increase to the next four-byte boundary for salvaging */
+ new_size = old_size + (4 - (old_size & 3));
memset(buf + old_size, 0, new_size - old_size);
retval = ext2fs_inline_data_set(fs, ino, 0, buf, new_size);
if (retval == EXT2_ET_INLINE_DATA_NO_SPACE) {
+ /* Or we can't, so truncate. */
new_size -= 4;
retval = ext2fs_inline_data_set(fs, ino, 0, buf, new_size);
if (retval) {
return retval;
}
+/* Return true if this type of file needs encryption */
+static int needs_encryption(e2fsck_t ctx, const struct ext2_dir_entry *dirent)
+{
+ int filetype = ext2fs_dirent_file_type(dirent);
+ ext2_ino_t ino = dirent->inode;
+ struct ext2_inode inode;
+
+ if (filetype != EXT2_FT_UNKNOWN)
+ return filetype == EXT2_FT_REG_FILE ||
+ filetype == EXT2_FT_DIR ||
+ filetype == EXT2_FT_SYMLINK;
+
+ if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map, ino) ||
+ ext2fs_test_inode_bitmap2(ctx->inode_dir_map, ino))
+ return 1;
+
+ e2fsck_read_inode(ctx, ino, &inode, "check_encryption_policy");
+ return LINUX_S_ISREG(inode.i_mode) ||
+ LINUX_S_ISDIR(inode.i_mode) ||
+ LINUX_S_ISLNK(inode.i_mode);
+}
+
+/*
+ * All regular files, directories, and symlinks in encrypted directories must be
+ * encrypted using the same encryption policy as their directory.
+ *
+ * Returns 1 if the dirent should be cleared, otherwise 0.
+ */
+static int check_encryption_policy(e2fsck_t ctx,
+ const struct ext2_dir_entry *dirent,
+ __u32 dir_encpolicy_id,
+ struct problem_context *pctx)
+{
+ __u32 file_encpolicy_id = find_encryption_policy(ctx, dirent->inode);
+
+ /* Same policy or both UNRECOGNIZED_ENCRYPTION_POLICY? */
+ if (file_encpolicy_id == dir_encpolicy_id)
+ return 0;
+
+ if (file_encpolicy_id == NO_ENCRYPTION_POLICY) {
+ if (!needs_encryption(ctx, dirent))
+ return 0;
+ return fix_problem(ctx, PR_2_UNENCRYPTED_FILE, pctx);
+ }
+
+ return fix_problem(ctx, PR_2_INCONSISTENT_ENCRYPTION_POLICY, pctx);
+}
+
+/*
+ * Check an encrypted directory entry.
+ *
+ * Returns 1 if the dirent should be cleared, otherwise 0.
+ */
+static int check_encrypted_dirent(e2fsck_t ctx,
+ const struct ext2_dir_entry *dirent,
+ __u32 dir_encpolicy_id,
+ struct problem_context *pctx)
+{
+ if (encrypted_check_name(ctx, dirent, pctx))
+ return 1;
+ if (check_encryption_policy(ctx, dirent, dir_encpolicy_id, pctx))
+ return 1;
+ return 0;
+}
+
+static int check_dir_block2(ext2_filsys fs,
+ struct ext2_db_entry2 *db,
+ void *priv_data)
+{
+ int err;
+ struct check_dir_struct *cd = priv_data;
+
+ if (cd->ra_entries && cd->list_offset >= cd->next_ra_off) {
+ err = e2fsck_readahead_dblist(fs,
+ E2FSCK_RA_DBLIST_IGNORE_BLOCKCNT,
+ fs->dblist,
+ cd->list_offset + cd->ra_entries / 8,
+ cd->ra_entries);
+ if (err)
+ cd->ra_entries = 0;
+ cd->next_ra_off = cd->list_offset + (cd->ra_entries * 7 / 8);
+ }
+
+ err = check_dir_block(fs, db, priv_data);
+ cd->list_offset++;
+ return err;
+}
+
static int check_dir_block(ext2_filsys fs,
struct ext2_db_entry2 *db,
void *priv_data)
{
struct dx_dir_info *dx_dir;
-#ifdef ENABLE_HTREE
struct dx_dirblock_info *dx_db = 0;
-#endif /* ENABLE_HTREE */
struct ext2_dir_entry *dirent, *prev, dot, dotdot;
ext2_dirhash_t hash;
unsigned int offset = 0;
ext2_ino_t subdir_parent;
__u16 links;
struct check_dir_struct *cd;
- char *buf;
+ char *buf, *ibuf;
e2fsck_t ctx;
problem_t problem;
struct ext2_dx_root_info *root;
int is_leaf = 1;
size_t inline_data_size = 0;
int filetype = 0;
+ __u32 dir_encpolicy_id = NO_ENCRYPTION_POLICY;
+ size_t max_block_size;
+ int hash_flags = 0;
cd = (struct check_dir_struct *) priv_data;
- buf = cd->buf;
+ ibuf = buf = cd->buf;
ctx = cd->ctx;
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK || ctx->flags & E2F_FLAG_RESTART)
+ if (ctx->flags & E2F_FLAG_RUN_RETURN)
return DIRENT_ABORT;
if (ctx->progress && (ctx->progress)(ctx, 2, cd->count++, cd->max))
return DIRENT_ABORT;
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
+ if (ext2fs_has_feature_metadata_csum(fs->super)) {
dx_csum_size = sizeof(struct ext2_dx_tail);
de_csum_size = sizeof(struct ext2_dir_entry_tail);
}
- if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
- EXT2_FEATURE_INCOMPAT_FILETYPE))
+ if (ext2fs_has_feature_filetype(fs->super))
filetype = EXT2_FT_DIR << 8;
/*
cd->pctx.dirent = 0;
cd->pctx.num = 0;
- if (EXT2_HAS_INCOMPAT_FEATURE(fs->super,
- EXT4_FEATURE_INCOMPAT_INLINE_DATA)) {
+ if (ext2fs_has_feature_inline_data(fs->super)) {
errcode_t ec;
ec = ext2fs_inline_data_size(fs, ino, &inline_data_size);
return DIRENT_ABORT;
}
+ /* This will allow (at some point in the future) to punch out empty
+ * directory blocks and reduce the space used by a directory that grows
+ * very large and then the files are deleted. For now, all that is
+ * needed is to avoid e2fsck filling in these holes as part of
+ * feature flag. */
+ if (db->blk == 0 && ext2fs_has_feature_largedir(fs->super) &&
+ !ext2fs_has_feature_inline_data(fs->super))
+ return 0;
+
if (db->blk == 0 && !inline_data_size) {
if (allocate_dir_block(ctx, db, buf, &cd->pctx))
return 0;
ehandler_operation(_("reading directory block"));
if (inline_data_size) {
+ memset(buf, 0, fs->blocksize - inline_data_size);
cd->pctx.errcode = ext2fs_inline_data_get(fs, ino, 0, buf, 0);
if (cd->pctx.errcode)
goto inline_read_fail;
#ifdef WORDS_BIGENDIAN
+ if (db->blockcnt)
+ goto skip_first_read_swab;
*((__u32 *)buf) = ext2fs_le32_to_cpu(*((__u32 *)buf));
cd->pctx.errcode = ext2fs_dirent_swab_in2(fs,
buf + EXT4_INLINE_DATA_DOTDOT_SIZE,
- inline_data_size - EXT4_INLINE_DATA_DOTDOT_SIZE,
+ EXT4_MIN_INLINE_DATA_SIZE - EXT4_INLINE_DATA_DOTDOT_SIZE,
+ 0);
+ if (cd->pctx.errcode)
+ goto inline_read_fail;
+skip_first_read_swab:
+ if (inline_data_size <= EXT4_MIN_INLINE_DATA_SIZE ||
+ !db->blockcnt)
+ goto inline_read_fail;
+ cd->pctx.errcode = ext2fs_dirent_swab_in2(fs,
+ buf + EXT4_MIN_INLINE_DATA_SIZE,
+ inline_data_size - EXT4_MIN_INLINE_DATA_SIZE,
0);
#endif
} else
inline_read_fail:
pctx.ino = ino;
pctx.num = inline_data_size;
- if ((inline_data_size & 3) &&
+ if (((inline_data_size & 3) ||
+ (inline_data_size > EXT4_MIN_INLINE_DATA_SIZE &&
+ inline_data_size < EXT4_MIN_INLINE_DATA_SIZE +
+ EXT2_DIR_REC_LEN(1))) &&
fix_problem(ctx, PR_2_BAD_INLINE_DIR_SIZE, &pctx)) {
errcode_t err = fix_inline_dir_size(ctx, ino,
&inline_data_size, &pctx,
memcpy(buf, buf2, fs->blocksize);
ext2fs_free_mem(&buf2);
}
-#ifdef ENABLE_HTREE
dx_dir = e2fsck_get_dx_dir_info(ctx, ino);
if (dx_dir && dx_dir->numblocks) {
if (db->blockcnt >= dx_dir->numblocks) {
+ pctx.dir = ino;
if (fix_problem(ctx, PR_2_UNEXPECTED_HTREE_BLOCK,
&pctx)) {
clear_htree(ctx, ino);
dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
if ((root->reserved_zero ||
root->info_length < 8 ||
- root->indirect_levels > 1) &&
+ root->indirect_levels >=
+ ext2_dir_htree_level(fs)) &&
fix_problem(ctx, PR_2_HTREE_BAD_ROOT, &cd->pctx)) {
clear_htree(ctx, ino);
dx_dir->numblocks = 0;
- dx_db = 0;
+ dx_db = NULL;
}
dx_dir->hashversion = root->hash_version;
if ((dx_dir->hashversion <= EXT2_HASH_TEA) &&
(ext2fs_dirent_name_len(dirent) == 0) &&
(ext2fs_le16_to_cpu(limit->limit) ==
((fs->blocksize - (8 + dx_csum_size)) /
- sizeof(struct ext2_dx_entry))))
+ sizeof(struct ext2_dx_entry)))) {
dx_db->type = DX_DIRBLOCK_NODE;
- is_leaf = 0;
+ }
+ is_leaf = dx_db ? (dx_db->type == DX_DIRBLOCK_LEAF) : 0;
}
out_htree:
-#endif /* ENABLE_HTREE */
/* Leaf node with no space for csum? Rebuild dirs in pass 3A. */
if (is_leaf && !inline_data_size && failed_csum &&
!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
de_csum_size = 0;
- if (e2fsck_dir_will_be_rehashed(ctx, ino) ||
- !fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
+ if (e2fsck_dir_will_be_rehashed(ctx, ino)) {
+ failed_csum = 0;
+ goto skip_checksum;
+ }
+ if (!fix_problem(cd->ctx, PR_2_LEAF_NODE_MISSING_CSUM,
&cd->pctx))
goto skip_checksum;
e2fsck_rehash_dir_later(ctx, ino);
+ failed_csum = 0;
goto skip_checksum;
}
/* htree nodes don't use fake dirents to store checksums */
de_csum_size = 0;
skip_checksum:
+ if (inline_data_size) {
+ if (db->blockcnt) {
+ buf += EXT4_MIN_INLINE_DATA_SIZE;
+ max_block_size = inline_data_size - EXT4_MIN_INLINE_DATA_SIZE;
+ /* Zero-length second block, just exit */
+ if (max_block_size == 0)
+ return 0;
+ } else {
+ max_block_size = EXT4_MIN_INLINE_DATA_SIZE;
+ }
+ } else
+ max_block_size = fs->blocksize - de_csum_size;
+
+ dir_encpolicy_id = find_encryption_policy(ctx, ino);
+
dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
prev = 0;
do {
problem = 0;
if (!inline_data_size || dot_state > 1) {
- size_t max_block_size = fs->blocksize - de_csum_size;
-
- if (inline_data_size)
- max_block_size = inline_data_size;
dirent = (struct ext2_dir_entry *) (buf + offset);
- (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
+ /*
+ * If there's not even space for the entry header,
+ * force salvaging this dir.
+ */
+ if (max_block_size - offset < EXT2_DIR_ENTRY_HEADER_LEN)
+ rec_len = EXT2_DIR_REC_LEN(1);
+ else
+ (void) ext2fs_get_rec_len(fs, dirent, &rec_len);
cd->pctx.dirent = dirent;
cd->pctx.num = offset;
- if (((offset + rec_len) > fs->blocksize) ||
- (inline_data_size > 0 &&
- (offset + rec_len) > inline_data_size) ||
+ if ((offset + rec_len > max_block_size) ||
(rec_len < 12) ||
((rec_len % 4) != 0) ||
- ((ext2fs_dirent_name_len(dirent) + 8) > rec_len)) {
+ (((unsigned) ext2fs_dirent_name_len(dirent) + EXT2_DIR_ENTRY_HEADER_LEN) > rec_len)) {
if (fix_problem(ctx, PR_2_DIR_CORRUPTED,
&cd->pctx)) {
#ifdef WORDS_BIGENDIAN
* swap routine finds a rec_len that it
* doesn't like, it continues
* processing the block as if rec_len
- * == 8. This means that the name
+ * == EXT2_DIR_ENTRY_HEADER_LEN. This means that the name
* field gets byte swapped, which means
* that salvage will not detect the
* correct name length (unless the name
* the salvaged dirent.
*/
int need_reswab = 0;
- if (rec_len < 8 || rec_len % 4) {
+ if (rec_len < EXT2_DIR_ENTRY_HEADER_LEN || rec_len % 4) {
need_reswab = 1;
ext2fs_dirent_swab_in2(fs,
- ((char *)dirent) + 8,
- max_block_size - offset - 8,
+ ((char *)dirent) + EXT2_DIR_ENTRY_HEADER_LEN,
+ max_block_size - offset - EXT2_DIR_ENTRY_HEADER_LEN,
0);
}
#endif
}
}
- if (check_name(ctx, dirent, ino, &cd->pctx))
- dir_modified++;
-
if (check_filetype(ctx, dirent, ino, &cd->pctx))
dir_modified++;
-#ifdef ENABLE_HTREE
+ if (dir_encpolicy_id == NO_ENCRYPTION_POLICY) {
+ /* Unencrypted directory */
+ if (check_name(ctx, dirent, &cd->pctx))
+ dir_modified++;
+ } else {
+ /* Encrypted directory */
+ if (dot_state > 1 &&
+ check_encrypted_dirent(ctx, dirent,
+ dir_encpolicy_id,
+ &cd->pctx)) {
+ dirent->inode = 0;
+ dir_modified++;
+ goto next;
+ }
+ }
+
if (dx_db) {
- ext2fs_dirhash(dx_dir->hashversion, dirent->name,
- ext2fs_dirent_name_len(dirent),
- fs->super->s_hash_seed, &hash, 0);
+ if (dx_dir->casefolded_hash)
+ hash_flags = EXT4_CASEFOLD_FL;
+
+ ext2fs_dirhash2(dx_dir->hashversion, dirent->name,
+ ext2fs_dirent_name_len(dirent),
+ fs->encoding, hash_flags,
+ fs->super->s_hash_seed, &hash, 0);
if (hash < dx_db->min_hash)
dx_db->min_hash = hash;
if (hash > dx_db->max_hash)
dx_db->max_hash = hash;
}
-#endif
/*
* If this is a directory, then mark its parent in its
}
}
dot_state++;
- } while (is_last_entry(fs, inline_data_size, offset, de_csum_size));
+ } while (offset < max_block_size);
#if 0
printf("\n");
#endif
-#ifdef ENABLE_HTREE
if (dx_db) {
#ifdef DX_DEBUG
printf("db_block %d, type %d, min_hash 0x%0x, max_hash 0x%0x\n",
(dx_db->type == DX_DIRBLOCK_NODE))
parse_int_node(fs, db, cd, dx_dir, buf, failed_csum);
}
-#endif /* ENABLE_HTREE */
- if (inline_data_size) {
- if (offset != inline_data_size) {
- cd->pctx.num = rec_len + offset - inline_data_size;
- if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
- dirent->rec_len = cd->pctx.num;
- dir_modified++;
- }
- }
- } else {
- if (offset != fs->blocksize - de_csum_size) {
- cd->pctx.num = rec_len - (fs->blocksize - de_csum_size) +
- offset;
- if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
- dirent->rec_len = cd->pctx.num;
- dir_modified++;
- }
+ if (offset != max_block_size) {
+ cd->pctx.num = rec_len + offset - max_block_size;
+ if (fix_problem(ctx, PR_2_FINAL_RECLEN, &cd->pctx)) {
+ dirent->rec_len = cd->pctx.num;
+ dir_modified++;
}
}
if (dir_modified) {
int flags, will_rehash;
/* leaf block with no tail? Rehash dirs later. */
- if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
- EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) &&
+ if (ext2fs_has_feature_metadata_csum(fs->super) &&
is_leaf &&
!inline_data_size &&
!ext2fs_dirent_has_tail(fs, (struct ext2_dir_entry *)buf)) {
ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
}
if (inline_data_size) {
+ buf = ibuf;
#ifdef WORDS_BIGENDIAN
+ if (db->blockcnt)
+ goto skip_first_write_swab;
*((__u32 *)buf) = ext2fs_le32_to_cpu(*((__u32 *)buf));
cd->pctx.errcode = ext2fs_dirent_swab_out2(fs,
buf + EXT4_INLINE_DATA_DOTDOT_SIZE,
- inline_data_size -
+ EXT4_MIN_INLINE_DATA_SIZE -
EXT4_INLINE_DATA_DOTDOT_SIZE,
0);
+ if (cd->pctx.errcode)
+ goto skip_second_write_swab;
+skip_first_write_swab:
+ if (inline_data_size <= EXT4_MIN_INLINE_DATA_SIZE ||
+ !db->blockcnt)
+ goto skip_second_write_swab;
+ cd->pctx.errcode = ext2fs_dirent_swab_out2(fs,
+ buf + EXT4_MIN_INLINE_DATA_SIZE,
+ inline_data_size -
+ EXT4_MIN_INLINE_DATA_SIZE,
+ 0);
+skip_second_write_swab:
if (cd->pctx.errcode &&
!fix_problem(ctx, PR_2_WRITE_DIRBLOCK, &cd->pctx))
goto abort_free_dict;
struct del_block {
e2fsck_t ctx;
e2_blkcnt_t num;
+ blk64_t last_cluster;
};
/*
void *priv_data)
{
struct del_block *p = priv_data;
+ blk64_t cluster = EXT2FS_B2C(fs, *block_nr);
- if (HOLE_BLKADDR(*block_nr))
+ if (*block_nr == 0)
return 0;
+
+ if (cluster == p->last_cluster)
+ return 0;
+
+ p->last_cluster = cluster;
if ((*block_nr < fs->super->s_first_data_block) ||
(*block_nr >= ext2fs_blocks_count(fs->super)))
return 0;
- if ((*block_nr % EXT2FS_CLUSTER_RATIO(fs)) == 0)
- ext2fs_block_alloc_stats2(fs, *block_nr, -1);
+
+ ext2fs_block_alloc_stats2(fs, *block_nr, -1);
p->num++;
return 0;
}
/*
- * This fuction deallocates an inode
+ * This function deallocates an inode
*/
static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
{
ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
if (ext2fs_file_acl_block(fs, &inode) &&
- (fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
+ ext2fs_has_feature_xattr(fs->super)) {
pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
ext2fs_file_acl_block(fs, &inode),
block_buf, -1, &count, ino);
del_block.ctx = ctx;
del_block.num = 0;
+ del_block.last_cluster = 0;
pctx.errcode = ext2fs_block_iterate3(fs, ino, 0, block_buf,
deallocate_inode_block,
&del_block);
}
/*
- * This fuction clears the htree flag on an inode
+ * This function clears the htree flag on an inode
*/
static void clear_htree(e2fsck_t ctx, ext2_ino_t ino)
{
pctx.inode = &inode;
if (ext2fs_file_acl_block(fs, &inode) &&
- !(fs->super->s_feature_compat & EXT2_FEATURE_COMPAT_EXT_ATTR)) {
+ !ext2fs_has_feature_xattr(fs->super)) {
if (fix_problem(ctx, PR_2_FILE_ACL_ZERO, &pctx)) {
ext2fs_file_acl_block_set(fs, &inode, 0);
inode_modified++;
}
if ((fs->super->s_creator_os == EXT2_OS_LINUX) &&
- !(fs->super->s_feature_ro_compat &
- EXT4_FEATURE_RO_COMPAT_HUGE_FILE) &&
+ !ext2fs_has_feature_huge_file(fs->super) &&
(inode.osd2.linux2.l_i_blocks_hi != 0)) {
pctx.num = inode.osd2.linux2.l_i_blocks_hi;
if (fix_problem(ctx, PR_2_BLOCKS_HI_ZERO, &pctx)) {
}
}
- if (!(fs->super->s_feature_incompat &
- EXT4_FEATURE_INCOMPAT_64BIT) &&
+ if ((fs->super->s_creator_os == EXT2_OS_LINUX) &&
+ !ext2fs_has_feature_64bit(fs->super) &&
inode.osd2.linux2.l_i_file_acl_high != 0) {
pctx.num = inode.osd2.linux2.l_i_file_acl_high;
if (fix_problem(ctx, PR_2_I_FILE_ACL_HI_ZERO, &pctx)) {
} else
not_fixed++;
}
- if (inode.i_dir_acl &&
+ if (inode.i_size_high && !ext2fs_has_feature_largedir(fs->super) &&
LINUX_S_ISDIR(inode.i_mode)) {
- if (fix_problem(ctx, PR_2_DIR_ACL_ZERO, &pctx)) {
- inode.i_dir_acl = 0;
+ if (fix_problem(ctx, PR_2_DIR_SIZE_HIGH_ZERO, &pctx)) {
+ inode.i_size_high = 0;
inode_modified++;
} else
not_fixed++;
pctx->errcode = ext2fs_map_cluster_block(fs, db->ino, &inode,
db->blockcnt, &blk);
if (pctx->errcode || blk == 0) {
- pctx->errcode = ext2fs_new_block2(fs, 0,
+ blk = ext2fs_find_inode_goal(fs, db->ino, &inode, db->blockcnt);
+ pctx->errcode = ext2fs_new_block2(fs, blk,
ctx->block_found_map, &blk);
if (pctx->errcode) {
pctx->str = "ext2fs_new_block";
* Update the inode block count
*/
ext2fs_iblk_add_blocks(fs, &inode, 1);
- if (EXT2_I_SIZE(&inode) < (db->blockcnt+1) * fs->blocksize) {
+ if (EXT2_I_SIZE(&inode) < ((__u64) db->blockcnt+1) * fs->blocksize) {
pctx->errcode = ext2fs_inode_size_set(fs, &inode,
(db->blockcnt+1) * fs->blocksize);
if (pctx->errcode) {