libext2fs: support encoding when calculating dx hashes

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>
This commit is contained in:
Gabriel Krisman Bertazi 2018-11-30 19:39:06 -05:00 committed by Theodore Ts'o
parent ab93e18318
commit 28b44ef08a
10 changed files with 100 additions and 18 deletions

View 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 \

View 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");

View 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);

View 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);
}
}

View 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)

View 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++;
}
}

View 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

View 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 \

View File

@ -14,9 +14,11 @@
#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);
}

View 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,