f2fs-tools: Add support for Casefolding

This adds support for f2fs casefolding. Similarly to ext4 casefolding,
this is controlled per-folder via the +F attribute. It can be toggled on
empty directories only. It is not currently compatible with encryption,
but that will likely change.

When enabling the casefold feature, use the -C flag. The format is:
-C encoding[:flag1,flag2,etc]

Signed-off-by: Daniel Rosenberg <drosen@google.com>
[Jaegeuk Kim: print "casefold" in sb->feature]
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
This commit is contained in:
Daniel Rosenberg 2019-07-11 13:45:41 -07:00 committed by Jaegeuk Kim
parent db84e73777
commit ce64ea0815
14 changed files with 5304 additions and 37 deletions

View File

@ -106,7 +106,7 @@ static struct f2fs_dir_entry *find_in_block(void *block,
return find_target_dentry(name, len, namehash, max_slots, &d);
}
static int find_in_level(struct f2fs_sb_info *sbi,struct f2fs_node *dir,
static int find_in_level(struct f2fs_sb_info *sbi, struct f2fs_node *dir,
unsigned int level, struct dentry *de)
{
unsigned int nbucket, nblock;
@ -120,7 +120,8 @@ static int find_in_level(struct f2fs_sb_info *sbi,struct f2fs_node *dir,
unsigned int dir_level = dir->i.i_dir_level;
int ret = 0;
namehash = f2fs_dentry_hash(de->name, de->len);
namehash = f2fs_dentry_hash(get_encoding(sbi), IS_CASEFOLDED(&dir->i),
de->name, de->len);
nbucket = dir_buckets(level, dir_level);
nblock = bucket_blocks(level);
@ -223,7 +224,9 @@ int f2fs_add_link(struct f2fs_sb_info *sbi, struct f2fs_node *parent,
int level = 0, current_depth, bit_pos;
int nbucket, nblock, bidx, block;
int slots = GET_DENTRY_SLOTS(name_len);
f2fs_hash_t dentry_hash = f2fs_dentry_hash(name, name_len);
f2fs_hash_t dentry_hash = f2fs_dentry_hash(get_encoding(sbi),
IS_CASEFOLDED(&parent->i),
name, name_len);
struct f2fs_dentry_block *dentry_blk;
struct f2fs_dentry_ptr d;
struct dnode_of_data dn;

View File

@ -513,4 +513,9 @@ static inline int is_dot_dotdot(const unsigned char *name, const int len)
return 0;
}
static inline int get_encoding(struct f2fs_sb_info *sbi)
{
return le16_to_cpu(F2FS_RAW_SUPER(sbi)->s_encoding);
}
#endif /* _F2FS_H_ */

View File

@ -833,6 +833,7 @@ void fsck_chk_inode_blk(struct f2fs_sb_info *sbi, u32 nid,
if (blkaddr != 0) {
ret = fsck_chk_data_blk(sbi,
IS_CASEFOLDED(&node_blk->i),
blkaddr,
&child, (i_blocks == *blk_cnt),
ftype, nid, idx, ni->version,
@ -1067,7 +1068,7 @@ int fsck_chk_dnode_blk(struct f2fs_sb_info *sbi, struct f2fs_inode *inode,
if (blkaddr == 0x0)
continue;
ret = fsck_chk_data_blk(sbi,
ret = fsck_chk_data_blk(sbi, IS_CASEFOLDED(inode),
blkaddr, child,
le64_to_cpu(inode->i_blocks) == *blk_cnt, ftype,
nid, idx, ni->version,
@ -1258,11 +1259,11 @@ static void print_dentry(__u32 depth, __u8 *name,
enc_name);
}
static int f2fs_check_hash_code(struct f2fs_dir_entry *dentry,
static int f2fs_check_hash_code(int encoding, int casefolded,
struct f2fs_dir_entry *dentry,
const unsigned char *name, u32 len, int enc_name)
{
f2fs_hash_t hash_code = f2fs_dentry_hash(name, len);
f2fs_hash_t hash_code = f2fs_dentry_hash(encoding, casefolded, name, len);
/* fix hash_code made by old buggy code */
if (dentry->hash_code != hash_code) {
char new[F2FS_PRINT_NAMELEN];
@ -1291,10 +1292,11 @@ static int __get_current_level(int dir_level, u32 pgofs)
return i;
}
static int f2fs_check_dirent_position(u8 *name, u16 name_len, u32 pgofs,
static int f2fs_check_dirent_position(int encoding, int casefolded,
u8 *name, u16 name_len, u32 pgofs,
u8 dir_level, u32 pino)
{
f2fs_hash_t namehash = f2fs_dentry_hash(name, name_len);
f2fs_hash_t namehash = f2fs_dentry_hash(encoding, casefolded, name, name_len);
unsigned int nbucket, nblock;
unsigned int bidx, end_block;
int level;
@ -1318,6 +1320,7 @@ static int f2fs_check_dirent_position(u8 *name, u16 name_len, u32 pgofs,
}
static int __chk_dots_dentries(struct f2fs_sb_info *sbi,
int casefolded,
struct f2fs_dir_entry *dentry,
struct child_info *child,
u8 *name, int len,
@ -1351,7 +1354,7 @@ static int __chk_dots_dentries(struct f2fs_sb_info *sbi,
}
}
if (f2fs_check_hash_code(dentry, name, len, enc_name))
if (f2fs_check_hash_code(get_encoding(sbi), casefolded, dentry, name, len, enc_name))
fixed = 1;
if (name[len] != '\0') {
@ -1371,7 +1374,8 @@ static void nullify_dentry(struct f2fs_dir_entry *dentry, int offs,
memset(*filename, 0, F2FS_SLOT_LEN);
}
static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child,
static int __chk_dentries(struct f2fs_sb_info *sbi, int casefolded,
struct child_info *child,
u8 *bitmap, struct f2fs_dir_entry *dentry,
__u8 (*filenames)[F2FS_SLOT_LEN],
int max, int last_blk, int enc_name)
@ -1464,7 +1468,7 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child,
if ((name[0] == '.' && name_len == 1) ||
(name[0] == '.' && name[1] == '.' &&
name_len == 2)) {
ret = __chk_dots_dentries(sbi, &dentry[i],
ret = __chk_dots_dentries(sbi, casefolded, &dentry[i],
child, name, name_len, &filenames[i],
enc_name);
switch (ret) {
@ -1489,12 +1493,12 @@ static int __chk_dentries(struct f2fs_sb_info *sbi, struct child_info *child,
}
}
if (f2fs_check_hash_code(dentry + i, name, name_len, enc_name))
if (f2fs_check_hash_code(get_encoding(sbi), casefolded, dentry + i, name, name_len, enc_name))
fixed = 1;
if (max == NR_DENTRY_IN_BLOCK) {
ret = f2fs_check_dirent_position(name, name_len,
child->pgofs,
ret = f2fs_check_dirent_position(get_encoding(sbi), casefolded,
name, name_len, child->pgofs,
child->dir_level, child->p_ino);
if (ret) {
if (c.fix_on) {
@ -1560,9 +1564,9 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
make_dentry_ptr(&d, node_blk, inline_dentry, 2);
fsck->dentry_depth++;
dentries = __chk_dentries(sbi, child,
dentries = __chk_dentries(sbi, IS_CASEFOLDED(&node_blk->i), child,
d.bitmap, d.dentry, d.filename, d.max, 1,
file_is_encrypt(&node_blk->i));
file_is_encrypt(&node_blk->i));// pass through
if (dentries < 0) {
DBG(1, "[%3d] Inline Dentry Block Fixed hash_codes\n\n",
fsck->dentry_depth);
@ -1576,7 +1580,7 @@ int fsck_chk_inline_dentries(struct f2fs_sb_info *sbi,
return dentries;
}
int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, int casefolded, u32 blk_addr,
struct child_info *child, int last_blk, int enc_name)
{
struct f2fs_fsck *fsck = F2FS_FSCK(sbi);
@ -1590,7 +1594,7 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
ASSERT(ret >= 0);
fsck->dentry_depth++;
dentries = __chk_dentries(sbi, child,
dentries = __chk_dentries(sbi, casefolded, child,
de_blk->dentry_bitmap,
de_blk->dentry, de_blk->filename,
NR_DENTRY_IN_BLOCK, last_blk, enc_name);
@ -1611,8 +1615,8 @@ int fsck_chk_dentry_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
return 0;
}
int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
struct child_info *child, int last_blk,
int fsck_chk_data_blk(struct f2fs_sb_info *sbi, int casefolded,
u32 blk_addr, struct child_info *child, int last_blk,
enum FILE_TYPE ftype, u32 parent_nid, u16 idx_in_node, u8 ver,
int enc_name)
{
@ -1647,7 +1651,7 @@ int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32 blk_addr,
if (ftype == F2FS_FT_DIR) {
f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_HOT_DATA);
return fsck_chk_dentry_blk(sbi, blk_addr, child,
return fsck_chk_dentry_blk(sbi, casefolded, blk_addr, child,
last_blk, enc_name);
} else {
f2fs_set_main_bitmap(sbi, blk_addr, CURSEG_WARM_DATA);

View File

@ -148,10 +148,10 @@ extern int fsck_chk_idnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
extern int fsck_chk_didnode_blk(struct f2fs_sb_info *, struct f2fs_inode *,
enum FILE_TYPE, struct f2fs_node *, u32 *, struct child_info *);
extern int fsck_chk_data_blk(struct f2fs_sb_info *sbi, u32, struct child_info *,
int, enum FILE_TYPE, u32, u16, u8, int);
extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, u32, struct child_info *,
int, int);
extern int fsck_chk_data_blk(struct f2fs_sb_info *, int,
u32, struct child_info *, int, enum FILE_TYPE, u32, u16, u8, int);
extern int fsck_chk_dentry_blk(struct f2fs_sb_info *, int,
u32, struct child_info *, int, int);
int fsck_chk_inline_dentries(struct f2fs_sb_info *, struct f2fs_node *,
struct child_info *);
void fsck_chk_checkpoint(struct f2fs_sb_info *sbi);

View File

@ -54,6 +54,7 @@ void fsck_usage()
MSG(0, "\nUsage: fsck.f2fs [options] device\n");
MSG(0, "[options]:\n");
MSG(0, " -a check/fix potential corruption, reported by f2fs\n");
MSG(0, " -C encoding[:flag1,flag2] Set options for enabling casefolding\n");
MSG(0, " -d debug level [default:0]\n");
MSG(0, " -f check/fix entire partition\n");
MSG(0, " -g add default options\n");
@ -186,8 +187,9 @@ void f2fs_parse_options(int argc, char *argv[])
}
if (!strcmp("fsck.f2fs", prog)) {
const char *option_string = ":ad:fg:O:p:q:StyV";
int opt = 0;
const char *option_string = ":aC:d:fg:O:p:q:StyV";
int opt = 0, val;
char *token;
struct option long_opt[] = {
{"dry-run", no_argument, 0, 1},
{0, 0, 0, 0}
@ -278,6 +280,22 @@ void f2fs_parse_options(int argc, char *argv[])
break;
}
break;
case 'C':
token = strtok(optarg, ":");
val = f2fs_str2encoding(token);
if (val < 0) {
MSG(0, "\tError: Unknown encoding %s\n", token);
fsck_usage();
}
c.s_encoding = val;
token = strtok(NULL, "");
val = f2fs_str2encoding_flags(&token, &c.s_encoding_flags);
if (val) {
MSG(0, "\tError: Unknown flag %s\n", token);
fsck_usage();
}
c.feature |= cpu_to_le32(F2FS_FEATURE_CASEFOLD);
break;
case 'V':
show_version(prog);
exit(0);

View File

@ -465,6 +465,9 @@ void print_sb_state(struct f2fs_super_block *sb)
if (f & cpu_to_le32(F2FS_FEATURE_SB_CHKSUM)) {
MSG(0, "%s", " sb_checksum");
}
if (f & cpu_to_le32(F2FS_FEATURE_CASEFOLD)) {
MSG(0, "%s", " casefold");
}
MSG(0, "\n");
MSG(0, "Info: superblock encrypt level = %d, salt = ",
sb->encryption_level);
@ -2762,24 +2765,41 @@ static int check_sector_size(struct f2fs_super_block *sb)
return 0;
}
static void tune_sb_features(struct f2fs_sb_info *sbi)
static int tune_sb_features(struct f2fs_sb_info *sbi)
{
int sb_changed = 0;
struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
if (c.feature & cpu_to_le32(F2FS_FEATURE_ENCRYPT) &&
c.feature & cpu_to_le32(F2FS_FEATURE_CASEFOLD)) {
ERR_MSG("ERROR: Cannot set both encrypt and casefold. Skipping.\n");
return -1;
}
if (!(sb->feature & cpu_to_le32(F2FS_FEATURE_ENCRYPT)) &&
c.feature & cpu_to_le32(F2FS_FEATURE_ENCRYPT)) {
sb->feature |= cpu_to_le32(F2FS_FEATURE_ENCRYPT);
MSG(0, "Info: Set Encryption feature\n");
sb_changed = 1;
}
if (!(sb->feature & cpu_to_le32(F2FS_FEATURE_CASEFOLD)) &&
c.feature & cpu_to_le32(F2FS_FEATURE_CASEFOLD)) {
if (!c.s_encoding) {
ERR_MSG("ERROR: Must specify encoding to enable casefolding.\n");
return -1;
}
sb->feature |= cpu_to_le32(F2FS_FEATURE_CASEFOLD);
MSG(0, "Info: Set Casefold feature\n");
sb_changed = 1;
}
/* TODO: quota needs to allocate inode numbers */
c.feature = sb->feature;
if (!sb_changed)
return;
return 0;
update_superblock(sb, SB_MASK_ALL);
return 0;
}
int f2fs_do_mount(struct f2fs_sb_info *sbi)
@ -2826,7 +2846,8 @@ int f2fs_do_mount(struct f2fs_sb_info *sbi)
c.bug_on = 0;
tune_sb_features(sbi);
if (tune_sb_features(sbi))
return -1;
/* precompute checksum seed for metadata */
if (c.feature & cpu_to_le32(F2FS_FEATURE_INODE_CHKSUM))

View File

@ -356,6 +356,8 @@ struct f2fs_configuration {
__u8 sb_version[VERSION_LEN + 1];
__u8 version[VERSION_LEN + 1];
char *vol_label;
u_int16_t s_encoding;
u_int16_t s_encoding_flags;
int heap;
int32_t kd;
int32_t dump_fd;
@ -564,6 +566,9 @@ enum {
#define FS_IMMUTABLE_FL 0x00000010 /* Immutable file */
#define F2FS_ENC_UTF8_12_1 1
#define F2FS_ENC_STRICT_MODE_FL (1 << 0)
/* This flag is used by node and meta inodes, and by recovery */
#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO)
@ -589,6 +594,7 @@ enum {
#define F2FS_FEATURE_LOST_FOUND 0x0200
#define F2FS_FEATURE_VERITY 0x0400 /* reserved */
#define F2FS_FEATURE_SB_CHKSUM 0x0800
#define F2FS_FEATURE_CASEFOLD 0x1000
#define MAX_VOLUME_NAME 512
@ -642,7 +648,9 @@ struct f2fs_super_block {
struct f2fs_device devs[MAX_DEVICES]; /* device list */
__le32 qf_ino[F2FS_MAX_QUOTAS]; /* quota inode numbers */
__u8 hot_ext_count; /* # of hot file extension */
__u8 reserved[310]; /* valid reserved region */
__le16 s_encoding; /* Filename charset encoding */
__le16 s_encoding_flags; /* Filename charset encoding flags */
__u8 reserved[306]; /* valid reserved region */
__le32 crc; /* checksum of superblock */
} __attribute__((packed));
@ -797,6 +805,9 @@ struct f2fs_extent {
#define file_is_encrypt(fi) ((fi)->i_advise & FADVISE_ENCRYPT_BIT)
#define file_enc_name(fi) ((fi)->i_advise & FADVISE_ENC_NAME_BIT)
#define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */
#define IS_CASEFOLDED(dir) ((dir)->i_flags & F2FS_CASEFOLD_FL)
struct f2fs_inode {
__le16 i_mode; /* file mode */
__u8 i_advise; /* file hints */
@ -1188,7 +1199,7 @@ extern int dev_reada_block(__u64);
extern int dev_read_version(void *, __u64, size_t);
extern void get_kernel_version(__u8 *);
extern void get_kernel_uname_version(__u8 *);
f2fs_hash_t f2fs_dentry_hash(const unsigned char *, int);
f2fs_hash_t f2fs_dentry_hash(int, int, const unsigned char *, int);
static inline bool f2fs_has_extra_isize(struct f2fs_inode *inode)
{
@ -1373,6 +1384,7 @@ struct feature feature_table[] = { \
{ "lost_found", F2FS_FEATURE_LOST_FOUND }, \
{ "verity", F2FS_FEATURE_VERITY }, /* reserved */ \
{ "sb_checksum", F2FS_FEATURE_SB_CHKSUM }, \
{ "casefold", F2FS_FEATURE_CASEFOLD }, \
{ NULL, 0x0}, \
};
@ -1446,4 +1458,25 @@ static inline int parse_root_owner(char *ids,
return 0;
}
/*
* NLS definitions
*/
struct f2fs_nls_table {
int version;
const struct f2fs_nls_ops *ops;
};
struct f2fs_nls_ops {
int (*casefold)(const struct f2fs_nls_table *charset,
const unsigned char *str, size_t len,
unsigned char *dest, size_t dlen);
};
extern const struct f2fs_nls_table *f2fs_load_nls_table(int encoding);
#define F2FS_ENC_UTF8_12_0 1
extern int f2fs_str2encoding(const char *string);
extern int f2fs_get_encoding_flags(int encoding);
extern int f2fs_str2encoding_flags(char **param, __u16 *flags);
#endif /*__F2FS_FS_H */

View File

@ -2,7 +2,7 @@
lib_LTLIBRARIES = libf2fs.la
libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c libf2fs_zoned.c
libf2fs_la_SOURCES = libf2fs.c libf2fs_io.c libf2fs_zoned.c nls_utf8.c
libf2fs_la_CFLAGS = -Wall
libf2fs_la_CPPFLAGS = -I$(top_srcdir)/include
libf2fs_la_LDFLAGS = -version-info $(LIBF2FS_CURRENT):$(LIBF2FS_REVISION):$(LIBF2FS_AGE)

View File

@ -441,7 +441,7 @@ static void str2hashbuf(const unsigned char *msg, int len,
* @param len name lenth
* @return return on success hash value, errno on failure
*/
f2fs_hash_t f2fs_dentry_hash(const unsigned char *name, int len)
static f2fs_hash_t __f2fs_dentry_hash(const unsigned char *name, int len)/* Need update */
{
__u32 hash;
f2fs_hash_t f2fs_hash;
@ -474,6 +474,31 @@ f2fs_hash_t f2fs_dentry_hash(const unsigned char *name, int len)
return f2fs_hash;
}
f2fs_hash_t f2fs_dentry_hash(int encoding, int casefolded,
const unsigned char *name, int len)
{
const struct f2fs_nls_table *table = f2fs_load_nls_table(encoding);
int r, dlen;
unsigned char *buff;
if (len && casefolded) {
buff = malloc(sizeof(char) * PATH_MAX);
if (!buff)
return -ENOMEM;
dlen = table->ops->casefold(table, name, len, buff, PATH_MAX);
if (dlen < 0) {
free(buff);
goto opaque_seq;
}
r = __f2fs_dentry_hash(buff, dlen);
free(buff);
return r;
}
opaque_seq:
return __f2fs_dentry_hash(name, len);
}
unsigned int addrs_per_inode(struct f2fs_inode *i)
{
return CUR_ADDRS_PER_INODE(i) - get_inline_xattr_addrs(i);
@ -647,6 +672,8 @@ void f2fs_init_configuration(void)
c.trim = 1;
c.kd = -1;
c.fixed_time = -1;
c.s_encoding = 0;
c.s_encoding_flags = 0;
/* default root owner */
c.root_uid = getuid();
@ -1220,3 +1247,85 @@ unsigned int calc_extra_isize(void)
return size - F2FS_EXTRA_ISIZE_OFFSET;
}
#define ARRAY_SIZE(array) \
(sizeof(array) / sizeof(array[0]))
static const struct {
char *name;
__u16 encoding_magic;
__u16 default_flags;
} f2fs_encoding_map[] = {
{
.encoding_magic = F2FS_ENC_UTF8_12_1,
.name = "utf8",
.default_flags = 0,
},
};
static const struct enc_flags {
__u16 flag;
char *param;
} encoding_flags[] = {
{ F2FS_ENC_STRICT_MODE_FL, "strict" },
};
/* Return a positive number < 0xff indicating the encoding magic number
* or a negative value indicating error. */
int f2fs_str2encoding(const char *string)
{
int i;
for (i = 0 ; i < ARRAY_SIZE(f2fs_encoding_map); i++)
if (!strcmp(string, f2fs_encoding_map[i].name))
return f2fs_encoding_map[i].encoding_magic;
return -EINVAL;
}
int f2fs_get_encoding_flags(int encoding)
{
int i;
for (i = 0 ; i < ARRAY_SIZE(f2fs_encoding_map); i++)
if (f2fs_encoding_map[i].encoding_magic == encoding)
return f2fs_encoding_map[encoding].default_flags;
return 0;
}
int f2fs_str2encoding_flags(char **param, __u16 *flags)
{
char *f = strtok(*param, ",");
const struct enc_flags *fl;
int i, neg = 0;
while (f) {
neg = 0;
if (!strncmp("no", f, 2)) {
neg = 1;
f += 2;
}
for (i = 0; i < ARRAY_SIZE(encoding_flags); i++) {
fl = &encoding_flags[i];
if (!strcmp(fl->param, f)) {
if (neg) {
MSG(0, "Sub %s\n", fl->param);
*flags &= ~fl->flag;
} else {
MSG(0, "Add %s\n", fl->param);
*flags |= fl->flag;
}
goto next_flag;
}
}
*param = f;
return -EINVAL;
next_flag:
f = strtok(NULL, ":");
}
return 0;
}

936
lib/nls_utf8.c Normal file
View File

@ -0,0 +1,936 @@
/*
* Copyright (c) 2014 SGI.
* Copyright (c) 2018 Collabora Ltd.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
/*
* This code is adapted from the Linux Kernel. We have a
* userspace version here such that the hashes will match that
* implementation.
*/
#include "config.h"
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "f2fs_fs.h"
/* Encoding a unicode version number as a single unsigned int. */
#define UNICODE_MAJ_SHIFT (16)
#define UNICODE_MIN_SHIFT (8)
#define UNICODE_AGE(MAJ, MIN, REV) \
(((unsigned int)(MAJ) << UNICODE_MAJ_SHIFT) | \
((unsigned int)(MIN) << UNICODE_MIN_SHIFT) | \
((unsigned int)(REV)))
/* Needed in struct utf8cursor below. */
#define UTF8HANGULLEAF (12)
/*
* Cursor structure used by the normalizer.
*/
struct utf8cursor {
const struct utf8data *data;
const char *s;
const char *p;
const char *ss;
const char *sp;
unsigned int len;
unsigned int slen;
short int ccc;
short int nccc;
unsigned char hangul[UTF8HANGULLEAF];
};
/*
* Initialize a utf8cursor to normalize a string.
* Returns 0 on success.
* Returns -1 on failure.
*/
// extern int utf8cursor(struct utf8cursor *u8c, const struct utf8data *data,
// const char *s);
// extern int utf8ncursor(struct utf8cursor *u8c, const struct utf8data *data,
// const char *s, size_t len);
/*
* Get the next byte in the normalization.
* Returns a value > 0 && < 256 on success.
* Returns 0 when the end of the normalization is reached.
* Returns -1 if the string being normalized is not valid UTF-8.
*/
// extern int utf8byte(struct utf8cursor *u8c);
struct utf8data {
unsigned int maxage;
unsigned int offset;
};
#define __INCLUDED_FROM_UTF8NORM_C__
#include "utf8data.h"
#undef __INCLUDED_FROM_UTF8NORM_C__
#define ARRAY_SIZE(array) \
(sizeof(array) / sizeof(array[0]))
#if 0
/* Highest unicode version supported by the data tables. */
static int utf8version_is_supported(uint8_t maj, uint8_t min, uint8_t rev)
{
int i = ARRAY_SIZE(utf8agetab) - 1;
unsigned int sb_utf8version = UNICODE_AGE(maj, min, rev);
while (i >= 0 && utf8agetab[i] != 0) {
if (sb_utf8version == utf8agetab[i])
return 1;
i--;
}
return 0;
}
#endif
#if 0
static int utf8version_latest(void)
{
return utf8vers;
}
#endif
/*
* UTF-8 valid ranges.
*
* The UTF-8 encoding spreads the bits of a 32bit word over several
* bytes. This table gives the ranges that can be held and how they'd
* be represented.
*
* 0x00000000 0x0000007F: 0xxxxxxx
* 0x00000000 0x000007FF: 110xxxxx 10xxxxxx
* 0x00000000 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
* 0x00000000 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* 0x00000000 0x03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
* 0x00000000 0x7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*
* There is an additional requirement on UTF-8, in that only the
* shortest representation of a 32bit value is to be used. A decoder
* must not decode sequences that do not satisfy this requirement.
* Thus the allowed ranges have a lower bound.
*
* 0x00000000 0x0000007F: 0xxxxxxx
* 0x00000080 0x000007FF: 110xxxxx 10xxxxxx
* 0x00000800 0x0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx
* 0x00010000 0x001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
* 0x00200000 0x03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
* 0x04000000 0x7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
*
* Actual unicode characters are limited to the range 0x0 - 0x10FFFF,
* 17 planes of 65536 values. This limits the sequences actually seen
* even more, to just the following.
*
* 0 - 0x7F: 0 - 0x7F
* 0x80 - 0x7FF: 0xC2 0x80 - 0xDF 0xBF
* 0x800 - 0xFFFF: 0xE0 0xA0 0x80 - 0xEF 0xBF 0xBF
* 0x10000 - 0x10FFFF: 0xF0 0x90 0x80 0x80 - 0xF4 0x8F 0xBF 0xBF
*
* Within those ranges the surrogates 0xD800 - 0xDFFF are not allowed.
*
* Note that the longest sequence seen with valid usage is 4 bytes,
* the same a single UTF-32 character. This makes the UTF-8
* representation of Unicode strictly smaller than UTF-32.
*
* The shortest sequence requirement was introduced by:
* Corrigendum #1: UTF-8 Shortest Form
* It can be found here:
* http://www.unicode.org/versions/corrigendum1.html
*
*/
/*
* Return the number of bytes used by the current UTF-8 sequence.
* Assumes the input points to the first byte of a valid UTF-8
* sequence.
*/
static inline int utf8clen(const char *s)
{
unsigned char c = *s;
return 1 + (c >= 0xC0) + (c >= 0xE0) + (c >= 0xF0);
}
/*
* Decode a 3-byte UTF-8 sequence.
*/
static unsigned int
utf8decode3(const char *str)
{
unsigned int uc;
uc = *str++ & 0x0F;
uc <<= 6;
uc |= *str++ & 0x3F;
uc <<= 6;
uc |= *str++ & 0x3F;
return uc;
}
/*
* Encode a 3-byte UTF-8 sequence.
*/
static int
utf8encode3(char *str, unsigned int val)
{
str[2] = (val & 0x3F) | 0x80;
val >>= 6;
str[1] = (val & 0x3F) | 0x80;
val >>= 6;
str[0] = val | 0xE0;
return 3;
}
/*
* utf8trie_t
*
* A compact binary tree, used to decode UTF-8 characters.
*
* Internal nodes are one byte for the node itself, and up to three
* bytes for an offset into the tree. The first byte contains the
* following information:
* NEXTBYTE - flag - advance to next byte if set
* BITNUM - 3 bit field - the bit number to tested
* OFFLEN - 2 bit field - number of bytes in the offset
* if offlen == 0 (non-branching node)
* RIGHTPATH - 1 bit field - set if the following node is for the
* right-hand path (tested bit is set)
* TRIENODE - 1 bit field - set if the following node is an internal
* node, otherwise it is a leaf node
* if offlen != 0 (branching node)
* LEFTNODE - 1 bit field - set if the left-hand node is internal
* RIGHTNODE - 1 bit field - set if the right-hand node is internal
*
* Due to the way utf8 works, there cannot be branching nodes with
* NEXTBYTE set, and moreover those nodes always have a righthand
* descendant.
*/
typedef const unsigned char utf8trie_t;
#define BITNUM 0x07
#define NEXTBYTE 0x08
#define OFFLEN 0x30
#define OFFLEN_SHIFT 4
#define RIGHTPATH 0x40
#define TRIENODE 0x80
#define RIGHTNODE 0x40
#define LEFTNODE 0x80
/*
* utf8leaf_t
*
* The leaves of the trie are embedded in the trie, and so the same
* underlying datatype: unsigned char.
*
* leaf[0]: The unicode version, stored as a generation number that is
* an index into utf8agetab[]. With this we can filter code
* points based on the unicode version in which they were
* defined. The CCC of a non-defined code point is 0.
* leaf[1]: Canonical Combining Class. During normalization, we need
* to do a stable sort into ascending order of all characters
* with a non-zero CCC that occur between two characters with
* a CCC of 0, or at the begin or end of a string.
* The unicode standard guarantees that all CCC values are
* between 0 and 254 inclusive, which leaves 255 available as
* a special value.
* Code points with CCC 0 are known as stoppers.
* leaf[2]: Decomposition. If leaf[1] == 255, then leaf[2] is the
* start of a NUL-terminated string that is the decomposition
* of the character.
* The CCC of a decomposable character is the same as the CCC
* of the first character of its decomposition.
* Some characters decompose as the empty string: these are
* characters with the Default_Ignorable_Code_Point property.
* These do affect normalization, as they all have CCC 0.
*
* The decompositions in the trie have been fully expanded, with the
* exception of Hangul syllables, which are decomposed algorithmically.
*
* Casefolding, if applicable, is also done using decompositions.
*
* The trie is constructed in such a way that leaves exist for all
* UTF-8 sequences that match the criteria from the "UTF-8 valid
* ranges" comment above, and only for those sequences. Therefore a
* lookup in the trie can be used to validate the UTF-8 input.
*/
typedef const unsigned char utf8leaf_t;
#define LEAF_GEN(LEAF) ((LEAF)[0])
#define LEAF_CCC(LEAF) ((LEAF)[1])
#define LEAF_STR(LEAF) ((const char *)((LEAF) + 2))
#define MINCCC (0)
#define MAXCCC (254)
#define STOPPER (0)
#define DECOMPOSE (255)
/* Marker for hangul syllable decomposition. */
#define HANGUL ((char)(255))
/* Size of the synthesized leaf used for Hangul syllable decomposition. */
#define UTF8HANGULLEAF (12)
/*
* Hangul decomposition (algorithm from Section 3.12 of Unicode 6.3.0)
*
* AC00;<Hangul Syllable, First>;Lo;0;L;;;;;N;;;;;
* D7A3;<Hangul Syllable, Last>;Lo;0;L;;;;;N;;;;;
*
* SBase = 0xAC00
* LBase = 0x1100
* VBase = 0x1161
* TBase = 0x11A7
* LCount = 19
* VCount = 21
* TCount = 28
* NCount = 588 (VCount * TCount)
* SCount = 11172 (LCount * NCount)
*
* Decomposition:
* SIndex = s - SBase
*
* LV (Canonical/Full)
* LIndex = SIndex / NCount
* VIndex = (Sindex % NCount) / TCount
* LPart = LBase + LIndex
* VPart = VBase + VIndex
*
* LVT (Canonical)
* LVIndex = (SIndex / TCount) * TCount
* TIndex = (Sindex % TCount)
* LVPart = SBase + LVIndex
* TPart = TBase + TIndex
*
* LVT (Full)
* LIndex = SIndex / NCount
* VIndex = (Sindex % NCount) / TCount
* TIndex = (Sindex % TCount)
* LPart = LBase + LIndex
* VPart = VBase + VIndex
* if (TIndex == 0) {
* d = <LPart, VPart>
* } else {
* TPart = TBase + TIndex
* d = <LPart, TPart, VPart>
* }
*/
/* Constants */
#define SB (0xAC00)
#define LB (0x1100)
#define VB (0x1161)
#define TB (0x11A7)
#define LC (19)
#define VC (21)
#define TC (28)
#define NC (VC * TC)
#define SC (LC * NC)
/* Algorithmic decomposition of hangul syllable. */
static utf8leaf_t *
utf8hangul(const char *str, unsigned char *hangul)
{
unsigned int si;
unsigned int li;
unsigned int vi;
unsigned int ti;
unsigned char *h;
/* Calculate the SI, LI, VI, and TI values. */
si = utf8decode3(str) - SB;
li = si / NC;
vi = (si % NC) / TC;
ti = si % TC;
/* Fill in base of leaf. */
h = hangul;
LEAF_GEN(h) = 2;
LEAF_CCC(h) = DECOMPOSE;
h += 2;
/* Add LPart, a 3-byte UTF-8 sequence. */
h += utf8encode3((char *)h, li + LB);
/* Add VPart, a 3-byte UTF-8 sequence. */
h += utf8encode3((char *)h, vi + VB);
/* Add TPart if required, also a 3-byte UTF-8 sequence. */
if (ti)
h += utf8encode3((char *)h, ti + TB);
/* Terminate string. */
h[0] = '\0';
return hangul;
}
/*
* Use trie to scan s, touching at most len bytes.
* Returns the leaf if one exists, NULL otherwise.
*
* A non-NULL return guarantees that the UTF-8 sequence starting at s
* is well-formed and corresponds to a known unicode code point. The
* shorthand for this will be "is valid UTF-8 unicode".
*/
static utf8leaf_t *utf8nlookup(const struct utf8data *data,
unsigned char *hangul, const char *s, size_t len)
{
utf8trie_t *trie;
int offlen;
int offset;
int mask;
int node;
if (!data)
return NULL;
if (len == 0)
return NULL;
trie = utf8data + data->offset;
node = 1;
while (node) {
offlen = (*trie & OFFLEN) >> OFFLEN_SHIFT;
if (*trie & NEXTBYTE) {
if (--len == 0)
return NULL;
s++;
}
mask = 1 << (*trie & BITNUM);
if (*s & mask) {
/* Right leg */
if (offlen) {
/* Right node at offset of trie */
node = (*trie & RIGHTNODE);
offset = trie[offlen];
while (--offlen) {
offset <<= 8;
offset |= trie[offlen];
}
trie += offset;
} else if (*trie & RIGHTPATH) {
/* Right node after this node */
node = (*trie & TRIENODE);
trie++;
} else {
/* No right node. */
return NULL;
}
} else {
/* Left leg */
if (offlen) {
/* Left node after this node. */
node = (*trie & LEFTNODE);
trie += offlen + 1;
} else if (*trie & RIGHTPATH) {
/* No left node. */
return NULL;
} else {
/* Left node after this node */
node = (*trie & TRIENODE);
trie++;
}
}
}
/*
* Hangul decomposition is done algorithmically. These are the
* codepoints >= 0xAC00 and <= 0xD7A3. Their UTF-8 encoding is
* always 3 bytes long, so s has been advanced twice, and the
* start of the sequence is at s-2.
*/
if (LEAF_CCC(trie) == DECOMPOSE && LEAF_STR(trie)[0] == HANGUL)
trie = utf8hangul(s - 2, hangul);
return trie;
}
/*
* Use trie to scan s.
* Returns the leaf if one exists, NULL otherwise.
*
* Forwards to utf8nlookup().
*/
static utf8leaf_t *utf8lookup(const struct utf8data *data,
unsigned char *hangul, const char *s)
{
return utf8nlookup(data, hangul, s, (size_t)-1);
}
#if 0
/*
* Maximum age of any character in s.
* Return -1 if s is not valid UTF-8 unicode.
* Return 0 if only non-assigned code points are used.
*/
static int utf8agemax(const struct utf8data *data, const char *s)
{
utf8leaf_t *leaf;
int age = 0;
int leaf_age;
unsigned char hangul[UTF8HANGULLEAF];
if (!data)
return -1;
while (*s) {
leaf = utf8lookup(data, hangul, s);
if (!leaf)
return -1;
leaf_age = utf8agetab[LEAF_GEN(leaf)];
if (leaf_age <= data->maxage && leaf_age > age)
age = leaf_age;
s += utf8clen(s);
}
return age;
}
#endif
#if 0
/*
* Minimum age of any character in s.
* Return -1 if s is not valid UTF-8 unicode.
* Return 0 if non-assigned code points are used.
*/
static int utf8agemin(const struct utf8data *data, const char *s)
{
utf8leaf_t *leaf;
int age;
int leaf_age;
unsigned char hangul[UTF8HANGULLEAF];
if (!data)
return -1;
age = data->maxage;
while (*s) {
leaf = utf8lookup(data, hangul, s);
if (!leaf)
return -1;
leaf_age = utf8agetab[LEAF_GEN(leaf)];
if (leaf_age <= data->maxage && leaf_age < age)
age = leaf_age;
s += utf8clen(s);
}
return age;
}
#endif
#if 0
/*
* Maximum age of any character in s, touch at most len bytes.
* Return -1 if s is not valid UTF-8 unicode.
*/
static int utf8nagemax(const struct utf8data *data, const char *s, size_t len)
{
utf8leaf_t *leaf;
int age = 0;
int leaf_age;
unsigned char hangul[UTF8HANGULLEAF];
if (!data)
return -1;
while (len && *s) {
leaf = utf8nlookup(data, hangul, s, len);
if (!leaf)
return -1;
leaf_age = utf8agetab[LEAF_GEN(leaf)];
if (leaf_age <= data->maxage && leaf_age > age)
age = leaf_age;
len -= utf8clen(s);
s += utf8clen(s);
}
return age;
}
#endif
#if 0
/*
* Maximum age of any character in s, touch at most len bytes.
* Return -1 if s is not valid UTF-8 unicode.
*/
static int utf8nagemin(const struct utf8data *data, const char *s, size_t len)
{
utf8leaf_t *leaf;
int leaf_age;
int age;
unsigned char hangul[UTF8HANGULLEAF];
if (!data)
return -1;
age = data->maxage;
while (len && *s) {
leaf = utf8nlookup(data, hangul, s, len);
if (!leaf)
return -1;
leaf_age = utf8agetab[LEAF_GEN(leaf)];
if (leaf_age <= data->maxage && leaf_age < age)
age = leaf_age;
len -= utf8clen(s);
s += utf8clen(s);
}
return age;
}
#endif
#if 0
/*
* Length of the normalization of s.
* Return -1 if s is not valid UTF-8 unicode.
*
* A string of Default_Ignorable_Code_Point has length 0.
*/
static ssize_t utf8len(const struct utf8data *data, const char *s)
{
utf8leaf_t *leaf;
size_t ret = 0;
unsigned char hangul[UTF8HANGULLEAF];
if (!data)
return -1;
while (*s) {
leaf = utf8lookup(data, hangul, s);
if (!leaf)
return -1;
if (utf8agetab[LEAF_GEN(leaf)] > data->maxage)
ret += utf8clen(s);
else if (LEAF_CCC(leaf) == DECOMPOSE)
ret += strlen(LEAF_STR(leaf));
else
ret += utf8clen(s);
s += utf8clen(s);
}
return ret;
}
#endif
#if 0
/*
* Length of the normalization of s, touch at most len bytes.
* Return -1 if s is not valid UTF-8 unicode.
*/
static ssize_t utf8nlen(const struct utf8data *data, const char *s, size_t len)
{
utf8leaf_t *leaf;
size_t ret = 0;
unsigned char hangul[UTF8HANGULLEAF];
if (!data)
return -1;
while (len && *s) {
leaf = utf8nlookup(data, hangul, s, len);
if (!leaf)
return -1;
if (utf8agetab[LEAF_GEN(leaf)] > data->maxage)
ret += utf8clen(s);
else if (LEAF_CCC(leaf) == DECOMPOSE)
ret += strlen(LEAF_STR(leaf));
else
ret += utf8clen(s);
len -= utf8clen(s);
s += utf8clen(s);
}
return ret;
}
#endif
/*
* Set up an utf8cursor for use by utf8byte().
*
* u8c : pointer to cursor.
* data : const struct utf8data to use for normalization.
* s : string.
* len : length of s.
*
* Returns -1 on error, 0 on success.
*/
static int utf8ncursor(struct utf8cursor *u8c, const struct utf8data *data,
const char *s, size_t len)
{
if (!data)
return -1;
if (!s)
return -1;
u8c->data = data;
u8c->s = s;
u8c->p = NULL;
u8c->ss = NULL;
u8c->sp = NULL;
u8c->len = len;
u8c->slen = 0;
u8c->ccc = STOPPER;
u8c->nccc = STOPPER;
/* Check we didn't clobber the maximum length. */
if (u8c->len != len)
return -1;
/* The first byte of s may not be an utf8 continuation. */
if (len > 0 && (*s & 0xC0) == 0x80)
return -1;
return 0;
}
#if 0
/*
* Set up an utf8cursor for use by utf8byte().
*
* u8c : pointer to cursor.
* data : const struct utf8data to use for normalization.
* s : NUL-terminated string.
*
* Returns -1 on error, 0 on success.
*/
static int utf8cursor(struct utf8cursor *u8c, const struct utf8data *data,
const char *s)
{
return utf8ncursor(u8c, data, s, (unsigned int)-1);
}
#endif
/*
* Get one byte from the normalized form of the string described by u8c.
*
* Returns the byte cast to an unsigned char on succes, and -1 on failure.
*
* The cursor keeps track of the location in the string in u8c->s.
* When a character is decomposed, the current location is stored in
* u8c->p, and u8c->s is set to the start of the decomposition. Note
* that bytes from a decomposition do not count against u8c->len.
*
* Characters are emitted if they match the current CCC in u8c->ccc.
* Hitting end-of-string while u8c->ccc == STOPPER means we're done,
* and the function returns 0 in that case.
*
* Sorting by CCC is done by repeatedly scanning the string. The
* values of u8c->s and u8c->p are stored in u8c->ss and u8c->sp at
* the start of the scan. The first pass finds the lowest CCC to be
* emitted and stores it in u8c->nccc, the second pass emits the
* characters with this CCC and finds the next lowest CCC. This limits
* the number of passes to 1 + the number of different CCCs in the
* sequence being scanned.
*
* Therefore:
* u8c->p != NULL -> a decomposition is being scanned.
* u8c->ss != NULL -> this is a repeating scan.
* u8c->ccc == -1 -> this is the first scan of a repeating scan.
*/
static int utf8byte(struct utf8cursor *u8c)
{
utf8leaf_t *leaf;
int ccc;
for (;;) {
/* Check for the end of a decomposed character. */
if (u8c->p && *u8c->s == '\0') {
u8c->s = u8c->p;
u8c->p = NULL;
}
/* Check for end-of-string. */
if (!u8c->p && (u8c->len == 0 || *u8c->s == '\0')) {
/* There is no next byte. */
if (u8c->ccc == STOPPER)
return 0;
/* End-of-string during a scan counts as a stopper. */
ccc = STOPPER;
goto ccc_mismatch;
} else if ((*u8c->s & 0xC0) == 0x80) {
/* This is a continuation of the current character. */
if (!u8c->p)
u8c->len--;
return (unsigned char)*u8c->s++;
}
/* Look up the data for the current character. */
if (u8c->p) {
leaf = utf8lookup(u8c->data, u8c->hangul, u8c->s);
} else {
leaf = utf8nlookup(u8c->data, u8c->hangul,
u8c->s, u8c->len);
}
/* No leaf found implies that the input is a binary blob. */
if (!leaf)
return -1;
ccc = LEAF_CCC(leaf);
/* Characters that are too new have CCC 0. */
if (utf8agetab[LEAF_GEN(leaf)] > u8c->data->maxage) {
ccc = STOPPER;
} else if (ccc == DECOMPOSE) {
u8c->len -= utf8clen(u8c->s);
u8c->p = u8c->s + utf8clen(u8c->s);
u8c->s = LEAF_STR(leaf);
/* Empty decomposition implies CCC 0. */
if (*u8c->s == '\0') {
if (u8c->ccc == STOPPER)
continue;
ccc = STOPPER;
goto ccc_mismatch;
}
leaf = utf8lookup(u8c->data, u8c->hangul, u8c->s);
if (!leaf)
return -1;
ccc = LEAF_CCC(leaf);
}
/*
* If this is not a stopper, then see if it updates
* the next canonical class to be emitted.
*/
if (ccc != STOPPER && u8c->ccc < ccc && ccc < u8c->nccc)
u8c->nccc = ccc;
/*
* Return the current byte if this is the current
* combining class.
*/
if (ccc == u8c->ccc) {
if (!u8c->p)
u8c->len--;
return (unsigned char)*u8c->s++;
}
/* Current combining class mismatch. */
ccc_mismatch:
if (u8c->nccc == STOPPER) {
/*
* Scan forward for the first canonical class
* to be emitted. Save the position from
* which to restart.
*/
u8c->ccc = MINCCC - 1;
u8c->nccc = ccc;
u8c->sp = u8c->p;
u8c->ss = u8c->s;
u8c->slen = u8c->len;
if (!u8c->p)
u8c->len -= utf8clen(u8c->s);
u8c->s += utf8clen(u8c->s);
} else if (ccc != STOPPER) {
/* Not a stopper, and not the ccc we're emitting. */
if (!u8c->p)
u8c->len -= utf8clen(u8c->s);
u8c->s += utf8clen(u8c->s);
} else if (u8c->nccc != MAXCCC + 1) {
/* At a stopper, restart for next ccc. */
u8c->ccc = u8c->nccc;
u8c->nccc = MAXCCC + 1;
u8c->s = u8c->ss;
u8c->p = u8c->sp;
u8c->len = u8c->slen;
} else {
/* All done, proceed from here. */
u8c->ccc = STOPPER;
u8c->nccc = STOPPER;
u8c->sp = NULL;
u8c->ss = NULL;
u8c->slen = 0;
}
}
}
#if 0
/*
* Look for the correct const struct utf8data for a unicode version.
* Returns NULL if the version requested is too new.
*
* Two normalization forms are supported: nfdi and nfdicf.
*
* nfdi:
* - Apply unicode normalization form NFD.
* - Remove any Default_Ignorable_Code_Point.
*
* nfdicf:
* - Apply unicode normalization form NFD.
* - Remove any Default_Ignorable_Code_Point.
* - Apply a full casefold (C + F).
*/
static const struct utf8data *utf8nfdi(unsigned int maxage)
{
int i = ARRAY_SIZE(utf8nfdidata) - 1;
while (maxage < utf8nfdidata[i].maxage)
i--;
if (maxage > utf8nfdidata[i].maxage)
return NULL;
return &utf8nfdidata[i];
}
#endif
static const struct utf8data *utf8nfdicf(unsigned int maxage)
{
int i = ARRAY_SIZE(utf8nfdicfdata) - 1;
while (maxage < utf8nfdicfdata[i].maxage)
i--;
if (maxage > utf8nfdicfdata[i].maxage)
return NULL;
return &utf8nfdicfdata[i];
}
static int utf8_casefold(const struct f2fs_nls_table *table,
const unsigned char *str, size_t len,
unsigned char *dest, size_t dlen)
{
const struct utf8data *data = utf8nfdicf(table->version);
struct utf8cursor cur;
size_t nlen = 0;
if (utf8ncursor(&cur, data, (const char *) str, len) < 0)
goto invalid_seq;
for (nlen = 0; nlen < dlen; nlen++) {
int c = utf8byte(&cur);
dest[nlen] = c;
if (!c)
return nlen;
if (c == -1)
break;
}
return -ENAMETOOLONG;
invalid_seq:
if (dlen < len)
return -ENAMETOOLONG;
/* Signal invalid sequence */
return -EINVAL;
}
static const struct f2fs_nls_ops utf8_ops = {
.casefold = utf8_casefold,
};
static const struct f2fs_nls_table nls_utf8 = {
.ops = &utf8_ops,
.version = UNICODE_AGE(12, 1, 0),
};
const struct f2fs_nls_table *f2fs_load_nls_table(int encoding)
{
if (encoding == F2FS_ENC_UTF8_12_1)
return &nls_utf8;
return NULL;
}

4109
lib/utf8data.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -516,6 +516,15 @@ static int f2fs_prepare_super_block(void)
memcpy(sb->version, c.version, VERSION_LEN);
memcpy(sb->init_version, c.version, VERSION_LEN);
if (c.feature & cpu_to_le32(F2FS_FEATURE_CASEFOLD)) {
if (c.feature & cpu_to_le32(F2FS_FEATURE_ENCRYPT)) {
MSG(0, "\tError: Casefolding and encryption are not compatible\n");
return -1;
}
set_sb(s_encoding, c.s_encoding);
set_sb(s_encoding_flags, c.s_encoding_flags);
}
sb->feature = c.feature;
if (get_sb(feature) & F2FS_FEATURE_SB_CHKSUM) {
@ -1548,7 +1557,7 @@ static int f2fs_add_default_dentry_root(void)
if (c.lpf_ino) {
int len = strlen(LPF);
f2fs_hash_t hash = f2fs_dentry_hash((unsigned char *)LPF, len);
f2fs_hash_t hash = f2fs_dentry_hash(0, 0, (unsigned char *)LPF, len);
dent_blk->dentry[2].hash_code = cpu_to_le32(hash);
dent_blk->dentry[2].ino = cpu_to_le32(c.lpf_ino);

View File

@ -64,6 +64,7 @@ static void mkfs_usage()
MSG(0, " -z # of sections per zone [default:1]\n");
MSG(0, " -V print the version number and exit\n");
MSG(0, "sectors: number of sectors. [default: determined by device size]\n");
MSG(0, " -C [encoding:flag1,flag2] Support casefolding with optional flags\n");
exit(1);
}
@ -107,8 +108,10 @@ static void add_default_options(void)
static void f2fs_parse_options(int argc, char *argv[])
{
static const char *option_string = "qa:c:d:e:E:g:il:mo:O:R:s:S:z:t:fw:V";
static const char *option_string = "qa:c:C:d:e:E:g:il:mo:O:R:s:S:z:t:Vfw:";
int32_t option=0;
int val;
char *token;
while ((option = getopt(argc,argv,option_string)) != EOF) {
switch (option) {
@ -192,6 +195,22 @@ static void f2fs_parse_options(int argc, char *argv[])
case 'V':
show_version("mkfs.f2fs");
exit(0);
case 'C':
token = strtok(optarg, ":");
val = f2fs_str2encoding(token);
if (val < 0) {
MSG(0, "\tError: Unknown encoding %s\n", token);
mkfs_usage();
}
c.s_encoding = val;
token = strtok(NULL, "");
val = f2fs_str2encoding_flags(&token, &c.s_encoding_flags);
if (val) {
MSG(0, "\tError: Unknown flag %s\n",token);
mkfs_usage();
}
c.feature |= cpu_to_le32(F2FS_FEATURE_CASEFOLD);
break;
default:
MSG(0, "\tError: Unknown option %c\n",option);
mkfs_usage();

View File

@ -25,6 +25,7 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/sysmacros.h>
#ifdef HAVE_SYS_SYSMACROS_H
#include <sys/sysmacros.h>
#endif