]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
libext2fs: support encoding when calculating dx hashes
authorGabriel Krisman Bertazi <krisman@collabora.co.uk>
Sat, 1 Dec 2018 00:39:06 +0000 (19:39 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 3 Dec 2018 04:58:50 +0000 (23:58 -0500)
fsck must be aware of the superblock encoding and the casefold directory
setting, such that it is able to correctly calculate the dentry hashes.

[ Changed to use a stack-allocated buffer instead of a calling
  calloc() with a fixed size -- TYT ]

Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.co.uk>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/Makefile.in
e2fsck/dx_dirinfo.c
e2fsck/e2fsck.h
e2fsck/pass1.c
e2fsck/pass2.c
e2fsck/rehash.c
e2fsck/unix.c
lib/ext2fs/Makefile.in
lib/ext2fs/dirhash.c
lib/ext2fs/ext2fs.h

index 676ab7ddcc1d2bec00a776d85b5400abea105dc1..9799274fa74efc2f8c209f5dd3d0d7f5eb2dd7d6 100644 (file)
@@ -293,7 +293,8 @@ pass1.o: $(srcdir)/pass1.c $(top_builddir)/lib/config.h \
  $(top_srcdir)/lib/ext2fs/bitops.h $(top_srcdir)/lib/support/profile.h \
  $(top_builddir)/lib/support/prof_err.h $(top_srcdir)/lib/support/quotaio.h \
  $(top_srcdir)/lib/support/dqblk_v2.h \
- $(top_srcdir)/lib/support/quotaio_tree.h $(srcdir)/problem.h
+ $(top_srcdir)/lib/support/quotaio_tree.h $(srcdir)/problem.h \
+ $(top_srcdir)/lib/ext2fs/nls.h
 pass1b.o: $(srcdir)/pass1b.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2_fs.h \
@@ -317,7 +318,7 @@ pass2.o: $(srcdir)/pass2.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/support/prof_err.h $(top_srcdir)/lib/support/quotaio.h \
  $(top_srcdir)/lib/support/dqblk_v2.h \
  $(top_srcdir)/lib/support/quotaio_tree.h $(srcdir)/problem.h \
- $(top_srcdir)/lib/support/dict.h
+ $(top_srcdir)/lib/support/dict.h $(top_srcdir)/lib/ext2fs/nls.h
 pass3.o: $(srcdir)/pass3.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/e2fsck.h \
  $(top_srcdir)/lib/ext2fs/ext2_fs.h $(top_builddir)/lib/ext2fs/ext2_types.h \
@@ -416,7 +417,7 @@ unix.o: $(srcdir)/unix.c $(top_builddir)/lib/config.h \
  $(top_srcdir)/lib/et/com_err.h $(top_srcdir)/lib/support/plausible.h \
  $(srcdir)/e2fsck.h $(top_srcdir)/lib/ext2fs/ext2fs.h \
  $(top_srcdir)/lib/ext2fs/ext3_extents.h $(top_srcdir)/lib/ext2fs/ext2_io.h \
- $(top_builddir)/lib/ext2fs/ext2_err.h \
+ $(top_builddir)/lib/ext2fs/ext2_err.h  $(top_srcdir)/lib/ext2fs/nls.h \
  $(top_srcdir)/lib/ext2fs/ext2_ext_attr.h $(top_srcdir)/lib/ext2fs/hashmap.h \
  $(top_srcdir)/lib/ext2fs/bitops.h $(top_srcdir)/lib/support/profile.h \
  $(top_builddir)/lib/support/prof_err.h $(top_srcdir)/lib/support/quotaio.h \
index c7b605685339d8a5a286cbf1dc2803a6f529fe4f..c0b0e9a41235f6435a6336fd04c335667c384eaf 100644 (file)
@@ -13,7 +13,8 @@
  * entry.  During pass1, the passed-in parent is 0; it will get filled
  * in during pass2.
  */
-void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
+void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, struct ext2_inode *inode,
+                      int num_blocks)
 {
        struct dx_dir_info *dir;
        int             i, j;
@@ -72,6 +73,7 @@ void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks)
        dir->ino = ino;
        dir->numblocks = num_blocks;
        dir->hashversion = 0;
+       dir->casefolded_hash = inode->i_flags & EXT4_CASEFOLD_FL;
        dir->dx_block = e2fsck_allocate_memory(ctx, num_blocks
                                       * sizeof (struct dx_dirblock_info),
                                       "dx_block info array");
index cd5cba2f6031e54771f7befe6ff547a6c9197ddd..1c7a67cba1cedd04e87f929d5fa0bba44d2fad39 100644 (file)
@@ -109,6 +109,7 @@ struct dx_dir_info {
        int                     hashversion;
        short                   depth;          /* depth of tree */
        struct dx_dirblock_info *dx_block;      /* Array of size numblocks */
+       int                     casefolded_hash;
 };
 
 #define DX_DIRBLOCK_ROOT       1
@@ -471,7 +472,8 @@ extern int e2fsck_dir_info_get_dotdot(e2fsck_t ctx, ext2_ino_t ino,
                                      ext2_ino_t *dotdot);
 
 /* dx_dirinfo.c */
