* - 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() */
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;
+ struct dx_dirblock_info *dx_db;
int b;
int i, depth;
problem_t code;
* 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;
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;
- }
+ destroy_encrypted_file_info(ctx);
clear_problem_context(&pctx);
if (ctx->large_files) {
}
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;
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)
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;
size_t max_block_size;
+ int hash_flags = 0;
cd = (struct check_dir_struct *) priv_data;
ibuf = buf = cd->buf;
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;
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 = (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);
dict_init(&de_dict, DICTCOUNT_T_MAX, dict_de_cmp);
prev = 0;
}
}
- 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) {
+ /* 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 (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;
+
+ 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;
}
/*
- * This fuction deallocates an inode
+ * This function deallocates an inode
*/
static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
{
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)
{
} 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++;