]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blobdiff - e2fsck/pass2.c
Merge branch 'maint' into next
[thirdparty/e2fsprogs.git] / e2fsck / pass2.c
index c1db3a1a5f49b871acfecd59c9d46bded6ebf2b6..8b7f84a7d24d79d5d3432243973f7ea5cfaedd8e 100644 (file)
  *     - The inode_used_map bitmap
  *     - The inode_bad_map 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
  */
 
 #define _GNU_SOURCE 1 /* get strnlen() */
@@ -285,10 +289,11 @@ void e2fsck_pass2(e2fsck_t ctx)
                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;
@@ -327,7 +332,7 @@ static short htree_depth(struct dx_dir_info *dx_dir,
        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;
@@ -343,6 +348,20 @@ static int dict_de_cmp(const void *a, const void *b)
        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, de_a->name, a_len, 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.
@@ -509,19 +528,41 @@ static int check_name(e2fsck_t ctx,
 }
 
 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)
  */
@@ -884,6 +925,71 @@ err:
        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)
@@ -938,17 +1044,25 @@ static int check_dir_block(ext2_filsys fs,
        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;
 
@@ -1162,13 +1276,19 @@ skip_checksum:
        } 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 = encrypted && casefolded;
+       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;
@@ -1435,18 +1555,29 @@ skip_checksum:
                        }
                }
 
-               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) {
                        if (dx_dir->casefolded_hash)
                                hash_flags = EXT4_CASEFOLD_FL;