[PATCH] hfs: add HFSX support
authorDavid Elliott <elliott@stcnet.com>
Thu, 19 Jan 2006 01:43:08 +0000 (17:43 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Thu, 19 Jan 2006 03:20:23 +0000 (19:20 -0800)
Add support for HFSX, which allows for case-sensitive filenames.

Signed-off-by: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/hfsplus/btree.c
fs/hfsplus/catalog.c
fs/hfsplus/extents.c
fs/hfsplus/hfsplus_fs.h
fs/hfsplus/hfsplus_raw.h
fs/hfsplus/super.c
fs/hfsplus/unicode.c
fs/hfsplus/wrapper.c

index 671290663838151b12f1bd1066516d6a4efdfa47..a67edfa34e9ec6ac847d9e2fe2f6bb08725e038d 100644 (file)
@@ -31,17 +31,8 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
 
        init_MUTEX(&tree->tree_lock);
        spin_lock_init(&tree->hash_lock);
-       /* Set the correct compare function */
        tree->sb = sb;
        tree->cnid = id;
-       if (id == HFSPLUS_EXT_CNID) {
-               tree->keycmp = hfsplus_ext_cmp_key;
-       } else if (id == HFSPLUS_CAT_CNID) {
-               tree->keycmp = hfsplus_cat_cmp_key;
-       } else {
-               printk(KERN_ERR "hfs: unknown B*Tree requested\n");
-               goto free_tree;
-       }
        tree->inode = iget(sb, id);
        if (!tree->inode)
                goto free_tree;
@@ -64,6 +55,20 @@ struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
        tree->max_key_len = be16_to_cpu(head->max_key_len);
        tree->depth = be16_to_cpu(head->depth);
 
+       /* Set the correct compare function */
+       if (id == HFSPLUS_EXT_CNID) {
+               tree->keycmp = hfsplus_ext_cmp_key;
+       } else if (id == HFSPLUS_CAT_CNID) {
+               if ((HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX) &&
+                   (head->key_type == HFSPLUS_KEY_BINARY))
+                       tree->keycmp = hfsplus_cat_bin_cmp_key;
+               else
+                       tree->keycmp = hfsplus_cat_case_cmp_key;
+       } else {
+               printk(KERN_ERR "hfs: unknown B*Tree requested\n");
+               goto fail_page;
+       }
+
        size = tree->node_size;
        if (!size || size & (size - 1))
                goto fail_page;
index 074451f1d95f772a59b8b1bb90a72e8e75c42407..662d176856d8333fd7ca904544d3904c4c2749b3 100644 (file)
@@ -13,7 +13,8 @@
 #include "hfsplus_fs.h"
 #include "hfsplus_raw.h"
 
-int hfsplus_cat_cmp_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2)
+int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
+                            const hfsplus_btree_key *k2)
 {
        __be32 k1p, k2p;
 
@@ -22,7 +23,20 @@ int hfsplus_cat_cmp_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2)
        if (k1p != k2p)
                return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
 
-       return hfsplus_unistrcmp(&k1->cat.name, &k2->cat.name);
+       return hfsplus_strcasecmp(&k1->cat.name, &k2->cat.name);
+}
+
+int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
+                           const hfsplus_btree_key *k2)
+{
+       __be32 k1p, k2p;
+
+       k1p = k1->cat.parent;
+       k2p = k2->cat.parent;
+       if (k1p != k2p)
+               return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
+
+       return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
 }
 
 void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key,
index c95559f69bcbd02037aab15f6450579a579ce7fb..1a7480089e82ef918cb71c91a52c4e86b214c2df 100644 (file)
@@ -16,7 +16,8 @@
 #include "hfsplus_raw.h"
 
 /* Compare two extents keys, returns 0 on same, pos/neg for difference */
