[patch 2/2] HFS+: Add custom dentry hash and comparison operations

From: Duane Griffin
Date: Tue Jun 19 2007 - 20:13:50 EST


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 <duaneg@xxxxxxxxx>
---

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