mirror of
https://gitee.com/openharmony/third_party_f2fs-tools
synced 2024-11-23 01:59:54 +00:00
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:
parent
db84e73777
commit
ce64ea0815
@ -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;
|
||||
|
@ -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_ */
|
||||
|
42
fsck/fsck.c
42
fsck/fsck.c
@ -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);
|
||||
|
@ -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);
|
||||
|
22
fsck/main.c
22
fsck/main.c
@ -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);
|
||||
|
27
fsck/mount.c
27
fsck/mount.c
@ -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))
|
||||
|
@ -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 */
|
||||
|
@ -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)
|
||||
|
111
lib/libf2fs.c
111
lib/libf2fs.c
@ -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
936
lib/nls_utf8.c
Normal 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
4109
lib/utf8data.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user