-extern void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino, int num_blocks);
+extern void e2fsck_add_dx_dir(e2fsck_t ctx, ext2_ino_t ino,
+                             struct ext2_inode *inode, int num_blocks);
 extern struct dx_dir_info *e2fsck_get_dx_dir_info(e2fsck_t ctx, ext2_ino_t ino);
 extern void e2fsck_free_dx_dir_info(e2fsck_t ctx);
 extern int e2fsck_get_num_dx_dirinfo(e2fsck_t ctx);
index 45534388a90dd0f9364f8c50cf679eea255541bc..5c0b92d5bfa9bb1fdeb2eb16a5d585579933cc55 100644 (file)
@@ -48,6 +48,7 @@
 
 #include "e2fsck.h"
 #include <ext2fs/ext2_ext_attr.h>
+#include <e2p/e2p.h>
 
 #include "problem.h"
 
@@ -3381,7 +3382,7 @@ static void check_blocks(e2fsck_t ctx, struct problem_context *pctx,
                        inode->i_flags &= ~EXT2_INDEX_FL;
                        dirty_inode++;
                } else {
-                       e2fsck_add_dx_dir(ctx, ino, pb.last_block+1);
+                       e2fsck_add_dx_dir(ctx, ino, inode, pb.last_block+1);
                }
        }
 
index b92eec1e149f3e9e30f0fd61e47a705e69c2f430..a7d9c47dbe8e914ec33ee249c80d63ffa50ff1a0 100644 (file)
@@ -933,6 +933,7 @@ static int check_dir_block(ext2_filsys fs,
        int     filetype = 0;
        int     encrypted = 0;
        size_t  max_block_size;
+       int     hash_flags = 0;
 
        cd = (struct check_dir_struct *) priv_data;
        ibuf = buf = cd->buf;
@@ -1426,9 +1427,13 @@ skip_checksum:
                        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)