-int hfsplus_ext_cmp_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2)
+int hfsplus_ext_cmp_key(const hfsplus_btree_key *k1,
+                       const hfsplus_btree_key *k2)
 {
        __be32 k1id, k2id;
        __be32 k1s, k2s;
index 0fa1ab6250bffb55d93af2676e874a483d86ba2b..4608171f45d3153bfc093af3ee226651b75e9450 100644 (file)
@@ -36,7 +36,7 @@
 #define HFSPLUS_TYPE_DATA 0x00
 #define HFSPLUS_TYPE_RSRC 0xFF
 
-typedef int (*btree_keycmp)(hfsplus_btree_key *, hfsplus_btree_key *);
+typedef int (*btree_keycmp)(const hfsplus_btree_key *, const hfsplus_btree_key *);
 
 #define NODE_HASH_SIZE 256
 
@@ -149,6 +149,7 @@ struct hfsplus_sb_info {
 #define HFSPLUS_SB_WRITEBACKUP 0x0001
 #define HFSPLUS_SB_NODECOMPOSE 0x0002
 #define HFSPLUS_SB_FORCE       0x0004
+#define HFSPLUS_SB_HFSX                0x0008
 
 
 struct hfsplus_inode_info {
@@ -303,7 +304,8 @@ int hfs_brec_read(struct hfs_find_data *, void *, int);
 int hfs_brec_goto(struct hfs_find_data *, int);
 
 /* catalog.c */
-int hfsplus_cat_cmp_key(hfsplus_btree_key *, hfsplus_btree_key *);
+int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *);
+int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *);
 void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *, u32, struct qstr *);
 int hfsplus_find_cat(struct super_block *, u32, struct hfs_find_data *);
 int hfsplus_create_cat(u32, struct inode *, struct qstr *, struct inode *);
@@ -312,7 +314,7 @@ int hfsplus_rename_cat(u32, struct inode *, struct qstr *,
                       struct inode *, struct qstr *);
 
 /* extents.c */
-int hfsplus_ext_cmp_key(hfsplus_btree_key *, hfsplus_btree_key *);
+int hfsplus_ext_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *);
 void hfsplus_ext_write_extent(struct inode *);
 int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int);
 int hfsplus_free_fork(struct super_block *, u32, struct hfsplus_fork_raw *, int);
@@ -350,7 +352,8 @@ extern u16 hfsplus_decompose_table[];
 extern u16 hfsplus_compose_table[];
 
 /* unicode.c */
-int hfsplus_unistrcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *);
+int hfsplus_strcasecmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *);
+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);
 
index b4fbed63321944dac1ff47676845e808512d0efb..ccc47966dc54e54d8fd0710cd121a900ff130972 100644 (file)
 #define HFSPLUS_SECTOR_SHIFT         9
 #define HFSPLUS_VOLHEAD_SECTOR       2
 #define HFSPLUS_VOLHEAD_SIG     0x482b
+#define HFSPLUS_VOLHEAD_SIGX    0x4858
 #define HFSPLUS_SUPER_MAGIC     0x482b
-#define HFSPLUS_CURRENT_VERSION      4
+#define HFSPLUS_MIN_VERSION          4
+#define HFSPLUS_CURRENT_VERSION      5
 
 #define HFSP_WRAP_MAGIC         0x4244
 #define HFSP_WRAP_ATTRIB_SLOCK  0x8000
@@ -161,7 +163,7 @@ struct hfs_btree_header_rec {
        u16 reserved1;
        __be32 clump_size;
        u8 btree_type;
-       u8 reserved2;
+       u8 key_type;
        __be32 attributes;
        u32 reserved3[16];
 } __packed;
