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 <[EMAIL PROTECTED]>
---

--- 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 [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to