index 7c4ab0836482dd33ad0a669ad2e8107a800de2c4..a5fc1be1a2104ca8dc93f7a21c6300e6115cf36e 100644 (file)
@@ -113,7 +113,7 @@ static int fill_dir_block(ext2_filsys fs,
        struct ext2_dir_entry   *dirent;
        char                    *dir;
        unsigned int            offset, dir_offset, rec_len, name_len;
-       int                     hash_alg;
+       int                     hash_alg, hash_flags;
 
        if (blockcnt < 0)
                return 0;
@@ -139,6 +139,7 @@ static int fill_dir_block(ext2_filsys fs,
                if (fd->err)
                        return BLOCK_ABORT;
        }
+       hash_flags = fd->inode->i_flags & EXT4_CASEFOLD_FL;
        hash_alg = fs->super->s_def_hash_version;
        if ((hash_alg <= EXT2_HASH_TEA) &&
            (fs->super->s_flags & EXT2_FLAGS_UNSIGNED_HASH))
@@ -184,10 +185,11 @@ static int fill_dir_block(ext2_filsys fs,
                if (fd->compress)
                        ent->hash = ent->minor_hash = 0;
                else {
-                       fd->err = ext2fs_dirhash(hash_alg, dirent->name,
-                                                name_len,
-                                                fs->super->s_hash_seed,
-                                                &ent->hash, &ent->minor_hash);
+                       fd->err = ext2fs_dirhash2(hash_alg,
+                                                 dirent->name, name_len,
+                                                 fs->encoding, hash_flags,
+                                                 fs->super->s_hash_seed,
+                                                 &ent->hash, &ent->minor_hash);
                        if (fd->err)
                                return BLOCK_ABORT;
                }
@@ -371,6 +373,7 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
        char                    new_name[256];
        unsigned int            new_len;
        int                     hash_alg;
+       int hash_flags = fd->inode->i_flags & EXT4_CASEFOLD_FL;
 
        clear_problem_context(&pctx);
        pctx.ino = ino;
@@ -415,9 +418,10 @@ static int duplicate_search_and_fix(e2fsck_t ctx, ext2_filsys fs,
                if (fix_problem(ctx, PR_2_NON_UNIQUE_FILE, &pctx)) {
                        memcpy(ent->dir->name, new_name, new_len);
                        ext2fs_dirent_set_name_len(ent->dir, new_len);
-                       ext2fs_dirhash(hash_alg, new_name, new_len,
-                                      fs->super->s_hash_seed,
-                                      &ent->hash, &ent->minor_hash);
+                       ext2fs_dirhash2(hash_alg, new_name, new_len,
+                                       fs->encoding, hash_flags,
+                                       fs->super->s_hash_seed,
+                                       &ent->hash, &ent->minor_hash);
                        fixed++;
                }
        }
index 2df22b17146fed813952db94158e63b5e6e67271..5b3552ece6b1fcd7f59bb62a742a92914b65856b 100644 (file)
@@ -55,6 +55,7 @@ extern int optind;
 #include "problem.h"
 #include "jfs_user.h"
 #include "../version.h"
+#include <ext2fs/nls.h>
 
 /* Command line options */
 static int cflag;              /* check disk */
@@ -1784,6 +1785,15 @@ print_unsupp_features:
                goto get_newer;
        }
 
+       if (ext2fs_has_feature_fname_encoding(sb)) {
+               fs->encoding = nls_load_table(sb->s_encoding);
+               if (!fs->encoding) {
+                       log_err(ctx, _("%s has unsupported encoding: %0x\n"),
+                               ctx->filesystem_name, sb->s_encoding);
+                       goto get_newer;
+               }
+       }
+
        /*
         * If the user specified a specific superblock, presumably the
         * master superblock has been trashed.  So we mark the
index a2f07403c9ae2e8b4ad37e426e84f89dd0647611..b756bbdf35a597cffb7d46d2444954f0adbc51d2 100644 (file)
@@ -779,7 +779,8 @@ dirhash.o: $(srcdir)/dirhash.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \
  $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \
  $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \
- $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h
+ $(srcdir)/ext2_ext_attr.h $(srcdir)/hashmap.h $(srcdir)/bitops.h \
+ $(srcdir)/nls.h
 dir_iterate.o: $(srcdir)/dir_iterate.c $(top_builddir)/lib/config.h \
  $(top_builddir)/lib/dirpaths.h $(srcdir)/ext2_fs.h \
  $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \
index 4ba3f35c091f2deff7db96f3bd367c25154c070a..f1e7734d38656d2391726323ae5e41b8551e858c 100644 (file)
 #include "config.h"
 #include <stdio.h>
 #include <string.h>
+#include <limits.h>
 
 #include "ext2_fs.h"
 #include "ext2fs.h"
+#include "nls.h"
 
 /*
  * Keyed 32-bit hash function using TEA in a Davis-Meyer function
@@ -184,6 +186,11 @@ static void str2hashbuf(const char *msg, int len, __u32 *buf, int num,
  * A particular hash version specifies whether or not the seed is
  * represented, and whether or not the returned hash is 32 bits or 64
  * bits.  32 bit hashes will return 0 for the minor hash.
+ *
+ * This function doesn't do any normalization or casefolding of the
+ * input string.  To take charset encoding into account, use
+ * ext2fs_dirhash2.
+ *
  */
 errcode_t ext2fs_dirhash(int version, const char *name, int len,
                         const __u32 *seed,
@@ -257,3 +264,44 @@ errcode_t ext2fs_dirhash(int version, const char *name, int len,
                *ret_minor_hash = minor_hash;
        return 0;
 }
+
+/*
+ * Returns the hash of a filename considering normalization and
+ * casefolding.  This is a wrapper around ext2fs_dirhash with string
+ * encoding support based on the nls_table and the flags. Check
+ * ext2fs_dirhash for documentation on the input and output parameters.
+ */
+errcode_t ext2fs_dirhash2(int version, const char *name, int len,
+                         const struct nls_table *charset, int hash_flags,
+                         const __u32 *seed,
+                         ext2_dirhash_t *ret_hash,
+                         ext2_dirhash_t *ret_minor_hash)
+{
+       errcode_t r;
+       int dlen;
+       unsigned char *buff;
+
+       if (len && charset) {
+               char buff[PATH_MAX];
+
+               if (hash_flags & EXT4_CASEFOLD_FL)
+                       dlen = charset->ops->casefold(charset, name, len, buff,
+                                                     sizeof(buff));
+               else
+                       dlen = charset->ops->normalize(charset, name, len, buff,
+                                                      sizeof(buff));
+               if (dlen < 0) {
+                       if (dlen == -EINVAL)
+                               goto opaque_seq;
+
+                       return dlen;
+               }
+               r = ext2fs_dirhash(version, buff, dlen, seed, ret_hash,
+                                  ret_minor_hash);
+               return r;
+       }
+
+opaque_seq:
+       return ext2fs_dirhash(version, name, len, seed, ret_hash,
+                             ret_minor_hash);
+}
index 5b87d3950a09912e6aeb1f7b59d3dae6c75dbd04..041e8dee3f534981a34f24f40dc22a0133f918a2 100644 (file)
@@ -307,6 +307,8 @@ struct struct_ext2_filsys {
 
        /* hashmap for SHA of data blocks */
        struct ext2fs_hashmap* block_sha_map;
+
+       const struct nls_table *encoding;
 };
 
 #if EXT2_FLAT_INCLUDES
@@ -1174,6 +1176,12 @@ extern errcode_t ext2fs_dirhash(int version, const char *name, int len,
                                ext2_dirhash_t *ret_hash,
                                ext2_dirhash_t *ret_minor_hash);
 
+extern errcode_t ext2fs_dirhash2(int version, const char *name, int len,
+                                const struct nls_table *charset,
+                                int hash_flags,
+                                const __u32 *seed,
+                                ext2_dirhash_t *ret_hash,
+                                ext2_dirhash_t *ret_minor_hash);
 
 /* dir_iterate.c */
 extern errcode_t ext2fs_get_rec_len(ext2_filsys fs,