* Pass 2 relies on the following information from previous passes:
* - The directory information collected in pass 1.
* - The inode_used_map bitmap
- * - The inode_bad_map bitmap
+ * - The inode_badness bitmap
* - The inode_dir_map bitmap
+ * - The encrypted_file_info
+ * - The inode_casefold_map bitmap
*
* Pass 2 frees the following data structures
- * - The inode_bad_map bitmap
* - The inode_reg_map bitmap
+ * - The encrypted_file_info
+ * - The inode_casefold_map bitmap
+ * - The inode_badness bitmap
*/
#define _GNU_SOURCE 1 /* get strnlen() */
struct ext2_db_entry2 *dir_blocks_info,
char *buf, struct problem_context *pctx);
static void clear_htree(e2fsck_t ctx, ext2_ino_t ino);
-static int htree_depth(struct dx_dir_info *dx_dir,
- struct dx_dirblock_info *dx_db);
+static short htree_depth(struct dx_dir_info *dx_dir,
+ struct dx_dirblock_info *dx_db);
static EXT2_QSORT_TYPE special_dir_block_cmp(const void *a, const void *b);
struct check_dir_struct {
static void update_parents(struct dx_dir_info *dx_dir, int type)
{
struct dx_dirblock_info *dx_db, *dx_parent, *dx_previous;
- int b;
+ blk_t b;
for (b = 0, dx_db = dx_dir->dx_block;
b < dx_dir->numblocks;
#endif
struct check_dir_struct cd;
struct dx_dir_info *dx_dir;
- struct dx_dirblock_info *dx_db, *dx_parent;
- int b;
- int i, depth;
+ struct dx_dirblock_info *dx_db;
+ blk_t b;
+ ext2_ino_t i;
+ short depth;
problem_t code;
int bad_dir;
int (*check_dir_func)(ext2_filsys fs,
ext2fs_free_mem(&buf);
ext2fs_free_dblist(fs->dblist);
- if (ctx->inode_bad_map) {
- ext2fs_free_inode_bitmap(ctx->inode_bad_map);
- ctx->inode_bad_map = 0;
- }
if (ctx->inode_reg_map) {
ext2fs_free_inode_bitmap(ctx->inode_reg_map);
ctx->inode_reg_map = 0;
}
- if (ctx->encrypted_dirs) {
- ext2fs_u32_list_free(ctx->encrypted_dirs);
- ctx->encrypted_dirs = 0;
+ if (ctx->inode_casefold_map) {
+ ext2fs_free_inode_bitmap(ctx->inode_casefold_map);
+ ctx->inode_casefold_map = 0;
+ }
+ destroy_encrypted_file_info(ctx);
+ if (ctx->casefolded_dirs) {
+ ext2fs_u32_list_free(ctx->casefolded_dirs);
+ ctx->casefolded_dirs = 0;
+ }
+ if (ctx->inode_badness) {
+ ext2fs_free_icount(ctx->inode_badness);
+ ctx->inode_badness = 0;
}
clear_problem_context(&pctx);
}
#define MAX_DEPTH 32000
-static int htree_depth(struct dx_dir_info *dx_dir,
- struct dx_dirblock_info *dx_db)
+static short htree_depth(struct dx_dir_info *dx_dir,
+ struct dx_dirblock_info *dx_db)
{
- int depth = 0;
+ short depth = 0;
while (dx_db->type != DX_DIRBLOCK_ROOT && depth < MAX_DEPTH) {
dx_db = &dx_dir->dx_block[dx_db->parent];
return depth;
}
-static int dict_de_cmp(const void *a, const void *b)
+static int dict_de_cmp(const void *cmp_ctx, const void *a, const void *b)
{
const struct ext2_dir_entry *de_a, *de_b;
int a_len, b_len;
return memcmp(de_a->name, de_b->name, a_len);
}
+static int dict_de_cf_cmp(const void *cmp_ctx, const void *a, const void *b)
+{
+ const struct ext2fs_nls_table *tbl = cmp_ctx;
+ const struct ext2_dir_entry *de_a, *de_b;
+ int a_len, b_len;
+
+ de_a = (const struct ext2_dir_entry *) a;
+ a_len = ext2fs_dirent_name_len(de_a);
+ de_b = (const struct ext2_dir_entry *) b;
+ b_len = ext2fs_dirent_name_len(de_b);
+
+ return ext2fs_casefold_cmp(tbl, (unsigned char *) de_a->name, a_len,
+ (unsigned char *) de_b->name, b_len);
+}
+
/*
* This is special sort function that makes sure that directory blocks
* with a dirblock of zero are sorted to the beginning of the list.
return (int) (db_a->blockcnt - db_b->blockcnt);
}
+void ext2_fix_dirent_dirdata(struct ext2_dir_entry *de)
+{
+ __u16 file_type = de->name_len & (EXT2_FT_MASK << 8);
+ __u8 de_flags = (de->name_len >> 8) & ~EXT2_FT_MASK;
+ __u8 name_len = de->name_len & EXT2_NAME_LEN;
+ __u8 new_flag = 0;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ __u8 flags = new_flag | (1 << i) << 4;
+
+ /* new_flag is accumulating flags that are set in de_flags
+ * and still fit inside rec_len. ext2_get_dirent_dirdata_size()
+ * returns the size of all the dirdata entries in flags, and
+ * chops off any that are beyond rec_len.
+ */
+ if ((de_flags & flags) == flags) {
+ int dirdatalen = ext2_get_dirdata_field_size(de,
+ flags);
+ int rlen = EXT2_DIR_NAME_LEN(name_len + dirdatalen);
+
+ if (rlen > de->rec_len)
+ break;
+
+ new_flag |= flags;
+ }
+ }
+
+ de->name_len = name_len | file_type | (new_flag << 8);
+}
+
+/*
+ * check for dirent data in ext3 dirent.
+ * return 0 if dirent data is ok.
+ * return 1 if dirent data does not exist.
+ * return 2 if dirent was modified due to error.
+ */
+int e2fsck_check_dirent_data(e2fsck_t ctx, struct ext2_dir_entry *de,
+ unsigned int offset, struct problem_context *pctx)
+{
+ if (!(ctx->fs->super->s_feature_incompat &
+ EXT4_FEATURE_INCOMPAT_DIRDATA)) {
+ if ((de->name_len >> 8) & ~EXT2_FT_MASK) {
+ /* clear dirent extra data flags. */
+ if (fix_problem(ctx, PR_2_CLEAR_DIRDATA, pctx)) {
+ de->name_len &= (EXT2_FT_MASK << 8) |
+ EXT2_NAME_LEN;
+ return 2;
+ }
+ }
+ return 1;
+ }
+ if ((de->name_len >> 8) & ~EXT2_FT_MASK) {
+ if (de->rec_len >= EXT2_DIR_REC_LEN(de) ||
+ de->rec_len + offset == EXT2_BLOCK_SIZE(ctx->fs->super)) {
+ if (ext2_get_dirdata_field_size(de,
+ EXT2_DIRENT_LUFID) %
+ EXT2_DIRENT_LUFID_SIZE == 1 /*size*/ + 1 /*NULL*/)
+ return 0;
+ }
+ /* just clear dirent data flags for now, we should fix FID data
+ * in lustre specific pass.
+ */
+ if (fix_problem(ctx, PR_2_CLEAR_DIRDATA, pctx)) {
+ ext2_fix_dirent_dirdata(de);
+ if (ext2_get_dirdata_field_size(de,
+ EXT2_DIRENT_LUFID) !=
+ EXT2_DIRENT_LUFID_SIZE)
+ de->name_len &= ~(EXT2_DIRENT_LUFID << 8);
+
+ return 2;
+ }
+ }
+ return 1;
+}
/*
* Make sure the first entry in the directory is '.', and that the
* directory entry is sane.
*/
static int check_dot(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry *dirent, unsigned int offset,
ext2_ino_t ino, struct problem_context *pctx)
{
struct ext2_dir_entry *nextdir;
int status = 0;
int created = 0;
problem_t problem = 0;
+ int dir_data_error;
+ int ftype = EXT2_FT_DIR;
if (!dirent->inode)
problem = PR_2_MISSING_DOT;
else if (dirent->name[1] != '\0')
problem = PR_2_DOT_NULL_TERM;
+ dir_data_error = e2fsck_check_dirent_data(ctx, dirent, offset, pctx);
+
(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
if (problem) {
+ if (!ext2fs_has_feature_filetype(ctx->fs->super))
+ ftype = EXT2_FT_UNKNOWN;
if (fix_problem(ctx, problem, pctx)) {
if (rec_len < 12)
rec_len = dirent->rec_len = 12;
dirent->inode = ino;
ext2fs_dirent_set_name_len(dirent, 1);
- ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
+ ext2fs_dirent_set_file_type(dirent, ftype);
dirent->name[0] = '.';
dirent->name[1] = '\0';
status = 1;
}
if (rec_len > 12) {
new_len = rec_len - 12;
- if (new_len > 12) {
+ if (new_len > 12 && dir_data_error) {
if (created ||
fix_problem(ctx, PR_2_SPLIT_DOT, pctx)) {
nextdir = (struct ext2_dir_entry *)
((char *) dirent + 12);
dirent->rec_len = 12;
- (void) ext2fs_set_rec_len(ctx->fs, new_len,
- nextdir);
- nextdir->inode = 0;
- ext2fs_dirent_set_name_len(nextdir, 0);
- ext2fs_dirent_set_file_type(nextdir,
- EXT2_FT_UNKNOWN);
+ /* if the next entry looks like "..", leave it
+ * and let check_dotdot() verify the dirent,
+ * otherwise zap the following entry. */
+ if (strncmp(nextdir->name, "..", 3) != 0) {
+ (void)ext2fs_set_rec_len(ctx->fs,
+ new_len,
+ nextdir);
+ nextdir->inode = 0;
+ ext2fs_dirent_set_name_len(nextdir, 0);
+ ext2fs_dirent_set_file_type(nextdir,
+ ftype);
+ }
status = 1;
}
}
* here; this gets done in pass 3.
*/
static int check_dotdot(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
+ struct ext2_dir_entry *dirent, unsigned int offset,
ext2_ino_t ino, struct problem_context *pctx)
{
problem_t problem = 0;
unsigned int rec_len;
+ int dir_data_error;
+ int ftype = EXT2_FT_DIR;
if (!dirent->inode)
problem = PR_2_MISSING_DOT_DOT;
else if (dirent->name[2] != '\0')
problem = PR_2_DOT_DOT_NULL_TERM;
+ dir_data_error = e2fsck_check_dirent_data(ctx, dirent, offset, pctx);
+
(void) ext2fs_get_rec_len(ctx->fs, dirent, &rec_len);
if (problem) {
+ if (!ext2fs_has_feature_filetype(ctx->fs->super))
+ ftype = EXT2_FT_UNKNOWN;
if (fix_problem(ctx, problem, pctx)) {
if (rec_len < 12)
dirent->rec_len = 12;
*/
dirent->inode = EXT2_ROOT_INO;
ext2fs_dirent_set_name_len(dirent, 2);
- ext2fs_dirent_set_file_type(dirent, EXT2_FT_UNKNOWN);
+ ext2fs_dirent_set_file_type(dirent, ftype);
dirent->name[0] = '.';
dirent->name[1] = '.';
dirent->name[2] = '\0';
}
static int encrypted_check_name(e2fsck_t ctx,
- struct ext2_dir_entry *dirent,
+ 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)) {
- dirent->inode = 0;
+ if (fix_problem(ctx, PR_2_BAD_ENCRYPTED_NAME, pctx))
return 1;
- }
ext2fs_unmark_valid(ctx->fs);
}
return 0;
}
+static int encoded_check_name(e2fsck_t ctx,
+ struct ext2_dir_entry *dirent,
+ struct problem_context *pctx)
+{
+ const struct ext2fs_nls_table *tbl = ctx->fs->encoding;
+ int ret;
+ int len = ext2fs_dirent_name_len(dirent);
+ char *pos, *end;
+
+ ret = ext2fs_check_encoded_name(tbl, dirent->name, len, &pos);
+ if (ret < 0) {
+ fatal_error(ctx, _("NLS is broken."));
+ } else if(ret > 0) {
+ ret = fix_problem(ctx, PR_2_BAD_ENCODED_NAME, pctx);
+ if (ret) {
+ end = &dirent->name[len];
+ for (; *pos && pos != end; pos++)
+ *pos = '.';
+ }
+ }
+
+ return (ret || check_name(ctx, dirent, pctx));
+}
+
/*
* Check the directory filetype (if present)
*/
{
int filetype = ext2fs_dirent_file_type(dirent);
int should_be = EXT2_FT_UNKNOWN;
+ __u16 badness = 0;
struct ext2_inode inode;
+ __u8 dirdata = 0;
+
+ if (ext2fs_has_feature_dirdata(ctx->fs->super)) {
+ dirdata = filetype & ~EXT2_FT_MASK;
+ filetype = filetype & EXT2_FT_MASK;
+ }
if (!ext2fs_has_feature_filetype(ctx->fs->super)) {
if (filetype == 0 ||
return 1;
}
+ if (ctx->inode_badness)
+ ext2fs_icount_fetch(ctx->inode_badness, dirent->inode,
+ &badness);
+
if (ext2fs_test_inode_bitmap2(ctx->inode_dir_map, dirent->inode)) {
should_be = EXT2_FT_DIR;
} else if (ext2fs_test_inode_bitmap2(ctx->inode_reg_map,
dirent->inode)) {
should_be = EXT2_FT_REG_FILE;
- } else if (ctx->inode_bad_map &&
- ext2fs_test_inode_bitmap2(ctx->inode_bad_map,
- dirent->inode))
+ } else if (badness & BADNESS_BAD_MODE) {
should_be = 0;
- else {
+ } else {
e2fsck_read_inode(ctx, dirent->inode, &inode,
"check_filetype");
should_be = ext2_file_type(inode.i_mode);
if (fix_problem(ctx, filetype ? PR_2_BAD_FILETYPE : PR_2_SET_FILETYPE,
pctx) == 0)
return 0;
-
- ext2fs_dirent_set_file_type(dirent, should_be);
+ ext2fs_dirent_set_file_type(dirent, should_be | dirdata);
return 1;
}
struct dx_dir_info *dx_dir,
char *block_buf, int failed_csum)
{
- struct ext2_dx_root_info *root;
- struct ext2_dx_entry *ent;
+ struct ext2_dx_root_info *root;
+ struct ext2_dx_entry *ent;
struct ext2_dx_countlimit *limit;
struct dx_dirblock_info *dx_db;
int i, expect_limit, count;
int csum_size = 0;
if (db->blockcnt == 0) {
- root = (struct ext2_dx_root_info *) (block_buf + 24);
+ root = get_ext2_dx_root_info(fs, block_buf);
#ifdef DX_DEBUG
printf("Root node dump:\n");
printf("\t Reserved zero: %u\n", root->reserved_zero);
- printf("\t Hash Version: %d\n", root->hash_version);
- printf("\t Info length: %d\n", root->info_length);
- printf("\t Indirect levels: %d\n", root->indirect_levels);
- printf("\t Flags: %d\n", root->unused_flags);
+ printf("\t Hash Version: %u\n", root->hash_version);
+ printf("\t Info length: %u\n", root->info_length);
+ printf("\t Indirect levels: %u\n", root->indirect_levels);
+ printf("\t Flags: %x\n", root->unused_flags);
#endif
-
- ent = (struct ext2_dx_entry *) (block_buf + 24 + root->info_length);
+ ent = (struct ext2_dx_entry *)((char *)root +
+ root->info_length);
if (failed_csum &&
(e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
&cd->pctx)))
goto clear_and_exit;
} else {
- ent = (struct ext2_dx_entry *) (block_buf+8);
+ ent = (struct ext2_dx_entry *)(block_buf + 8);
if (failed_csum &&
(e2fsck_dir_will_be_rehashed(cd->ctx, cd->pctx.ino) ||
&cd->pctx)))
goto clear_and_exit;
}
-
- limit = (struct ext2_dx_countlimit *) ent;
+ limit = (struct ext2_dx_countlimit *)ent;
#ifdef DX_DEBUG
printf("Number of entries (count): %d\n",
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) {
+ if (blk >= dx_dir->numblocks) {
cd->pctx.blk = blk;
if (fix_problem(cd->ctx, PR_2_HTREE_BADBLK,
&cd->pctx))
}
dx_db->previous =
- i ? ext2fs_le32_to_cpu(ent[i-1].block & 0x0ffffff) : 0;
+ i ? (ext2fs_le32_to_cpu(ent[i-1].block) &
+ EXT4_DX_BLOCK_MASK) : 0;
if (hash < min_hash)
min_hash = hash;
struct ext2_dir_entry *dirent,
struct ext2_dir_entry *prev,
unsigned int *offset,
- unsigned int block_len)
+ unsigned int block_len,
+ int hash_in_dirent)
{
char *cp = (char *) dirent;
int left;
* Special case of directory entry of size 8: copy what's left
* of the directory block up to cover up the invalid hole.
*/
- if ((left >= 12) && (rec_len == EXT2_DIR_ENTRY_HEADER_LEN)) {
+ if ((left >= ext2fs_dir_rec_len(1, hash_in_dirent)) &&
+ (rec_len == EXT2_DIR_ENTRY_HEADER_LEN)) {
memmove(cp, cp+EXT2_DIR_ENTRY_HEADER_LEN, left);
memset(cp + left, 0, EXT2_DIR_ENTRY_HEADER_LEN);
return;
*/
if ((left < 0) &&
((int) rec_len + left > EXT2_DIR_ENTRY_HEADER_LEN) &&
- ((int) name_len + EXT2_DIR_ENTRY_HEADER_LEN <= (int) rec_len + left) &&
+ ((int) ext2fs_dir_rec_len(name_len, hash_in_dirent) <= (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);
}
}
-#define NEXT_DIRENT(d) ((void *)((char *)(d) + (d)->rec_len))
static errcode_t insert_dirent_tail(ext2_filsys fs, void *dirbuf)
{
struct ext2_dir_entry *d;
d = dirbuf;
top = EXT2_DIRENT_TAIL(dirbuf, fs->blocksize);
- while (d->rec_len && !(d->rec_len & 0x3) && NEXT_DIRENT(d) <= top)
- d = NEXT_DIRENT(d);
+ while (d->rec_len && !(d->rec_len & 0x3) && EXT2_NEXT_DIRENT(d) <= top)
+ d = EXT2_NEXT_DIRENT(d);
if (d != top) {
- unsigned int min_size = EXT2_DIR_REC_LEN(
+ unsigned int min_size = EXT2_DIR_NAME_LEN(
ext2fs_dirent_name_len(dirbuf));
if (min_size > (char *)top - (char *)d)
return EXT2_ET_DIR_NO_SPACE_FOR_CSUM;
return 0;
}
-#undef NEXT_DIRENT
static errcode_t fix_inline_dir_size(e2fsck_t ctx, ext2_ino_t ino,
size_t *inline_data_size,
*/
if (old_size > EXT4_MIN_INLINE_DATA_SIZE &&
old_size < EXT4_MIN_INLINE_DATA_SIZE +
- EXT2_DIR_REC_LEN(1)) {
+ EXT2_DIR_NAME_LEN(1)) {
old_size = EXT4_MIN_INLINE_DATA_SIZE;
new_size = old_size;
} else
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 is_leaf = 1;
size_t inline_data_size = 0;
int filetype = 0;
- int encrypted = 0;
+ __u32 dir_encpolicy_id = NO_ENCRYPTION_POLICY;
+ int hash_in_dirent = 0;
+ int casefolded = 0;
size_t max_block_size;
+ int hash_flags = 0;
+ static char *eop_read_dirblock = NULL;
+ int cf_dir = 0;
cd = (struct check_dir_struct *) priv_data;
ibuf = buf = cd->buf;
ctx = cd->ctx;
+ /* We only want filename encoding verification on strict
+ * mode or if explicitly requested by user. */
+ if (ext2fs_test_inode_bitmap2(ctx->inode_casefold_map, ino) &&
+ ((ctx->fs->super->s_encoding_flags & EXT4_ENC_STRICT_MODE_FL) ||
+ (ctx->options & E2F_OPT_CHECK_ENCODING)))
+ cf_dir = 1;
+
if (ctx->flags & E2F_FLAG_RUN_RETURN)
return DIRENT_ABORT;
* 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))
+ 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) {
db->blockcnt, ino);
#endif
- ehandler_operation(_("reading directory block"));
+ if (!eop_read_dirblock)
+ eop_read_dirblock = (char *) _("reading directory block");
+ ehandler_operation(eop_read_dirblock);
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 (((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))) &&
+ EXT2_DIR_NAME_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,
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
limit = (struct ext2_dx_countlimit *) (buf+8);
if (db->blockcnt == 0) {
- root = (struct ext2_dx_root_info *) (buf + 24);
+ root = get_ext2_dx_root_info(fs, buf);
dx_db->type = DX_DIRBLOCK_ROOT;
dx_db->flags |= DX_FLAG_FIRST | DX_FLAG_LAST;
+
+ /* large_dir was set in pass1 if large dirs were found,
+ * so ext2_dir_htree_level() should now be correct */
if ((root->reserved_zero ||
root->info_length < 8 ||
root->indirect_levels >=
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 = (dx_db->type == DX_DIRBLOCK_LEAF);
+ }
+ is_leaf = dx_db ? (dx_db->type == DX_DIRBLOCK_LEAF) : 0;
}
out_htree:
} else
max_block_size = fs->blocksize - de_csum_size;
- if (ctx->encrypted_dirs)
- encrypted = ext2fs_u32_list_test(ctx->encrypted_dirs, ino);
+ dir_encpolicy_id = find_encryption_policy(ctx, ino);
+
+ if (cf_dir) {
+ dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cf_cmp);
+ dict_set_cmp_context(&de_dict, (void *)ctx->fs->encoding);
+ } else {
+ dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
+ }
+ if (ctx->casefolded_dirs)
+ casefolded = ext2fs_u32_list_test(ctx->casefolded_dirs, ino);
+ hash_in_dirent = (casefolded &&
+ (dir_encpolicy_id != NO_ENCRYPTION_POLICY));
- dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
prev = 0;
do {
dgrp_t group;
ext2_ino_t first_unused_inode;
unsigned int name_len;
+ /* csum entry is not checked here, so don't worry about it */
+ int extended = (dot_state > 1) && hash_in_dirent;
+ int min_dir_len = ext2fs_dir_rec_len(1, extended);
problem = 0;
if (!inline_data_size || dot_state > 1) {
* force salvaging this dir.
*/
if (max_block_size - offset < EXT2_DIR_ENTRY_HEADER_LEN)
- rec_len = EXT2_DIR_REC_LEN(1);
+ rec_len = ext2fs_dir_rec_len(1, extended);
else
(void) ext2fs_get_rec_len(fs, dirent, &rec_len);
cd->pctx.dirent = dirent;
cd->pctx.num = offset;
if ((offset + rec_len > max_block_size) ||
- (rec_len < 12) ||
+ (rec_len < min_dir_len) ||
((rec_len % 4) != 0) ||
- (((unsigned) ext2fs_dirent_name_len(dirent) + EXT2_DIR_ENTRY_HEADER_LEN) > rec_len)) {
+ ((ext2fs_dir_rec_len(ext2fs_dirent_name_len(dirent),
+ extended)) > rec_len)) {
if (fix_problem(ctx, PR_2_DIR_CORRUPTED,
&cd->pctx)) {
#ifdef WORDS_BIGENDIAN
#endif
salvage_directory(fs, dirent, prev,
&offset,
- max_block_size);
+ max_block_size,
+ hash_in_dirent);
#ifdef WORDS_BIGENDIAN
if (need_reswab) {
(void) ext2fs_get_rec_len(fs,
memset(&dot, 0, sizeof(dot));
dirent = ˙
dirent->inode = ino;
- dirent->rec_len = EXT2_DIR_REC_LEN(1);
+ dirent->rec_len = EXT2_DIR_NAME_LEN(1);
dirent->name_len = 1 | filetype;
dirent->name[0] = '.';
} else if (dot_state == 1) {
dirent = &dotdot;
dirent->inode =
((struct ext2_dir_entry *)buf)->inode;
- dirent->rec_len = EXT2_DIR_REC_LEN(2);
+ dirent->rec_len = EXT2_DIR_NAME_LEN(2);
dirent->name_len = 2 | filetype;
dirent->name[0] = '.';
dirent->name[1] = '.';
}
if (dot_state == 0) {
- if (check_dot(ctx, dirent, ino, &cd->pctx))
+ if (check_dot(ctx, dirent, offset, ino, &cd->pctx))
dir_modified++;
} else if (dot_state == 1) {
- ret = check_dotdot(ctx, dirent, ino, &cd->pctx);
+ ret = check_dotdot(ctx, dirent, offset, ino, &cd->pctx);
if (ret < 0)
goto abort_free_dict;
if (ret)
if (!dirent->inode)
goto next;
+ ret = e2fsck_check_dirent_data(ctx, dirent, offset, &cd->pctx);
+ if (ret == 2)
+ dir_modified++;
+
/*
* Make sure the inode listed is a legal one.
*/
}
}
- /*
- * If the inode was marked as having bad fields in
- * pass1, process it and offer to fix/clear it.
- * (We wait until now so that we can display the
- * pathname to the user.)
- */
- if (ctx->inode_bad_map &&
- ext2fs_test_inode_bitmap2(ctx->inode_bad_map,
- dirent->inode)) {
- if (e2fsck_process_bad_inode(ctx, ino,
- dirent->inode,
- buf + fs->blocksize)) {
- dirent->inode = 0;
- dir_modified++;
- goto next;
- }
- if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
- return DIRENT_ABORT;
- }
-
group = ext2fs_group_of_ino(fs, dirent->inode);
first_unused_inode = group * fs->super->s_inodes_per_group +
1 + fs->super->s_inodes_per_group -
}
}
+ /*
+ * If the inode was marked as having bad fields in
+ * pass1, process it and offer to fix/clear it.
+ * (We wait until now so that we can display the
+ * pathname to the user.)
+ */
+ if (!(ctx->flags & E2F_FLAG_RESTART_LATER) &&
+ ctx->inode_badness &&
+ ext2fs_icount_is_set(ctx->inode_badness, dirent->inode)) {
+ if (e2fsck_process_bad_inode(ctx, ino, dirent->inode,
+ buf + fs->blocksize)) {
+ dirent->inode = 0;
+ dir_modified++;
+ goto next;
+ }
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return DIRENT_ABORT;
+ }
+
/*
* Offer to clear unused inodes; if we are going to be
* restarting the scan due to bg_itable_unused being
problem = PR_2_UNUSED_INODE;
if (problem) {
- if (fix_problem(ctx, problem, &cd->pctx)) {
+ int next = 0;
+
+ if (fix_problem_bad(ctx, problem, &cd->pctx, 0)) {
dirent->inode = 0;
dir_modified++;
- goto next;
+ next = 1;
} else {
ext2fs_unmark_valid(fs);
if (problem == PR_2_BAD_INO)
- goto next;
+ next = 1;
}
+ if (next)
+ goto next;
}
- if (!encrypted && check_name(ctx, dirent, &cd->pctx))
+ if (check_filetype(ctx, dirent, ino, &cd->pctx))
dir_modified++;
- if (encrypted && (dot_state) > 1 &&
- encrypted_check_name(ctx, dirent, &cd->pctx)) {
- dir_modified++;
- goto next;
+ if (dir_encpolicy_id != NO_ENCRYPTION_POLICY) {
+ /* Encrypted directory */
+ if (dot_state > 1 &&
+ check_encrypted_dirent(ctx, dirent,
+ dir_encpolicy_id,
+ &cd->pctx)) {
+ dirent->inode = 0;
+ dir_modified++;
+ goto next;
+ }
+ } else if (cf_dir) {
+ /* Casefolded directory */
+ if (encoded_check_name(ctx, dirent, &cd->pctx))
+ dir_modified++;
+ } else {
+ /* Unencrypted and uncasefolded directory */
+ if (check_name(ctx, dirent, &cd->pctx))
+ dir_modified++;
}
- if (check_filetype(ctx, dirent, ino, &cd->pctx))
- dir_modified++;
-
if (dx_db) {
- ext2fs_dirhash(dx_dir->hashversion, dirent->name,
- ext2fs_dirent_name_len(dirent),
- fs->super->s_hash_seed, &hash, 0);
+ if (dx_dir->casefolded_hash)
+ hash_flags = EXT4_CASEFOLD_FL;
+
+ if (dx_dir->hashversion == EXT2_HASH_SIPHASH) {
+ if (dot_state > 1)
+ hash = EXT2_DIRENT_HASH(dirent);
+ } else {
+ 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)
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 (*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;
}
struct problem_context pctx;
__u32 count;
struct del_block del_block;
+ int extent_fs = 0;
e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
+ /* ext2fs_block_iterate2() depends on the extents flags */
+ if (inode.i_flags & EXT4_EXTENTS_FL)
+ extent_fs = 1;
+ e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode");
+ if (extent_fs) {
+ inode.i_flags |= EXT4_EXTENTS_FL;
+ e2fsck_write_inode(ctx, ino, &inode, "deallocate_inode");
+ }
clear_problem_context(&pctx);
pctx.ino = ino;
ext2fs_block_alloc_stats2(fs,
ext2fs_file_acl_block(fs, &inode), -1);
}
+ if (ctx->inode_badness)
+ ext2fs_icount_store(ctx->inode_badness, ino, 0);
ext2fs_file_acl_block_set(fs, &inode, 0);
}
if (inode.i_flags & EXT4_INLINE_DATA_FL)
goto clear_inode;
- if (LINUX_S_ISREG(inode.i_mode) &&
- ext2fs_needs_large_file_feature(EXT2_I_SIZE(&inode)))
- ctx->large_files--;
+ if (ext2fs_needs_large_file_feature(EXT2_I_SIZE(&inode))) {
+ if (LINUX_S_ISREG(inode.i_mode))
+ ctx->large_files--;
+ else if (LINUX_S_ISDIR(inode.i_mode))
+ ctx->large_dirs--;
+ }
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);
unsigned char *frag, *fsize;
struct problem_context pctx;
problem_t problem = 0;
+ __u16 badness = 0;
+ unsigned int flags = ctx->fs->flags;
+ flags = ctx->fs->flags;
+ ctx->fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
e2fsck_read_inode(ctx, ino, &inode, "process_bad_inode");
+ ctx->fs->flags = (flags & EXT2_FLAG_IGNORE_CSUM_ERRORS) |
+ (ctx->fs->flags & ~EXT2_FLAG_IGNORE_CSUM_ERRORS);
clear_problem_context(&pctx);
pctx.ino = ino;
else if (LINUX_S_ISSOCK(inode.i_mode)
&& !e2fsck_pass1_check_device_inode(fs, &inode))
problem = PR_2_BAD_SOCKET;
- else if (LINUX_S_ISLNK(inode.i_mode)
- && !e2fsck_pass1_check_symlink(fs, ino, &inode, buf)) {
+ else if (LINUX_S_ISLNK(inode.i_mode) &&
+ !e2fsck_pass1_check_symlink(ctx, ino, &inode, buf)) {
problem = PR_2_INVALID_SYMLINK;
}
} else
not_fixed++;
problem = 0;
+ /*
+ * A large value is associated with bad mode in order to detect
+ * that mode was corrupt in check_filetype()
+ */
+ e2fsck_mark_inode_bad_loc(ctx, &pctx, problem, BADNESS_BAD_MODE,
+ __func__, __LINE__);
}
if (inode.i_faddr) {
not_fixed++;
}
if (inode.i_size_high && !ext2fs_has_feature_largedir(fs->super) &&
+ inode.i_blocks < 1ULL << (29 - EXT2_BLOCK_SIZE_BITS(fs->super)) &&
LINUX_S_ISDIR(inode.i_mode)) {
if (fix_problem(ctx, PR_2_DIR_SIZE_HIGH_ZERO, &pctx)) {
inode.i_size_high = 0;
not_fixed++;
}
+ /* The high value from BADNESS_BAD_MODE should not delete the inode */
+ if (e2fsck_fix_bad_inode(ctx, &pctx)) {
+ deallocate_inode(ctx, ino, 0);
+ if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+ return 0;
+ inode_modified = 0;
+ } else {
+ not_fixed++;
+ }
+
if (inode_modified)
e2fsck_write_inode(ctx, ino, &inode, "process_bad_inode");
- if (!not_fixed && ctx->inode_bad_map)
- ext2fs_unmark_inode_bitmap2(ctx->inode_bad_map, ino);
return 0;
}
char *block;
struct ext2_inode inode;
- if (fix_problem(ctx, PR_2_DIRECTORY_HOLE, pctx) == 0)
+ if (fix_problem_bad(ctx, PR_2_DIRECTORY_HOLE, pctx, 0) == 0)
return 1;
/*