Add custom dentry hash and comparison operations for HFS+ filesystems that are case-insensitive and/or do automatic unicode decomposition. The new operations reuse the existing HFS+ ASCII to unicode conversion, unicode decomposition and case folding functionality. Signed-off-by: Duane Griffin --- --- linux-2.6.21.orig/fs/hfsplus/dir.c +++ linux-2.6.21/fs/hfsplus/dir.c @@ -36,6 +36,11 @@ static struct dentry *hfsplus_lookup(str u16 type; sb = dir->i_sb; + + if (!(HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX) || + !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE)) + dentry->d_op = &hfsplus_dentry_operations; + dentry->d_fsdata = NULL; hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd); hfsplus_cat_build_key(sb, fd.search_key, dir->i_ino, &dentry->d_name); --- linux-2.6.21.orig/fs/hfsplus/hfsplus_fs.h +++ linux-2.6.21/fs/hfsplus/hfsplus_fs.h @@ -321,6 +321,7 @@ void hfsplus_file_truncate(struct inode /* inode.c */ extern const struct address_space_operations hfsplus_aops; extern const struct address_space_operations hfsplus_btree_aops; +extern struct dentry_operations hfsplus_dentry_operations; void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *); void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *); @@ -353,6 +354,8 @@ int hfsplus_strcasecmp(const struct hfsp int hfsplus_strcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *); int hfsplus_uni2asc(struct super_block *, const struct hfsplus_unistr *, char *, int *); int hfsplus_asc2uni(struct super_block *, struct hfsplus_unistr *, const char *, int); +int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str); +int hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2); /* wrapper.c */ int hfsplus_read_wrapper(struct super_block *); --- linux-2.6.21.orig/fs/hfsplus/inode.c +++ linux-2.6.21/fs/hfsplus/inode.c @@ -130,6 +130,12 @@ const struct address_space_operations hf .writepages = hfsplus_writepages, }; +struct dentry_operations hfsplus_dentry_operations = { + .d_revalidate = NULL, + .d_hash = hfsplus_hash_dentry, + .d_compare = hfsplus_compare_dentry, +}; + static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { --- linux-2.6.21.orig/fs/hfsplus/super.c +++ linux-2.6.21/fs/hfsplus/super.c @@ -396,6 +396,10 @@ static int hfsplus_fill_super(struct sup } else hfs_find_exit(&fd); + if (!(HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX) || + !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE)) + sb->s_root->d_op = &hfsplus_dentry_operations; + if (sb->s_flags & MS_RDONLY) goto out; --- linux-2.6.21.orig/fs/hfsplus/unicode.c +++ linux-2.6.21/fs/hfsplus/unicode.c @@ -314,6 +314,22 @@ static int asc2ucd(struct super_block *s return size; } +/* + * Convert a ASCII character(s) to a unicode character. + * The unicode character will be decomposed and/or case-folded, if required. + * Returns the number of ASCII characters converted. + */ +static int asc2ucdf(struct super_block *sb, + const char *astr, int len, struct decomposed_uc *duc) +{ + int ii; + int size = asc2ucd(sb, astr, len, duc); + bool fold = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX); + for (ii = 0; fold && ii <= duc->size; ++ii) + duc->str[ii] = case_fold(duc->str[ii]); + return size; +} + int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, const char *astr, int len) { @@ -336,3 +352,89 @@ int hfsplus_asc2uni(struct super_block * return -ENAMETOOLONG; return 0; } + +/* + * Hash a string to an integer as appropriate for the HFS+ filesystem. + * Composed unicode characters are decomposed and case-folding is performed + * if the appropriate bits are (un)set on the superblock. + */ +int hfsplus_hash_dentry(struct dentry *dentry, struct qstr *str) +{ + int len = str->len; + const char *astr = str->name; + struct decomposed_uc duc; + unsigned hash = init_name_hash(); + + while (len > 0) { + int ii; + int size = asc2ucdf(dentry->d_sb, astr, len, &duc); + astr += size; + len -= size; + for (ii = 0; ii < duc.size; ++ii) { + if (duc.str[ii]) + hash = partial_name_hash(duc.str[ii], hash); + } + } + str->hash = end_name_hash(hash); + + return 0; +} + +/* + * Compare strings with HFS+ filename ordering. + * Composed unicode characters are decomposed and case-folding is performed + * if the appropriate bits are (un)set on the superblock. + */ +int +hfsplus_compare_dentry(struct dentry *dentry, struct qstr *s1, struct qstr *s2) +{ + int off1 = 0; + int off2 = 0; + struct decomposed_uc duc1; + struct decomposed_uc duc2; + int len1 = s1->len; + int len2 = s2->len; + const char *astr1 = s1->name; + const char *astr2 = s2->name; + + duc1.size = 0; + duc2.size = 0; + + while ((off1 < duc1.size || len1 > 0) && + (off2 < duc2.size || len2 > 0)) { + + if (off1 == duc1.size) { + int size = asc2ucdf(dentry->d_sb, astr1, len1, &duc1); + astr1 += size; + len1 -= size; + off1 = 0; + } + + if (off2 == duc2.size) { + int size = asc2ucdf(dentry->d_sb, astr2, len2, &duc2); + astr2 += size; + len2 -= size; + off2 = 0; + } + + while (off1 < duc1.size && !duc1.str[off1]) + ++off1; + while (off2 < duc2.size && !duc2.str[off2]) + ++off2; + + while (off1 < duc1.size && off2 < duc2.size) { + if (duc1.str[off1] < duc2.str[off2]) + return -1; + if (duc1.str[off1] > duc2.str[off2]) + return 1; + ++off1; + ++off2; + } + } + + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; + return 0; +} -- "I never could learn to drink that blood and call it wine" - Bob Dylan - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/