@@ -186,6 +188,10 @@ struct hfs_btree_header_rec {
 #define HFSPLUS_EXCH_CNID              15      /* ExchangeFiles temp id */
 #define HFSPLUS_FIRSTUSER_CNID         16      /* first available user id */
 
+/* btree key type */
+#define HFSPLUS_KEY_CASEFOLDING                0xCF    /* case-insensitive */
+#define HFSPLUS_KEY_BINARY             0xBC    /* case-sensitive */
+
 /* HFS+ catalog entry key */
 struct hfsplus_cat_key {
        __be16 key_len;
index b712d34d458d67ea9a6798b8d7dd407fa46c660d..7843f792a4b79494919338965bba8aa9dfdb27f4 100644 (file)
@@ -316,8 +316,9 @@ static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
        vhdr = HFSPLUS_SB(sb).s_vhdr;
 
        /* Copy parts of the volume header into the superblock */
-       sb->s_magic = be16_to_cpu(vhdr->signature);
-       if (be16_to_cpu(vhdr->version) != HFSPLUS_CURRENT_VERSION) {
+       sb->s_magic = HFSPLUS_VOLHEAD_SIG;
+       if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
+           be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
                printk(KERN_ERR "hfs: wrong filesystem version\n");
                goto cleanup;
        }
index 060c69048c3ddc38fc867250f40c3dda66c7d1be..689c8bd721fb9231fa9d71853c033edaecbb365e 100644 (file)
@@ -28,7 +28,8 @@ static inline u16 case_fold(u16 c)
 }
 
 /* Compare unicode strings, return values like normal strcmp */
-int hfsplus_unistrcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unistr *s2)
+int hfsplus_strcasecmp(const struct hfsplus_unistr *s1,
+                      const struct hfsplus_unistr *s2)
 {
        u16 len1, len2, c1, c2;
        const hfsplus_unichr *p1, *p2;
@@ -59,6 +60,33 @@ int hfsplus_unistrcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unis
        }
 }
 
+/* Compare names as a sequence of 16-bit unsigned integers */
+int hfsplus_strcmp(const struct hfsplus_unistr *s1,
+                  const struct hfsplus_unistr *s2)
+{
+       u16 len1, len2, c1, c2;
+       const hfsplus_unichr *p1, *p2;
+       int len;
+
+       len1 = be16_to_cpu(s1->length);
+       len2 = be16_to_cpu(s2->length);
+       p1 = s1->unicode;
+       p2 = s2->unicode;
+
+       for (len = min(len1, len2); len > 0; len--) {
+               c1 = be16_to_cpu(*p1);
+               c2 = be16_to_cpu(*p2);
+               if (c1 != c2)
+                       return c1 < c2 ? -1 : 1;
+               p1++;
+               p2++;
+       }
+
+       return len1 < len2 ? -1 :
+              len1 > len2 ? 1 : 0;
+}
+
+
 #define Hangul_SBase   0xac00
 #define Hangul_LBase   0x1100
 #define Hangul_VBase   0x1161
index 6b2dc3a061a8c8e2b5dc725dc8ae972087c08252..72cab78f05091282843baef84d6e03abdc8263aa 100644 (file)
@@ -28,8 +28,11 @@ static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
 {
        u32 extent;
        u16 attrib;
+       __be16 sig;
 
-       if (be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG)) != HFSPLUS_VOLHEAD_SIG)
+       sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG);
+       if (sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIG) &&
+           sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
                return 0;
 
        attrib = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ATTRIB));
@@ -114,6 +117,10 @@ int hfsplus_read_wrapper(struct super_block *sb)
                }
                if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
                        break;
+               if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) {
+                       HFSPLUS_SB(sb).flags |= HFSPLUS_SB_HFSX;
+                       break;
+               }
                brelse(bh);
 
                /* check for a partition block
@@ -158,7 +165,9 @@ int hfsplus_read_wrapper(struct super_block *sb)
                return -EIO;
 
        /* should still be the same... */
-       if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG)
+       if (vhdr->signature != (HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX ?
+                               cpu_to_be16(HFSPLUS_VOLHEAD_SIGX) :
+                               cpu_to_be16(HFSPLUS_VOLHEAD_SIG)))
                goto error;
        HFSPLUS_SB(sb).s_vhbh = bh;
        HFSPLUS_SB(sb).s_vhdr = vhdr;