]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] HFS+ support
authorAndrew Morton <akpm@osdl.org>
Thu, 26 Feb 2004 00:17:48 +0000 (16:17 -0800)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Thu, 26 Feb 2004 00:17:48 +0000 (16:17 -0800)
From: Roman Zippel <zippel@linux-m68k.org>

This driver adds full read/write support for HFS+ and is based on the
readonly driver by Brad Broyer.

Thanks to Ethan Benson <erbenson@alaska.net> for a number of patches to
make the driver more compliant with the spec.

21 files changed:
fs/Kconfig
fs/Makefile
fs/hfsplus/Makefile [new file with mode: 0644]
fs/hfsplus/bfind.c [new file with mode: 0644]
fs/hfsplus/bitmap.c [new file with mode: 0644]
fs/hfsplus/bnode.c [new file with mode: 0644]
fs/hfsplus/brec.c [new file with mode: 0644]
fs/hfsplus/btree.c [new file with mode: 0644]
fs/hfsplus/catalog.c [new file with mode: 0644]
fs/hfsplus/dir.c [new file with mode: 0644]
fs/hfsplus/extents.c [new file with mode: 0644]
fs/hfsplus/hfsplus_fs.h [new file with mode: 0644]
fs/hfsplus/hfsplus_raw.h [new file with mode: 0644]
fs/hfsplus/inode.c [new file with mode: 0644]
fs/hfsplus/ioctl.c [new file with mode: 0644]
fs/hfsplus/options.c [new file with mode: 0644]
fs/hfsplus/part_tbl.c [new file with mode: 0644]
fs/hfsplus/super.c [new file with mode: 0644]
fs/hfsplus/tables.c [new file with mode: 0644]
fs/hfsplus/unicode.c [new file with mode: 0644]
fs/hfsplus/wrapper.c [new file with mode: 0644]

index 65202333d1797c6a103130b361d36d325604de9c..80bc94106b7220f5889c22d81a5ac352a2ee087e 100644 (file)
@@ -951,6 +951,18 @@ config HFS_FS
          To compile this file system support as a module, choose M here: the
          module will be called hfs.
 
+config HFSPLUS_FS
+       tristate "Apple Extended HFS file system support"
+       select NLS
+       help
+         If you say Y here, you will be able to mount extended format
+         Macintosh-formatted hard drive partitions with full read-write access.
+
+         This file system is often called HFS+ and was introduced with
+         MacOS 8. It includes all Mac specific filesystem data such as
+         data forks and creator codes, but it also has several UNIX
+         style features such as file ownership and permissions.
+
 config BEFS_FS
        tristate "BeOS file systemv(BeFS) support (read only) (EXPERIMENTAL)"
        depends on EXPERIMENTAL
index 21021023447a3c08ec9b8a270ef0aac258997e64..9647bebd4895ad4a9004fb28bda7d796ac192d4e 100644 (file)
@@ -62,6 +62,7 @@ obj-$(CONFIG_VFAT_FS)         += vfat/
 obj-$(CONFIG_BFS_FS)           += bfs/
 obj-$(CONFIG_ISO9660_FS)       += isofs/
 obj-$(CONFIG_DEVFS_FS)         += devfs/
+obj-$(CONFIG_HFSPLUS_FS)       += hfsplus/ # Before hfs to find wrapped HFS+
 obj-$(CONFIG_HFS_FS)           += hfs/
 obj-$(CONFIG_VXFS_FS)          += freevxfs/
 obj-$(CONFIG_NFS_FS)           += nfs/
diff --git a/fs/hfsplus/Makefile b/fs/hfsplus/Makefile
new file mode 100644 (file)
index 0000000..3cc0df7
--- /dev/null
@@ -0,0 +1,9 @@
+#
+## Makefile for the linux hfsplus filesystem routines.
+#
+
+obj-$(CONFIG_HFSPLUS_FS) += hfsplus.o
+
+hfsplus-objs := super.o options.o inode.o ioctl.o extents.o catalog.o dir.o btree.o \
+               bnode.o brec.o bfind.o tables.o unicode.o wrapper.o bitmap.o part_tbl.o
+
diff --git a/fs/hfsplus/bfind.c b/fs/hfsplus/bfind.c
new file mode 100644 (file)
index 0000000..d8305b7
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ *  linux/fs/hfsplus/bfind.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Search routines for btrees
+ */
+
+#include <linux/slab.h>
+#include "hfsplus_fs.h"
+
+int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd)
+{
+       void *ptr;
+
+       fd->tree = tree;
+       fd->bnode = NULL;
+       ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+       fd->search_key = ptr;
+       fd->key = ptr + tree->max_key_len + 2;
+       dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", tree->cnid, __builtin_return_address(0));
+       down(&tree->tree_lock);
+       return 0;
+}
+
+void hfs_find_exit(struct hfs_find_data *fd)
+{
+       hfs_bnode_put(fd->bnode);
+       kfree(fd->search_key);
+       dprint(DBG_BNODE_REFS, "find_exit: %d (%p)\n", fd->tree->cnid, __builtin_return_address(0));
+       up(&fd->tree->tree_lock);
+       fd->tree = NULL;
+}
+
+/* Find the record in bnode that best matches key (not greater than...)*/
+int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd)
+{
+       int cmpval;
+       u16 off, len, keylen;
+       int rec;
+       int b, e;
+       int res;
+
+       b = 0;
+       e = bnode->num_recs - 1;
+       res = -ENOENT;
+       do {
+               rec = (e + b) / 2;
+               len = hfs_brec_lenoff(bnode, rec, &off);
+               keylen = hfs_brec_keylen(bnode, rec);
+               hfs_bnode_read(bnode, fd->key, off, keylen);
+               cmpval = bnode->tree->keycmp(fd->key, fd->search_key);
+               if (!cmpval) {
+                       e = rec;
+                       res = 0;
+                       goto done;
+               }
+               if (cmpval < 0)
+                       b = rec + 1;
+               else
+                       e = rec - 1;
+       } while (b <= e);
+       //printk("%d: %d,%d,%d\n", bnode->this, b, e, rec);
+       if (rec != e && e >= 0) {
+               len = hfs_brec_lenoff(bnode, e, &off);
+               keylen = hfs_brec_keylen(bnode, e);
+               hfs_bnode_read(bnode, fd->key, off, keylen);
+       }
+done:
+       fd->record = e;
+       fd->keyoffset = off;
+       fd->keylength = keylen;
+       fd->entryoffset = off + keylen;
+       fd->entrylength = len - keylen;
+       return res;
+}
+
+/* Traverse a B*Tree from the root to a leaf finding best fit to key */
+/* Return allocated copy of node found, set recnum to best record */
+int hfs_brec_find(struct hfs_find_data *fd)
+{
+       struct hfs_btree *tree;
+       struct hfs_bnode *bnode;
+       u32 data, nidx, parent;
+       int height, res;
+
+       tree = fd->tree;
+       if (fd->bnode)
+               hfs_bnode_put(fd->bnode);
+       fd->bnode = NULL;
+       nidx = tree->root;
+       if (!nidx)
+               return -ENOENT;
+       height = tree->depth;
+       res = 0;
+       parent = 0;
+       for (;;) {
+               bnode = hfs_bnode_find(tree, nidx);
+               if (IS_ERR(bnode)) {
+                       res = PTR_ERR(bnode);
+                       bnode = NULL;
+                       break;
+               }
+               if (bnode->height != height)
+                       goto invalid;
+               if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF))
+                       goto invalid;
+               bnode->parent = parent;
+
+               res = __hfs_brec_find(bnode, fd);
+               if (!height)
+                       break;
+               if (fd->record < 0)
+                       goto release;
+
+               parent = nidx;
+               hfs_bnode_read(bnode, &data, fd->entryoffset, 4);
+               nidx = be32_to_cpu(data);
+               hfs_bnode_put(bnode);
+       }
+       fd->bnode = bnode;
+       return res;
+
+invalid:
+       printk("HFS+-fs: inconsistency in B*Tree (%d,%d,%d,%u,%u)\n",
+               height, bnode->height, bnode->type, nidx, parent);
+       res = -EIO;
+release:
+       hfs_bnode_put(bnode);
+       return res;
+}
+
+int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
+{
+       int res;
+
+       res = hfs_brec_find(fd);
+       if (res)
+               return res;
+       if (fd->entrylength > rec_len)
+               return -EINVAL;
+       hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength);
+       return 0;
+}
+
+int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
+{
+       struct hfs_btree *tree;
+       struct hfs_bnode *bnode;
+       int idx, res = 0;
+       u16 off, len, keylen;
+
+       bnode = fd->bnode;
+       tree = bnode->tree;
+
+       if (cnt < 0) {
+               cnt = -cnt;
+               while (cnt > fd->record) {
+                       cnt -= fd->record + 1;
+                       fd->record = bnode->num_recs - 1;
+                       idx = bnode->prev;
+                       if (!idx) {
+                               res = -ENOENT;
+                               goto out;
+                       }
+                       hfs_bnode_put(bnode);
+                       bnode = hfs_bnode_find(tree, idx);
+                       if (IS_ERR(bnode)) {
+                               res = PTR_ERR(bnode);
+                               bnode = NULL;
+                               goto out;
+                       }
+               }
+               fd->record -= cnt;
+       } else {
+               while (cnt >= bnode->num_recs - fd->record) {
+                       cnt -= bnode->num_recs - fd->record;
+                       fd->record = 0;
+                       idx = bnode->next;
+                       if (!idx) {
+                               res = -ENOENT;
+                               goto out;
+                       }
+                       hfs_bnode_put(bnode);
+                       bnode = hfs_bnode_find(tree, idx);
+                       if (IS_ERR(bnode)) {
+                               res = PTR_ERR(bnode);
+                               bnode = NULL;
+                               goto out;
+                       }
+               }
+               fd->record += cnt;
+       }
+
+       len = hfs_brec_lenoff(bnode, fd->record, &off);
+       keylen = hfs_brec_keylen(bnode, fd->record);
+       fd->keyoffset = off;
+       fd->keylength = keylen;
+       fd->entryoffset = off + keylen;
+       fd->entrylength = len - keylen;
+       hfs_bnode_read(bnode, fd->key, off, keylen);
+out:
+       fd->bnode = bnode;
+       return res;
+}
diff --git a/fs/hfsplus/bitmap.c b/fs/hfsplus/bitmap.c
new file mode 100644 (file)
index 0000000..644dcb5
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ *  linux/fs/hfsplus/bitmap.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of allocation file
+ */
+
+#include <linux/pagemap.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+#define PAGE_CACHE_BITS        (PAGE_CACHE_SIZE * 8)
+
+int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, u32 *max)
+{
+       struct page *page;
+       struct address_space *mapping;
+       u32 *pptr, *curr, *end;
+       u32 val, mask, start, len;
+       int i;
+
+       len = *max;
+       if (!len)
+               return size;
+
+       dprint(DBG_BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len);
+       down(&HFSPLUS_SB(sb).alloc_file->i_sem);
+       mapping = HFSPLUS_SB(sb).alloc_file->i_mapping;
+       page = read_cache_page(mapping, offset / PAGE_CACHE_BITS,
+                              (filler_t *)mapping->a_ops->readpage, NULL);
+       pptr = kmap(page);
+       curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
+       i = offset % 32;
+       offset &= ~(PAGE_CACHE_BITS - 1);
+       if ((size ^ offset) / PAGE_CACHE_BITS)
+               end = pptr + PAGE_CACHE_BITS / 32;
+       else
+               end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
+
+       /* scan the first partial u32 for zero bits */
+       val = *curr;
+       if (~val) {
+               val = be32_to_cpu(val);
+               mask = (1U << 31) >> i;
+               for (; i < 32; mask >>= 1, i++) {
+                       if (!(val & mask))
+                               goto found;
+               }
+       }
+       curr++;
+
+       /* scan complete u32s for the first zero bit */
+       while (1) {
+               while (curr < end) {
+                       val = *curr;
+                       if (~val) {
+                               val = be32_to_cpu(val);
+                               mask = 1 << 31;
+                               for (i = 0; i < 32; mask >>= 1, i++) {
+                                       if (!(val & mask))
+                                               goto found;
+                               }
+                       }
+                       curr++;
+               }
+               kunmap(page);
+               offset += PAGE_CACHE_BITS;
+               if (offset >= size)
+                       break;
+               page = read_cache_page(mapping, offset / PAGE_CACHE_BITS,
+                                      (filler_t *)mapping->a_ops->readpage, NULL);
+               curr = pptr = kmap(page);
+               if ((size ^ offset) / PAGE_CACHE_BITS)
+                       end = pptr + PAGE_CACHE_BITS / 32;
+               else
+                       end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
+       }
+       dprint(DBG_BITMAP, "bitmap full\n");
+       start = size;
+       goto out;
+
+found:
+       start = offset + (curr - pptr) * 32 + i;
+       if (start >= size) {
+               dprint(DBG_BITMAP, "bitmap full\n");
+               goto out;
+       }
+       /* do any partial u32 at the start */
+       len = min(size - start, len);
+       while (1) {
+               val |= mask;
+               if (++i >= 32)
+                       break;
+               mask >>= 1;
+               if (!--len || val & mask)
+                       goto done;
+       }
+       if (!--len)
+               goto done;
+       *curr++ = cpu_to_be32(val);
+       /* do full u32s */
+       while (1) {
+               while (curr < end) {
+                       val = be32_to_cpu(*curr);
+                       if (len < 32)
+                               goto last;
+                       if (val) {
+                               len = 32;
+                               goto last;
+                       }
+                       *curr++ = 0xffffffffU;
+                       len -= 32;
+               }
+               set_page_dirty(page);
+               kunmap(page);
+               offset += PAGE_CACHE_BITS;
+               page = read_cache_page(mapping, offset / PAGE_CACHE_BITS,
+                                      (filler_t *)mapping->a_ops->readpage, NULL);
+               pptr = kmap(page);
+               curr = pptr;
+               end = pptr + PAGE_CACHE_BITS / 32;
+       }
+last:
+       /* do any partial u32 at end */
+       mask = 1U << 31;
+       for (i = 0; i < len; i++) {
+               if (val & mask)
+                       break;
+               val |= mask;
+               mask >>= 1;
+       }
+done:
+       *curr = cpu_to_be32(val);
+       set_page_dirty(page);
+       kunmap(page);
+       *max = offset + (curr - pptr) * 32 + i - start;
+       HFSPLUS_SB(sb).free_blocks -= *max;
+       sb->s_dirt = 1;
+       dprint(DBG_BITMAP, "-> %u,%u\n", start, *max);
+out:
+       up(&HFSPLUS_SB(sb).alloc_file->i_sem);
+       return start;
+}
+
+int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count)
+{
+       struct page *page;
+       struct address_space *mapping;
+       u32 *pptr, *curr, *end;
+       u32 mask, len, pnr;
+       int i;
+
+       /* is there any actual work to be done? */
+       if (!count)
+               return 0;
+
+       dprint(DBG_BITMAP, "block_free: %u,%u\n", offset, count);
+       /* are all of the bits in range? */
+       if ((offset + count) > HFSPLUS_SB(sb).total_blocks)
+               return -2;
+
+       down(&HFSPLUS_SB(sb).alloc_file->i_sem);
+       mapping = HFSPLUS_SB(sb).alloc_file->i_mapping;
+       pnr = offset / PAGE_CACHE_BITS;
+       page = read_cache_page(mapping, pnr, (filler_t *)mapping->a_ops->readpage, NULL);
+       pptr = kmap(page);
+       curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
+       end = pptr + PAGE_CACHE_BITS / 32;
+       len = count;
+
+       /* do any partial u32 at the start */
+       i = offset % 32;
+       if (i) {
+               int j = 32 - i;
+               mask = 0xffffffffU << j;
+               if (j > count) {
+                       mask |= 0xffffffffU >> (i + count);
+                       *curr++ &= cpu_to_be32(mask);
+                       goto out;
+               }
+               *curr++ &= cpu_to_be32(mask);
+               count -= j;
+       }
+
+       /* do full u32s */
+       while (1) {
+               while (curr < end) {
+                       if (count < 32)
+                               goto done;
+                       *curr++ = 0;
+                       count -= 32;
+               }
+               if (!count)
+                       break;
+               set_page_dirty(page);
+               kunmap(page);
+               page = read_cache_page(mapping, ++pnr, (filler_t *)mapping->a_ops->readpage, NULL);
+               pptr = kmap(page);
+               curr = pptr;
+               end = pptr + PAGE_CACHE_BITS / 32;
+       }
+done:
+       /* do any partial u32 at end */
+       if (count) {
+               mask = 0xffffffffU >> count;
+               *curr &= cpu_to_be32(mask);
+       }
+out:
+       set_page_dirty(page);
+       kunmap(page);
+       HFSPLUS_SB(sb).free_blocks += len;
+       sb->s_dirt = 1;
+       up(&HFSPLUS_SB(sb).alloc_file->i_sem);
+
+       return 0;
+}
diff --git a/fs/hfsplus/bnode.c b/fs/hfsplus/bnode.c
new file mode 100644 (file)
index 0000000..ec8cb5d
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ *  linux/fs/hfsplus/bnode.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handle basic btree node operations
+ */
+
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/swap.h>
+#include <linux/version.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+#define REF_PAGES      0
+
+/* Copy a specified range of bytes from the raw data of a node */
+void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
+{
+       struct page **pagep;
+       int l;
+
+       off += node->page_offset;
+       pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+       off &= ~PAGE_CACHE_MASK;
+
+       l = min(len, (int)PAGE_CACHE_SIZE - off);
+       memcpy(buf, kmap(*pagep) + off, l);
+       kunmap(*pagep);
+
+       while ((len -= l)) {
+               buf += l;
+               l = min(len, (int)PAGE_CACHE_SIZE);
+               memcpy(buf, kmap(*++pagep), l);
+               kunmap(*pagep);
+       }
+}
+
+u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
+{
+       u16 data;
+       // optimize later...
+       hfs_bnode_read(node, &data, off, 2);
+       return be16_to_cpu(data);
+}
+
+u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
+{
+       u8 data;
+       // optimize later...
+       hfs_bnode_read(node, &data, off, 1);
+       return be16_to_cpu(data);
+}
+
+void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
+{
+       struct hfs_btree *tree;
+       int key_len;
+
+       tree = node->tree;
+       if (node->type == HFS_NODE_LEAF ||
+           tree->attributes & HFS_TREE_VARIDXKEYS)
+               key_len = hfs_bnode_read_u16(node, off) + 2;
+       else
+               key_len = tree->max_key_len + 2;
+
+       hfs_bnode_read(node, key, off, key_len);
+}
+
+void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
+{
+       struct page **pagep;
+       int l;
+
+       off += node->page_offset;
+       pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+       off &= ~PAGE_CACHE_MASK;
+
+       l = min(len, (int)PAGE_CACHE_SIZE - off);
+       memcpy(kmap(*pagep) + off, buf, l);
+       set_page_dirty(*pagep);
+       kunmap(*pagep);
+
+       while ((len -= l)) {
+               buf += l;
+               l = min(len, (int)PAGE_CACHE_SIZE);
+               memcpy(kmap(*++pagep), buf, l);
+               set_page_dirty(*pagep);
+               kunmap(*pagep);
+       }
+}
+
+void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
+{
+       data = cpu_to_be16(data);
+       // optimize later...
+       hfs_bnode_write(node, &data, off, 2);
+}
+
+void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
+{
+       struct page **pagep;
+       int l;
+
+       off += node->page_offset;
+       pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+       off &= ~PAGE_CACHE_MASK;
+
+       l = min(len, (int)PAGE_CACHE_SIZE - off);
+       memset(kmap(*pagep) + off, 0, l);
+       set_page_dirty(*pagep);
+       kunmap(*pagep);
+
+       while ((len -= l)) {
+               l = min(len, (int)PAGE_CACHE_SIZE);
+               memset(kmap(*++pagep), 0, l);
+               set_page_dirty(*pagep);
+               kunmap(*pagep);
+       }
+}
+
+void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
+                   struct hfs_bnode *src_node, int src, int len)
+{
+       struct hfs_btree *tree;
+       struct page **src_page, **dst_page;
+       int l;
+
+       dprint(DBG_BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
+       if (!len)
+               return;
+       tree = src_node->tree;
+       src += src_node->page_offset;
+       dst += dst_node->page_offset;
+       src_page = src_node->page + (src >> PAGE_CACHE_SHIFT);
+       src &= ~PAGE_CACHE_MASK;
+       dst_page = dst_node->page + (dst >> PAGE_CACHE_SHIFT);
+       dst &= ~PAGE_CACHE_MASK;
+
+       if (src == dst) {
+               l = min(len, (int)PAGE_CACHE_SIZE - src);
+               memcpy(kmap(*dst_page) + src, kmap(*src_page) + src, l);
+               kunmap(*src_page);
+               set_page_dirty(*dst_page);
+               kunmap(*dst_page);
+
+               while ((len -= l)) {
+                       l = min(len, (int)PAGE_CACHE_SIZE);
+                       memcpy(kmap(*++dst_page), kmap(*++src_page), l);
+                       kunmap(*src_page);
+                       set_page_dirty(*dst_page);
+                       kunmap(*dst_page);
+               }
+       } else {
+               void *src_ptr, *dst_ptr;
+
+               do {
+                       src_ptr = kmap(*src_page) + src;
+                       dst_ptr = kmap(*dst_page) + dst;
+                       if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) {
+                               l = PAGE_CACHE_SIZE - src;
+                               src = 0;
+                               dst += l;
+                       } else {
+                               l = PAGE_CACHE_SIZE - dst;
+                               src += l;
+                               dst = 0;
+                       }
+                       l = min(len, l);
+                       memcpy(dst_ptr, src_ptr, l);
+                       kunmap(*src_page);
+                       set_page_dirty(*dst_page);
+                       kunmap(*dst_page);
+                       if (!dst)
+                               dst_page++;
+                       else
+                               src_page++;
+               } while ((len -= l));
+       }
+}
+
+void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
+{
+       struct page **src_page, **dst_page;
+       int l;
+
+       dprint(DBG_BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
+       if (!len)
+               return;
+       src += node->page_offset;
+       dst += node->page_offset;
+       if (dst > src) {
+               src += len - 1;
+               src_page = node->page + (src >> PAGE_CACHE_SHIFT);
+               src = (src & ~PAGE_CACHE_MASK) + 1;
+               dst += len - 1;
+               dst_page = node->page + (dst >> PAGE_CACHE_SHIFT);
+               dst = (dst & ~PAGE_CACHE_MASK) + 1;
+
+               if (src == dst) {
+                       while (src < len) {
+                               memmove(kmap(*dst_page), kmap(*src_page), src);
+                               kunmap(*src_page);
+                               set_page_dirty(*dst_page);
+                               kunmap(*dst_page);
+                               len -= src;
+                               src = PAGE_CACHE_SIZE;
+                               src_page--;
+                               dst_page--;
+                       }
+                       src -= len;
+                       memmove(kmap(*dst_page) + src, kmap(*src_page) + src, len);
+                       kunmap(*src_page);
+                       set_page_dirty(*dst_page);
+                       kunmap(*dst_page);
+               } else {
+                       void *src_ptr, *dst_ptr;
+
+                       do {
+                               src_ptr = kmap(*src_page) + src;
+                               dst_ptr = kmap(*dst_page) + dst;
+                               if (src < dst) {
+                                       l = src;
+                                       src = PAGE_CACHE_SIZE;
+                                       dst -= l;
+                               } else {
+                                       l = dst;
+                                       src -= l;
+                                       dst = PAGE_CACHE_SIZE;
+                               }
+                               l = min(len, l);
+                               memmove(dst_ptr - l, src_ptr - l, l);
+                               kunmap(*src_page);
+                               set_page_dirty(*dst_page);
+                               kunmap(*dst_page);
+                               if (dst == PAGE_CACHE_SIZE)
+                                       dst_page--;
+                               else
+                                       src_page--;
+                       } while ((len -= l));
+               }
+       } else {
+               src_page = node->page + (src >> PAGE_CACHE_SHIFT);
+               src &= ~PAGE_CACHE_MASK;
+               dst_page = node->page + (dst >> PAGE_CACHE_SHIFT);
+               dst &= ~PAGE_CACHE_MASK;
+
+               if (src == dst) {
+                       l = min(len, (int)PAGE_CACHE_SIZE - src);
+                       memmove(kmap(*dst_page) + src, kmap(*src_page) + src, l);
+                       kunmap(*src_page);
+                       set_page_dirty(*dst_page);
+                       kunmap(*dst_page);
+
+                       while ((len -= l)) {
+                               l = min(len, (int)PAGE_CACHE_SIZE);
+                               memmove(kmap(*++dst_page), kmap(*++src_page), l);
+                               kunmap(*src_page);
+                               set_page_dirty(*dst_page);
+                               kunmap(*dst_page);
+                       }
+               } else {
+                       void *src_ptr, *dst_ptr;
+
+                       do {
+                               src_ptr = kmap(*src_page) + src;
+                               dst_ptr = kmap(*dst_page) + dst;
+                               if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) {
+                                       l = PAGE_CACHE_SIZE - src;
+                                       src = 0;
+                                       dst += l;
+                               } else {
+                                       l = PAGE_CACHE_SIZE - dst;
+                                       src += l;
+                                       dst = 0;
+                               }
+                               l = min(len, l);
+                               memmove(dst_ptr, src_ptr, l);
+                               kunmap(*src_page);
+                               set_page_dirty(*dst_page);
+                               kunmap(*dst_page);
+                               if (!dst)
+                                       dst_page++;
+                               else
+                                       src_page++;
+                       } while ((len -= l));
+               }
+       }
+}
+
+void hfs_bnode_dump(struct hfs_bnode *node)
+{
+       struct hfs_bnode_desc desc;
+       u32 cnid;
+       int i, off, key_off;
+
+       dprint(DBG_BNODE_MOD, "bnode: %d\n", node->this);
+       hfs_bnode_read(node, &desc, 0, sizeof(desc));
+       dprint(DBG_BNODE_MOD, "%d, %d, %d, %d, %d\n",
+               be32_to_cpu(desc.next), be32_to_cpu(desc.prev),
+               desc.type, desc.height, be16_to_cpu(desc.num_recs));
+
+       off = node->tree->node_size - 2;
+       for (i = be16_to_cpu(desc.num_recs); i >= 0; off -= 2, i--) {
+               key_off = hfs_bnode_read_u16(node, off);
+               dprint(DBG_BNODE_MOD, " %d", key_off);
+               if (i && node->type == HFS_NODE_INDEX) {
+                       int tmp;
+
+                       if (node->tree->attributes & HFS_TREE_VARIDXKEYS)
+                               tmp = hfs_bnode_read_u16(node, key_off) + 2;
+                       else
+                               tmp = node->tree->max_key_len + 2;
+                       dprint(DBG_BNODE_MOD, " (%d", tmp);
+                       hfs_bnode_read(node, &cnid, key_off + tmp, 4);
+                       dprint(DBG_BNODE_MOD, ",%d)", be32_to_cpu(cnid));
+               } else if (i && node->type == HFS_NODE_LEAF) {
+                       int tmp;
+
+                       tmp = hfs_bnode_read_u16(node, key_off);
+                       dprint(DBG_BNODE_MOD, " (%d)", tmp);
+               }
+       }
+       dprint(DBG_BNODE_MOD, "\n");
+}
+
+void hfs_bnode_unlink(struct hfs_bnode *node)
+{
+       struct hfs_btree *tree;
+       struct hfs_bnode *tmp;
+       u32 cnid;
+
+       tree = node->tree;
+       if (node->prev) {
+               tmp = hfs_bnode_find(tree, node->prev);
+               if (IS_ERR(tmp))
+                       return;
+               tmp->next = node->next;
+               cnid = cpu_to_be32(tmp->next);
+               hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, next), 4);
+               hfs_bnode_put(tmp);
+       } else if (node->type == HFS_NODE_LEAF)
+               tree->leaf_head = node->next;
+
+       if (node->next) {
+               tmp = hfs_bnode_find(tree, node->next);
+               if (IS_ERR(tmp))
+                       return;
+               tmp->prev = node->prev;
+               cnid = cpu_to_be32(tmp->prev);
+               hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, prev), 4);
+               hfs_bnode_put(tmp);
+       } else if (node->type == HFS_NODE_LEAF)
+               tree->leaf_tail = node->prev;
+
+       // move down?
+       if (!node->prev && !node->next) {
+               printk("hfs_btree_del_level\n");
+       }
+       if (!node->parent) {
+               tree->root = 0;
+               tree->depth = 0;
+       }
+       set_bit(HFS_BNODE_DELETED, &node->flags);
+}
+
+static inline int hfs_bnode_hash(u32 num)
+{
+       num = (num >> 16) + num;
+       num += num >> 8;
+       return num & (NODE_HASH_SIZE - 1);
+}
+
+struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *tree, u32 cnid)
+{
+       struct hfs_bnode *node;
+
+       if (cnid >= tree->node_count) {
+               printk("HFS+-fs: request for non-existent node %d in B*Tree\n", cnid);
+               return NULL;
+       }
+
+       for (node = tree->node_hash[hfs_bnode_hash(cnid)];
+            node; node = node->next_hash) {
+               if (node->this == cnid) {
+                       return node;
+               }
+       }
+       return NULL;
+}
+
+static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
+{
+       struct super_block *sb;
+       struct hfs_bnode *node, *node2;
+       struct address_space *mapping;
+       struct page *page;
+       int size, block, i, hash;
+       loff_t off;
+
+       if (cnid >= tree->node_count) {
+               printk("HFS+-fs: request for non-existent node %d in B*Tree\n", cnid);
+               return NULL;
+       }
+
+       sb = tree->inode->i_sb;
+       size = sizeof(struct hfs_bnode) + tree->pages_per_bnode *
+               sizeof(struct page *);
+       node = kmalloc(size, GFP_KERNEL);
+       if (!node)
+               return NULL;
+       memset(node, 0, size);
+       node->tree = tree;
+       node->this = cnid;
+       set_bit(HFS_BNODE_NEW, &node->flags);
+       atomic_set(&node->refcnt, 1);
+       dprint(DBG_BNODE_REFS, "new_node(%d:%d): 1\n",
+              node->tree->cnid, node->this);
+       init_waitqueue_head(&node->lock_wq);
+       spin_lock(&tree->hash_lock);
+       node2 = hfs_bnode_findhash(tree, cnid);
+       if (!node2) {
+               hash = hfs_bnode_hash(cnid);
+               node->next_hash = tree->node_hash[hash];
+               tree->node_hash[hash] = node;
+               tree->node_hash_cnt++;
+       } else {
+               spin_unlock(&tree->hash_lock);
+               kfree(node);
+               wait_event(node2->lock_wq, !test_bit(HFS_BNODE_NEW, &node2->flags));
+               return node2;
+       }
+       spin_unlock(&tree->hash_lock);
+
+       mapping = tree->inode->i_mapping;
+       off = (loff_t)cnid << tree->node_size_shift;
+       block = off >> PAGE_CACHE_SHIFT;
+       node->page_offset = off & ~PAGE_CACHE_MASK;
+       for (i = 0; i < tree->pages_per_bnode; block++, i++) {
+               page = read_cache_page(mapping, block, (filler_t *)mapping->a_ops->readpage, NULL);
+               if (IS_ERR(page))
+                       goto fail;
+#if !REF_PAGES
+               page_cache_release(page);
+#endif
+               node->page[i] = page;
+       }
+
+       return node;
+fail:
+       set_bit(HFS_BNODE_ERROR, &node->flags);
+       return node;
+}
+
+void hfs_bnode_unhash(struct hfs_bnode *node)
+{
+       struct hfs_bnode **p;
+
+       dprint(DBG_BNODE_REFS, "remove_node(%d:%d): %d\n",
+               node->tree->cnid, node->this, atomic_read(&node->refcnt));
+       for (p = &node->tree->node_hash[hfs_bnode_hash(node->this)];
+            *p && *p != node; p = &(*p)->next_hash)
+               ;
+       if (!*p)
+               BUG();
+       *p = node->next_hash;
+       node->tree->node_hash_cnt--;
+}
+
+/* Load a particular node out of a tree */
+struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num)
+{
+       struct hfs_bnode *node;
+       struct hfs_bnode_desc *desc;
+       int i, rec_off, off, next_off;
+       int entry_size, key_size;
+
+       spin_lock(&tree->hash_lock);
+       node = hfs_bnode_findhash(tree, num);
+       if (node) {
+               hfs_bnode_get(node);
+               spin_unlock(&tree->hash_lock);
+               wait_event(node->lock_wq, !test_bit(HFS_BNODE_NEW, &node->flags));
+               return node;
+       }
+       spin_unlock(&tree->hash_lock);
+       node = __hfs_bnode_create(tree, num);
+       if (!node)
+               return ERR_PTR(-ENOMEM);
+       if (!test_bit(HFS_BNODE_NEW, &node->flags))
+               return node;
+
+       desc = (struct hfs_bnode_desc *)(kmap(node->page[0]) + node->page_offset);
+       node->prev = be32_to_cpu(desc->prev);
+       node->next = be32_to_cpu(desc->next);
+       node->num_recs = be16_to_cpu(desc->num_recs);
+       node->type = desc->type;
+       node->height = desc->height;
+       kunmap(node->page[0]);
+
+       switch (node->type) {
+       case HFS_NODE_HEADER:
+       case HFS_NODE_MAP:
+               if (node->height != 0)
+                       goto node_error;
+               break;
+       case HFS_NODE_LEAF:
+               if (node->height != 1)
+                       goto node_error;
+               break;
+       case HFS_NODE_INDEX:
+               if (node->height <= 1 || node->height > tree->depth)
+                       goto node_error;
+               break;
+       default:
+               goto node_error;
+       }
+
+       rec_off = tree->node_size - 2;
+       off = hfs_bnode_read_u16(node, rec_off);
+       if (off != sizeof(struct hfs_bnode_desc))
+               goto node_error;
+       for (i = 1; i <= node->num_recs; off = next_off, i++) {
+               rec_off -= 2;
+               next_off = hfs_bnode_read_u16(node, rec_off);
+               if (next_off <= off ||
+                   next_off > tree->node_size ||
+                   next_off & 1)
+                       goto node_error;
+               entry_size = next_off - off;
+               if (node->type != HFS_NODE_INDEX &&
+                   node->type != HFS_NODE_LEAF)
+                       continue;
+               key_size = hfs_bnode_read_u16(node, off) + 2;
+               if (key_size >= entry_size || key_size & 1)
+                       goto node_error;
+       }
+       clear_bit(HFS_BNODE_NEW, &node->flags);
+       wake_up(&node->lock_wq);
+       return node;
+
+node_error:
+       set_bit(HFS_BNODE_ERROR, &node->flags);
+       clear_bit(HFS_BNODE_NEW, &node->flags);
+       wake_up(&node->lock_wq);
+       hfs_bnode_put(node);
+       return ERR_PTR(-EIO);
+}
+
+void hfs_bnode_free(struct hfs_bnode *node)
+{
+       //int i;
+
+       //for (i = 0; i < node->tree->pages_per_bnode; i++)
+       //      if (node->page[i])
+       //              page_cache_release(node->page[i]);
+       kfree(node);
+}
+
+struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num)
+{
+       struct hfs_bnode *node;
+       struct page **pagep;
+       int i;
+
+       spin_lock(&tree->hash_lock);
+       node = hfs_bnode_findhash(tree, num);
+       spin_unlock(&tree->hash_lock);
+       if (node) {
+               printk("new node %u already hashed?\n", num);
+               BUG();
+       }
+       node = __hfs_bnode_create(tree, num);
+       if (!node)
+               return ERR_PTR(-ENOMEM);
+
+       pagep = node->page;
+       memset(kmap(*pagep) + node->page_offset, 0,
+              min((int)PAGE_CACHE_SIZE, (int)tree->node_size));
+       set_page_dirty(*pagep);
+       kunmap(*pagep);
+       for (i = 1; i < tree->pages_per_bnode; i++) {
+               memset(kmap(*++pagep), 0, PAGE_CACHE_SIZE);
+               set_page_dirty(*pagep);
+               kunmap(*pagep);
+       }
+       clear_bit(HFS_BNODE_NEW, &node->flags);
+       wake_up(&node->lock_wq);
+
+       return node;
+}
+
+void hfs_bnode_get(struct hfs_bnode *node)
+{
+       if (node) {
+               atomic_inc(&node->refcnt);
+#if REF_PAGES
+               {
+               int i;
+               for (i = 0; i < node->tree->pages_per_bnode; i++)
+                       get_page(node->page[i]);
+               }
+#endif
+               dprint(DBG_BNODE_REFS, "get_node(%d:%d): %d\n",
+                      node->tree->cnid, node->this, atomic_read(&node->refcnt));
+       }
+}
+
+/* Dispose of resources used by a node */
+void hfs_bnode_put(struct hfs_bnode *node)
+{
+       if (node) {
+               struct hfs_btree *tree = node->tree;
+               int i;
+
+               dprint(DBG_BNODE_REFS, "put_node(%d:%d): %d\n",
+                      node->tree->cnid, node->this, atomic_read(&node->refcnt));
+               if (!atomic_read(&node->refcnt))
+                       BUG();
+               if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock)) {
+#if REF_PAGES
+                       for (i = 0; i < tree->pages_per_bnode; i++)
+                               put_page(node->page[i]);
+#endif
+                       return;
+               }
+               for (i = 0; i < tree->pages_per_bnode; i++) {
+                       mark_page_accessed(node->page[i]);
+#if REF_PAGES
+                       put_page(node->page[i]);
+#endif
+               }
+
+               if (test_bit(HFS_BNODE_DELETED, &node->flags)) {
+                       hfs_bnode_unhash(node);
+                       spin_unlock(&tree->hash_lock);
+                       hfs_bmap_free(node);
+                       hfs_bnode_free(node);
+                       return;
+               }
+               spin_unlock(&tree->hash_lock);
+       }
+}
+
+void hfsplus_lock_bnode(struct hfs_bnode *node)
+{
+       wait_event(node->lock_wq, !test_and_set_bit(HFS_BNODE_LOCK, &node->flags));
+}
+
+void hfsplus_unlock_bnode(struct hfs_bnode *node)
+{
+       clear_bit(HFS_BNODE_LOCK, &node->flags);
+       if (waitqueue_active(&node->lock_wq))
+               wake_up(&node->lock_wq);
+}
diff --git a/fs/hfsplus/brec.c b/fs/hfsplus/brec.c
new file mode 100644 (file)
index 0000000..2d1ff31
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ *  linux/fs/hfsplus/brec.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handle individual btree records
+ */
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Get the length and offset of the given record in the given node */
+u16 hfs_brec_lenoff(struct hfs_bnode *node, u16 rec, u16 *off)
+{
+       u16 retval[2];
+       u16 dataoff;
+
+       dataoff = node->tree->node_size - (rec + 2) * 2;
+       hfs_bnode_read(node, retval, dataoff, 4);
+       *off = be16_to_cpu(retval[1]);
+       return be16_to_cpu(retval[0]) - *off;
+}
+
+/* Get the length of the key from a keyed record */
+u16 hfs_brec_keylen(struct hfs_bnode *node, u16 rec)
+{
+       u16 retval, recoff;
+
+       if (node->type != HFS_NODE_INDEX && node->type != HFS_NODE_LEAF)
+               return 0;
+
+       if ((node->type == HFS_NODE_INDEX) &&
+          !(node->tree->attributes & HFS_TREE_VARIDXKEYS)) {
+               retval = node->tree->max_key_len;
+       } else {
+               recoff = hfs_bnode_read_u16(node, node->tree->node_size - (rec + 1) * 2);
+               if (!recoff)
+                       return 0;
+               if (node->tree->attributes & HFS_TREE_BIGKEYS)
+                       retval = hfs_bnode_read_u16(node, recoff) + 2;
+               else
+                       retval = (hfs_bnode_read_u8(node, recoff) | 1) + 1;
+       }
+       return retval;
+}
+
+int hfs_brec_insert(struct hfs_find_data *fd, void *entry, int entry_len)
+{
+       struct hfs_btree *tree;
+       struct hfs_bnode *node, *new_node;
+       int size, key_len, rec;
+       int data_off, end_off;
+       int idx_rec_off, data_rec_off, end_rec_off;
+       u32 cnid;
+
+       tree = fd->tree;
+       if (!fd->bnode) {
+               if (!tree->root)
+                       hfs_btree_inc_height(tree);
+               fd->bnode = hfs_bnode_find(tree, tree->leaf_head);
+               if (IS_ERR(fd->bnode))
+                       return PTR_ERR(fd->bnode);
+               fd->record = -1;
+       }
+       new_node = NULL;
+       key_len = be16_to_cpu(fd->search_key->key_len) + 2;
+again:
+       /* new record idx and complete record size */
+       rec = fd->record + 1;
+       size = key_len + entry_len;
+
+       node = fd->bnode;
+       hfs_bnode_dump(node);
+       /* get last offset */
+       end_rec_off = tree->node_size - (node->num_recs + 1) * 2;
+       end_off = hfs_bnode_read_u16(node, end_rec_off);
+       end_rec_off -= 2;
+       dprint(DBG_BNODE_MOD, "insert_rec: %d, %d, %d, %d\n", rec, size, end_off, end_rec_off);
+       if (size > end_rec_off - end_off) {
+               if (new_node)
+                       panic("not enough room!\n");
+               new_node = hfs_bnode_split(fd);
+               if (IS_ERR(new_node))
+                       return PTR_ERR(new_node);
+               goto again;
+       }
+       if (node->type == HFS_NODE_LEAF) {
+               tree->leaf_count++;
+               mark_inode_dirty(tree->inode);
+       }
+       node->num_recs++;
+       /* write new last offset */
+       hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs);
+       hfs_bnode_write_u16(node, end_rec_off, end_off + size);
+       data_off = end_off;
+       data_rec_off = end_rec_off + 2;
+       idx_rec_off = tree->node_size - (rec + 1) * 2;
+       if (idx_rec_off == data_rec_off)
+               goto skip;
+       /* move all following entries */
+       do {
+               data_off = hfs_bnode_read_u16(node, data_rec_off + 2);
+               hfs_bnode_write_u16(node, data_rec_off, data_off + size);
+               data_rec_off += 2;
+       } while (data_rec_off < idx_rec_off);
+
+       /* move data away */
+       hfs_bnode_move(node, data_off + size, data_off,
+                      end_off - data_off);
+
+skip:
+       hfs_bnode_write(node, fd->search_key, data_off, key_len);
+       hfs_bnode_write(node, entry, data_off + key_len, entry_len);
+       hfs_bnode_dump(node);
+
+       if (new_node) {
+               /* update parent key if we inserted a key
+                * at the start of the first node
+                */
+               if (!rec && new_node != node)
+                       hfs_brec_update_parent(fd);
+
+               hfs_bnode_put(fd->bnode);
+               if (!new_node->parent) {
+                       hfs_btree_inc_height(tree);
+                       new_node->parent = tree->root;
+               }
+               fd->bnode = hfs_bnode_find(tree, new_node->parent);
+
+               /* create index data entry */
+               cnid = cpu_to_be32(new_node->this);
+               entry = &cnid;
+               entry_len = sizeof(cnid);
+
+               /* get index key */
+               hfs_bnode_read_key(new_node, fd->search_key, 14);
+               __hfs_brec_find(fd->bnode, fd);
+
+               hfs_bnode_put(new_node);
+               new_node = NULL;
+
+               if (tree->attributes & HFS_TREE_VARIDXKEYS)
+                       key_len = be16_to_cpu(fd->search_key->key_len) + 2;
+               else {
+                       fd->search_key->key_len = tree->max_key_len;
+                       key_len = tree->max_key_len + 2;
+               }
+               goto again;
+       }
+
+       if (!rec)
+               hfs_brec_update_parent(fd);
+
+       return 0;
+}
+
+int hfs_brec_remove(struct hfs_find_data *fd)
+{
+       struct hfs_btree *tree;
+       struct hfs_bnode *node, *parent;
+       int end_off, rec_off, data_off, size;
+
+       tree = fd->tree;
+       node = fd->bnode;
+again:
+       rec_off = tree->node_size - (fd->record + 2) * 2;
+       end_off = tree->node_size - (node->num_recs + 1) * 2;
+
+       if (node->type == HFS_NODE_LEAF) {
+               tree->leaf_count--;
+               mark_inode_dirty(tree->inode);
+       }
+       hfs_bnode_dump(node);
+       dprint(DBG_BNODE_MOD, "remove_rec: %d, %d\n", fd->record, fd->keylength + fd->entrylength);
+       if (!--node->num_recs) {
+               hfs_bnode_unlink(node);
+               if (!node->parent)
+                       return 0;
+               parent = hfs_bnode_find(tree, node->parent);
+               if (IS_ERR(parent))
+                       return PTR_ERR(parent);
+               hfs_bnode_put(node);
+               node = fd->bnode = parent;
+
+               __hfs_brec_find(node, fd);
+               goto again;
+       }
+       hfs_bnode_write_u16(node, offsetof(struct hfs_bnode_desc, num_recs), node->num_recs);
+
+       if (rec_off == end_off)
+               goto skip;
+       size = fd->keylength + fd->entrylength;
+
+       do {
+               data_off = hfs_bnode_read_u16(node, rec_off);
+               hfs_bnode_write_u16(node, rec_off + 2, data_off - size);
+               rec_off -= 2;
+       } while (rec_off >= end_off);
+
+       /* fill hole */
+       hfs_bnode_move(node, fd->keyoffset, fd->keyoffset + size,
+                      data_off - fd->keyoffset - size);
+skip:
+       hfs_bnode_dump(node);
+       if (!fd->record)
+               hfs_brec_update_parent(fd);
+       return 0;
+}
+
+struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd)
+{
+       struct hfs_btree *tree;
+       struct hfs_bnode *node, *new_node;
+       struct hfs_bnode_desc node_desc;
+       int num_recs, new_rec_off, new_off, old_rec_off;
+       int data_start, data_end, size;
+
+       tree = fd->tree;
+       node = fd->bnode;
+       new_node = hfs_bmap_alloc(tree);
+       if (IS_ERR(new_node))
+               return new_node;
+       hfs_bnode_get(node);
+       dprint(DBG_BNODE_MOD, "split_nodes: %d - %d - %d\n",
+               node->this, new_node->this, node->next);
+       new_node->next = node->next;
+       new_node->prev = node->this;
+       new_node->parent = node->parent;
+       new_node->type = node->type;
+       new_node->height = node->height;
+
+       size = tree->node_size / 2 - node->num_recs * 2 - 14;
+       old_rec_off = tree->node_size - 4;
+       num_recs = 1;
+       for (;;) {
+               data_start = hfs_bnode_read_u16(node, old_rec_off);
+               if (data_start > size)
+                       break;
+               old_rec_off -= 2;
+               if (++num_recs < node->num_recs)
+                       continue;
+               /* panic? */
+               hfs_bnode_put(node);
+               hfs_bnode_put(new_node);
+               return ERR_PTR(-ENOSPC);
+       }
+
+       if (fd->record + 1 < num_recs) {
+               /* new record is in the lower half,
+                * so leave some more space there
+                */
+               old_rec_off += 2;
+               num_recs--;
+               data_start = hfs_bnode_read_u16(node, old_rec_off);
+       } else {
+               hfs_bnode_put(node);
+               hfs_bnode_get(new_node);
+               fd->bnode = new_node;
+               fd->record -= num_recs;
+               fd->keyoffset -= data_start - 14;
+               fd->entryoffset -= data_start - 14;
+       }
+       new_node->num_recs = node->num_recs - num_recs;
+       node->num_recs = num_recs;
+
+       new_rec_off = tree->node_size - 2;
+       new_off = 14;
+       size = data_start - new_off;
+       num_recs = new_node->num_recs;
+       data_end = data_start;
+       while (num_recs) {
+               hfs_bnode_write_u16(new_node, new_rec_off, new_off);
+               old_rec_off -= 2;
+               new_rec_off -= 2;
+               data_end = hfs_bnode_read_u16(node, old_rec_off);
+               new_off = data_end - size;
+               num_recs--;
+       }
+       hfs_bnode_write_u16(new_node, new_rec_off, new_off);
+       hfs_bnode_copy(new_node, 14, node, data_start, data_end - data_start);
+
+       /* update new bnode header */
+       node_desc.next = cpu_to_be32(new_node->next);
+       node_desc.prev = cpu_to_be32(new_node->prev);
+       node_desc.type = new_node->type;
+       node_desc.height = new_node->height;
+       node_desc.num_recs = cpu_to_be16(new_node->num_recs);
+       node_desc.reserved = 0;
+       hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc));
+
+       /* update previous bnode header */
+       node->next = new_node->this;
+       hfs_bnode_read(node, &node_desc, 0, sizeof(node_desc));
+       node_desc.next = cpu_to_be32(node->next);
+       node_desc.num_recs = cpu_to_be16(node->num_recs);
+       hfs_bnode_write(node, &node_desc, 0, sizeof(node_desc));
+
+       /* update next bnode header */
+       if (new_node->next) {
+               struct hfs_bnode *next_node = hfs_bnode_find(tree, new_node->next);
+               next_node->prev = new_node->this;
+               hfs_bnode_read(next_node, &node_desc, 0, sizeof(node_desc));
+               node_desc.prev = cpu_to_be32(next_node->prev);
+               hfs_bnode_write(next_node, &node_desc, 0, sizeof(node_desc));
+               hfs_bnode_put(next_node);
+       } else if (node->this == tree->leaf_tail) {
+               /* if there is no next node, this might be the new tail */
+               tree->leaf_tail = new_node->this;
+               mark_inode_dirty(tree->inode);
+       }
+
+       hfs_bnode_dump(node);
+       hfs_bnode_dump(new_node);
+       hfs_bnode_put(node);
+
+       return new_node;
+}
+
+int hfs_brec_update_parent(struct hfs_find_data *fd)
+{
+       struct hfs_btree *tree;
+       struct hfs_bnode *node, *new_node, *parent;
+       int newkeylen, diff;
+       int rec, rec_off, end_rec_off;
+       int start_off, end_off;
+
+       tree = fd->tree;
+       node = fd->bnode;
+       new_node = NULL;
+       if (!node->parent)
+               return 0;
+
+again:
+       parent = hfs_bnode_find(tree, node->parent);
+       if (IS_ERR(parent))
+               return PTR_ERR(parent);
+       __hfs_brec_find(parent, fd);
+       hfs_bnode_dump(parent);
+       rec = fd->record;
+
+       /* size difference between old and new key */
+       if (tree->attributes & HFS_TREE_VARIDXKEYS)
+               newkeylen = hfs_bnode_read_u16(node, 14) + 2;
+       else
+               fd->keylength = newkeylen = tree->max_key_len + 2;
+       dprint(DBG_BNODE_MOD, "update_rec: %d, %d, %d\n", rec, fd->keylength, newkeylen);
+
+       rec_off = tree->node_size - (rec + 2) * 2;
+       end_rec_off = tree->node_size - (parent->num_recs + 1) * 2;
+       diff = newkeylen - fd->keylength;
+       if (!diff)
+               goto skip;
+       if (diff > 0) {
+               end_off = hfs_bnode_read_u16(parent, end_rec_off);
+               if (end_rec_off - end_off < diff) {
+
+                       printk("splitting index node...\n");
+                       fd->bnode = parent;
+                       new_node = hfs_bnode_split(fd);
+                       if (IS_ERR(new_node))
+                               return PTR_ERR(new_node);
+                       parent = fd->bnode;
+                       rec = fd->record;
+                       rec_off = tree->node_size - (rec + 2) * 2;
+                       end_rec_off = tree->node_size - (parent->num_recs + 1) * 2;
+               }
+       }
+
+       end_off = start_off = hfs_bnode_read_u16(parent, rec_off);
+       hfs_bnode_write_u16(parent, rec_off, start_off + diff);
+       start_off -= 4; /* move previous cnid too */
+
+       while (rec_off > end_rec_off) {
+               rec_off -= 2;
+               end_off = hfs_bnode_read_u16(parent, rec_off);
+               hfs_bnode_write_u16(parent, rec_off, end_off + diff);
+       }
+       hfs_bnode_move(parent, start_off + diff, start_off,
+                      end_off - start_off);
+skip:
+       hfs_bnode_copy(parent, fd->keyoffset, node, 14, newkeylen);
+       hfs_bnode_dump(parent);
+
+       hfs_bnode_put(node);
+       node = parent;
+
+       if (new_node) {
+               u32 cnid;
+
+               fd->bnode = hfs_bnode_find(tree, new_node->parent);
+               /* create index key and entry */
+               hfs_bnode_read_key(new_node, fd->search_key, 14);
+               cnid = cpu_to_be32(new_node->this);
+
+               __hfs_brec_find(fd->bnode, fd);
+               hfs_brec_insert(fd, &cnid, sizeof(cnid));
+               hfs_bnode_put(fd->bnode);
+               hfs_bnode_put(new_node);
+
+               if (!rec) {
+                       if (new_node == node)
+                               goto out;
+                       /* restore search_key */
+                       hfs_bnode_read_key(node, fd->search_key, 14);
+               }
+       }
+
+       if (!rec && node->parent)
+               goto again;
+out:
+       fd->bnode = node;
+       return 0;
+}
+
+int hfs_btree_inc_height(struct hfs_btree *tree)
+{
+       struct hfs_bnode *node, *new_node;
+       struct hfs_bnode_desc node_desc;
+       int key_size, rec;
+       u32 cnid;
+
+       node = NULL;
+       if (tree->root) {
+               node = hfs_bnode_find(tree, tree->root);
+               if (IS_ERR(node))
+                       return PTR_ERR(node);
+       }
+       new_node = hfs_bmap_alloc(tree);
+       if (IS_ERR(new_node)) {
+               hfs_bnode_put(node);
+               return PTR_ERR(new_node);
+       }
+
+       tree->root = new_node->this;
+       if (!tree->depth) {
+               tree->leaf_head = tree->leaf_tail = new_node->this;
+               new_node->type = HFS_NODE_LEAF;
+               new_node->num_recs = 0;
+       } else {
+               new_node->type = HFS_NODE_INDEX;
+               new_node->num_recs = 1;
+       }
+       new_node->parent = 0;
+       new_node->next = 0;
+       new_node->prev = 0;
+       new_node->height = ++tree->depth;
+
+       node_desc.next = cpu_to_be32(new_node->next);
+       node_desc.prev = cpu_to_be32(new_node->prev);
+       node_desc.type = new_node->type;
+       node_desc.height = new_node->height;
+       node_desc.num_recs = cpu_to_be16(new_node->num_recs);
+       node_desc.reserved = 0;
+       hfs_bnode_write(new_node, &node_desc, 0, sizeof(node_desc));
+
+       rec = tree->node_size - 2;
+       hfs_bnode_write_u16(new_node, rec, 14);
+
+       if (node) {
+               /* insert old root idx into new root */
+               node->parent = tree->root;
+               if (node->type == HFS_NODE_LEAF ||
+                   tree->attributes & HFS_TREE_VARIDXKEYS)
+                       key_size = hfs_bnode_read_u16(node, 14) + 2;
+               else
+                       key_size = tree->max_key_len + 2;
+               hfs_bnode_copy(new_node, 14, node, 14, key_size);
+
+               if (!(tree->attributes & HFS_TREE_VARIDXKEYS)) {
+                       key_size = tree->max_key_len + 2;
+                       hfs_bnode_write_u16(new_node, 14, tree->max_key_len);
+               }
+               cnid = cpu_to_be32(node->this);
+               hfs_bnode_write(new_node, &cnid, 14 + key_size, 4);
+
+               rec -= 2;
+               hfs_bnode_write_u16(new_node, rec, 14 + key_size + 4);
+
+               hfs_bnode_put(node);
+       }
+       hfs_bnode_put(new_node);
+       mark_inode_dirty(tree->inode);
+
+       return 0;
+}
diff --git a/fs/hfsplus/btree.c b/fs/hfsplus/btree.c
new file mode 100644 (file)
index 0000000..84870a1
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ *  linux/fs/hfsplus/btree.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handle opening/closing btree
+ */
+
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+
+/* Get a reference to a B*Tree and do some initial checks */
+struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
+{
+       struct hfs_btree *tree;
+       struct hfs_btree_header_rec *head;
+       struct address_space *mapping;
+       struct page *page;
+       unsigned int shift, size;
+
+       tree = kmalloc(sizeof(*tree), GFP_KERNEL);
+       if (!tree)
+               return NULL;
+       memset(tree, 0, sizeof(*tree));
+
+       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("HFS+-fs: unknown B*Tree requested\n");
+               goto free_tree;
+       }
+       tree->inode = iget(sb, id);
+       if (!tree->inode)
+               goto free_tree;
+
+       mapping = tree->inode->i_mapping;
+       page = read_cache_page(mapping, 0, (filler_t *)mapping->a_ops->readpage, NULL);
+       if (IS_ERR(page))
+               goto free_tree;
+
+       /* Load the header */
+       head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc));
+       tree->root = be32_to_cpu(head->root);
+       tree->leaf_count = be32_to_cpu(head->leaf_count);
+       tree->leaf_head = be32_to_cpu(head->leaf_head);
+       tree->leaf_tail = be32_to_cpu(head->leaf_tail);
+       tree->node_count = be32_to_cpu(head->node_count);
+       tree->free_nodes = be32_to_cpu(head->free_nodes);
+       tree->attributes = be32_to_cpu(head->attributes);
+       tree->node_size = be16_to_cpu(head->node_size);
+       tree->max_key_len = be16_to_cpu(head->max_key_len);
+       tree->depth = be16_to_cpu(head->depth);
+
+       size = tree->node_size;
+       if (!size || size & (size - 1))
+               goto fail_page;
+       if (!tree->node_count)
+               goto fail_page;
+       for (shift = 0; size >>= 1; shift += 1)
+               ;
+       tree->node_size_shift = shift;
+
+       tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+
+       kunmap(page);
+       page_cache_release(page);
+       return tree;
+
+ fail_page:
+       tree->inode->i_mapping->a_ops = &hfsplus_aops;
+       page_cache_release(page);
+ free_tree:
+       iput(tree->inode);
+       kfree(tree);
+       return NULL;
+}
+
+/* Release resources used by a btree */
+void hfs_btree_close(struct hfs_btree *tree)
+{
+       struct hfs_bnode *node;
+       int i;
+
+       if (!tree)
+               return;
+
+       for (i = 0; i < NODE_HASH_SIZE; i++) {
+               while ((node = tree->node_hash[i])) {
+                       tree->node_hash[i] = node->next_hash;
+                       if (atomic_read(&node->refcnt))
+                               printk("HFS+: node %d:%d still has %d user(s)!\n",
+                                       node->tree->cnid, node->this, atomic_read(&node->refcnt));
+                       hfs_bnode_free(node);
+                       tree->node_hash_cnt--;
+               }
+       }
+       iput(tree->inode);
+       kfree(tree);
+}
+
+void hfs_btree_write(struct hfs_btree *tree)
+{
+       struct hfs_btree_header_rec *head;
+       struct hfs_bnode *node;
+       struct page *page;
+
+       node = hfs_bnode_find(tree, 0);
+       if (IS_ERR(node))
+               /* panic? */
+               return;
+       /* Load the header */
+       page = node->page[0];
+       head = (struct hfs_btree_header_rec *)(kmap(page) + sizeof(struct hfs_bnode_desc));
+
+       head->root = cpu_to_be32(tree->root);
+       head->leaf_count = cpu_to_be32(tree->leaf_count);
+       head->leaf_head = cpu_to_be32(tree->leaf_head);
+       head->leaf_tail = cpu_to_be32(tree->leaf_tail);
+       head->node_count = cpu_to_be32(tree->node_count);
+       head->free_nodes = cpu_to_be32(tree->free_nodes);
+       head->attributes = cpu_to_be32(tree->attributes);
+       head->depth = cpu_to_be16(tree->depth);
+
+       kunmap(page);
+       set_page_dirty(page);
+       hfs_bnode_put(node);
+}
+
+static struct hfs_bnode *hfs_bmap_new_bmap(struct hfs_bnode *prev, u32 idx)
+{
+       struct hfs_btree *tree = prev->tree;
+       struct hfs_bnode *node;
+       struct hfs_bnode_desc desc;
+       u32 cnid;
+
+       node = hfs_bnode_create(tree, idx);
+       if (IS_ERR(node))
+               return node;
+
+       tree->free_nodes--;
+       prev->next = idx;
+       cnid = cpu_to_be32(idx);
+       hfs_bnode_write(prev, &cnid, offsetof(struct hfs_bnode_desc, next), 4);
+
+       node->type = HFS_NODE_MAP;
+       node->num_recs = 1;
+       hfs_bnode_clear(node, 0, tree->node_size);
+       desc.next = 0;
+       desc.prev = 0;
+       desc.type = HFS_NODE_MAP;
+       desc.height = 0;
+       desc.num_recs = cpu_to_be16(1);
+       desc.reserved = 0;
+       hfs_bnode_write(node, &desc, 0, sizeof(desc));
+       hfs_bnode_write_u16(node, 14, 0x8000);
+       hfs_bnode_write_u16(node, tree->node_size - 2, 14);
+       hfs_bnode_write_u16(node, tree->node_size - 4, tree->node_size - 6);
+
+       return node;
+}
+
+struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *tree)
+{
+       struct hfs_bnode *node, *next_node;
+       struct page **pagep;
+       u32 nidx, idx;
+       u16 off, len;
+       u8 *data, byte, m;
+       int i;
+
+       while (!tree->free_nodes) {
+               struct inode *inode = tree->inode;
+               u32 count;
+               int res;
+
+               res = hfsplus_file_extend(inode);
+               if (res)
+                       return ERR_PTR(res);
+               inode->i_blocks = HFSPLUS_I(inode).alloc_blocks <<
+                                 HFSPLUS_SB(tree->sb).fs_shift;
+               HFSPLUS_I(inode).phys_size = inode->i_size =
+                       (loff_t)inode->i_blocks << tree->sb->s_blocksize_bits;
+               count = inode->i_size >> tree->node_size_shift;
+               tree->free_nodes = count - tree->node_count;
+               tree->node_count = count;
+       }
+
+       nidx = 0;
+       node = hfs_bnode_find(tree, nidx);
+       if (IS_ERR(node))
+               return node;
+       len = hfs_brec_lenoff(node, 2, &off);
+
+       off += node->page_offset;
+       pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+       data = kmap(*pagep);
+       off &= ~PAGE_CACHE_MASK;
+       idx = 0;
+
+       for (;;) {
+               while (len) {
+                       byte = data[off];
+                       if (byte != 0xff) {
+                               for (m = 0x80, i = 0; i < 8; m >>= 1, i++) {
+                                       if (!(byte & m)) {
+                                               idx += i;
+                                               data[off] |= m;
+                                               set_page_dirty(*pagep);
+                                               kunmap(*pagep);
+                                               tree->free_nodes--;
+                                               mark_inode_dirty(tree->inode);
+                                               hfs_bnode_put(node);
+                                               if (!idx) {
+                                                       printk("unexpected idx %u (%u)\n", idx, node->this);
+                                                       BUG();
+                                               }
+                                               return hfs_bnode_create(tree, idx);
+                                       }
+                               }
+                       }
+                       if (++off >= PAGE_CACHE_SIZE) {
+                               kunmap(*pagep);
+                               data = kmap(*++pagep);
+                               off = 0;
+                       }
+                       idx += 8;
+                       len--;
+               }
+               kunmap(*pagep);
+               nidx = node->next;
+               if (!nidx) {
+                       printk("create new bmap node...\n");
+                       next_node = hfs_bmap_new_bmap(node, idx);
+               } else
+                       next_node = hfs_bnode_find(tree, nidx);
+               hfs_bnode_put(node);
+               if (IS_ERR(next_node))
+                       return next_node;
+               node = next_node;
+
+               len = hfs_brec_lenoff(node, 0, &off);
+               off += node->page_offset;
+               pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+               data = kmap(*pagep);
+               off &= ~PAGE_CACHE_MASK;
+       }
+}
+
+void hfs_bmap_free(struct hfs_bnode *node)
+{
+       struct hfs_btree *tree;
+       struct page *page;
+       u16 off, len;
+       u32 nidx;
+       u8 *data, byte, m;
+
+       dprint(DBG_BNODE_MOD, "btree_free_node: %u\n", node->this);
+       if (!node->this)
+               BUG();
+       tree = node->tree;
+       nidx = node->this;
+       node = hfs_bnode_find(tree, 0);
+       if (IS_ERR(node))
+               return;
+       len = hfs_brec_lenoff(node, 2, &off);
+       while (nidx >= len * 8) {
+               u32 i;
+
+               nidx -= len * 8;
+               i = node->next;
+               hfs_bnode_put(node);
+               if (!i) {
+                       /* panic */;
+                       printk("HFS: unable to free bnode %u. bmap not found!\n", node->this);
+                       return;
+               }
+               node = hfs_bnode_find(tree, i);
+               if (IS_ERR(node))
+                       return;
+               if (node->type != HFS_NODE_MAP) {
+                       /* panic */;
+                       printk("HFS: invalid bmap found! (%u,%d)\n", node->this, node->type);
+                       hfs_bnode_put(node);
+                       return;
+               }
+               len = hfs_brec_lenoff(node, 0, &off);
+       }
+       off += node->page_offset + nidx / 8;
+       page = node->page[off >> PAGE_CACHE_SHIFT];
+       data = kmap(page);
+       off &= ~PAGE_CACHE_MASK;
+       m = 1 << (~nidx & 7);
+       byte = data[off];
+       if (!(byte & m)) {
+               printk("HFS: trying to free free bnode %u(%d)\n", node->this, node->type);
+               kunmap(page);
+               hfs_bnode_put(node);
+               return;
+       }
+       data[off] = byte & ~m;
+       set_page_dirty(page);
+       kunmap(page);
+       hfs_bnode_put(node);
+       tree->free_nodes++;
+       mark_inode_dirty(tree->inode);
+}
diff --git a/fs/hfsplus/catalog.c b/fs/hfsplus/catalog.c
new file mode 100644 (file)
index 0000000..03a01a3
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ *  linux/fs/hfsplus/catalog.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of catalog records
+ */
+
+#include <linux/sched.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+int hfsplus_cat_cmp_key(hfsplus_btree_key *k1, hfsplus_btree_key *k2)
+{
+       u32 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_unistrcmp(&k1->cat.name, &k2->cat.name);
+}
+
+void hfsplus_cat_build_key(hfsplus_btree_key *key, u32 parent,
+                         struct qstr *str)
+{
+       int len;
+
+       key->cat.parent = cpu_to_be32(parent);
+       if (str) {
+               hfsplus_asc2uni(&key->cat.name, str->name, str->len);
+               len = be16_to_cpu(key->cat.name.length);
+       } else
+               len = key->cat.name.length = 0;
+       key->key_len = cpu_to_be16(6 + 2 * len);
+}
+
+static void hfsplus_cat_build_key_uni(hfsplus_btree_key *key, u32 parent,
+                                     struct hfsplus_unistr *name)
+{
+       int ustrlen;
+
+       ustrlen = be16_to_cpu(name->length);
+       key->cat.parent = cpu_to_be32(parent);
+       key->cat.name.length = cpu_to_be16(ustrlen);
+       ustrlen *= 2;
+       memcpy(key->cat.name.unicode, name->unicode, ustrlen);
+       key->key_len = cpu_to_be16(6 + ustrlen);
+}
+
+static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms)
+{
+       if (inode->i_flags & S_IMMUTABLE)
+               perms->rootflags |= HFSPLUS_FLG_IMMUTABLE;
+       else
+               perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
+       if (inode->i_flags & S_APPEND)
+               perms->rootflags |= HFSPLUS_FLG_APPEND;
+       else
+               perms->rootflags &= ~HFSPLUS_FLG_APPEND;
+       HFSPLUS_I(inode).rootflags = perms->rootflags;
+       HFSPLUS_I(inode).userflags = perms->userflags;
+       perms->mode = cpu_to_be16(inode->i_mode);
+       perms->owner = cpu_to_be32(inode->i_uid);
+       perms->group = cpu_to_be32(inode->i_gid);
+}
+
+static int hfsplus_cat_build_record(hfsplus_cat_entry *entry, u32 cnid, struct inode *inode)
+{
+       if (S_ISDIR(inode->i_mode)) {
+               struct hfsplus_cat_folder *folder;
+
+               folder = &entry->folder;
+               memset(folder, 0, sizeof(*folder));
+               folder->type = cpu_to_be16(HFSPLUS_FOLDER);
+               folder->id = cpu_to_be32(inode->i_ino);
+               folder->create_date = folder->content_mod_date =
+                       folder->attribute_mod_date = folder->access_date = hfsp_now2mt();
+               hfsplus_set_perms(inode, &folder->permissions);
+               if (inode == HFSPLUS_SB(inode->i_sb).hidden_dir)
+                       /* invisible and namelocked */
+                       folder->user_info.frFlags = cpu_to_be16(0x5000);
+               return sizeof(*folder);
+       } else {
+               struct hfsplus_cat_file *file;
+
+               file = &entry->file;
+               memset(file, 0, sizeof(*file));
+               file->type = cpu_to_be16(HFSPLUS_FILE);
+               file->flags = cpu_to_be16(HFSPLUS_FILE_THREAD_EXISTS);
+               file->id = cpu_to_be32(cnid);
+               file->create_date = file->content_mod_date =
+                       file->attribute_mod_date = file->access_date = hfsp_now2mt();
+               if (cnid == inode->i_ino) {
+                       hfsplus_set_perms(inode, &file->permissions);
+                       file->user_info.fdType = cpu_to_be32(HFSPLUS_SB(inode->i_sb).type);
+                       file->user_info.fdCreator = cpu_to_be32(HFSPLUS_SB(inode->i_sb).creator);
+                       if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE)
+                               file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED);
+               } else {
+                       file->user_info.fdType = cpu_to_be32(HFSP_HARDLINK_TYPE);
+                       file->user_info.fdCreator = cpu_to_be32(HFSP_HFSPLUS_CREATOR);
+                       file->user_info.fdFlags = cpu_to_be16(0x100);
+                       file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode).dev);
+               }
+               return sizeof(*file);
+       }
+}
+
+static int hfsplus_fill_cat_thread(hfsplus_cat_entry *entry, int type,
+                                  u32 parentid, struct qstr *str)
+{
+       entry->type = cpu_to_be16(type);
+       entry->thread.reserved = 0;
+       entry->thread.parentID = cpu_to_be32(parentid);
+       hfsplus_asc2uni(&entry->thread.nodeName, str->name, str->len);
+       return 10 + be16_to_cpu(entry->thread.nodeName.length) * 2;
+}
+
+/* Try to get a catalog entry for given catalog id */
+int hfsplus_find_cat(struct super_block *sb, u32 cnid,
+                    struct hfs_find_data *fd)
+{
+       hfsplus_cat_entry tmp;
+       int err;
+       u16 type;
+
+       hfsplus_cat_build_key(fd->search_key, cnid, NULL);
+       err = hfs_brec_read(fd, &tmp, sizeof(hfsplus_cat_entry));
+       if (err)
+               return err;
+
+       type = be16_to_cpu(tmp.type);
+       if (type != HFSPLUS_FOLDER_THREAD && type != HFSPLUS_FILE_THREAD) {
+               printk("HFS+-fs: Found bad thread record in catalog\n");
+               return -EIO;
+       }
+
+       hfsplus_cat_build_key_uni(fd->search_key, be32_to_cpu(tmp.thread.parentID),
+                                &tmp.thread.nodeName);
+       return hfs_brec_find(fd);
+}
+
+int hfsplus_create_cat(u32 cnid, struct inode *dir, struct qstr *str, struct inode *inode)
+{
+       struct hfs_find_data fd;
+       struct super_block *sb;
+       hfsplus_cat_entry entry;
+       int entry_size;
+       int err;
+
+       dprint(DBG_CAT_MOD, "create_cat: %s,%u(%d)\n", str->name, cnid, inode->i_nlink);
+       sb = dir->i_sb;
+       hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+
+       hfsplus_cat_build_key(fd.search_key, cnid, NULL);
+       entry_size = hfsplus_fill_cat_thread(&entry, S_ISDIR(inode->i_mode) ?
+                       HFSPLUS_FOLDER_THREAD : HFSPLUS_FILE_THREAD,
+                       dir->i_ino, str);
+       err = hfs_brec_find(&fd);
+       if (err != -ENOENT) {
+               if (!err)
+                       err = -EEXIST;
+               goto out;
+       }
+       err = hfs_brec_insert(&fd, &entry, entry_size);
+       if (err)
+               goto out;
+
+       hfsplus_cat_build_key(fd.search_key, dir->i_ino, str);
+       entry_size = hfsplus_cat_build_record(&entry, cnid, inode);
+       err = hfs_brec_find(&fd);
+       if (err != -ENOENT) {
+               /* panic? */
+               if (!err)
+                       err = -EEXIST;
+               goto out;
+       }
+       err = hfs_brec_insert(&fd, &entry, entry_size);
+       if (!err) {
+               dir->i_size++;
+               mark_inode_dirty(dir);
+       }
+out:
+       hfs_find_exit(&fd);
+
+       return err;
+}
+
+int hfsplus_delete_cat(u32 cnid, struct inode *dir, struct qstr *str)
+{
+       struct super_block *sb;
+       struct hfs_find_data fd;
+       struct hfsplus_fork_raw fork;
+       struct list_head *pos;
+       int err, off;
+       u16 type;
+
+       dprint(DBG_CAT_MOD, "delete_cat: %s,%u\n", str ? str->name : NULL, cnid);
+       sb = dir->i_sb;
+       hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+
+       if (!str) {
+               int len;
+
+               hfsplus_cat_build_key(fd.search_key, cnid, NULL);
+               err = hfs_brec_find(&fd);
+               if (err)
+                       goto out;
+
+               off = fd.entryoffset + offsetof(struct hfsplus_cat_thread, nodeName);
+               fd.search_key->cat.parent = cpu_to_be32(dir->i_ino);
+               hfs_bnode_read(fd.bnode, &fd.search_key->cat.name.length, off, 2);
+               len = be16_to_cpu(fd.search_key->cat.name.length) * 2;
+               hfs_bnode_read(fd.bnode, &fd.search_key->cat.name.unicode, off + 2, len);
+               fd.search_key->key_len = cpu_to_be16(6 + len);
+       } else
+               hfsplus_cat_build_key(fd.search_key, dir->i_ino, str);
+
+       err = hfs_brec_find(&fd);
+       if (err)
+               goto out;
+
+       type = hfs_bnode_read_u16(fd.bnode, fd.entryoffset);
+       if (type == HFSPLUS_FILE) {
+#if 0
+               off = fd.entryoffset + offsetof(hfsplus_cat_file, data_fork);
+               hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
+               hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_DATA);
+#endif
+
+               off = fd.entryoffset + offsetof(struct hfsplus_cat_file, rsrc_fork);
+               hfs_bnode_read(fd.bnode, &fork, off, sizeof(fork));
+               hfsplus_free_fork(sb, cnid, &fork, HFSPLUS_TYPE_RSRC);
+       }
+
+       list_for_each(pos, &HFSPLUS_I(dir).open_dir_list) {
+               struct hfsplus_readdir_data *rd =
+                       list_entry(pos, struct hfsplus_readdir_data, list);
+               if (fd.tree->keycmp(fd.search_key, (void *)&rd->key) < 0)
+                       rd->file->f_pos--;
+       }
+
+       err = hfs_brec_remove(&fd);
+       if (err)
+               goto out;
+
+       hfsplus_cat_build_key(fd.search_key, cnid, NULL);
+       err = hfs_brec_find(&fd);
+       if (err)
+               goto out;
+
+       err = hfs_brec_remove(&fd);
+       if (err)
+               goto out;
+
+       dir->i_size--;
+       mark_inode_dirty(dir);
+out:
+       hfs_find_exit(&fd);
+
+       return err;
+}
+
+int hfsplus_rename_cat(u32 cnid,
+                      struct inode *src_dir, struct qstr *src_name,
+                      struct inode *dst_dir, struct qstr *dst_name)
+{
+       struct super_block *sb;
+       struct hfs_find_data src_fd, dst_fd;
+       hfsplus_cat_entry entry;
+       int entry_size, type;
+       int err = 0;
+
+       dprint(DBG_CAT_MOD, "rename_cat: %u - %lu,%s - %lu,%s\n", cnid, src_dir->i_ino, src_name->name,
+               dst_dir->i_ino, dst_name->name);
+       sb = src_dir->i_sb;
+       hfs_find_init(HFSPLUS_SB(sb).cat_tree, &src_fd);
+       dst_fd = src_fd;
+
+       /* find the old dir entry and read the data */
+       hfsplus_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name);
+       err = hfs_brec_find(&src_fd);
+       if (err)
+               goto out;
+
+       hfs_bnode_read(src_fd.bnode, &entry, src_fd.entryoffset,
+                               src_fd.entrylength);
+
+       /* create new dir entry with the data from the old entry */
+       hfsplus_cat_build_key(dst_fd.search_key, dst_dir->i_ino, dst_name);
+       err = hfs_brec_find(&dst_fd);
+       if (err != -ENOENT) {
+               if (!err)
+                       err = -EEXIST;
+               goto out;
+       }
+
+       err = hfs_brec_insert(&dst_fd, &entry, src_fd.entrylength);
+       if (err)
+               goto out;
+       dst_dir->i_size++;
+       mark_inode_dirty(dst_dir);
+
+       /* finally remove the old entry */
+       hfsplus_cat_build_key(src_fd.search_key, src_dir->i_ino, src_name);
+       err = hfs_brec_find(&src_fd);
+       if (err)
+               goto out;
+       err = hfs_brec_remove(&src_fd);
+       if (err)
+               goto out;
+       src_dir->i_size--;
+       mark_inode_dirty(src_dir);
+
+       /* remove old thread entry */
+       hfsplus_cat_build_key(src_fd.search_key, cnid, NULL);
+       err = hfs_brec_find(&src_fd);
+       if (err)
+               goto out;
+       type = hfs_bnode_read_u16(src_fd.bnode, src_fd.entryoffset);
+       err = hfs_brec_remove(&src_fd);
+       if (err)
+               goto out;
+
+       /* create new thread entry */
+       hfsplus_cat_build_key(dst_fd.search_key, cnid, NULL);
+       entry_size = hfsplus_fill_cat_thread(&entry, type, dst_dir->i_ino, dst_name);
+       err = hfs_brec_find(&dst_fd);
+       if (err != -ENOENT) {
+               if (!err)
+                       err = -EEXIST;
+               goto out;
+       }
+       err = hfs_brec_insert(&dst_fd, &entry, entry_size);
+out:
+       hfs_bnode_put(dst_fd.bnode);
+       hfs_find_exit(&src_fd);
+       return err;
+}
diff --git a/fs/hfsplus/dir.c b/fs/hfsplus/dir.c
new file mode 100644 (file)
index 0000000..bcdd952
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ *  linux/fs/hfsplus/dir.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of directories
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/version.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Find the entry inside dir named dentry->d_name */
+static struct dentry *hfsplus_lookup(struct inode *dir, struct dentry *dentry,
+                                    struct nameidata *nd)
+{
+       struct inode *inode = NULL;
+       struct hfs_find_data fd;
+       struct super_block *sb;
+       hfsplus_cat_entry entry;
+       int err;
+       u32 cnid, linkid = 0;
+       u16 type;
+
+       sb = dir->i_sb;
+       dentry->d_fsdata = NULL;
+       hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+       hfsplus_cat_build_key(fd.search_key, dir->i_ino, &dentry->d_name);
+again:
+       err = hfs_brec_read(&fd, &entry, sizeof(entry));
+       if (err) {
+               if (err == -ENOENT) {
+                       hfs_find_exit(&fd);
+                       /* No such entry */
+                       inode = NULL;
+                       goto out;
+               }
+               goto fail;
+       }
+       type = be16_to_cpu(entry.type);
+       if (type == HFSPLUS_FOLDER) {
+               if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
+                       err = -EIO;
+                       goto fail;
+               }
+               cnid = be32_to_cpu(entry.folder.id);
+       } else if (type == HFSPLUS_FILE) {
+               if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
+                       err = -EIO;
+                       goto fail;
+               }
+               cnid = be32_to_cpu(entry.file.id);
+               if (entry.file.user_info.fdType == cpu_to_be32(HFSP_HARDLINK_TYPE) &&
+                   entry.file.user_info.fdCreator == cpu_to_be32(HFSP_HFSPLUS_CREATOR)) {
+                       struct qstr str;
+                       char name[32];
+
+                       if (dentry->d_fsdata) {
+                               err = -ENOENT;
+                               inode = NULL;
+                               goto out;
+                       }
+                       dentry->d_fsdata = (void *)(unsigned long)cnid;
+                       linkid = be32_to_cpu(entry.file.permissions.dev);
+                       str.len = sprintf(name, "iNode%d", linkid);
+                       str.name = name;
+                       hfsplus_cat_build_key(fd.search_key, HFSPLUS_SB(sb).hidden_dir->i_ino, &str);
+                       goto again;
+               } else if (!dentry->d_fsdata)
+                       dentry->d_fsdata = (void *)(unsigned long)cnid;
+       } else {
+               printk("HFS+-fs: Illegal catalog entry type in lookup\n");
+               err = -EIO;
+               goto fail;
+       }
+       hfs_find_exit(&fd);
+       inode = iget(dir->i_sb, cnid);
+       if (!inode)
+               return ERR_PTR(-EACCES);
+       if (S_ISREG(inode->i_mode))
+               HFSPLUS_I(inode).dev = linkid;
+out:
+       d_add(dentry, inode);
+       return NULL;
+fail:
+       hfs_find_exit(&fd);
+       return ERR_PTR(err);
+}
+
+static int hfsplus_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+       struct inode *inode = filp->f_dentry->d_inode;
+       struct super_block *sb = inode->i_sb;
+       int len, err;
+       char strbuf[HFSPLUS_MAX_STRLEN + 1];
+       hfsplus_cat_entry entry;
+       struct hfs_find_data fd;
+       struct hfsplus_readdir_data *rd;
+       u16 type;
+
+       if (filp->f_pos >= inode->i_size)
+               return 0;
+
+       hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+       hfsplus_cat_build_key(fd.search_key, inode->i_ino, NULL);
+       err = hfs_brec_find(&fd);
+       if (err)
+               goto out;
+
+       switch ((u32)filp->f_pos) {
+       case 0:
+               /* This is completely artificial... */
+               if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR))
+                       goto out;
+               filp->f_pos++;
+               /* fall through */
+       case 1:
+               hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
+               if (be16_to_cpu(entry.type) != HFSPLUS_FOLDER_THREAD) {
+                       printk("HFS+-fs: bad catalog folder thread\n");
+                       err = -EIO;
+                       goto out;
+               }
+               if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
+                       printk("HFS+-fs: truncated catalog thread\n");
+                       err = -EIO;
+                       goto out;
+               }
+               if (filldir(dirent, "..", 2, 1,
+                           be32_to_cpu(entry.thread.parentID), DT_DIR))
+                       goto out;
+               filp->f_pos++;
+               /* fall through */
+       default:
+               if (filp->f_pos >= inode->i_size)
+                       goto out;
+               err = hfs_brec_goto(&fd, filp->f_pos - 1);
+               if (err)
+                       goto out;
+       }
+
+       for (;;) {
+               if (be32_to_cpu(fd.key->cat.parent) != inode->i_ino) {
+                       printk("HFS+-fs: walked past end of dir\n");
+                       err = -EIO;
+                       goto out;
+               }
+               hfs_bnode_read(fd.bnode, &entry, fd.entryoffset, fd.entrylength);
+               type = be16_to_cpu(entry.type);
+               len = HFSPLUS_MAX_STRLEN;
+               err = hfsplus_uni2asc(&fd.key->cat.name, strbuf, &len);
+               if (err)
+                       goto out;
+               if (type == HFSPLUS_FOLDER) {
+                       if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
+                               printk("HFS+-fs: small dir entry\n");
+                               err = -EIO;
+                               goto out;
+                       }
+                       if (HFSPLUS_SB(sb).hidden_dir &&
+                           HFSPLUS_SB(sb).hidden_dir->i_ino == be32_to_cpu(entry.folder.id))
+                               goto next;
+                       if (filldir(dirent, strbuf, len, filp->f_pos,
+                                   be32_to_cpu(entry.folder.id), DT_DIR))
+                               break;
+               } else if (type == HFSPLUS_FILE) {
+                       if (fd.entrylength < sizeof(struct hfsplus_cat_file)) {
+                               printk("HFS+-fs: small file entry\n");
+                               err = -EIO;
+                               goto out;
+                       }
+                       if (filldir(dirent, strbuf, len, filp->f_pos,
+                                   be32_to_cpu(entry.file.id), DT_REG))
+                               break;
+               } else {
+                       printk("HFS+-fs: bad catalog entry type\n");
+                       err = -EIO;
+                       goto out;
+               }
+       next:
+               filp->f_pos++;
+               if (filp->f_pos >= inode->i_size)
+                       goto out;
+               err = hfs_brec_goto(&fd, 1);
+               if (err)
+                       goto out;
+       }
+       rd = filp->private_data;
+       if (!rd) {
+               rd = kmalloc(sizeof(struct hfsplus_readdir_data), GFP_KERNEL);
+               if (!rd) {
+                       err = -ENOMEM;
+                       goto out;
+               }
+               filp->private_data = rd;
+               rd->file = filp;
+               list_add(&rd->list, &HFSPLUS_I(inode).open_dir_list);
+       }
+       memcpy(&rd->key, fd.key, sizeof(struct hfsplus_cat_key));
+out:
+       hfs_find_exit(&fd);
+       return err;
+}
+
+static int hfsplus_dir_release(struct inode *inode, struct file *file)
+{
+       struct hfsplus_readdir_data *rd = file->private_data;
+       if (rd) {
+               list_del(&rd->list);
+               kfree(rd);
+       }
+       return 0;
+}
+
+int hfsplus_create(struct inode *dir, struct dentry *dentry, int mode,
+                  struct nameidata *nd)
+{
+       struct inode *inode;
+       int res;
+
+       inode = hfsplus_new_inode(dir->i_sb, mode);
+       if (!inode)
+               return -ENOSPC;
+
+       res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
+       if (res) {
+               inode->i_nlink = 0;
+               iput(inode);
+               return res;
+       }
+       dentry->d_fsdata = (void *)inode->i_ino;
+       d_instantiate(dentry, inode);
+       mark_inode_dirty(inode);
+       return 0;
+}
+
+int hfsplus_link(struct dentry *src_dentry, struct inode *dst_dir, struct dentry *dst_dentry)
+{
+       struct super_block *sb = dst_dir->i_sb;
+       struct inode *inode = src_dentry->d_inode;
+       struct inode *src_dir = src_dentry->d_parent->d_inode;
+       struct qstr str;
+       char name[32];
+       u32 cnid, id;
+       int res;
+
+       if (HFSPLUS_IS_RSRC(inode))
+               return -EPERM;
+
+       if (inode->i_ino == (u32)(unsigned long)src_dentry->d_fsdata) {
+               for (;;) {
+                       get_random_bytes(&id, sizeof(cnid));
+                       id &= 0x3fffffff;
+                       str.name = name;
+                       str.len = sprintf(name, "iNode%d", id);
+                       res = hfsplus_rename_cat(inode->i_ino,
+                                                src_dir, &src_dentry->d_name,
+                                                HFSPLUS_SB(sb).hidden_dir, &str);
+                       if (!res)
+                               break;
+                       if (res != -EEXIST)
+                               return res;
+               }
+               HFSPLUS_I(inode).dev = id;
+               cnid = HFSPLUS_SB(sb).next_cnid++;
+               src_dentry->d_fsdata = (void *)(unsigned long)cnid;
+               res = hfsplus_create_cat(cnid, src_dir, &src_dentry->d_name, inode);
+               if (res)
+                       /* panic? */
+                       return res;
+               HFSPLUS_SB(sb).file_count++;
+       }
+       cnid = HFSPLUS_SB(sb).next_cnid++;
+       res = hfsplus_create_cat(cnid, dst_dir, &dst_dentry->d_name, inode);
+       if (res)
+               return res;
+
+       inode->i_nlink++;
+       dst_dentry->d_fsdata = (void *)(unsigned long)cnid;
+       d_instantiate(dst_dentry, inode);
+       atomic_inc(&inode->i_count);
+       inode->i_ctime = CURRENT_TIME;
+       mark_inode_dirty(inode);
+       HFSPLUS_SB(sb).file_count++;
+       sb->s_dirt = 1;
+
+       return 0;
+}
+
+int hfsplus_unlink(struct inode *dir, struct dentry *dentry)
+{
+       struct super_block *sb = dir->i_sb;
+       struct inode *inode = dentry->d_inode;
+       struct qstr str;
+       char name[32];
+       u32 cnid;
+       int res;
+
+       if (HFSPLUS_IS_RSRC(inode))
+               return -EPERM;
+
+       cnid = (u32)(unsigned long)dentry->d_fsdata;
+       if (inode->i_ino == cnid &&
+           atomic_read(&HFSPLUS_I(inode).opencnt)) {
+               str.name = name;
+               str.len = sprintf(name, "temp%lu", inode->i_ino);
+               res = hfsplus_rename_cat(inode->i_ino,
+                                        dir, &dentry->d_name,
+                                        HFSPLUS_SB(sb).hidden_dir, &str);
+               if (!res)
+                       inode->i_flags |= S_DEAD;
+               return res;
+       }
+       res = hfsplus_delete_cat(cnid, dir, &dentry->d_name);
+       if (res)
+               return res;
+
+       inode->i_nlink--;
+       hfsplus_delete_inode(inode);
+       if (inode->i_ino != cnid && !inode->i_nlink) {
+               if (!atomic_read(&HFSPLUS_I(inode).opencnt)) {
+                       res = hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL);
+                       if (!res)
+                               hfsplus_delete_inode(inode);
+               } else
+                       inode->i_flags |= S_DEAD;
+       }
+       inode->i_ctime = CURRENT_TIME;
+       mark_inode_dirty(inode);
+
+       return res;
+}
+
+int hfsplus_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       struct inode *inode;
+       int res;
+
+       inode = hfsplus_new_inode(dir->i_sb, S_IFDIR | mode);
+       if (!inode)
+               return -ENOSPC;
+
+       res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
+       if (res) {
+               inode->i_nlink = 0;
+               iput(inode);
+               return res;
+       }
+       d_instantiate(dentry, inode);
+       mark_inode_dirty(inode);
+       return 0;
+}
+
+int hfsplus_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       struct inode *inode;
+       int res;
+
+       inode = dentry->d_inode;
+       if (inode->i_size != 2)
+               return -ENOTEMPTY;
+       res = hfsplus_delete_cat(inode->i_ino, dir, &dentry->d_name);
+       if (res)
+               return res;
+       inode->i_nlink = 0;
+       inode->i_ctime = CURRENT_TIME;
+       hfsplus_delete_inode(inode);
+       mark_inode_dirty(inode);
+       return 0;
+}
+
+int hfsplus_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+       struct super_block *sb;
+       struct inode *inode;
+       int res;
+
+       sb = dir->i_sb;
+       inode = hfsplus_new_inode(sb, S_IFLNK | S_IRWXUGO);
+       if (!inode)
+               return -ENOSPC;
+
+       res = page_symlink(inode, symname, strlen(symname) + 1);
+       if (res) {
+               inode->i_nlink = 0;
+               iput (inode);
+               return res;
+       }
+
+       mark_inode_dirty(inode);
+       res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
+
+       if (!res) {
+               d_instantiate(dentry, inode);
+               mark_inode_dirty(inode);
+       }
+
+       return res;
+}
+
+int hfsplus_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+{
+       struct super_block *sb;
+       struct inode *inode;
+       int res;
+
+       sb = dir->i_sb;
+       inode = hfsplus_new_inode(sb, mode);
+       if (!inode)
+               return -ENOSPC;
+
+       res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
+       if (res) {
+               inode->i_nlink = 0;
+               iput(inode);
+               return res;
+       }
+       init_special_inode(inode, mode, rdev);
+       d_instantiate(dentry, inode);
+       mark_inode_dirty(inode);
+
+       return 0;
+}
+
+int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
+                  struct inode *new_dir, struct dentry *new_dentry)
+{
+       int res;
+
+       /* Unlink destination if it already exists */
+       if (new_dentry->d_inode) {
+               res = hfsplus_unlink(new_dir, new_dentry);
+               if (res)
+                       return res;
+       }
+
+       res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata,
+                                old_dir, &old_dentry->d_name,
+                                new_dir, &new_dentry->d_name);
+       if (!res)
+               new_dentry->d_fsdata = old_dentry->d_fsdata;
+       return res;
+}
+
+struct inode_operations hfsplus_dir_inode_operations = {
+       .lookup         = hfsplus_lookup,
+       .create         = hfsplus_create,
+       .link           = hfsplus_link,
+       .unlink         = hfsplus_unlink,
+       .mkdir          = hfsplus_mkdir,
+       .rmdir          = hfsplus_rmdir,
+       .symlink        = hfsplus_symlink,
+       .mknod          = hfsplus_mknod,
+       .rename         = hfsplus_rename,
+};
+
+struct file_operations hfsplus_dir_operations = {
+       .read           = generic_read_dir,
+       .readdir        = hfsplus_readdir,
+       .ioctl          = hfsplus_ioctl,
+       .llseek         = generic_file_llseek,
+       .release        = hfsplus_dir_release,
+};
diff --git a/fs/hfsplus/extents.c b/fs/hfsplus/extents.c
new file mode 100644 (file)
index 0000000..3cf2721
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ *  linux/fs/hfsplus/extents.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of Extents both in catalog and extents overflow trees
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+
+#include "hfsplus_fs.h"
+#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)
+{
+       u32 k1id, k2id;
+       u32 k1s, k2s;
+
+       k1id = k1->ext.cnid;
+       k2id = k2->ext.cnid;
+       if (k1id != k2id)
+               return be32_to_cpu(k1id) < be32_to_cpu(k2id) ? -1 : 1;
+
+       if (k1->ext.fork_type != k2->ext.fork_type)
+               return k1->ext.fork_type < k2->ext.fork_type ? -1 : 1;
+
+       k1s = k1->ext.start_block;
+       k2s = k2->ext.start_block;
+       if (k1s == k2s)
+               return 0;
+       return be32_to_cpu(k1s) < be32_to_cpu(k2s) ? -1 : 1;
+}
+
+void hfsplus_ext_build_key(hfsplus_btree_key *key, u32 cnid,
+                         u32 block, u8 type)
+{
+       key->key_len = cpu_to_be16(HFSPLUS_EXT_KEYLEN - 2);
+       key->ext.cnid = cpu_to_be32(cnid);
+       key->ext.start_block = cpu_to_be32(block);
+       key->ext.fork_type = type;
+       key->ext.pad = 0;
+}
+
+static u32 hfsplus_ext_find_block(struct hfsplus_extent *ext, u32 off)
+{
+       int i;
+       u32 count;
+
+       for (i = 0; i < 8; ext++, i++) {
+               count = be32_to_cpu(ext->block_count);
+               if (off < count)
+                       return be32_to_cpu(ext->start_block) + off;
+               off -= count;
+       }
+       /* panic? */
+       return 0;
+}
+
+static int hfsplus_ext_block_count(struct hfsplus_extent *ext)
+{
+       int i;
+       u32 count = 0;
+
+       for (i = 0; i < 8; ext++, i++)
+               count += be32_to_cpu(ext->block_count);
+       return count;
+}
+
+static u32 hfsplus_ext_lastblock(struct hfsplus_extent *ext)
+{
+       int i;
+
+       ext += 7;
+       for (i = 0; i < 7; ext--, i++)
+               if (ext->block_count)
+                       break;
+       return be32_to_cpu(ext->start_block) + be32_to_cpu(ext->block_count);
+}
+
+static void __hfsplus_ext_write_extent(struct inode *inode, struct hfs_find_data *fd)
+{
+       int res;
+
+       hfsplus_ext_build_key(fd->search_key, inode->i_ino, HFSPLUS_I(inode).cached_start,
+                             HFSPLUS_IS_RSRC(inode) ?  HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
+       res = hfs_brec_find(fd);
+       if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_NEW) {
+               if (res != -ENOENT)
+                       return;
+               hfs_brec_insert(fd, HFSPLUS_I(inode).cached_extents, sizeof(hfsplus_extent_rec));
+               HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW);
+       } else {
+               if (res)
+                       return;
+               hfs_bnode_write(fd->bnode, HFSPLUS_I(inode).cached_extents, fd->entryoffset, fd->entrylength);
+               HFSPLUS_I(inode).flags &= ~HFSPLUS_FLG_EXT_DIRTY;
+       }
+}
+
+void hfsplus_ext_write_extent(struct inode *inode)
+{
+       if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY) {
+               struct hfs_find_data fd;
+
+               hfs_find_init(HFSPLUS_SB(inode->i_sb).ext_tree, &fd);
+               __hfsplus_ext_write_extent(inode, &fd);
+               hfs_find_exit(&fd);
+       }
+}
+
+static inline int __hfsplus_ext_read_extent(struct hfs_find_data *fd,
+                                           struct hfsplus_extent *extent,
+                                           u32 cnid, u32 block, u8 type)
+{
+       int res;
+
+       hfsplus_ext_build_key(fd->search_key, cnid, block, type);
+       fd->key->ext.cnid = 0;
+       res = hfs_brec_find(fd);
+       if (res && res != -ENOENT)
+               return res;
+       if (fd->key->ext.cnid != fd->search_key->ext.cnid ||
+           fd->key->ext.fork_type != fd->search_key->ext.fork_type)
+               return -ENOENT;
+       if (fd->entrylength != sizeof(hfsplus_extent_rec))
+               return -EIO;
+       hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfsplus_extent_rec));
+       return 0;
+}
+
+static inline int __hfsplus_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block)
+{
+       int res;
+
+       if (HFSPLUS_I(inode).flags & HFSPLUS_FLG_EXT_DIRTY)
+               __hfsplus_ext_write_extent(inode, fd);
+
+       res = __hfsplus_ext_read_extent(fd, HFSPLUS_I(inode).cached_extents, inode->i_ino,
+                                       block, HFSPLUS_IS_RSRC(inode) ? HFSPLUS_TYPE_RSRC : HFSPLUS_TYPE_DATA);
+       if (!res) {
+               HFSPLUS_I(inode).cached_start = be32_to_cpu(fd->key->ext.start_block);
+               HFSPLUS_I(inode).cached_blocks = hfsplus_ext_block_count(HFSPLUS_I(inode).cached_extents);
+       } else {
+               HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0;
+               HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW);
+       }
+       return res;
+}
+
+static int hfsplus_ext_read_extent(struct inode *inode, u32 block)
+{
+       struct hfs_find_data fd;
+       int res;
+
+       if (block >= HFSPLUS_I(inode).cached_start &&
+           block < HFSPLUS_I(inode).cached_start + HFSPLUS_I(inode).cached_blocks)
+               return 0;
+
+       hfs_find_init(HFSPLUS_SB(inode->i_sb).ext_tree, &fd);
+       res = __hfsplus_ext_cache_extent(&fd, inode, block);
+       hfs_find_exit(&fd);
+       return res;
+}
+
+/* Get a block at iblock for inode, possibly allocating if create */
+int hfsplus_get_block(struct inode *inode, sector_t iblock,
+                     struct buffer_head *bh_result, int create)
+{
+       struct super_block *sb;
+       int res = -EIO;
+       u32 ablock, dblock, mask;
+       int shift;
+
+       sb = inode->i_sb;
+
+       /* Convert inode block to disk allocation block */
+       shift = HFSPLUS_SB(sb).alloc_blksz_shift - sb->s_blocksize_bits;
+       ablock = iblock >> HFSPLUS_SB(sb).fs_shift;
+
+       if (iblock >= inode->i_blocks) {
+               if (iblock > inode->i_blocks || !create)
+                       return -EIO;
+               if (ablock >= HFSPLUS_I(inode).alloc_blocks) {
+                       res = hfsplus_file_extend(inode);
+                       if (res)
+                               return res;
+               }
+       } else
+               create = 0;
+
+       if (ablock < HFSPLUS_I(inode).first_blocks) {
+               dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).first_extents, ablock);
+               goto done;
+       }
+
+       down(&HFSPLUS_I(inode).extents_lock);
+       res = hfsplus_ext_read_extent(inode, ablock);
+       if (!res) {
+               dblock = hfsplus_ext_find_block(HFSPLUS_I(inode).cached_extents, ablock -
+                                            HFSPLUS_I(inode).cached_start);
+       } else {
+               up(&HFSPLUS_I(inode).extents_lock);
+               return -EIO;
+       }
+       up(&HFSPLUS_I(inode).extents_lock);
+
+done:
+       dprint(DBG_EXTENT, "get_block(%lu): %llu - %u\n", inode->i_ino, (long long)iblock, dblock);
+       mask = (1 << HFSPLUS_SB(sb).fs_shift) - 1;
+       map_bh(bh_result, sb, (dblock << HFSPLUS_SB(sb).fs_shift) + HFSPLUS_SB(sb).blockoffset + (iblock & mask));
+       if (create) {
+               set_buffer_new(bh_result);
+               HFSPLUS_I(inode).phys_size += sb->s_blocksize;
+               inode->i_blocks++;
+               mark_inode_dirty(inode);
+       }
+       return 0;
+}
+
+static void hfsplus_dump_extent(struct hfsplus_extent *extent)
+{
+       int i;
+
+       dprint(DBG_EXTENT, "   ");
+       for (i = 0; i < 8; i++)
+               dprint(DBG_EXTENT, " %u:%u", be32_to_cpu(extent[i].start_block),
+                                be32_to_cpu(extent[i].block_count));
+       dprint(DBG_EXTENT, "\n");
+}
+
+static int hfsplus_add_extent(struct hfsplus_extent *extent, u32 offset,
+                             u32 alloc_block, u32 block_count)
+{
+       u32 count, start;
+       int i;
+
+       hfsplus_dump_extent(extent);
+       for (i = 0; i < 8; extent++, i++) {
+               count = be32_to_cpu(extent->block_count);
+               if (offset == count) {
+                       start = be32_to_cpu(extent->start_block);
+                       if (alloc_block != start + count) {
+                               if (++i >= 8)
+                                       return -ENOSPC;
+                               extent++;
+                               extent->start_block = cpu_to_be32(alloc_block);
+                       } else
+                               block_count += count;
+                       extent->block_count = cpu_to_be32(block_count);
+                       return 0;
+               } else if (offset < count)
+                       break;
+               offset -= count;
+       }
+       /* panic? */
+       return -EIO;
+}
+
+int hfsplus_free_extents(struct super_block *sb, struct hfsplus_extent *extent,
+                        u32 offset, u32 block_nr)
+{
+       u32 count, start;
+       int i;
+
+       hfsplus_dump_extent(extent);
+       for (i = 0; i < 8; extent++, i++) {
+               count = be32_to_cpu(extent->block_count);
+               if (offset == count)
+                       goto found;
+               else if (offset < count)
+                       break;
+               offset -= count;
+       }
+       /* panic? */
+       return -EIO;
+found:
+       for (;;) {
+               start = be32_to_cpu(extent->start_block);
+               if (count <= block_nr) {
+                       hfsplus_block_free(sb, start, count);
+                       extent->block_count = 0;
+                       extent->start_block = 0;
+                       block_nr -= count;
+               } else {
+                       count -= block_nr;
+                       hfsplus_block_free(sb, start + count, block_nr);
+                       extent->block_count = cpu_to_be32(count);
+                       block_nr = 0;
+               }
+               if (!block_nr || !i)
+                       return 0;
+               i--;
+               extent--;
+               count = be32_to_cpu(extent->block_count);
+       }
+}
+
+int hfsplus_free_fork(struct super_block *sb, u32 cnid, struct hfsplus_fork_raw *fork, int type)
+{
+       struct hfs_find_data fd;
+       hfsplus_extent_rec ext_entry;
+       u32 total_blocks, blocks, start;
+       int res, i;
+
+       total_blocks = be32_to_cpu(fork->total_blocks);
+       if (!total_blocks)
+               return 0;
+
+       blocks = 0;
+       for (i = 0; i < 8; i++)
+               blocks += be32_to_cpu(fork->extents[i].block_count);
+
+       res = hfsplus_free_extents(sb, fork->extents, blocks, blocks);
+       if (res)
+               return res;
+       if (total_blocks == blocks)
+               return 0;
+
+       hfs_find_init(HFSPLUS_SB(sb).ext_tree, &fd);
+       do {
+               res = __hfsplus_ext_read_extent(&fd, ext_entry, cnid,
+                                               total_blocks, type);
+               if (res)
+                       break;
+               start = be32_to_cpu(fd.key->ext.start_block);
+               hfsplus_free_extents(sb, ext_entry,
+                                    total_blocks - start,
+                                    total_blocks);
+               hfs_brec_remove(&fd);
+               total_blocks = start;
+       } while (total_blocks > blocks);
+       hfs_find_exit(&fd);
+
+       return res;
+}
+
+int hfsplus_file_extend(struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+       u32 start, len, goal;
+       int res;
+
+       if (HFSPLUS_SB(sb).alloc_file->i_size * 8 < HFSPLUS_SB(sb).total_blocks - HFSPLUS_SB(sb).free_blocks + 8) {
+               // extend alloc file
+               printk("extend alloc file! (%Lu,%u,%u)\n", HFSPLUS_SB(sb).alloc_file->i_size * 8,
+                       HFSPLUS_SB(sb).total_blocks, HFSPLUS_SB(sb).free_blocks);
+               return -ENOSPC;
+               //BUG();
+       }
+
+       down(&HFSPLUS_I(inode).extents_lock);
+       if (HFSPLUS_I(inode).alloc_blocks == HFSPLUS_I(inode).first_blocks)
+               goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).first_extents);
+       else {
+               res = hfsplus_ext_read_extent(inode, HFSPLUS_I(inode).alloc_blocks);
+               if (res)
+                       goto out;
+               goal = hfsplus_ext_lastblock(HFSPLUS_I(inode).cached_extents);
+       }
+
+       len = HFSPLUS_I(inode).clump_blocks;
+       start = hfsplus_block_allocate(sb, HFSPLUS_SB(sb).total_blocks, goal, &len);
+       if (start >= HFSPLUS_SB(sb).total_blocks) {
+               start = hfsplus_block_allocate(sb, goal, 0, &len);
+               if (start >= goal) {
+                       res = -ENOSPC;
+                       goto out;
+               }
+       }
+
+       dprint(DBG_EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
+       if (HFSPLUS_I(inode).alloc_blocks <= HFSPLUS_I(inode).first_blocks) {
+               if (!HFSPLUS_I(inode).first_blocks) {
+                       dprint(DBG_EXTENT, "first extents\n");
+                       /* no extents yet */
+                       HFSPLUS_I(inode).first_extents[0].start_block = cpu_to_be32(start);
+                       HFSPLUS_I(inode).first_extents[0].block_count = cpu_to_be32(len);
+                       res = 0;
+               } else {
+                       /* try to append to extents in inode */
+                       res = hfsplus_add_extent(HFSPLUS_I(inode).first_extents,
+                                                HFSPLUS_I(inode).alloc_blocks,
+                                                start, len);
+                       if (res == -ENOSPC)
+                               goto insert_extent;
+               }
+               if (!res) {
+                       hfsplus_dump_extent(HFSPLUS_I(inode).first_extents);
+                       HFSPLUS_I(inode).first_blocks += len;
+               }
+       } else {
+               res = hfsplus_add_extent(HFSPLUS_I(inode).cached_extents,
+                                        HFSPLUS_I(inode).alloc_blocks -
+                                        HFSPLUS_I(inode).cached_start,
+                                        start, len);
+               if (!res) {
+                       hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents);
+                       HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY;
+                       HFSPLUS_I(inode).cached_blocks += len;
+               } else if (res == -ENOSPC)
+                       goto insert_extent;
+       }
+out:
+       up(&HFSPLUS_I(inode).extents_lock);
+       if (!res) {
+               HFSPLUS_I(inode).alloc_blocks += len;
+               mark_inode_dirty(inode);
+       }
+       return res;
+
+insert_extent:
+       dprint(DBG_EXTENT, "insert new extent\n");
+       hfsplus_ext_write_extent(inode);
+
+       memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec));
+       HFSPLUS_I(inode).cached_extents[0].start_block = cpu_to_be32(start);
+       HFSPLUS_I(inode).cached_extents[0].block_count = cpu_to_be32(len);
+       hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents);
+       HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW;
+       HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).alloc_blocks;
+       HFSPLUS_I(inode).cached_blocks = len;
+
+       res = 0;
+       goto out;
+}
+
+void hfsplus_file_truncate(struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+       struct hfs_find_data fd;
+       u32 alloc_cnt, blk_cnt, start;
+       int res;
+
+       dprint(DBG_INODE, "truncate: %lu, %Lu -> %Lu\n", inode->i_ino,
+              (long long)HFSPLUS_I(inode).phys_size, inode->i_size);
+       if (inode->i_size > HFSPLUS_I(inode).phys_size) {
+               struct address_space *mapping = inode->i_mapping;
+               struct page *page;
+               u32 size = inode->i_size - 1;
+               int res;
+
+               page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT);
+               if (!page)
+                       return;
+               size &= PAGE_CACHE_SIZE - 1;
+               size++;
+               res = mapping->a_ops->prepare_write(NULL, page, size, size);
+               if (!res)
+                       res = mapping->a_ops->commit_write(NULL, page, size, size);
+               if (res)
+                       inode->i_size = HFSPLUS_I(inode).phys_size;
+               unlock_page(page);
+               page_cache_release(page);
+               mark_inode_dirty(inode);
+               return;
+       }
+       blk_cnt = (inode->i_size + HFSPLUS_SB(sb).alloc_blksz - 1) >> HFSPLUS_SB(sb).alloc_blksz_shift;
+       alloc_cnt = HFSPLUS_I(inode).alloc_blocks;
+       if (blk_cnt == alloc_cnt)
+               goto out;
+
+       down(&HFSPLUS_I(inode).extents_lock);
+       hfs_find_init(HFSPLUS_SB(sb).ext_tree, &fd);
+       while (1) {
+               if (alloc_cnt == HFSPLUS_I(inode).first_blocks) {
+                       hfsplus_free_extents(sb, HFSPLUS_I(inode).first_extents,
+                                            alloc_cnt, alloc_cnt - blk_cnt);
+                       hfsplus_dump_extent(HFSPLUS_I(inode).first_extents);
+                       HFSPLUS_I(inode).first_blocks = blk_cnt;
+                       break;
+               }
+               res = __hfsplus_ext_cache_extent(&fd, inode, alloc_cnt);
+               if (res)
+                       break;
+               start = HFSPLUS_I(inode).cached_start;
+               hfsplus_free_extents(sb, HFSPLUS_I(inode).cached_extents,
+                                    alloc_cnt - start, alloc_cnt - blk_cnt);
+               hfsplus_dump_extent(HFSPLUS_I(inode).cached_extents);
+               if (blk_cnt > start) {
+                       HFSPLUS_I(inode).flags |= HFSPLUS_FLG_EXT_DIRTY;
+                       break;
+               }
+               alloc_cnt = start;
+               HFSPLUS_I(inode).cached_start = HFSPLUS_I(inode).cached_blocks = 0;
+               HFSPLUS_I(inode).flags &= ~(HFSPLUS_FLG_EXT_DIRTY | HFSPLUS_FLG_EXT_NEW);
+               hfs_brec_remove(&fd);
+       }
+       hfs_find_exit(&fd);
+       up(&HFSPLUS_I(inode).extents_lock);
+
+       HFSPLUS_I(inode).alloc_blocks = blk_cnt;
+out:
+       HFSPLUS_I(inode).phys_size = inode->i_size;
+       mark_inode_dirty(inode);
+       inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+}
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
new file mode 100644 (file)
index 0000000..2e172a8
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ *  linux/include/linux/hfsplus_fs.h
+ *
+ * Copyright (C) 1999
+ * Brad Boyer (flar@pants.nu)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ */
+
+#ifndef _LINUX_HFSPLUS_FS_H
+#define _LINUX_HFSPLUS_FS_H
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/buffer_head.h>
+#include "hfsplus_raw.h"
+
+#define DBG_BNODE_REFS 0x00000001
+#define DBG_BNODE_MOD  0x00000002
+#define DBG_CAT_MOD    0x00000004
+#define DBG_INODE      0x00000008
+#define DBG_SUPER      0x00000010
+#define DBG_EXTENT     0x00000020
+#define DBG_BITMAP     0x00000040
+
+//#define DBG_MASK     (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD)
+//#define DBG_MASK     (DBG_BNODE_MOD|DBG_CAT_MOD|DBG_INODE)
+//#define DBG_MASK     (DBG_CAT_MOD|DBG_BNODE_REFS|DBG_INODE|DBG_EXTENT)
+#define DBG_MASK       (0)
+
+#define dprint(flg, fmt, args...) \
+       if (flg & DBG_MASK) printk(fmt , ## args)
+
+/* Runtime config options */
+#define HFSPLUS_DEF_CR_TYPE    0x3F3F3F3F  /* '????' */
+
+#define HFSPLUS_TYPE_DATA 0x00
+#define HFSPLUS_TYPE_RSRC 0xFF
+
+typedef int (*btree_keycmp)(hfsplus_btree_key *, hfsplus_btree_key *);
+
+#define NODE_HASH_SIZE 256
+
+/* An HFS+ BTree held in memory */
+struct hfs_btree {
+       struct super_block *sb;
+       struct inode *inode;
+       btree_keycmp keycmp;
+
+       u32 cnid;
+       u32 root;
+       u32 leaf_count;
+       u32 leaf_head;
+       u32 leaf_tail;
+       u32 node_count;
+       u32 free_nodes;
+       u32 attributes;
+
+       unsigned int node_size;
+       unsigned int node_size_shift;
+       unsigned int max_key_len;
+       unsigned int depth;
+
+       //unsigned int map1_size, map_size;
+       struct semaphore tree_lock;
+
+       unsigned int pages_per_bnode;
+       spinlock_t hash_lock;
+       struct hfs_bnode *node_hash[NODE_HASH_SIZE];
+       int node_hash_cnt;
+};
+
+struct page;
+
+/* An HFS+ BTree node in memory */
+struct hfs_bnode {
+       struct hfs_btree *tree;
+
+       u32 prev;
+       u32 this;
+       u32 next;
+       u32 parent;
+
+       u16 num_recs;
+       u8 type;
+       u8 height;
+
+       struct hfs_bnode *next_hash;
+       unsigned long flags;
+       wait_queue_head_t lock_wq;
+       atomic_t refcnt;
+       unsigned int page_offset;
+       struct page *page[0];
+};
+
+#define HFS_BNODE_LOCK         0
+#define HFS_BNODE_ERROR                1
+#define HFS_BNODE_NEW          2
+#define HFS_BNODE_DIRTY                3
+#define HFS_BNODE_DELETED      4
+
+/*
+ * HFS+ superblock info (built from Volume Header on disk)
+ */
+
+struct hfsplus_vh;
+struct hfs_btree;
+
+struct hfsplus_sb_info {
+       struct buffer_head *s_vhbh;
+       struct hfsplus_vh *s_vhdr;
+       struct hfs_btree *ext_tree;
+       struct hfs_btree *cat_tree;
+       struct hfs_btree *attr_tree;
+       struct inode *alloc_file;
+       struct inode *hidden_dir;
+
+       /* Runtime variables */
+       u32 blockoffset;
+       u32 sect_count;
+       int fs_shift;
+
+       /* Stuff in host order from Vol Header */
+       u32 alloc_blksz;
+       int alloc_blksz_shift;
+       u32 total_blocks;
+       u32 free_blocks;
+       u32 next_alloc;
+       u32 next_cnid;
+       u32 file_count;
+       u32 folder_count;
+       u32 data_clump_blocks, rsrc_clump_blocks;
+
+       /* Config options */
+       u32 creator;
+       u32 type;
+
+       umode_t umask;
+       uid_t uid;
+       gid_t gid;
+
+       int part, session;
+
+       unsigned long flags;
+
+       atomic_t inode_cnt;
+       u32 last_inode_cnt;
+
+       struct hlist_head rsrc_inodes;
+};
+
+#define HFSPLUS_SB_WRITEBACKUP 0x0001
+
+
+struct hfsplus_inode_info {
+       struct semaphore extents_lock;
+       u32 clump_blocks, alloc_blocks;
+       /* Allocation extents from catalog record or volume header */
+       hfsplus_extent_rec first_extents;
+       u32 first_blocks;
+       hfsplus_extent_rec cached_extents;
+       u32 cached_start, cached_blocks;
+       atomic_t opencnt;
+
+       struct inode *rsrc_inode;
+       unsigned long flags;
+
+       /* Device number in hfsplus_permissions in catalog */
+       u32 dev;
+       /* BSD system and user file flags */
+       u8 rootflags;
+       u8 userflags;
+
+       struct list_head open_dir_list;
+       loff_t phys_size;
+       struct inode vfs_inode;
+};
+
+#define HFSPLUS_FLG_RSRC       0x0001
+#define HFSPLUS_FLG_EXT_DIRTY  0x0002
+#define HFSPLUS_FLG_EXT_NEW    0x0004
+
+#define HFSPLUS_IS_DATA(inode)   (!(HFSPLUS_I(inode).flags & HFSPLUS_FLG_RSRC))
+#define HFSPLUS_IS_RSRC(inode)   (HFSPLUS_I(inode).flags & HFSPLUS_FLG_RSRC)
+
+struct hfs_find_data {
+       /* filled by caller */
+       hfsplus_btree_key *search_key;
+       hfsplus_btree_key *key;
+       /* filled by find */
+       struct hfs_btree *tree;
+       struct hfs_bnode *bnode;
+       /* filled by findrec */
+       int record;
+       int keyoffset, keylength;
+       int entryoffset, entrylength;
+};
+
+struct hfsplus_readdir_data {
+       struct list_head list;
+       struct file *file;
+       struct hfsplus_cat_key key;
+};
+
+#define hfs_btree_open hfsplus_btree_open
+#define hfs_btree_close hfsplus_btree_close
+#define hfs_btree_write hfsplus_btree_write
+#define hfs_bmap_alloc hfsplus_bmap_alloc
+#define hfs_bmap_free hfsplus_bmap_free
+#define hfs_bnode_read hfsplus_bnode_read
+#define hfs_bnode_read_u16 hfsplus_bnode_read_u16
+#define hfs_bnode_read_u8 hfsplus_bnode_read_u8
+#define hfs_bnode_read_key hfsplus_bnode_read_key
+#define hfs_bnode_write hfsplus_bnode_write
+#define hfs_bnode_write_u16 hfsplus_bnode_write_u16
+#define hfs_bnode_clear hfsplus_bnode_clear
+#define hfs_bnode_copy hfsplus_bnode_copy
+#define hfs_bnode_move hfsplus_bnode_move
+#define hfs_bnode_dump hfsplus_bnode_dump
+#define hfs_bnode_unlink hfsplus_bnode_unlink
+#define hfs_bnode_findhash hfsplus_bnode_findhash
+#define hfs_bnode_find hfsplus_bnode_find
+#define hfs_bnode_unhash hfsplus_bnode_unhash
+#define hfs_bnode_free hfsplus_bnode_free
+#define hfs_bnode_create hfsplus_bnode_create
+#define hfs_bnode_get hfsplus_bnode_get
+#define hfs_bnode_put hfsplus_bnode_put
+#define hfs_brec_lenoff hfsplus_brec_lenoff
+#define hfs_brec_keylen hfsplus_brec_keylen
+#define hfs_brec_insert hfsplus_brec_insert
+#define hfs_brec_remove hfsplus_brec_remove
+#define hfs_bnode_split hfsplus_bnode_split
+#define hfs_brec_update_parent hfsplus_brec_update_parent
+#define hfs_btree_inc_height hfsplus_btree_inc_height
+#define hfs_find_init hfsplus_find_init
+#define hfs_find_exit hfsplus_find_exit
+#define __hfs_brec_find __hplusfs_brec_find
+#define hfs_brec_find hfsplus_brec_find
+#define hfs_brec_read hfsplus_brec_read
+#define hfs_brec_goto hfsplus_brec_goto
+#define hfs_part_find hfsplus_part_find
+
+/*
+ * definitions for ext2 flag ioctls (linux really needs a generic
+ * interface for this).
+ */
+
+/* ext2 ioctls (EXT2_IOC_GETFLAGS and EXT2_IOC_SETFLAGS) to support
+ * chattr/lsattr */
+#define HFSPLUS_IOC_EXT2_GETFLAGS      _IOR('f', 1, long)
+#define HFSPLUS_IOC_EXT2_SETFLAGS      _IOW('f', 2, long)
+
+#define EXT2_FLAG_IMMUTABLE            0x00000010 /* Immutable file */
+#define EXT2_FLAG_APPEND               0x00000020 /* writes to file may only append */
+#define EXT2_FLAG_NODUMP               0x00000040 /* do not dump file */
+
+
+/*
+ * Functions in any *.c used in other files
+ */
+
+/* bitmap.c */
+int hfsplus_block_allocate(struct super_block *, u32, u32, u32 *);
+int hfsplus_block_free(struct super_block *, u32, u32);
+
+/* btree.c */
+struct hfs_btree *hfs_btree_open(struct super_block *, u32);
+void hfs_btree_close(struct hfs_btree *);
+void hfs_btree_write(struct hfs_btree *);
+struct hfs_bnode *hfs_bmap_alloc(struct hfs_btree *);
+void hfs_bmap_free(struct hfs_bnode *);
+
+/* bnode.c */
+void hfs_bnode_read(struct hfs_bnode *, void *, int, int);
+u16 hfs_bnode_read_u16(struct hfs_bnode *, int);
+u8 hfs_bnode_read_u8(struct hfs_bnode *, int);
+void hfs_bnode_read_key(struct hfs_bnode *, void *, int);
+void hfs_bnode_write(struct hfs_bnode *, void *, int, int);
+void hfs_bnode_write_u16(struct hfs_bnode *, int, u16);
+void hfs_bnode_clear(struct hfs_bnode *, int, int);
+void hfs_bnode_copy(struct hfs_bnode *, int,
+                   struct hfs_bnode *, int, int);
+void hfs_bnode_move(struct hfs_bnode *, int, int, int);
+void hfs_bnode_dump(struct hfs_bnode *);
+void hfs_bnode_unlink(struct hfs_bnode *);
+struct hfs_bnode *hfs_bnode_findhash(struct hfs_btree *, u32);
+struct hfs_bnode *hfs_bnode_find(struct hfs_btree *, u32);
+void hfs_bnode_unhash(struct hfs_bnode *);
+void hfs_bnode_free(struct hfs_bnode *);
+struct hfs_bnode *hfs_bnode_create(struct hfs_btree *, u32);
+void hfs_bnode_get(struct hfs_bnode *);
+void hfs_bnode_put(struct hfs_bnode *);
+
+/* brec.c */
+u16 hfs_brec_lenoff(struct hfs_bnode *, u16, u16 *);
+u16 hfs_brec_keylen(struct hfs_bnode *, u16);
+int hfs_brec_insert(struct hfs_find_data *, void *, int);
+int hfs_brec_remove(struct hfs_find_data *);
+struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *);
+int hfs_brec_update_parent(struct hfs_find_data *);
+int hfs_btree_inc_height(struct hfs_btree *);
+
+/* bfind.c */
+int hfs_find_init(struct hfs_btree *, struct hfs_find_data *);
+void hfs_find_exit(struct hfs_find_data *);
+int __hfs_brec_find(struct hfs_bnode *, struct hfs_find_data *);
+int hfs_brec_find(struct hfs_find_data *);
+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 *);
+void hfsplus_cat_build_key(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 *);
+int hfsplus_delete_cat(u32, struct inode *, struct qstr *);
+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 *);
+void hfsplus_ext_build_key(hfsplus_btree_key *, u32, u32, u8);
+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);
+int hfsplus_file_extend(struct inode *);
+void hfsplus_file_truncate(struct inode *);
+
+/* inode.c */
+extern struct address_space_operations hfsplus_aops;
+extern struct address_space_operations hfsplus_btree_aops;
+
+void hfsplus_inode_read_fork(struct inode *, struct hfsplus_fork_raw *);
+void hfsplus_inode_write_fork(struct inode *, struct hfsplus_fork_raw *);
+int hfsplus_cat_read_inode(struct inode *, struct hfs_find_data *);
+void hfsplus_cat_write_inode(struct inode *);
+struct inode *hfsplus_new_inode(struct super_block *, int);
+void hfsplus_delete_inode(struct inode *);
+
+/* ioctl.c */
+int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+                 unsigned long arg);
+
+/* options.c */
+int parse_options(char *, struct hfsplus_sb_info *);
+void fill_defaults(struct hfsplus_sb_info *);
+void fill_current(struct hfsplus_sb_info *, struct hfsplus_sb_info *);
+
+/* tables.c */
+extern u16 case_fold_table[];
+
+/* unicode.c */
+int hfsplus_unistrcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *);
+int hfsplus_uni2asc(const struct hfsplus_unistr *, char *, int *);
+int hfsplus_asc2uni(struct hfsplus_unistr *, const char *, int);
+
+/* wrapper.c */
+int hfsplus_read_wrapper(struct super_block *);
+
+int hfs_part_find(struct super_block *, sector_t *, sector_t *);
+
+/* access macros */
+/*
+static inline struct hfsplus_sb_info *HFSPLUS_SB(struct super_block *sb)
+{
+       return sb->s_fs_info;
+}
+static inline struct hfsplus_inode_info *HFSPLUS_I(struct inode *inode)
+{
+       return list_entry(inode, struct hfsplus_inode_info, vfs_inode);
+}
+*/
+#define HFSPLUS_SB(super)      (*(struct hfsplus_sb_info *)(super)->s_fs_info)
+#define HFSPLUS_I(inode)       (*list_entry(inode, struct hfsplus_inode_info, vfs_inode))
+
+#if 1
+#define hfsplus_kmap(p)                ({ struct page *__p = (p); kmap(__p); })
+#define hfsplus_kunmap(p)      ({ struct page *__p = (p); kunmap(__p); __p; })
+#else
+#define hfsplus_kmap(p)                kmap(p)
+#define hfsplus_kunmap(p)      kunmap(p)
+#endif
+
+#define sb_bread512(sb, sec, data) ({                  \
+       struct buffer_head *__bh;                       \
+       sector_t __block;                               \
+       loff_t __start;                                 \
+       int __offset;                                   \
+                                                       \
+       __start = (loff_t)(sec) << HFSPLUS_SECTOR_SHIFT;\
+       __block = __start >> (sb)->s_blocksize_bits;    \
+       __offset = __start & ((sb)->s_blocksize - 1);   \
+       __bh = sb_bread((sb), __block);                 \
+       if (likely(__bh != NULL))                       \
+               data = (void *)(__bh->b_data + __offset);\
+       else                                            \
+               data = NULL;                            \
+       __bh;                                           \
+})
+
+/* time macros */
+#define __hfsp_mt2ut(t)                (be32_to_cpu(t) - 2082844800U)
+#define __hfsp_ut2mt(t)                (cpu_to_be32(t + 2082844800U))
+
+/* compatibility */
+#define hfsp_mt2ut(t)          (struct timespec){ .tv_sec = __hfsp_mt2ut(t) }
+#define hfsp_ut2mt(t)          __hfsp_ut2mt((t).tv_sec)
+#define hfsp_now2mt()          __hfsp_ut2mt(get_seconds())
+
+#define kdev_t_to_nr(x)                (x)
+
+#endif
diff --git a/fs/hfsplus/hfsplus_raw.h b/fs/hfsplus/hfsplus_raw.h
new file mode 100644 (file)
index 0000000..696f06f
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ *  linux/include/linux/hfsplus_raw.h
+ *
+ * Copyright (C) 1999
+ * Brad Boyer (flar@pants.nu)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Format of structures on disk
+ * Information taken from Apple Technote #1150 (HFS Plus Volume Format)
+ *
+ */
+
+#ifndef _LINUX_HFSPLUS_RAW_H
+#define _LINUX_HFSPLUS_RAW_H
+
+#include <linux/types.h>
+
+#define __packed __attribute__ ((packed))
+
+/* Some constants */
+#define HFSPLUS_SECTOR_SIZE        512
+#define HFSPLUS_SECTOR_SHIFT         9
+#define HFSPLUS_VOLHEAD_SECTOR       2
+#define HFSPLUS_VOLHEAD_SIG     0x482b
+#define HFSPLUS_SUPER_MAGIC     0x482b
+#define HFSPLUS_CURRENT_VERSION      4
+
+#define HFSP_WRAP_MAGIC         0x4244
+#define HFSP_WRAP_ATTRIB_SLOCK  0x8000
+#define HFSP_WRAP_ATTRIB_SPARED 0x0200
+
+#define HFSP_WRAPOFF_SIG          0x00
+#define HFSP_WRAPOFF_ATTRIB       0x0A
+#define HFSP_WRAPOFF_ABLKSIZE     0x14
+#define HFSP_WRAPOFF_ABLKSTART    0x1C
+#define HFSP_WRAPOFF_EMBEDSIG     0x7C
+#define HFSP_WRAPOFF_EMBEDEXT     0x7E
+
+#define HFSP_HIDDENDIR_NAME    "\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80\xe2\x90\x80HFS+ Private Data"
+
+#define HFSP_HARDLINK_TYPE     0x686c6e6b      /* 'hlnk' */
+#define HFSP_HFSPLUS_CREATOR   0x6866732b      /* 'hfs+' */
+
+#define HFSP_MOUNT_VERSION     0x482b4c78      /* 'H+Lx' */
+
+/* Structures used on disk */
+
+typedef u32 hfsplus_cnid;
+typedef u16 hfsplus_unichr;
+
+/* A "string" as used in filenames, etc. */
+struct hfsplus_unistr {
+       u16 length;
+       hfsplus_unichr unicode[255];
+} __packed;
+
+#define HFSPLUS_MAX_STRLEN 255
+
+/* POSIX permissions */
+struct hfsplus_perm {
+       u32 owner;
+       u32 group;
+       u8  rootflags;
+       u8  userflags;
+       u16 mode;
+       u32 dev;
+} __packed;
+
+#define HFSPLUS_FLG_NODUMP     0x01
+#define HFSPLUS_FLG_IMMUTABLE  0x02
+#define HFSPLUS_FLG_APPEND     0x04
+
+/* A single contiguous area of a file */
+struct hfsplus_extent {
+       u32 start_block;
+       u32 block_count;
+} __packed;
+typedef struct hfsplus_extent hfsplus_extent_rec[8];
+
+/* Information for a "Fork" in a file */
+struct hfsplus_fork_raw {
+       u64 total_size;
+       u32 clump_size;
+       u32 total_blocks;
+       hfsplus_extent_rec extents;
+} __packed;
+
+/* HFS+ Volume Header */
+struct hfsplus_vh {
+       u16 signature;
+       u16 version;
+       u32 attributes;
+       u32 last_mount_vers;
+       u32 reserved;
+
+       u32 create_date;
+       u32 modify_date;
+       u32 backup_date;
+       u32 checked_date;
+
+       u32 file_count;
+       u32 folder_count;
+
+       u32 blocksize;
+       u32 total_blocks;
+       u32 free_blocks;
+
+       u32 next_alloc;
+       u32 rsrc_clump_sz;
+       u32 data_clump_sz;
+       hfsplus_cnid next_cnid;
+
+       u32 write_count;
+       u64 encodings_bmp;
+
+       u8 finder_info[32];
+
+       struct hfsplus_fork_raw alloc_file;
+       struct hfsplus_fork_raw ext_file;
+       struct hfsplus_fork_raw cat_file;
+       struct hfsplus_fork_raw attr_file;
+       struct hfsplus_fork_raw start_file;
+} __packed;
+
+/* HFS+ volume attributes */
+#define HFSPLUS_VOL_UNMNT     (1 << 8)
+#define HFSPLUS_VOL_SPARE_BLK (1 << 9)
+#define HFSPLUS_VOL_NOCACHE   (1 << 10)
+#define HFSPLUS_VOL_INCNSTNT  (1 << 11)
+#define HFSPLUS_VOL_SOFTLOCK  (1 << 15)
+
+/* HFS+ BTree node descriptor */
+struct hfs_bnode_desc {
+       u32 next;
+       u32 prev;
+       s8 type;
+       u8 height;
+       u16 num_recs;
+       u16 reserved;
+} __packed;
+
+/* HFS+ BTree node types */
+#define HFS_NODE_INDEX 0x00
+#define HFS_NODE_HEADER        0x01
+#define HFS_NODE_MAP   0x02
+#define HFS_NODE_LEAF  0xFF
+
+/* HFS+ BTree header */
+struct hfs_btree_header_rec {
+       u16 depth;
+       u32 root;
+       u32 leaf_count;
+       u32 leaf_head;
+       u32 leaf_tail;
+       u16 node_size;
+       u16 max_key_len;
+       u32 node_count;
+       u32 free_nodes;
+       u16 reserved1;
+       u32 clump_size;
+       u8 btree_type;
+       u8 reserved2;
+       u32 attributes;
+       u32 reserved3[16];
+} __packed;
+
+/* BTree attributes */
+#define HFS_TREE_BIGKEYS       2
+#define HFS_TREE_VARIDXKEYS    4
+
+/* HFS+ BTree misc info */
+#define HFSPLUS_TREE_HEAD 0
+#define HFSPLUS_NODE_MXSZ 32768
+
+/* Some special File ID numbers (stolen from hfs.h) */
+#define HFSPLUS_POR_CNID               1       /* Parent Of the Root */
+#define HFSPLUS_ROOT_CNID              2       /* ROOT directory */
+#define HFSPLUS_EXT_CNID               3       /* EXTents B-tree */
+#define HFSPLUS_CAT_CNID               4       /* CATalog B-tree */
+#define HFSPLUS_BAD_CNID               5       /* BAD blocks file */
+#define HFSPLUS_ALLOC_CNID             6       /* ALLOCation file */
+#define HFSPLUS_START_CNID             7       /* STARTup file */
+#define HFSPLUS_ATTR_CNID              8       /* ATTRibutes file */
+#define HFSPLUS_EXCH_CNID              15      /* ExchangeFiles temp id */
+#define HFSPLUS_FIRSTUSER_CNID         16      /* first available user id */
+
+/* HFS+ catalog entry key */
+struct hfsplus_cat_key {
+       u16 key_len;
+       hfsplus_cnid parent;
+       struct hfsplus_unistr name;
+} __packed;
+
+
+/* Structs from hfs.h */
+struct hfsp_point {
+       u16 v;
+       u16 h;
+} __packed;
+
+struct hfsp_rect {
+       u16 top;
+       u16 left;
+       u16 bottom;
+       u16 right;
+} __packed;
+
+
+/* HFS directory info (stolen from hfs.h */
+struct DInfo {
+       struct hfsp_rect frRect;
+       u16 frFlags;
+       struct hfsp_point frLocation;
+       u16 frView;
+} __packed;
+
+struct DXInfo {
+       struct hfsp_point frScroll;
+       u32 frOpenChain;
+       u16 frUnused;
+       u16 frComment;
+       u32 frPutAway;
+} __packed;
+
+/* HFS+ folder data (part of an hfsplus_cat_entry) */
+struct hfsplus_cat_folder {
+       s16 type;
+       u16 flags;
+       u32 valence;
+       hfsplus_cnid id;
+       u32 create_date;
+       u32 content_mod_date;
+       u32 attribute_mod_date;
+       u32 access_date;
+       u32 backup_date;
+       struct hfsplus_perm permissions;
+       struct DInfo user_info;
+       struct DXInfo finder_info;
+       u32 text_encoding;
+       u32 reserved;
+} __packed;
+
+/* HFS file info (stolen from hfs.h) */
+struct FInfo {
+       u32 fdType;
+       u32 fdCreator;
+       u16 fdFlags;
+       struct hfsp_point fdLocation;
+       u16 fdFldr;
+} __packed;
+
+struct FXInfo {
+       u16 fdIconID;
+       u8 fdUnused[8];
+       u16 fdComment;
+       u32 fdPutAway;
+} __packed;
+
+/* HFS+ file data (part of a cat_entry) */
+struct hfsplus_cat_file {
+       s16 type;
+       u16 flags;
+       u32 reserved1;
+       hfsplus_cnid id;
+       u32 create_date;
+       u32 content_mod_date;
+       u32 attribute_mod_date;
+       u32 access_date;
+       u32 backup_date;
+       struct hfsplus_perm permissions;
+       struct FInfo user_info;
+       struct FXInfo finder_info;
+       u32 text_encoding;
+       u32 reserved2;
+
+       struct hfsplus_fork_raw data_fork;
+       struct hfsplus_fork_raw rsrc_fork;
+} __packed;
+
+/* File attribute bits */
+#define HFSPLUS_FILE_LOCKED            0x0001
+#define HFSPLUS_FILE_THREAD_EXISTS     0x0002
+
+/* HFS+ catalog thread (part of a cat_entry) */
+struct hfsplus_cat_thread {
+       s16 type;
+       s16 reserved;
+       hfsplus_cnid parentID;
+       struct hfsplus_unistr nodeName;
+} __packed;
+
+#define HFSPLUS_MIN_THREAD_SZ 10
+
+/* A data record in the catalog tree */
+typedef union {
+       s16 type;
+       struct hfsplus_cat_folder folder;
+       struct hfsplus_cat_file file;
+       struct hfsplus_cat_thread thread;
+} __packed hfsplus_cat_entry;
+
+/* HFS+ catalog entry type */
+#define HFSPLUS_FOLDER         0x0001
+#define HFSPLUS_FILE           0x0002
+#define HFSPLUS_FOLDER_THREAD  0x0003
+#define HFSPLUS_FILE_THREAD    0x0004
+
+/* HFS+ extents tree key */
+struct hfsplus_ext_key {
+       u16 key_len;
+       u8 fork_type;
+       u8 pad;
+       hfsplus_cnid cnid;
+       u32 start_block;
+} __packed;
+
+#define HFSPLUS_EXT_KEYLEN 12
+
+/* HFS+ generic BTree key */
+typedef union {
+       u16 key_len;
+       struct hfsplus_cat_key cat;
+       struct hfsplus_ext_key ext;
+} __packed hfsplus_btree_key;
+
+#endif
diff --git a/fs/hfsplus/inode.c b/fs/hfsplus/inode.c
new file mode 100644 (file)
index 0000000..435e7e4
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ *  linux/fs/hfsplus/inode.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Inode handling routines
+ */
+
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/pagemap.h>
+#include <linux/version.h>
+#include <linux/mpage.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+static int hfsplus_readpage(struct file *file, struct page *page)
+{
+       //printk("readpage: %lu\n", page->index);
+       return block_read_full_page(page, hfsplus_get_block);
+}
+
+static int hfsplus_writepage(struct page *page, struct writeback_control *wbc)
+{
+       //printk("writepage: %lu\n", page->index);
+       return block_write_full_page(page, hfsplus_get_block, wbc);
+}
+
+static int hfsplus_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to)
+{
+       return cont_prepare_write(page, from, to, hfsplus_get_block,
+               &HFSPLUS_I(page->mapping->host).phys_size);
+}
+
+static sector_t hfsplus_bmap(struct address_space *mapping, sector_t block)
+{
+       return generic_block_bmap(mapping, block, hfsplus_get_block);
+}
+
+int hfsplus_releasepage(struct page *page, int mask)
+{
+       struct inode *inode = page->mapping->host;
+       struct super_block *sb = inode->i_sb;
+       struct hfs_btree *tree;
+       struct hfs_bnode *node;
+       u32 nidx;
+       int i, res = 1;
+
+       switch (inode->i_ino) {
+       case HFSPLUS_EXT_CNID:
+               tree = HFSPLUS_SB(sb).ext_tree;
+               break;
+       case HFSPLUS_CAT_CNID:
+               tree = HFSPLUS_SB(sb).cat_tree;
+               break;
+       case HFSPLUS_ATTR_CNID:
+               tree = HFSPLUS_SB(sb).attr_tree;
+               break;
+       default:
+               BUG();
+               return 0;
+       }
+       if (tree->node_size >= PAGE_CACHE_SIZE) {
+               nidx = page->index >> (tree->node_size_shift - PAGE_CACHE_SHIFT);
+               spin_lock(&tree->hash_lock);
+               node = hfs_bnode_findhash(tree, nidx);
+               if (!node)
+                       ;
+               else if (atomic_read(&node->refcnt))
+                       res = 0;
+               else for (i = 0; i < tree->pages_per_bnode; i++) {
+                       if (PageActive(node->page[i])) {
+                               res = 0;
+                               break;
+                       }
+               }
+               if (res && node) {
+                       hfs_bnode_unhash(node);
+                       hfs_bnode_free(node);
+               }
+               spin_unlock(&tree->hash_lock);
+       } else {
+               nidx = page->index << (PAGE_CACHE_SHIFT - tree->node_size_shift);
+               i = 1 << (PAGE_CACHE_SHIFT - tree->node_size_shift);
+               spin_lock(&tree->hash_lock);
+               do {
+                       node = hfs_bnode_findhash(tree, nidx++);
+                       if (!node)
+                               continue;
+                       if (atomic_read(&node->refcnt)) {
+                               res = 0;
+                               break;
+                       }
+                       hfs_bnode_unhash(node);
+                       hfs_bnode_free(node);
+               } while (--i);
+               spin_unlock(&tree->hash_lock);
+       }
+       //printk("releasepage: %lu,%x = %d\n", page->index, mask, res);
+       return res;
+}
+
+static int hfsplus_get_blocks(struct inode *inode, sector_t iblock, unsigned long max_blocks,
+                             struct buffer_head *bh_result, int create)
+{
+       int ret;
+
+       ret = hfsplus_get_block(inode, iblock, bh_result, create);
+       if (!ret)
+               bh_result->b_size = (1 << inode->i_blkbits);
+       return ret;
+}
+
+static int hfsplus_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov,
+                         loff_t offset, unsigned long nr_segs)
+{
+       struct file *file = iocb->ki_filp;
+       struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
+
+       return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
+                                 offset, nr_segs, hfsplus_get_blocks, NULL);
+}
+
+static int hfsplus_writepages(struct address_space *mapping,
+                             struct writeback_control *wbc)
+{
+       return mpage_writepages(mapping, wbc, hfsplus_get_block);
+}
+
+struct address_space_operations hfsplus_btree_aops = {
+       .readpage       = hfsplus_readpage,
+       .writepage      = hfsplus_writepage,
+       .sync_page      = block_sync_page,
+       .prepare_write  = hfsplus_prepare_write,
+       .commit_write   = generic_commit_write,
+       .bmap           = hfsplus_bmap,
+       .releasepage    = hfsplus_releasepage,
+};
+
+struct address_space_operations hfsplus_aops = {
+       .readpage       = hfsplus_readpage,
+       .writepage      = hfsplus_writepage,
+       .sync_page      = block_sync_page,
+       .prepare_write  = hfsplus_prepare_write,
+       .commit_write   = generic_commit_write,
+       .bmap           = hfsplus_bmap,
+       .direct_IO      = hfsplus_direct_IO,
+       .writepages     = hfsplus_writepages,
+};
+
+static struct dentry *hfsplus_file_lookup(struct inode *dir, struct dentry *dentry,
+                                         struct nameidata *nd)
+{
+       struct hfs_find_data fd;
+       struct super_block *sb = dir->i_sb;
+       struct inode *inode = NULL;
+       int err;
+
+       if (HFSPLUS_IS_RSRC(dir) || strcmp(dentry->d_name.name, "rsrc"))
+               goto out;
+
+       inode = HFSPLUS_I(dir).rsrc_inode;
+       if (inode)
+               goto out;
+
+       inode = new_inode(sb);
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+
+       inode->i_ino = dir->i_ino;
+       INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
+       init_MUTEX(&HFSPLUS_I(inode).extents_lock);
+       HFSPLUS_I(inode).flags = HFSPLUS_FLG_RSRC;
+
+       hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+       err = hfsplus_find_cat(sb, dir->i_ino, &fd);
+       if (!err)
+               err = hfsplus_cat_read_inode(inode, &fd);
+       hfs_find_exit(&fd);
+       if (err) {
+               iput(inode);
+               return ERR_PTR(err);
+       }
+       HFSPLUS_I(inode).rsrc_inode = dir;
+       HFSPLUS_I(dir).rsrc_inode = inode;
+       igrab(dir);
+       hlist_add_head(&inode->i_hash, &HFSPLUS_SB(sb).rsrc_inodes);
+       mark_inode_dirty(inode);
+       {
+       void hfsplus_inode_check(struct super_block *sb);
+       atomic_inc(&HFSPLUS_SB(sb).inode_cnt);
+       hfsplus_inode_check(sb);
+       }
+out:
+       d_add(dentry, inode);
+       return NULL;
+}
+
+static void hfsplus_get_perms(struct inode *inode, struct hfsplus_perm *perms, int dir)
+{
+       struct super_block *sb = inode->i_sb;
+       u16 mode;
+
+       mode = be16_to_cpu(perms->mode);
+
+       inode->i_uid = be32_to_cpu(perms->owner);
+       if (!inode->i_uid && !mode)
+               inode->i_uid = HFSPLUS_SB(sb).uid;
+
+       inode->i_gid = be32_to_cpu(perms->group);
+       if (!inode->i_gid && !mode)
+               inode->i_gid = HFSPLUS_SB(sb).gid;
+
+       if (dir) {
+               mode = mode ? (mode & S_IALLUGO) :
+                       (S_IRWXUGO & ~(HFSPLUS_SB(sb).umask));
+               mode |= S_IFDIR;
+       } else if (!mode)
+               mode = S_IFREG | ((S_IRUGO|S_IWUGO) &
+                       ~(HFSPLUS_SB(sb).umask));
+       inode->i_mode = mode;
+
+       HFSPLUS_I(inode).rootflags = perms->rootflags;
+       HFSPLUS_I(inode).userflags = perms->userflags;
+       if (perms->rootflags & HFSPLUS_FLG_IMMUTABLE)
+               inode->i_flags |= S_IMMUTABLE;
+       else
+               inode->i_flags &= ~S_IMMUTABLE;
+       if (perms->rootflags & HFSPLUS_FLG_APPEND)
+               inode->i_flags |= S_APPEND;
+       else
+               inode->i_flags &= ~S_APPEND;
+}
+
+static void hfsplus_set_perms(struct inode *inode, struct hfsplus_perm *perms)
+{
+       if (inode->i_flags & S_IMMUTABLE)
+               perms->rootflags |= HFSPLUS_FLG_IMMUTABLE;
+       else
+               perms->rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
+       if (inode->i_flags & S_APPEND)
+               perms->rootflags |= HFSPLUS_FLG_APPEND;
+       else
+               perms->rootflags &= ~HFSPLUS_FLG_APPEND;
+       perms->userflags = HFSPLUS_I(inode).userflags;
+       perms->mode = cpu_to_be16(inode->i_mode);
+       perms->owner = cpu_to_be32(inode->i_uid);
+       perms->group = cpu_to_be32(inode->i_gid);
+       perms->dev = cpu_to_be32(HFSPLUS_I(inode).dev);
+}
+
+static int hfsplus_permission(struct inode *inode, int mask, struct nameidata *nd)
+{
+       /* MAY_EXEC is also used for lookup, if no x bit is set allow lookup,
+        * open_exec has the same test, so it's still not executable, if a x bit
+        * is set fall back to standard permission check.
+        */
+       if (S_ISREG(inode->i_mode) && mask & MAY_EXEC && !(inode->i_mode & 0111))
+               return 0;
+       return vfs_permission(inode, mask);
+}
+
+
+static int hfsplus_file_open(struct inode *inode, struct file *file)
+{
+       if (HFSPLUS_IS_RSRC(inode))
+               inode = HFSPLUS_I(inode).rsrc_inode;
+       if (atomic_read(&file->f_count) != 1)
+               return 0;
+       atomic_inc(&HFSPLUS_I(inode).opencnt);
+       return 0;
+}
+
+static int hfsplus_file_release(struct inode *inode, struct file *file)
+{
+       struct super_block *sb = inode->i_sb;
+
+       if (HFSPLUS_IS_RSRC(inode))
+               inode = HFSPLUS_I(inode).rsrc_inode;
+       if (atomic_read(&file->f_count) != 0)
+               return 0;
+       if (atomic_dec_and_test(&HFSPLUS_I(inode).opencnt)) {
+               down(&inode->i_sem);
+               hfsplus_file_truncate(inode);
+               if (inode->i_flags & S_DEAD) {
+                       hfsplus_delete_cat(inode->i_ino, HFSPLUS_SB(sb).hidden_dir, NULL);
+                       hfsplus_delete_inode(inode);
+               }
+               up(&inode->i_sem);
+       }
+       return 0;
+}
+
+extern struct inode_operations hfsplus_dir_inode_operations;
+extern struct file_operations hfsplus_dir_operations;
+
+struct inode_operations hfsplus_file_inode_operations = {
+       .lookup         = hfsplus_file_lookup,
+       .truncate       = hfsplus_file_truncate,
+       .permission     = hfsplus_permission,
+};
+
+struct file_operations hfsplus_file_operations = {
+       .llseek         = generic_file_llseek,
+       .read           = generic_file_read,
+       .write          = generic_file_write,
+       .mmap           = generic_file_mmap,
+       .fsync          = file_fsync,
+       .open           = hfsplus_file_open,
+       .release        = hfsplus_file_release,
+       .ioctl          = hfsplus_ioctl,
+};
+
+struct inode *hfsplus_new_inode(struct super_block *sb, int mode)
+{
+       struct inode *inode = new_inode(sb);
+       if (!inode)
+               return NULL;
+
+       {
+       void hfsplus_inode_check(struct super_block *sb);
+       atomic_inc(&HFSPLUS_SB(sb).inode_cnt);
+       hfsplus_inode_check(sb);
+       }
+       inode->i_ino = HFSPLUS_SB(sb).next_cnid++;
+       inode->i_mode = mode;
+       inode->i_uid = current->fsuid;
+       inode->i_gid = current->fsgid;
+       inode->i_nlink = 1;
+       inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+       INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
+       init_MUTEX(&HFSPLUS_I(inode).extents_lock);
+       atomic_set(&HFSPLUS_I(inode).opencnt, 0);
+       HFSPLUS_I(inode).flags = 0;
+       if (S_ISDIR(inode->i_mode)) {
+               inode->i_size = 2;
+               HFSPLUS_SB(sb).folder_count++;
+               inode->i_op = &hfsplus_dir_inode_operations;
+               inode->i_fop = &hfsplus_dir_operations;
+       } else if (S_ISREG(inode->i_mode)) {
+               HFSPLUS_SB(sb).file_count++;
+               inode->i_op = &hfsplus_file_inode_operations;
+               inode->i_fop = &hfsplus_file_operations;
+               inode->i_mapping->a_ops = &hfsplus_aops;
+               HFSPLUS_I(inode).clump_blocks = HFSPLUS_SB(sb).data_clump_blocks;
+               memset(HFSPLUS_I(inode).first_extents, 0, sizeof(hfsplus_extent_rec));
+               memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec));
+               HFSPLUS_I(inode).alloc_blocks = 0;
+               HFSPLUS_I(inode).first_blocks = 0;
+               HFSPLUS_I(inode).cached_start = 0;
+               HFSPLUS_I(inode).cached_blocks = 0;
+               HFSPLUS_I(inode).phys_size = 0;
+               HFSPLUS_I(inode).rsrc_inode = 0;
+       } else if (S_ISLNK(inode->i_mode)) {
+               HFSPLUS_SB(sb).file_count++;
+               inode->i_op = &page_symlink_inode_operations;
+               inode->i_mapping->a_ops = &hfsplus_aops;
+               HFSPLUS_I(inode).clump_blocks = 1;
+       } else
+               HFSPLUS_SB(sb).file_count++;
+       insert_inode_hash(inode);
+       mark_inode_dirty(inode);
+       sb->s_dirt = 1;
+
+       return inode;
+}
+
+void hfsplus_delete_inode(struct inode *inode)
+{
+       struct super_block *sb = inode->i_sb;
+
+       if (S_ISDIR(inode->i_mode)) {
+               HFSPLUS_SB(sb).folder_count--;
+               sb->s_dirt = 1;
+               return;
+       }
+       HFSPLUS_SB(sb).file_count--;
+       if (S_ISREG(inode->i_mode)) {
+               if (!inode->i_nlink) {
+                       inode->i_size = 0;
+                       hfsplus_file_truncate(inode);
+               }
+       } else if (S_ISLNK(inode->i_mode)) {
+               inode->i_size = 0;
+               hfsplus_file_truncate(inode);
+       }
+       sb->s_dirt = 1;
+}
+
+void hfsplus_inode_read_fork(struct inode *inode, struct hfsplus_fork_raw *fork)
+{
+       struct super_block *sb = inode->i_sb;
+       u32 count;
+       int i;
+
+       memcpy(&HFSPLUS_I(inode).first_extents, &fork->extents,
+              sizeof(hfsplus_extent_rec));
+       for (count = 0, i = 0; i < 8; i++)
+               count += be32_to_cpu(fork->extents[i].block_count);
+       HFSPLUS_I(inode).first_blocks = count;
+       memset(HFSPLUS_I(inode).cached_extents, 0, sizeof(hfsplus_extent_rec));
+       HFSPLUS_I(inode).cached_start = 0;
+       HFSPLUS_I(inode).cached_blocks = 0;
+
+       HFSPLUS_I(inode).alloc_blocks = be32_to_cpu(fork->total_blocks);
+       inode->i_size = HFSPLUS_I(inode).phys_size = be64_to_cpu(fork->total_size);
+       inode->i_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
+       HFSPLUS_I(inode).clump_blocks = be32_to_cpu(fork->clump_size) >> HFSPLUS_SB(sb).alloc_blksz_shift;
+       if (!HFSPLUS_I(inode).clump_blocks)
+               HFSPLUS_I(inode).clump_blocks = HFSPLUS_IS_RSRC(inode) ? HFSPLUS_SB(sb).rsrc_clump_blocks :
+                               HFSPLUS_SB(sb).data_clump_blocks;
+}
+
+void hfsplus_inode_write_fork(struct inode *inode, struct hfsplus_fork_raw *fork)
+{
+       memcpy(&fork->extents, &HFSPLUS_I(inode).first_extents,
+              sizeof(hfsplus_extent_rec));
+       fork->total_size = cpu_to_be64(inode->i_size);
+       fork->total_blocks = cpu_to_be32(HFSPLUS_I(inode).alloc_blocks);
+}
+
+int hfsplus_cat_read_inode(struct inode *inode, struct hfs_find_data *fd)
+{
+       hfsplus_cat_entry entry;
+       int res = 0;
+       u16 type;
+
+       type = hfs_bnode_read_u16(fd->bnode, fd->entryoffset);
+
+       HFSPLUS_I(inode).dev = 0;
+       inode->i_blksize = PAGE_SIZE; /* Doesn't seem to be useful... */
+       if (type == HFSPLUS_FOLDER) {
+               struct hfsplus_cat_folder *folder = &entry.folder;
+
+               if (fd->entrylength < sizeof(struct hfsplus_cat_folder))
+                       /* panic? */;
+               hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
+                                       sizeof(struct hfsplus_cat_folder));
+               hfsplus_get_perms(inode, &folder->permissions, 1);
+               inode->i_nlink = 1;
+               inode->i_size = 2 + be32_to_cpu(folder->valence);
+               inode->i_atime = hfsp_mt2ut(folder->access_date);
+               inode->i_mtime = hfsp_mt2ut(folder->content_mod_date);
+               inode->i_ctime = inode->i_mtime;
+               inode->i_blocks = 0;
+               inode->i_op = &hfsplus_dir_inode_operations;
+               inode->i_fop = &hfsplus_dir_operations;
+       } else if (type == HFSPLUS_FILE) {
+               struct hfsplus_cat_file *file = &entry.file;
+
+               if (fd->entrylength < sizeof(struct hfsplus_cat_file))
+                       /* panic? */;
+               hfs_bnode_read(fd->bnode, &entry, fd->entryoffset,
+                                       sizeof(struct hfsplus_cat_file));
+
+               hfsplus_inode_read_fork(inode, HFSPLUS_IS_DATA(inode) ?
+                                       &file->data_fork : &file->rsrc_fork);
+               hfsplus_get_perms(inode, &file->permissions, 0);
+               inode->i_nlink = 1;
+               if (S_ISREG(inode->i_mode)) {
+                       if (file->permissions.dev)
+                               inode->i_nlink = be32_to_cpu(file->permissions.dev);
+                       inode->i_op = &hfsplus_file_inode_operations;
+                       inode->i_fop = &hfsplus_file_operations;
+                       inode->i_mapping->a_ops = &hfsplus_aops;
+               } else if (S_ISLNK(inode->i_mode)) {
+                       inode->i_op = &page_symlink_inode_operations;
+                       inode->i_mapping->a_ops = &hfsplus_aops;
+               } else {
+                       init_special_inode(inode, inode->i_mode,
+                                          be32_to_cpu(file->permissions.dev));
+               }
+               inode->i_atime = hfsp_mt2ut(file->access_date);
+               inode->i_mtime = hfsp_mt2ut(file->content_mod_date);
+               inode->i_ctime = inode->i_mtime;
+       } else {
+               printk("HFS+-fs: bad catalog entry used to create inode\n");
+               res = -EIO;
+       }
+       return res;
+}
+
+void hfsplus_cat_write_inode(struct inode *inode)
+{
+       struct hfs_find_data fd;
+       hfsplus_cat_entry entry;
+
+       if (HFSPLUS_IS_RSRC(inode)) {
+               mark_inode_dirty(HFSPLUS_I(inode).rsrc_inode);
+               return;
+       }
+
+       if (!inode->i_nlink)
+               return;
+
+       if (hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd))
+               /* panic? */
+               return;
+
+       if (hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd))
+               /* panic? */
+               goto out;
+
+       if (S_ISDIR(inode->i_mode)) {
+               struct hfsplus_cat_folder *folder = &entry.folder;
+
+               if (fd.entrylength < sizeof(struct hfsplus_cat_folder))
+                       /* panic? */;
+               hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
+                                       sizeof(struct hfsplus_cat_folder));
+               /* simple node checks? */
+               hfsplus_set_perms(inode, &folder->permissions);
+               folder->access_date = hfsp_ut2mt(inode->i_atime);
+               folder->content_mod_date = hfsp_ut2mt(inode->i_mtime);
+               folder->attribute_mod_date = hfsp_ut2mt(inode->i_ctime);
+               folder->valence = cpu_to_be32(inode->i_size - 2);
+               hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
+                                        sizeof(struct hfsplus_cat_folder));
+       } else {
+               struct hfsplus_cat_file *file = &entry.file;
+
+               if (fd.entrylength < sizeof(struct hfsplus_cat_file))
+                       /* panic? */;
+               hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
+                                       sizeof(struct hfsplus_cat_file));
+               hfsplus_inode_write_fork(inode, &file->data_fork);
+               if (HFSPLUS_I(inode).rsrc_inode)
+                       hfsplus_inode_write_fork(HFSPLUS_I(inode).rsrc_inode, &file->rsrc_fork);
+               if (S_ISREG(inode->i_mode))
+                       HFSPLUS_I(inode).dev = inode->i_nlink;
+               if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode))
+                       HFSPLUS_I(inode).dev = kdev_t_to_nr(inode->i_rdev);
+               hfsplus_set_perms(inode, &file->permissions);
+               if ((file->permissions.rootflags | file->permissions.userflags) & HFSPLUS_FLG_IMMUTABLE)
+                       file->flags |= cpu_to_be16(HFSPLUS_FILE_LOCKED);
+               else
+                       file->flags &= cpu_to_be16(~HFSPLUS_FILE_LOCKED);
+               file->access_date = hfsp_ut2mt(inode->i_atime);
+               file->content_mod_date = hfsp_ut2mt(inode->i_mtime);
+               file->attribute_mod_date = hfsp_ut2mt(inode->i_ctime);
+               hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
+                                        sizeof(struct hfsplus_cat_file));
+       }
+out:
+       hfs_find_exit(&fd);
+}
diff --git a/fs/hfsplus/ioctl.c b/fs/hfsplus/ioctl.c
new file mode 100644 (file)
index 0000000..440e0da
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ *  linux/fs/hfsplus/ioctl.c
+ *
+ * Copyright (C) 2003
+ * Ethan Benson <erbenson@alaska.net>
+ * partially derived from linux/fs/ext2/ioctl.c
+ * Copyright (C) 1993, 1994, 1995
+ * Remy Card (card@masi.ibp.fr)
+ * Laboratoire MASI - Institut Blaise Pascal
+ * Universite Pierre et Marie Curie (Paris VI)
+ *
+ * hfsplus ioctls
+ */
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include "hfsplus_fs.h"
+
+int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+                 unsigned long arg)
+{
+       unsigned int flags;
+
+       switch (cmd) {
+       case HFSPLUS_IOC_EXT2_GETFLAGS:
+               flags = 0;
+               if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE)
+                       flags |= EXT2_FLAG_IMMUTABLE; /* EXT2_IMMUTABLE_FL */
+               if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND)
+                       flags |= EXT2_FLAG_APPEND; /* EXT2_APPEND_FL */
+               if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP)
+                       flags |= EXT2_FLAG_NODUMP; /* EXT2_NODUMP_FL */
+               return put_user(flags, (int *)arg);
+       case HFSPLUS_IOC_EXT2_SETFLAGS: {
+               if (IS_RDONLY(inode))
+                       return -EROFS;
+
+               if ((current->fsuid != inode->i_uid) && !capable(CAP_FOWNER))
+                       return -EACCES;
+
+               if (get_user(flags, (int *)arg))
+                       return -EFAULT;
+
+               if (flags & (EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND) ||
+                   HFSPLUS_I(inode).rootflags & (HFSPLUS_FLG_IMMUTABLE|HFSPLUS_FLG_APPEND)) {
+                       if (!capable(CAP_LINUX_IMMUTABLE))
+                               return -EPERM;
+               }
+
+               /* don't silently ignore unsupported ext2 flags */
+               if (flags & ~(EXT2_FLAG_IMMUTABLE|EXT2_FLAG_APPEND|
+                             EXT2_FLAG_NODUMP))
+                       return -EOPNOTSUPP;
+
+               if (flags & EXT2_FLAG_IMMUTABLE) { /* EXT2_IMMUTABLE_FL */
+                       inode->i_flags |= S_IMMUTABLE;
+                       HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_IMMUTABLE;
+               } else {
+                       inode->i_flags &= ~S_IMMUTABLE;
+                       HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_IMMUTABLE;
+               }
+               if (flags & EXT2_FLAG_APPEND) { /* EXT2_APPEND_FL */
+                       inode->i_flags |= S_APPEND;
+                       HFSPLUS_I(inode).rootflags |= HFSPLUS_FLG_APPEND;
+               } else {
+                       inode->i_flags &= ~S_APPEND;
+                       HFSPLUS_I(inode).rootflags &= ~HFSPLUS_FLG_APPEND;
+               }
+               if (flags & EXT2_FLAG_NODUMP) /* EXT2_NODUMP_FL */
+                       HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP;
+               else
+                       HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP;
+
+               inode->i_ctime = CURRENT_TIME;
+               mark_inode_dirty(inode);
+               return 0;
+       }
+       default:
+               return -ENOTTY;
+       }
+}
diff --git a/fs/hfsplus/options.c b/fs/hfsplus/options.c
new file mode 100644 (file)
index 0000000..20c802f
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ *  linux/fs/hfsplus/options.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Option parsing
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include "hfsplus_fs.h"
+
+/* Initialize an options object to reasonable defaults */
+void fill_defaults(struct hfsplus_sb_info *opts)
+{
+       if (!opts)
+               return;
+
+       opts->creator = HFSPLUS_DEF_CR_TYPE;
+       opts->type = HFSPLUS_DEF_CR_TYPE;
+       opts->umask = current->fs->umask;
+       opts->uid = current->uid;
+       opts->gid = current->gid;
+       opts->part = -1;
+       opts->session = -1;
+}
+
+/* convert a "four byte character" to a 32 bit int with error checks */
+static int fill_fourchar(u32 *result, char *input)
+{
+       u32 out;
+       int i;
+
+       if (!result || !input || !*input || (strlen(input) != 4))
+               return 0;
+
+       for (out = 0, i = 0; i < 4; i++) {
+               out <<= 8;
+               out |= ((int)(input[i])) & 0xFF;
+       }
+       *result = out;
+       return 1;
+}
+
+/* convert a string to int with error checks */
+static int fill_int(int *result, char *input, int base)
+{
+       char *tmp = input;
+       int intval;
+
+       if (!result || !input || !*input)
+               return 0;
+
+       intval = simple_strtoul(tmp, &tmp, base);
+       if (*tmp)
+               return 0;
+
+       *result = intval;
+       return 1;
+}
+
+/* Parse options from mount. Returns 0 on failure */
+/* input is the options passed to mount() as a string */
+int parse_options(char *input, struct hfsplus_sb_info *results)
+{
+       char *curropt, *value;
+       int tmp;
+
+       if (!input)
+               return 1;
+
+       while ((curropt = strsep(&input,",")) != NULL) {
+               if (!*curropt)
+                       continue;
+
+               if ((value = strchr(curropt, '=')) != NULL)
+                       *value++ = '\0';
+
+               if (!strcmp(curropt, "creator")) {
+                       if (!fill_fourchar(&(results->creator), value)) {
+                               printk("HFS+-fs: creator requires a 4 character value\n");
+                               return 0;
+                       }
+               } else if (!strcmp(curropt, "type")) {
+                       if (!fill_fourchar(&(results->type), value)) {
+                               printk("HFS+-fs: type requires a 4 character value\n");
+                               return 0;
+                       }
+               } else if (!strcmp(curropt, "umask")) {
+                       if (!fill_int(&tmp, value, 8)) {
+                               printk("HFS+-fs: umask requires a value\n");
+                               return 0;
+                       }
+                       results->umask = (umode_t)tmp;
+               } else if (!strcmp(curropt, "uid")) {
+                       if (!fill_int(&tmp, value, 0)) {
+                               printk("HFS+-fs: uid requires an argument\n");
+                               return 0;
+                       }
+                       results->uid = (uid_t)tmp;
+               } else if (!strcmp(curropt, "gid")) {
+                       if (!fill_int(&tmp, value, 0)) {
+                               printk("HFS+-fs: gid requires an argument\n");
+                               return 0;
+                       }
+                       results->gid = (gid_t)tmp;
+               } else if (!strcmp(curropt, "part")) {
+                       if (!fill_int(&results->part, value, 0)) {
+                               printk("HFS+-fs: part requires an argument\n");
+                               return 0;
+                       }
+               } else if (!strcmp(curropt, "session")) {
+                       if (!fill_int(&results->session, value, 0)) {
+                               printk("HFS+-fs: session requires an argument\n");
+                               return 0;
+                       }
+               } else {
+                       printk("HFS+-fs: unknown option %s\n", curropt);
+                       return 0;
+               }
+       }
+
+       return 1;
+}
diff --git a/fs/hfsplus/part_tbl.c b/fs/hfsplus/part_tbl.c
new file mode 100644 (file)
index 0000000..3bc137f
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * linux/fs/hfs/part_tbl.c
+ *
+ * Copyright (C) 1996-1997  Paul H. Hargrove
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * Original code to handle the new style Mac partition table based on
+ * a patch contributed by Holger Schemel (aeglos@valinor.owl.de).
+ *
+ * In function preconditions the term "valid" applied to a pointer to
+ * a structure means that the pointer is non-NULL and the structure it
+ * points to has all fields initialized to consistent values.
+ *
+ */
+
+#include "hfsplus_fs.h"
+
+/* offsets to various blocks */
+#define HFS_DD_BLK             0 /* Driver Descriptor block */
+#define HFS_PMAP_BLK           1 /* First block of partition map */
+#define HFS_MDB_BLK            2 /* Block (w/i partition) of MDB */
+
+/* magic numbers for various disk blocks */
+#define HFS_DRVR_DESC_MAGIC    0x4552 /* "ER": driver descriptor map */
+#define HFS_OLD_PMAP_MAGIC     0x5453 /* "TS": old-type partition map */
+#define HFS_NEW_PMAP_MAGIC     0x504D /* "PM": new-type partition map */
+#define HFS_SUPER_MAGIC                0x4244 /* "BD": HFS MDB (super block) */
+#define HFS_MFS_SUPER_MAGIC    0xD2D7 /* MFS MDB (super block) */
+
+/*
+ * The new style Mac partition map
+ *
+ * For each partition on the media there is a physical block (512-byte
+ * block) containing one of these structures.  These blocks are
+ * contiguous starting at block 1.
+ */
+struct new_pmap {
+       u16     pmSig;          /* signature */
+       u16     reSigPad;       /* padding */
+       u32     pmMapBlkCnt;    /* partition blocks count */
+       u32     pmPyPartStart;  /* physical block start of partition */
+       u32     pmPartBlkCnt;   /* physical block count of partition */
+       u8      pmPartName[32]; /* (null terminated?) string
+                                  giving the name of this
+                                  partition */
+       u8      pmPartType[32]; /* (null terminated?) string
+                                  giving the type of this
+                                  partition */
+       /* a bunch more stuff we don't need */
+} __packed;
+
+/*
+ * The old style Mac partition map
+ *
+ * The partition map consists for a 2-byte signature followed by an
+ * array of these structures.  The map is terminated with an all-zero
+ * one of these.
+ */
+struct old_pmap {
+       u16             pdSig;  /* Signature bytes */
+       struct  old_pmap_entry {
+               u32     pdStart;
+               u32     pdSize;
+               u32     pdFSID;
+       }       pdEntry[42];
+} __packed;
+
+/*
+ * hfs_part_find()
+ *
+ * Parse the partition map looking for the
+ * start and length of the 'part'th HFS partition.
+ */
+int hfs_part_find(struct super_block *sb,
+                 sector_t *part_start, sector_t *part_size)
+{
+       struct buffer_head *bh;
+       u16 *data;
+       int i, size, res;
+
+       res = -ENOENT;
+       bh = sb_bread512(sb, *part_start + HFS_PMAP_BLK, data);
+       if (!bh)
+               return -EIO;
+
+       switch (be16_to_cpu(*data)) {
+       case HFS_OLD_PMAP_MAGIC:
+         {
+               struct old_pmap *pm;
+               struct old_pmap_entry *p;
+
+               pm = (struct old_pmap *)bh->b_data;
+               p = pm->pdEntry;
+               size = 42;
+               for (i = 0; i < size; p++, i++) {
+                       if (p->pdStart && p->pdSize &&
+                           p->pdFSID == cpu_to_be32(0x54465331)/*"TFS1"*/ &&
+                           (HFSPLUS_SB(sb).part < 0 || HFSPLUS_SB(sb).part == i)) {
+                               *part_start += be32_to_cpu(p->pdStart);
+                               *part_size = be32_to_cpu(p->pdSize);
+                               res = 0;
+                       }
+               }
+               break;
+         }
+       case HFS_NEW_PMAP_MAGIC:
+         {
+               struct new_pmap *pm;
+
+               pm = (struct new_pmap *)bh->b_data;
+               size = be32_to_cpu(pm->pmMapBlkCnt);
+               for (i = 0; i < size;) {
+                       if (!memcmp(pm->pmPartType,"Apple_HFS", 9) &&
+                           (HFSPLUS_SB(sb).part < 0 || HFSPLUS_SB(sb).part == i)) {
+                               *part_start += be32_to_cpu(pm->pmPyPartStart);
+                               *part_size = be32_to_cpu(pm->pmPartBlkCnt);
+                               res = 0;
+                               break;
+                       }
+                       brelse(bh);
+                       bh = sb_bread512(sb, *part_start + HFS_PMAP_BLK + ++i, pm);
+                       if (!bh)
+                               return -EIO;
+                       if (pm->pmSig != cpu_to_be16(HFS_NEW_PMAP_MAGIC))
+                               break;
+               }
+               break;
+         }
+       }
+       brelse(bh);
+
+       return res;
+}
diff --git a/fs/hfsplus/super.c b/fs/hfsplus/super.c
new file mode 100644 (file)
index 0000000..3040a75
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ *  linux/fs/hfsplus/super.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/vfs.h>
+
+static struct inode *hfsplus_alloc_inode(struct super_block *sb);
+static void hfsplus_destroy_inode(struct inode *inode);
+
+#include "hfsplus_fs.h"
+
+void hfsplus_inode_check(struct super_block *sb)
+{
+#if 0
+       u32 cnt = atomic_read(&HFSPLUS_SB(sb).inode_cnt);
+       u32 last_cnt = HFSPLUS_SB(sb).last_inode_cnt;
+
+       if (cnt <= (last_cnt / 2) ||
+           cnt >= (last_cnt * 2)) {
+               HFSPLUS_SB(sb).last_inode_cnt = cnt;
+               printk("inode_check: %u,%u,%u\n", cnt, last_cnt,
+                       HFSPLUS_SB(sb).cat_tree ? HFSPLUS_SB(sb).cat_tree->node_hash_cnt : 0);
+       }
+#endif
+}
+
+static void hfsplus_read_inode(struct inode *inode)
+{
+       struct hfs_find_data fd;
+       struct hfsplus_vh *vhdr;
+       int err;
+
+       atomic_inc(&HFSPLUS_SB(inode->i_sb).inode_cnt);
+       hfsplus_inode_check(inode->i_sb);
+       INIT_LIST_HEAD(&HFSPLUS_I(inode).open_dir_list);
+       init_MUTEX(&HFSPLUS_I(inode).extents_lock);
+       HFSPLUS_I(inode).flags = 0;
+       HFSPLUS_I(inode).rsrc_inode = NULL;
+
+       if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
+       read_inode:
+               hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
+               err = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
+               if (!err)
+                       err = hfsplus_cat_read_inode(inode, &fd);
+               hfs_find_exit(&fd);
+               if (err)
+                       goto bad_inode;
+               return;
+       }
+       vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
+       switch(inode->i_ino) {
+       case HFSPLUS_ROOT_CNID:
+               goto read_inode;
+       case HFSPLUS_EXT_CNID:
+               hfsplus_inode_read_fork(inode, &vhdr->ext_file);
+               inode->i_mapping->a_ops = &hfsplus_btree_aops;
+               break;
+       case HFSPLUS_CAT_CNID:
+               hfsplus_inode_read_fork(inode, &vhdr->cat_file);
+               inode->i_mapping->a_ops = &hfsplus_btree_aops;
+               break;
+       case HFSPLUS_ALLOC_CNID:
+               hfsplus_inode_read_fork(inode, &vhdr->alloc_file);
+               inode->i_mapping->a_ops = &hfsplus_aops;
+               break;
+       case HFSPLUS_START_CNID:
+               hfsplus_inode_read_fork(inode, &vhdr->start_file);
+               break;
+       case HFSPLUS_ATTR_CNID:
+               hfsplus_inode_read_fork(inode, &vhdr->attr_file);
+               inode->i_mapping->a_ops = &hfsplus_btree_aops;
+               break;
+       default:
+               goto bad_inode;
+       }
+
+       return;
+
+ bad_inode:
+       make_bad_inode(inode);
+}
+
+void hfsplus_write_inode(struct inode *inode, int unused)
+{
+       struct hfsplus_vh *vhdr;
+
+       dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
+       hfsplus_ext_write_extent(inode);
+       if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
+               hfsplus_cat_write_inode(inode);
+               return;
+       }
+       vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
+       switch (inode->i_ino) {
+       case HFSPLUS_ROOT_CNID:
+               hfsplus_cat_write_inode(inode);
+               break;
+       case HFSPLUS_EXT_CNID:
+               if (vhdr->ext_file.total_size != cpu_to_be64(inode->i_size)) {
+                       HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+                       inode->i_sb->s_dirt = 1;
+               }
+               hfsplus_inode_write_fork(inode, &vhdr->ext_file);
+               hfs_btree_write(HFSPLUS_SB(inode->i_sb).ext_tree);
+               break;
+       case HFSPLUS_CAT_CNID:
+               if (vhdr->cat_file.total_size != cpu_to_be64(inode->i_size)) {
+                       HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+                       inode->i_sb->s_dirt = 1;
+               }
+               hfsplus_inode_write_fork(inode, &vhdr->cat_file);
+               hfs_btree_write(HFSPLUS_SB(inode->i_sb).cat_tree);
+               break;
+       case HFSPLUS_ALLOC_CNID:
+               if (vhdr->alloc_file.total_size != cpu_to_be64(inode->i_size)) {
+                       HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+                       inode->i_sb->s_dirt = 1;
+               }
+               hfsplus_inode_write_fork(inode, &vhdr->alloc_file);
+               break;
+       case HFSPLUS_START_CNID:
+               if (vhdr->start_file.total_size != cpu_to_be64(inode->i_size)) {
+                       HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+                       inode->i_sb->s_dirt = 1;
+               }
+               hfsplus_inode_write_fork(inode, &vhdr->start_file);
+               break;
+       case HFSPLUS_ATTR_CNID:
+               if (vhdr->attr_file.total_size != cpu_to_be64(inode->i_size)) {
+                       HFSPLUS_SB(inode->i_sb).flags |= HFSPLUS_SB_WRITEBACKUP;
+                       inode->i_sb->s_dirt = 1;
+               }
+               hfsplus_inode_write_fork(inode, &vhdr->attr_file);
+               hfs_btree_write(HFSPLUS_SB(inode->i_sb).attr_tree);
+               break;
+       }
+}
+
+static void hfsplus_clear_inode(struct inode *inode)
+{
+       dprint(DBG_INODE, "hfsplus_clear_inode: %lu\n", inode->i_ino);
+       atomic_dec(&HFSPLUS_SB(inode->i_sb).inode_cnt);
+       if (HFSPLUS_IS_RSRC(inode)) {
+               HFSPLUS_I(HFSPLUS_I(inode).rsrc_inode).rsrc_inode = NULL;
+               iput(HFSPLUS_I(inode).rsrc_inode);
+       }
+       hfsplus_inode_check(inode->i_sb);
+}
+
+static void hfsplus_write_super(struct super_block *sb)
+{
+       struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
+
+       dprint(DBG_SUPER, "hfsplus_write_super\n");
+       sb->s_dirt = 0;
+       if (sb->s_flags & MS_RDONLY)
+               /* warn? */
+               return;
+
+       vhdr->free_blocks = cpu_to_be32(HFSPLUS_SB(sb).free_blocks);
+       vhdr->next_alloc = cpu_to_be32(HFSPLUS_SB(sb).next_alloc);
+       vhdr->next_cnid = cpu_to_be32(HFSPLUS_SB(sb).next_cnid);
+       vhdr->folder_count = cpu_to_be32(HFSPLUS_SB(sb).folder_count);
+       vhdr->file_count = cpu_to_be32(HFSPLUS_SB(sb).file_count);
+
+       mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
+       if (HFSPLUS_SB(sb).flags & HFSPLUS_SB_WRITEBACKUP) {
+               if (HFSPLUS_SB(sb).sect_count) {
+                       struct buffer_head *bh;
+                       u32 block, offset;
+
+                       block = HFSPLUS_SB(sb).blockoffset;
+                       block += (HFSPLUS_SB(sb).sect_count - 2) >> (sb->s_blocksize_bits - 9);
+                       offset = ((HFSPLUS_SB(sb).sect_count - 2) << 9) & (sb->s_blocksize - 1);
+                       printk("backup: %u,%u,%u,%u\n", HFSPLUS_SB(sb).blockoffset,
+                               HFSPLUS_SB(sb).sect_count, block, offset);
+                       bh = sb_bread(sb, block);
+                       if (bh) {
+                               vhdr = (struct hfsplus_vh *)(bh->b_data + offset);
+                               if (be16_to_cpu(vhdr->signature) == HFSPLUS_VOLHEAD_SIG) {
+                                       memcpy(vhdr, HFSPLUS_SB(sb).s_vhdr, sizeof(*vhdr));
+                                       mark_buffer_dirty(bh);
+                                       brelse(bh);
+                               } else
+                                       printk("backup not found!\n");
+                       }
+               }
+               HFSPLUS_SB(sb).flags &= ~HFSPLUS_SB_WRITEBACKUP;
+       }
+}
+
+static void hfsplus_put_super(struct super_block *sb)
+{
+       dprint(DBG_SUPER, "hfsplus_put_super\n");
+       if (!(sb->s_flags & MS_RDONLY)) {
+               struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
+
+               vhdr->modify_date = hfsp_now2mt();
+               vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_UNMNT);
+               vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_INCNSTNT);
+               mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
+               ll_rw_block(WRITE, 1, &HFSPLUS_SB(sb).s_vhbh);
+               wait_on_buffer(HFSPLUS_SB(sb).s_vhbh);
+       }
+
+       hfs_btree_close(HFSPLUS_SB(sb).cat_tree);
+       hfs_btree_close(HFSPLUS_SB(sb).ext_tree);
+       iput(HFSPLUS_SB(sb).alloc_file);
+       iput(HFSPLUS_SB(sb).hidden_dir);
+       brelse(HFSPLUS_SB(sb).s_vhbh);
+}
+
+static int hfsplus_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+       buf->f_type = HFSPLUS_SUPER_MAGIC;
+       buf->f_bsize = sb->s_blocksize;
+       buf->f_blocks = HFSPLUS_SB(sb).total_blocks << HFSPLUS_SB(sb).fs_shift;
+       buf->f_bfree = HFSPLUS_SB(sb).free_blocks << HFSPLUS_SB(sb).fs_shift;
+       buf->f_bavail = buf->f_bfree;
+       buf->f_files = 0xFFFFFFFF;
+       buf->f_ffree = 0xFFFFFFFF - HFSPLUS_SB(sb).next_cnid;
+       buf->f_namelen = HFSPLUS_MAX_STRLEN;
+
+       return 0;
+}
+
+int hfsplus_remount(struct super_block *sb, int *flags, char *data)
+{
+       if ((*flags & MS_RDONLY) == (sb->s_flags & MS_RDONLY))
+               return 0;
+       if (!(*flags & MS_RDONLY)) {
+               struct hfsplus_vh *vhdr = HFSPLUS_SB(sb).s_vhdr;
+
+               if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_INCNSTNT)) ||
+                   !(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
+                       printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
+                              "running fsck.hfsplus is recommended.  leaving read-only.\n");
+                       sb->s_flags |= MS_RDONLY;
+                       *flags |= MS_RDONLY;
+               } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
+                       printk("HFS+-fs: Filesystem is marked locked, leaving read-only.\n");
+                       sb->s_flags |= MS_RDONLY;
+                       *flags |= MS_RDONLY;
+               }
+       }
+       return 0;
+}
+
+static struct super_operations hfsplus_sops = {
+       .alloc_inode    = hfsplus_alloc_inode,
+       .destroy_inode  = hfsplus_destroy_inode,
+       .read_inode     = hfsplus_read_inode,
+       .write_inode    = hfsplus_write_inode,
+       .clear_inode    = hfsplus_clear_inode,
+       .put_super      = hfsplus_put_super,
+       .write_super    = hfsplus_write_super,
+       .statfs         = hfsplus_statfs,
+       .remount_fs     = hfsplus_remount,
+};
+
+static int hfsplus_fill_super(struct super_block *sb, void *data, int silent)
+{
+       struct hfsplus_vh *vhdr;
+       struct hfsplus_sb_info *sbi;
+       hfsplus_cat_entry entry;
+       struct hfs_find_data fd;
+       struct qstr str;
+       int err = -EINVAL;
+
+       sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL);
+       if (!sbi) {
+               err = -ENOMEM;
+               goto out2;
+       }
+       memset(sbi, 0, sizeof(HFSPLUS_SB(sb)));
+       sb->s_fs_info = sbi;
+       INIT_HLIST_HEAD(&sbi->rsrc_inodes);
+       fill_defaults(sbi);
+       if (!parse_options(data, sbi)) {
+               if (!silent)
+                       printk("HFS+-fs: unable to parse mount options\n");
+               err = -EINVAL;
+               goto out2;
+       }
+
+       /* Grab the volume header */
+       if (hfsplus_read_wrapper(sb)) {
+               if (!silent)
+                       printk("HFS+-fs: unable to find HFS+ superblock\n");
+               err = -EINVAL;
+               goto out2;
+       }
+       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) {
+               if (!silent)
+                       printk("HFS+-fs: wrong filesystem version\n");
+               goto cleanup;
+       }
+       HFSPLUS_SB(sb).total_blocks = be32_to_cpu(vhdr->total_blocks);
+       HFSPLUS_SB(sb).free_blocks = be32_to_cpu(vhdr->free_blocks);
+       HFSPLUS_SB(sb).next_alloc = be32_to_cpu(vhdr->next_alloc);
+       HFSPLUS_SB(sb).next_cnid = be32_to_cpu(vhdr->next_cnid);
+       HFSPLUS_SB(sb).file_count = be32_to_cpu(vhdr->file_count);
+       HFSPLUS_SB(sb).folder_count = be32_to_cpu(vhdr->folder_count);
+       HFSPLUS_SB(sb).data_clump_blocks = be32_to_cpu(vhdr->data_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift;
+       if (!HFSPLUS_SB(sb).data_clump_blocks)
+               HFSPLUS_SB(sb).data_clump_blocks = 1;
+       HFSPLUS_SB(sb).rsrc_clump_blocks = be32_to_cpu(vhdr->rsrc_clump_sz) >> HFSPLUS_SB(sb).alloc_blksz_shift;
+       if (!HFSPLUS_SB(sb).rsrc_clump_blocks)
+               HFSPLUS_SB(sb).rsrc_clump_blocks = 1;
+
+       /* Set up operations so we can load metadata */
+       sb->s_op = &hfsplus_sops;
+       sb->s_maxbytes = MAX_LFS_FILESIZE;
+
+       if ((vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_INCNSTNT)) ||
+           !(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
+               if (!silent)
+                       printk("HFS+-fs warning: Filesystem was not cleanly unmounted, "
+                              "running fsck.hfsplus is recommended.  mounting read-only.\n");
+               sb->s_flags |= MS_RDONLY;
+       } else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
+               if (!silent)
+                       printk("HFS+-fs: Filesystem is marked locked, mounting read-only.\n");
+               sb->s_flags |= MS_RDONLY;
+       }
+
+       /* Load metadata objects (B*Trees) */
+       HFSPLUS_SB(sb).ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
+       if (!HFSPLUS_SB(sb).ext_tree) {
+               if (!silent)
+                       printk("HFS+-fs: failed to load extents file\n");
+               goto cleanup;
+       }
+       HFSPLUS_SB(sb).cat_tree = hfs_btree_open(sb, HFSPLUS_CAT_CNID);
+       if (!HFSPLUS_SB(sb).cat_tree) {
+               if (!silent)
+                       printk("HFS+-fs: failed to load catalog file\n");
+               goto cleanup;
+       }
+
+       HFSPLUS_SB(sb).alloc_file = iget(sb, HFSPLUS_ALLOC_CNID);
+       if (!HFSPLUS_SB(sb).alloc_file) {
+               if (!silent)
+                       printk("HFS+-fs: failed to load allocation file\n");
+               goto cleanup;
+       }
+
+       /* Load the root directory */
+       sb->s_root = d_alloc_root(iget(sb, HFSPLUS_ROOT_CNID));
+       if (!sb->s_root) {
+               if (!silent)
+                       printk("HFS+-fs: failed to load root directory\n");
+               goto cleanup;
+       }
+
+       str.len = sizeof(HFSP_HIDDENDIR_NAME) - 1;
+       str.name = HFSP_HIDDENDIR_NAME;
+       hfs_find_init(HFSPLUS_SB(sb).cat_tree, &fd);
+       hfsplus_cat_build_key(fd.search_key, HFSPLUS_ROOT_CNID, &str);
+       if (!hfs_brec_read(&fd, &entry, sizeof(entry))) {
+               hfs_find_exit(&fd);
+               if (entry.type != cpu_to_be16(HFSPLUS_FOLDER))
+                       goto cleanup;
+               HFSPLUS_SB(sb).hidden_dir = iget(sb, be32_to_cpu(entry.folder.id));
+               if (!HFSPLUS_SB(sb).hidden_dir)
+                       goto cleanup;
+       } else
+               hfs_find_exit(&fd);
+
+       if (sb->s_flags & MS_RDONLY)
+               goto out;
+
+       /* H+LX == hfsplusutils, H+Lx == this driver, H+lx is unused
+        * all three are registered with Apple for our use
+        */
+       vhdr->last_mount_vers = cpu_to_be32(HFSP_MOUNT_VERSION);
+       vhdr->modify_date = hfsp_now2mt();
+       vhdr->write_count = cpu_to_be32(be32_to_cpu(vhdr->write_count) + 1);
+       vhdr->attributes &= cpu_to_be32(~HFSPLUS_VOL_UNMNT);
+       vhdr->attributes |= cpu_to_be32(HFSPLUS_VOL_INCNSTNT);
+       mark_buffer_dirty(HFSPLUS_SB(sb).s_vhbh);
+       ll_rw_block(WRITE, 1, &HFSPLUS_SB(sb).s_vhbh);
+       wait_on_buffer(HFSPLUS_SB(sb).s_vhbh);
+
+       if (!HFSPLUS_SB(sb).hidden_dir) {
+               printk("HFS+: create hidden dir...\n");
+               HFSPLUS_SB(sb).hidden_dir = hfsplus_new_inode(sb, S_IFDIR);
+               hfsplus_create_cat(HFSPLUS_SB(sb).hidden_dir->i_ino, sb->s_root->d_inode,
+                                  &str, HFSPLUS_SB(sb).hidden_dir);
+               mark_inode_dirty(HFSPLUS_SB(sb).hidden_dir);
+       }
+out:
+       return 0;
+
+cleanup:
+       hfsplus_put_super(sb);
+out2:
+       return err;
+}
+
+MODULE_AUTHOR("Brad Boyer");
+MODULE_DESCRIPTION("Extended Macintosh Filesystem");
+MODULE_LICENSE("GPL");
+
+static kmem_cache_t *hfsplus_inode_cachep;
+
+static struct inode *hfsplus_alloc_inode(struct super_block *sb)
+{
+       struct hfsplus_inode_info *i;
+
+       i = kmem_cache_alloc(hfsplus_inode_cachep, SLAB_KERNEL);
+       return i ? &i->vfs_inode : NULL;
+}
+
+static void hfsplus_destroy_inode(struct inode *inode)
+{
+       kmem_cache_free(hfsplus_inode_cachep, &HFSPLUS_I(inode));
+}
+
+#define HFSPLUS_INODE_SIZE     sizeof(struct hfsplus_inode_info)
+
+static struct super_block *hfsplus_get_sb(struct file_system_type *fs_type,
+                                         int flags, const char *dev_name, void *data)
+{
+       return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super);
+}
+
+static struct file_system_type hfsplus_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "hfsplus",
+       .get_sb         = hfsplus_get_sb,
+       .kill_sb        = kill_block_super,
+       .fs_flags       = FS_REQUIRES_DEV,
+};
+
+static void hfsplus_init_once(void *p, kmem_cache_t *cachep, unsigned long flags)
+{
+       struct hfsplus_inode_info *i = p;
+
+       if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR)
+               inode_init_once(&i->vfs_inode);
+}
+
+static int __init init_hfsplus_fs(void)
+{
+       int err;
+
+       hfsplus_inode_cachep = kmem_cache_create("hfsplus_icache",
+               HFSPLUS_INODE_SIZE, 0, SLAB_HWCACHE_ALIGN,
+               hfsplus_init_once, NULL);
+       if (!hfsplus_inode_cachep)
+               return -ENOMEM;
+       err = register_filesystem(&hfsplus_fs_type);
+       if (err)
+               kmem_cache_destroy(hfsplus_inode_cachep);
+       return err;
+}
+
+static void __exit exit_hfsplus_fs(void)
+{
+       unregister_filesystem(&hfsplus_fs_type);
+       if (kmem_cache_destroy(hfsplus_inode_cachep))
+               printk(KERN_INFO "hfsplus_inode_cache: not all structures were freed\n");
+}
+
+module_init(init_hfsplus_fs)
+module_exit(exit_hfsplus_fs)
diff --git a/fs/hfsplus/tables.c b/fs/hfsplus/tables.c
new file mode 100644 (file)
index 0000000..ce2ed66
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * linux/fs/hfsplus/tables.c
+ *
+ * Various data tables
+ */
+
+#include "hfsplus_fs.h"
+
+/*
+ *  Unicode case folding table taken from Apple Technote #1150
+ *  (HFS Plus Volume Format)
+ */
+
+u16 case_fold_table[] = {
+/*
+ *  The lower case table consists of a 256-entry high-byte table followed by
+ *  some number of 256-entry subtables. The high-byte table contains either an
+ *  offset to the subtable for characters with that high byte or zero, which
+ *  means that there are no case mappings or ignored characters in that block.
+ *  Ignored characters are mapped to zero.
+ */
+
+    // High-byte indices ( == 0 iff no case mapping and no ignorables )
+
+
+    /* 0 */ 0x0100, 0x0200, 0x0000, 0x0300, 0x0400, 0x0500, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 1 */ 0x0600, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 2 */ 0x0700, 0x0800, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 3 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 4 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 5 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 6 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 7 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 8 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 9 */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* A */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* B */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* C */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* D */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* E */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* F */ 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+            0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0900, 0x0A00,
+
+    // Table 1 (for high byte 0x00)
+
+    /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+            0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
+    /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+            0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
+    /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+            0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
+    /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+            0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
+    /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+            0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+    /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+            0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
+    /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
+            0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
+    /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
+            0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
+    /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087,
+            0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
+    /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097,
+            0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
+    /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+            0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+            0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7,
+            0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+            0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF,
+    /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+            0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+            0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,
+
+    // Table 2 (for high byte 0x01)
+
+    /* 0 */ 0x0100, 0x0101, 0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107,
+            0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E, 0x010F,
+    /* 1 */ 0x0111, 0x0111, 0x0112, 0x0113, 0x0114, 0x0115, 0x0116, 0x0117,
+            0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F,
+    /* 2 */ 0x0120, 0x0121, 0x0122, 0x0123, 0x0124, 0x0125, 0x0127, 0x0127,
+            0x0128, 0x0129, 0x012A, 0x012B, 0x012C, 0x012D, 0x012E, 0x012F,
+    /* 3 */ 0x0130, 0x0131, 0x0133, 0x0133, 0x0134, 0x0135, 0x0136, 0x0137,
+            0x0138, 0x0139, 0x013A, 0x013B, 0x013C, 0x013D, 0x013E, 0x0140,
+    /* 4 */ 0x0140, 0x0142, 0x0142, 0x0143, 0x0144, 0x0145, 0x0146, 0x0147,
+            0x0148, 0x0149, 0x014B, 0x014B, 0x014C, 0x014D, 0x014E, 0x014F,
+    /* 5 */ 0x0150, 0x0151, 0x0153, 0x0153, 0x0154, 0x0155, 0x0156, 0x0157,
+            0x0158, 0x0159, 0x015A, 0x015B, 0x015C, 0x015D, 0x015E, 0x015F,
+    /* 6 */ 0x0160, 0x0161, 0x0162, 0x0163, 0x0164, 0x0165, 0x0167, 0x0167,
+            0x0168, 0x0169, 0x016A, 0x016B, 0x016C, 0x016D, 0x016E, 0x016F,
+    /* 7 */ 0x0170, 0x0171, 0x0172, 0x0173, 0x0174, 0x0175, 0x0176, 0x0177,
+            0x0178, 0x0179, 0x017A, 0x017B, 0x017C, 0x017D, 0x017E, 0x017F,
+    /* 8 */ 0x0180, 0x0253, 0x0183, 0x0183, 0x0185, 0x0185, 0x0254, 0x0188,
+            0x0188, 0x0256, 0x0257, 0x018C, 0x018C, 0x018D, 0x01DD, 0x0259,
+    /* 9 */ 0x025B, 0x0192, 0x0192, 0x0260, 0x0263, 0x0195, 0x0269, 0x0268,
+            0x0199, 0x0199, 0x019A, 0x019B, 0x026F, 0x0272, 0x019E, 0x0275,
+    /* A */ 0x01A0, 0x01A1, 0x01A3, 0x01A3, 0x01A5, 0x01A5, 0x01A6, 0x01A8,
+            0x01A8, 0x0283, 0x01AA, 0x01AB, 0x01AD, 0x01AD, 0x0288, 0x01AF,
+    /* B */ 0x01B0, 0x028A, 0x028B, 0x01B4, 0x01B4, 0x01B6, 0x01B6, 0x0292,
+            0x01B9, 0x01B9, 0x01BA, 0x01BB, 0x01BD, 0x01BD, 0x01BE, 0x01BF,
+    /* C */ 0x01C0, 0x01C1, 0x01C2, 0x01C3, 0x01C6, 0x01C6, 0x01C6, 0x01C9,
+            0x01C9, 0x01C9, 0x01CC, 0x01CC, 0x01CC, 0x01CD, 0x01CE, 0x01CF,
+    /* D */ 0x01D0, 0x01D1, 0x01D2, 0x01D3, 0x01D4, 0x01D5, 0x01D6, 0x01D7,
+            0x01D8, 0x01D9, 0x01DA, 0x01DB, 0x01DC, 0x01DD, 0x01DE, 0x01DF,
+    /* E */ 0x01E0, 0x01E1, 0x01E2, 0x01E3, 0x01E5, 0x01E5, 0x01E6, 0x01E7,
+            0x01E8, 0x01E9, 0x01EA, 0x01EB, 0x01EC, 0x01ED, 0x01EE, 0x01EF,
+    /* F */ 0x01F0, 0x01F3, 0x01F3, 0x01F3, 0x01F4, 0x01F5, 0x01F6, 0x01F7,
+            0x01F8, 0x01F9, 0x01FA, 0x01FB, 0x01FC, 0x01FD, 0x01FE, 0x01FF,
+
+    // Table 3 (for high byte 0x03)
+
+    /* 0 */ 0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307,
+            0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E, 0x030F,
+    /* 1 */ 0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317,
+            0x0318, 0x0319, 0x031A, 0x031B, 0x031C, 0x031D, 0x031E, 0x031F,
+    /* 2 */ 0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327,
+            0x0328, 0x0329, 0x032A, 0x032B, 0x032C, 0x032D, 0x032E, 0x032F,
+    /* 3 */ 0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337,
+            0x0338, 0x0339, 0x033A, 0x033B, 0x033C, 0x033D, 0x033E, 0x033F,
+    /* 4 */ 0x0340, 0x0341, 0x0342, 0x0343, 0x0344, 0x0345, 0x0346, 0x0347,
+            0x0348, 0x0349, 0x034A, 0x034B, 0x034C, 0x034D, 0x034E, 0x034F,
+    /* 5 */ 0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357,
+            0x0358, 0x0359, 0x035A, 0x035B, 0x035C, 0x035D, 0x035E, 0x035F,
+    /* 6 */ 0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367,
+            0x0368, 0x0369, 0x036A, 0x036B, 0x036C, 0x036D, 0x036E, 0x036F,
+    /* 7 */ 0x0370, 0x0371, 0x0372, 0x0373, 0x0374, 0x0375, 0x0376, 0x0377,
+            0x0378, 0x0379, 0x037A, 0x037B, 0x037C, 0x037D, 0x037E, 0x037F,
+    /* 8 */ 0x0380, 0x0381, 0x0382, 0x0383, 0x0384, 0x0385, 0x0386, 0x0387,
+            0x0388, 0x0389, 0x038A, 0x038B, 0x038C, 0x038D, 0x038E, 0x038F,
+    /* 9 */ 0x0390, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+            0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+    /* A */ 0x03C0, 0x03C1, 0x03A2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+            0x03C8, 0x03C9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+    /* B */ 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+            0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+    /* C */ 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+            0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x03CF,
+    /* D */ 0x03D0, 0x03D1, 0x03D2, 0x03D3, 0x03D4, 0x03D5, 0x03D6, 0x03D7,
+            0x03D8, 0x03D9, 0x03DA, 0x03DB, 0x03DC, 0x03DD, 0x03DE, 0x03DF,
+    /* E */ 0x03E0, 0x03E1, 0x03E3, 0x03E3, 0x03E5, 0x03E5, 0x03E7, 0x03E7,
+            0x03E9, 0x03E9, 0x03EB, 0x03EB, 0x03ED, 0x03ED, 0x03EF, 0x03EF,
+    /* F */ 0x03F0, 0x03F1, 0x03F2, 0x03F3, 0x03F4, 0x03F5, 0x03F6, 0x03F7,
+            0x03F8, 0x03F9, 0x03FA, 0x03FB, 0x03FC, 0x03FD, 0x03FE, 0x03FF,
+
+    // Table 4 (for high byte 0x04)
+
+    /* 0 */ 0x0400, 0x0401, 0x0452, 0x0403, 0x0454, 0x0455, 0x0456, 0x0407,
+            0x0458, 0x0459, 0x045A, 0x045B, 0x040C, 0x040D, 0x040E, 0x045F,
+    /* 1 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+            0x0438, 0x0419, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+    /* 2 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+            0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+    /* 3 */ 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+            0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+    /* 4 */ 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+            0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+    /* 5 */ 0x0450, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457,
+            0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x045D, 0x045E, 0x045F,
+    /* 6 */ 0x0461, 0x0461, 0x0463, 0x0463, 0x0465, 0x0465, 0x0467, 0x0467,
+            0x0469, 0x0469, 0x046B, 0x046B, 0x046D, 0x046D, 0x046F, 0x046F,
+    /* 7 */ 0x0471, 0x0471, 0x0473, 0x0473, 0x0475, 0x0475, 0x0476, 0x0477,
+            0x0479, 0x0479, 0x047B, 0x047B, 0x047D, 0x047D, 0x047F, 0x047F,
+    /* 8 */ 0x0481, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487,
+            0x0488, 0x0489, 0x048A, 0x048B, 0x048C, 0x048D, 0x048E, 0x048F,
+    /* 9 */ 0x0491, 0x0491, 0x0493, 0x0493, 0x0495, 0x0495, 0x0497, 0x0497,
+            0x0499, 0x0499, 0x049B, 0x049B, 0x049D, 0x049D, 0x049F, 0x049F,
+    /* A */ 0x04A1, 0x04A1, 0x04A3, 0x04A3, 0x04A5, 0x04A5, 0x04A7, 0x04A7,
+            0x04A9, 0x04A9, 0x04AB, 0x04AB, 0x04AD, 0x04AD, 0x04AF, 0x04AF,
+    /* B */ 0x04B1, 0x04B1, 0x04B3, 0x04B3, 0x04B5, 0x04B5, 0x04B7, 0x04B7,
+            0x04B9, 0x04B9, 0x04BB, 0x04BB, 0x04BD, 0x04BD, 0x04BF, 0x04BF,
+    /* C */ 0x04C0, 0x04C1, 0x04C2, 0x04C4, 0x04C4, 0x04C5, 0x04C6, 0x04C8,
+            0x04C8, 0x04C9, 0x04CA, 0x04CC, 0x04CC, 0x04CD, 0x04CE, 0x04CF,
+    /* D */ 0x04D0, 0x04D1, 0x04D2, 0x04D3, 0x04D4, 0x04D5, 0x04D6, 0x04D7,
+            0x04D8, 0x04D9, 0x04DA, 0x04DB, 0x04DC, 0x04DD, 0x04DE, 0x04DF,
+    /* E */ 0x04E0, 0x04E1, 0x04E2, 0x04E3, 0x04E4, 0x04E5, 0x04E6, 0x04E7,
+            0x04E8, 0x04E9, 0x04EA, 0x04EB, 0x04EC, 0x04ED, 0x04EE, 0x04EF,
+    /* F */ 0x04F0, 0x04F1, 0x04F2, 0x04F3, 0x04F4, 0x04F5, 0x04F6, 0x04F7,
+            0x04F8, 0x04F9, 0x04FA, 0x04FB, 0x04FC, 0x04FD, 0x04FE, 0x04FF,
+
+    // Table 5 (for high byte 0x05)
+
+    /* 0 */ 0x0500, 0x0501, 0x0502, 0x0503, 0x0504, 0x0505, 0x0506, 0x0507,
+            0x0508, 0x0509, 0x050A, 0x050B, 0x050C, 0x050D, 0x050E, 0x050F,
+    /* 1 */ 0x0510, 0x0511, 0x0512, 0x0513, 0x0514, 0x0515, 0x0516, 0x0517,
+            0x0518, 0x0519, 0x051A, 0x051B, 0x051C, 0x051D, 0x051E, 0x051F,
+    /* 2 */ 0x0520, 0x0521, 0x0522, 0x0523, 0x0524, 0x0525, 0x0526, 0x0527,
+            0x0528, 0x0529, 0x052A, 0x052B, 0x052C, 0x052D, 0x052E, 0x052F,
+    /* 3 */ 0x0530, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+            0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
+    /* 4 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
+            0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
+    /* 5 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0557,
+            0x0558, 0x0559, 0x055A, 0x055B, 0x055C, 0x055D, 0x055E, 0x055F,
+    /* 6 */ 0x0560, 0x0561, 0x0562, 0x0563, 0x0564, 0x0565, 0x0566, 0x0567,
+            0x0568, 0x0569, 0x056A, 0x056B, 0x056C, 0x056D, 0x056E, 0x056F,
+    /* 7 */ 0x0570, 0x0571, 0x0572, 0x0573, 0x0574, 0x0575, 0x0576, 0x0577,
+            0x0578, 0x0579, 0x057A, 0x057B, 0x057C, 0x057D, 0x057E, 0x057F,
+    /* 8 */ 0x0580, 0x0581, 0x0582, 0x0583, 0x0584, 0x0585, 0x0586, 0x0587,
+            0x0588, 0x0589, 0x058A, 0x058B, 0x058C, 0x058D, 0x058E, 0x058F,
+    /* 9 */ 0x0590, 0x0591, 0x0592, 0x0593, 0x0594, 0x0595, 0x0596, 0x0597,
+            0x0598, 0x0599, 0x059A, 0x059B, 0x059C, 0x059D, 0x059E, 0x059F,
+    /* A */ 0x05A0, 0x05A1, 0x05A2, 0x05A3, 0x05A4, 0x05A5, 0x05A6, 0x05A7,
+            0x05A8, 0x05A9, 0x05AA, 0x05AB, 0x05AC, 0x05AD, 0x05AE, 0x05AF,
+    /* B */ 0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
+            0x05B8, 0x05B9, 0x05BA, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
+    /* C */ 0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05C4, 0x05C5, 0x05C6, 0x05C7,
+            0x05C8, 0x05C9, 0x05CA, 0x05CB, 0x05CC, 0x05CD, 0x05CE, 0x05CF,
+    /* D */ 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+            0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+    /* E */ 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+            0x05E8, 0x05E9, 0x05EA, 0x05EB, 0x05EC, 0x05ED, 0x05EE, 0x05EF,
+    /* F */ 0x05F0, 0x05F1, 0x05F2, 0x05F3, 0x05F4, 0x05F5, 0x05F6, 0x05F7,
+            0x05F8, 0x05F9, 0x05FA, 0x05FB, 0x05FC, 0x05FD, 0x05FE, 0x05FF,
+
+    // Table 6 (for high byte 0x10)
+
+    /* 0 */ 0x1000, 0x1001, 0x1002, 0x1003, 0x1004, 0x1005, 0x1006, 0x1007,
+            0x1008, 0x1009, 0x100A, 0x100B, 0x100C, 0x100D, 0x100E, 0x100F,
+    /* 1 */ 0x1010, 0x1011, 0x1012, 0x1013, 0x1014, 0x1015, 0x1016, 0x1017,
+            0x1018, 0x1019, 0x101A, 0x101B, 0x101C, 0x101D, 0x101E, 0x101F,
+    /* 2 */ 0x1020, 0x1021, 0x1022, 0x1023, 0x1024, 0x1025, 0x1026, 0x1027,
+            0x1028, 0x1029, 0x102A, 0x102B, 0x102C, 0x102D, 0x102E, 0x102F,
+    /* 3 */ 0x1030, 0x1031, 0x1032, 0x1033, 0x1034, 0x1035, 0x1036, 0x1037,
+            0x1038, 0x1039, 0x103A, 0x103B, 0x103C, 0x103D, 0x103E, 0x103F,
+    /* 4 */ 0x1040, 0x1041, 0x1042, 0x1043, 0x1044, 0x1045, 0x1046, 0x1047,
+            0x1048, 0x1049, 0x104A, 0x104B, 0x104C, 0x104D, 0x104E, 0x104F,
+    /* 5 */ 0x1050, 0x1051, 0x1052, 0x1053, 0x1054, 0x1055, 0x1056, 0x1057,
+            0x1058, 0x1059, 0x105A, 0x105B, 0x105C, 0x105D, 0x105E, 0x105F,
+    /* 6 */ 0x1060, 0x1061, 0x1062, 0x1063, 0x1064, 0x1065, 0x1066, 0x1067,
+            0x1068, 0x1069, 0x106A, 0x106B, 0x106C, 0x106D, 0x106E, 0x106F,
+    /* 7 */ 0x1070, 0x1071, 0x1072, 0x1073, 0x1074, 0x1075, 0x1076, 0x1077,
+            0x1078, 0x1079, 0x107A, 0x107B, 0x107C, 0x107D, 0x107E, 0x107F,
+    /* 8 */ 0x1080, 0x1081, 0x1082, 0x1083, 0x1084, 0x1085, 0x1086, 0x1087,
+            0x1088, 0x1089, 0x108A, 0x108B, 0x108C, 0x108D, 0x108E, 0x108F,
+    /* 9 */ 0x1090, 0x1091, 0x1092, 0x1093, 0x1094, 0x1095, 0x1096, 0x1097,
+            0x1098, 0x1099, 0x109A, 0x109B, 0x109C, 0x109D, 0x109E, 0x109F,
+    /* A */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
+            0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
+    /* B */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
+            0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
+    /* C */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10C6, 0x10C7,
+            0x10C8, 0x10C9, 0x10CA, 0x10CB, 0x10CC, 0x10CD, 0x10CE, 0x10CF,
+    /* D */ 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10D7,
+            0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10DD, 0x10DE, 0x10DF,
+    /* E */ 0x10E0, 0x10E1, 0x10E2, 0x10E3, 0x10E4, 0x10E5, 0x10E6, 0x10E7,
+            0x10E8, 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10EF,
+    /* F */ 0x10F0, 0x10F1, 0x10F2, 0x10F3, 0x10F4, 0x10F5, 0x10F6, 0x10F7,
+            0x10F8, 0x10F9, 0x10FA, 0x10FB, 0x10FC, 0x10FD, 0x10FE, 0x10FF,
+
+    // Table 7 (for high byte 0x20)
+
+    /* 0 */ 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007,
+            0x2008, 0x2009, 0x200A, 0x200B, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 1 */ 0x2010, 0x2011, 0x2012, 0x2013, 0x2014, 0x2015, 0x2016, 0x2017,
+            0x2018, 0x2019, 0x201A, 0x201B, 0x201C, 0x201D, 0x201E, 0x201F,
+    /* 2 */ 0x2020, 0x2021, 0x2022, 0x2023, 0x2024, 0x2025, 0x2026, 0x2027,
+            0x2028, 0x2029, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x202F,
+    /* 3 */ 0x2030, 0x2031, 0x2032, 0x2033, 0x2034, 0x2035, 0x2036, 0x2037,
+            0x2038, 0x2039, 0x203A, 0x203B, 0x203C, 0x203D, 0x203E, 0x203F,
+    /* 4 */ 0x2040, 0x2041, 0x2042, 0x2043, 0x2044, 0x2045, 0x2046, 0x2047,
+            0x2048, 0x2049, 0x204A, 0x204B, 0x204C, 0x204D, 0x204E, 0x204F,
+    /* 5 */ 0x2050, 0x2051, 0x2052, 0x2053, 0x2054, 0x2055, 0x2056, 0x2057,
+            0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F,
+    /* 6 */ 0x2060, 0x2061, 0x2062, 0x2063, 0x2064, 0x2065, 0x2066, 0x2067,
+            0x2068, 0x2069, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    /* 7 */ 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077,
+            0x2078, 0x2079, 0x207A, 0x207B, 0x207C, 0x207D, 0x207E, 0x207F,
+    /* 8 */ 0x2080, 0x2081, 0x2082, 0x2083, 0x2084, 0x2085, 0x2086, 0x2087,
+            0x2088, 0x2089, 0x208A, 0x208B, 0x208C, 0x208D, 0x208E, 0x208F,
+    /* 9 */ 0x2090, 0x2091, 0x2092, 0x2093, 0x2094, 0x2095, 0x2096, 0x2097,
+            0x2098, 0x2099, 0x209A, 0x209B, 0x209C, 0x209D, 0x209E, 0x209F,
+    /* A */ 0x20A0, 0x20A1, 0x20A2, 0x20A3, 0x20A4, 0x20A5, 0x20A6, 0x20A7,
+            0x20A8, 0x20A9, 0x20AA, 0x20AB, 0x20AC, 0x20AD, 0x20AE, 0x20AF,
+    /* B */ 0x20B0, 0x20B1, 0x20B2, 0x20B3, 0x20B4, 0x20B5, 0x20B6, 0x20B7,
+            0x20B8, 0x20B9, 0x20BA, 0x20BB, 0x20BC, 0x20BD, 0x20BE, 0x20BF,
+    /* C */ 0x20C0, 0x20C1, 0x20C2, 0x20C3, 0x20C4, 0x20C5, 0x20C6, 0x20C7,
+            0x20C8, 0x20C9, 0x20CA, 0x20CB, 0x20CC, 0x20CD, 0x20CE, 0x20CF,
+    /* D */ 0x20D0, 0x20D1, 0x20D2, 0x20D3, 0x20D4, 0x20D5, 0x20D6, 0x20D7,
+            0x20D8, 0x20D9, 0x20DA, 0x20DB, 0x20DC, 0x20DD, 0x20DE, 0x20DF,
+    /* E */ 0x20E0, 0x20E1, 0x20E2, 0x20E3, 0x20E4, 0x20E5, 0x20E6, 0x20E7,
+            0x20E8, 0x20E9, 0x20EA, 0x20EB, 0x20EC, 0x20ED, 0x20EE, 0x20EF,
+    /* F */ 0x20F0, 0x20F1, 0x20F2, 0x20F3, 0x20F4, 0x20F5, 0x20F6, 0x20F7,
+            0x20F8, 0x20F9, 0x20FA, 0x20FB, 0x20FC, 0x20FD, 0x20FE, 0x20FF,
+
+    // Table 8 (for high byte 0x21)
+
+    /* 0 */ 0x2100, 0x2101, 0x2102, 0x2103, 0x2104, 0x2105, 0x2106, 0x2107,
+            0x2108, 0x2109, 0x210A, 0x210B, 0x210C, 0x210D, 0x210E, 0x210F,
+    /* 1 */ 0x2110, 0x2111, 0x2112, 0x2113, 0x2114, 0x2115, 0x2116, 0x2117,
+            0x2118, 0x2119, 0x211A, 0x211B, 0x211C, 0x211D, 0x211E, 0x211F,
+    /* 2 */ 0x2120, 0x2121, 0x2122, 0x2123, 0x2124, 0x2125, 0x2126, 0x2127,
+            0x2128, 0x2129, 0x212A, 0x212B, 0x212C, 0x212D, 0x212E, 0x212F,
+    /* 3 */ 0x2130, 0x2131, 0x2132, 0x2133, 0x2134, 0x2135, 0x2136, 0x2137,
+            0x2138, 0x2139, 0x213A, 0x213B, 0x213C, 0x213D, 0x213E, 0x213F,
+    /* 4 */ 0x2140, 0x2141, 0x2142, 0x2143, 0x2144, 0x2145, 0x2146, 0x2147,
+            0x2148, 0x2149, 0x214A, 0x214B, 0x214C, 0x214D, 0x214E, 0x214F,
+    /* 5 */ 0x2150, 0x2151, 0x2152, 0x2153, 0x2154, 0x2155, 0x2156, 0x2157,
+            0x2158, 0x2159, 0x215A, 0x215B, 0x215C, 0x215D, 0x215E, 0x215F,
+    /* 6 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
+            0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
+    /* 7 */ 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177,
+            0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F,
+    /* 8 */ 0x2180, 0x2181, 0x2182, 0x2183, 0x2184, 0x2185, 0x2186, 0x2187,
+            0x2188, 0x2189, 0x218A, 0x218B, 0x218C, 0x218D, 0x218E, 0x218F,
+    /* 9 */ 0x2190, 0x2191, 0x2192, 0x2193, 0x2194, 0x2195, 0x2196, 0x2197,
+            0x2198, 0x2199, 0x219A, 0x219B, 0x219C, 0x219D, 0x219E, 0x219F,
+    /* A */ 0x21A0, 0x21A1, 0x21A2, 0x21A3, 0x21A4, 0x21A5, 0x21A6, 0x21A7,
+            0x21A8, 0x21A9, 0x21AA, 0x21AB, 0x21AC, 0x21AD, 0x21AE, 0x21AF,
+    /* B */ 0x21B0, 0x21B1, 0x21B2, 0x21B3, 0x21B4, 0x21B5, 0x21B6, 0x21B7,
+            0x21B8, 0x21B9, 0x21BA, 0x21BB, 0x21BC, 0x21BD, 0x21BE, 0x21BF,
+    /* C */ 0x21C0, 0x21C1, 0x21C2, 0x21C3, 0x21C4, 0x21C5, 0x21C6, 0x21C7,
+            0x21C8, 0x21C9, 0x21CA, 0x21CB, 0x21CC, 0x21CD, 0x21CE, 0x21CF,
+    /* D */ 0x21D0, 0x21D1, 0x21D2, 0x21D3, 0x21D4, 0x21D5, 0x21D6, 0x21D7,
+            0x21D8, 0x21D9, 0x21DA, 0x21DB, 0x21DC, 0x21DD, 0x21DE, 0x21DF,
+    /* E */ 0x21E0, 0x21E1, 0x21E2, 0x21E3, 0x21E4, 0x21E5, 0x21E6, 0x21E7,
+            0x21E8, 0x21E9, 0x21EA, 0x21EB, 0x21EC, 0x21ED, 0x21EE, 0x21EF,
+    /* F */ 0x21F0, 0x21F1, 0x21F2, 0x21F3, 0x21F4, 0x21F5, 0x21F6, 0x21F7,
+            0x21F8, 0x21F9, 0x21FA, 0x21FB, 0x21FC, 0x21FD, 0x21FE, 0x21FF,
+
+    // Table 9 (for high byte 0xFE)
+
+    /* 0 */ 0xFE00, 0xFE01, 0xFE02, 0xFE03, 0xFE04, 0xFE05, 0xFE06, 0xFE07,
+            0xFE08, 0xFE09, 0xFE0A, 0xFE0B, 0xFE0C, 0xFE0D, 0xFE0E, 0xFE0F,
+    /* 1 */ 0xFE10, 0xFE11, 0xFE12, 0xFE13, 0xFE14, 0xFE15, 0xFE16, 0xFE17,
+            0xFE18, 0xFE19, 0xFE1A, 0xFE1B, 0xFE1C, 0xFE1D, 0xFE1E, 0xFE1F,
+    /* 2 */ 0xFE20, 0xFE21, 0xFE22, 0xFE23, 0xFE24, 0xFE25, 0xFE26, 0xFE27,
+            0xFE28, 0xFE29, 0xFE2A, 0xFE2B, 0xFE2C, 0xFE2D, 0xFE2E, 0xFE2F,
+    /* 3 */ 0xFE30, 0xFE31, 0xFE32, 0xFE33, 0xFE34, 0xFE35, 0xFE36, 0xFE37,
+            0xFE38, 0xFE39, 0xFE3A, 0xFE3B, 0xFE3C, 0xFE3D, 0xFE3E, 0xFE3F,
+    /* 4 */ 0xFE40, 0xFE41, 0xFE42, 0xFE43, 0xFE44, 0xFE45, 0xFE46, 0xFE47,
+            0xFE48, 0xFE49, 0xFE4A, 0xFE4B, 0xFE4C, 0xFE4D, 0xFE4E, 0xFE4F,
+    /* 5 */ 0xFE50, 0xFE51, 0xFE52, 0xFE53, 0xFE54, 0xFE55, 0xFE56, 0xFE57,
+            0xFE58, 0xFE59, 0xFE5A, 0xFE5B, 0xFE5C, 0xFE5D, 0xFE5E, 0xFE5F,
+    /* 6 */ 0xFE60, 0xFE61, 0xFE62, 0xFE63, 0xFE64, 0xFE65, 0xFE66, 0xFE67,
+            0xFE68, 0xFE69, 0xFE6A, 0xFE6B, 0xFE6C, 0xFE6D, 0xFE6E, 0xFE6F,
+    /* 7 */ 0xFE70, 0xFE71, 0xFE72, 0xFE73, 0xFE74, 0xFE75, 0xFE76, 0xFE77,
+            0xFE78, 0xFE79, 0xFE7A, 0xFE7B, 0xFE7C, 0xFE7D, 0xFE7E, 0xFE7F,
+    /* 8 */ 0xFE80, 0xFE81, 0xFE82, 0xFE83, 0xFE84, 0xFE85, 0xFE86, 0xFE87,
+            0xFE88, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C, 0xFE8D, 0xFE8E, 0xFE8F,
+    /* 9 */ 0xFE90, 0xFE91, 0xFE92, 0xFE93, 0xFE94, 0xFE95, 0xFE96, 0xFE97,
+            0xFE98, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C, 0xFE9D, 0xFE9E, 0xFE9F,
+    /* A */ 0xFEA0, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4, 0xFEA5, 0xFEA6, 0xFEA7,
+            0xFEA8, 0xFEA9, 0xFEAA, 0xFEAB, 0xFEAC, 0xFEAD, 0xFEAE, 0xFEAF,
+    /* B */ 0xFEB0, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4, 0xFEB5, 0xFEB6, 0xFEB7,
+            0xFEB8, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC, 0xFEBD, 0xFEBE, 0xFEBF,
+    /* C */ 0xFEC0, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4, 0xFEC5, 0xFEC6, 0xFEC7,
+            0xFEC8, 0xFEC9, 0xFECA, 0xFECB, 0xFECC, 0xFECD, 0xFECE, 0xFECF,
+    /* D */ 0xFED0, 0xFED1, 0xFED2, 0xFED3, 0xFED4, 0xFED5, 0xFED6, 0xFED7,
+            0xFED8, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC, 0xFEDD, 0xFEDE, 0xFEDF,
+    /* E */ 0xFEE0, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4, 0xFEE5, 0xFEE6, 0xFEE7,
+            0xFEE8, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC, 0xFEED, 0xFEEE, 0xFEEF,
+    /* F */ 0xFEF0, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4, 0xFEF5, 0xFEF6, 0xFEF7,
+            0xFEF8, 0xFEF9, 0xFEFA, 0xFEFB, 0xFEFC, 0xFEFD, 0xFEFE, 0x0000,
+
+    // Table 10 (for high byte 0xFF)
+
+    /* 0 */ 0xFF00, 0xFF01, 0xFF02, 0xFF03, 0xFF04, 0xFF05, 0xFF06, 0xFF07,
+            0xFF08, 0xFF09, 0xFF0A, 0xFF0B, 0xFF0C, 0xFF0D, 0xFF0E, 0xFF0F,
+    /* 1 */ 0xFF10, 0xFF11, 0xFF12, 0xFF13, 0xFF14, 0xFF15, 0xFF16, 0xFF17,
+            0xFF18, 0xFF19, 0xFF1A, 0xFF1B, 0xFF1C, 0xFF1D, 0xFF1E, 0xFF1F,
+    /* 2 */ 0xFF20, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
+            0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
+    /* 3 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
+            0xFF58, 0xFF59, 0xFF5A, 0xFF3B, 0xFF3C, 0xFF3D, 0xFF3E, 0xFF3F,
+    /* 4 */ 0xFF40, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47,
+            0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F,
+    /* 5 */ 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57,
+            0xFF58, 0xFF59, 0xFF5A, 0xFF5B, 0xFF5C, 0xFF5D, 0xFF5E, 0xFF5F,
+    /* 6 */ 0xFF60, 0xFF61, 0xFF62, 0xFF63, 0xFF64, 0xFF65, 0xFF66, 0xFF67,
+            0xFF68, 0xFF69, 0xFF6A, 0xFF6B, 0xFF6C, 0xFF6D, 0xFF6E, 0xFF6F,
+    /* 7 */ 0xFF70, 0xFF71, 0xFF72, 0xFF73, 0xFF74, 0xFF75, 0xFF76, 0xFF77,
+            0xFF78, 0xFF79, 0xFF7A, 0xFF7B, 0xFF7C, 0xFF7D, 0xFF7E, 0xFF7F,
+    /* 8 */ 0xFF80, 0xFF81, 0xFF82, 0xFF83, 0xFF84, 0xFF85, 0xFF86, 0xFF87,
+            0xFF88, 0xFF89, 0xFF8A, 0xFF8B, 0xFF8C, 0xFF8D, 0xFF8E, 0xFF8F,
+    /* 9 */ 0xFF90, 0xFF91, 0xFF92, 0xFF93, 0xFF94, 0xFF95, 0xFF96, 0xFF97,
+            0xFF98, 0xFF99, 0xFF9A, 0xFF9B, 0xFF9C, 0xFF9D, 0xFF9E, 0xFF9F,
+    /* A */ 0xFFA0, 0xFFA1, 0xFFA2, 0xFFA3, 0xFFA4, 0xFFA5, 0xFFA6, 0xFFA7,
+            0xFFA8, 0xFFA9, 0xFFAA, 0xFFAB, 0xFFAC, 0xFFAD, 0xFFAE, 0xFFAF,
+    /* B */ 0xFFB0, 0xFFB1, 0xFFB2, 0xFFB3, 0xFFB4, 0xFFB5, 0xFFB6, 0xFFB7,
+            0xFFB8, 0xFFB9, 0xFFBA, 0xFFBB, 0xFFBC, 0xFFBD, 0xFFBE, 0xFFBF,
+    /* C */ 0xFFC0, 0xFFC1, 0xFFC2, 0xFFC3, 0xFFC4, 0xFFC5, 0xFFC6, 0xFFC7,
+            0xFFC8, 0xFFC9, 0xFFCA, 0xFFCB, 0xFFCC, 0xFFCD, 0xFFCE, 0xFFCF,
+    /* D */ 0xFFD0, 0xFFD1, 0xFFD2, 0xFFD3, 0xFFD4, 0xFFD5, 0xFFD6, 0xFFD7,
+            0xFFD8, 0xFFD9, 0xFFDA, 0xFFDB, 0xFFDC, 0xFFDD, 0xFFDE, 0xFFDF,
+    /* E */ 0xFFE0, 0xFFE1, 0xFFE2, 0xFFE3, 0xFFE4, 0xFFE5, 0xFFE6, 0xFFE7,
+            0xFFE8, 0xFFE9, 0xFFEA, 0xFFEB, 0xFFEC, 0xFFED, 0xFFEE, 0xFFEF,
+    /* F */ 0xFFF0, 0xFFF1, 0xFFF2, 0xFFF3, 0xFFF4, 0xFFF5, 0xFFF6, 0xFFF7,
+            0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF,
+};
diff --git a/fs/hfsplus/unicode.c b/fs/hfsplus/unicode.c
new file mode 100644 (file)
index 0000000..4821cd2
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ *  linux/fs/hfsplus/unicode.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handler routines for unicode strings
+ */
+
+#include <linux/types.h>
+#include <linux/nls.h>
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Fold the case of a unicode char, given the 16 bit value */
+/* Returns folded char, or 0 if ignorable */
+static inline u16 case_fold(u16 c)
+{
+        u16 tmp;
+
+        tmp = case_fold_table[(c>>8)];
+        if (tmp)
+                tmp = case_fold_table[tmp + (c & 0xFF)];
+        else
+                tmp = c;
+        return tmp;
+}
+
+/* Compare unicode strings, return values like normal strcmp */
+int hfsplus_unistrcmp(const struct hfsplus_unistr *s1, const struct hfsplus_unistr *s2)
+{
+       u16 len1, len2, c1, c2;
+       const hfsplus_unichr *p1, *p2;
+
+       len1 = be16_to_cpu(s1->length);
+       len2 = be16_to_cpu(s2->length);
+       p1 = s1->unicode;
+       p2 = s2->unicode;
+
+       while (1) {
+               c1 = c2 = 0;
+
+               while (len1 && !c1) {
+                       c1 = case_fold(be16_to_cpu(*p1));
+                       p1++;
+                       len1--;
+               }
+               while (len2 && !c2) {
+                       c2 = case_fold(be16_to_cpu(*p2));
+                       p2++;
+                       len2--;
+               }
+
+               if (c1 != c2)
+                       return (c1 < c2) ? -1 : 1;
+               if (!c1 && !c2)
+                       return 0;
+       }
+}
+
+int hfsplus_uni2asc(const struct hfsplus_unistr *ustr, char *astr, int *len)
+{
+       const hfsplus_unichr *ip;
+       u8 *op;
+       u16 ustrlen, cc;
+       int size, tmp;
+
+       op = astr;
+       ip = ustr->unicode;
+       ustrlen = be16_to_cpu(ustr->length);
+       tmp = *len;
+       while (ustrlen > 0 && tmp > 0) {
+               cc = be16_to_cpu(*ip);
+               switch (cc) {
+               case 0:
+                       cc = 0x2400;
+                       break;
+               case '/':
+                       cc = ':';
+                       break;
+               }
+               if (cc > 0x7f) {
+                       size = utf8_wctomb(op, cc, tmp);
+                       if (size == -1) {
+                               /* ignore */
+                       } else {
+                               op += size;
+                               tmp -= size;
+                       }
+               } else {
+                       *op++ = (u8) cc;
+                       tmp--;
+               }
+               ip++;
+               ustrlen--;
+       }
+       *len = (char *)op - astr;
+       if (ustrlen)
+               return -ENAMETOOLONG;
+       return 0;
+}
+
+int hfsplus_asc2uni(struct hfsplus_unistr *ustr, const char *astr, int len)
+{
+       int tmp;
+       wchar_t c;
+       u16 outlen = 0;
+
+       while (outlen <= HFSPLUS_MAX_STRLEN && len > 0) {
+               if (*astr & 0x80) {
+                       tmp = utf8_mbtowc(&c, astr, len);
+                       if (tmp < 0) {
+                               astr++;
+                               len--;
+                               continue;
+                       } else {
+                               astr += tmp;
+                               len -= tmp;
+                       }
+               } else {
+                       c = *astr++;
+                       len--;
+               }
+               switch (c) {
+               case 0x2400:
+                       c = 0;
+                       break;
+               case ':':
+                       c = '/';
+                       break;
+               }
+               ustr->unicode[outlen] = cpu_to_be16(c);
+               outlen++;
+       }
+       ustr->length = cpu_to_be16(outlen);
+       if (len > 0)
+               return -ENAMETOOLONG;
+       return 0;
+}
diff --git a/fs/hfsplus/wrapper.c b/fs/hfsplus/wrapper.c
new file mode 100644 (file)
index 0000000..ca59522
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ *  linux/fs/hfsplus/wrapper.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handling of HFS wrappers around HFS+ volumes
+ */
+
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/cdrom.h>
+#include <linux/genhd.h>
+#include <linux/version.h>
+#include <asm/unaligned.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+struct hfsplus_wd {
+       u32 ablk_size;
+       u16 ablk_start;
+       u16 embed_start;
+       u16 embed_count;
+};
+
+static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
+{
+       u32 extent;
+       u16 attrib;
+
+       if (be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG)) != HFSPLUS_VOLHEAD_SIG)
+               return 0;
+
+       attrib = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ATTRIB));
+       if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) ||
+          !(attrib & HFSP_WRAP_ATTRIB_SPARED))
+               return 0;
+
+       wd->ablk_size = be32_to_cpu(*(u32 *)(bufptr + HFSP_WRAPOFF_ABLKSIZE));
+       if (wd->ablk_size < HFSPLUS_SECTOR_SIZE)
+               return 0;
+       if (wd->ablk_size % HFSPLUS_SECTOR_SIZE)
+               return 0;
+       wd->ablk_start = be16_to_cpu(*(u16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART));
+
+       extent = be32_to_cpu(get_unaligned((u32 *)(bufptr + HFSP_WRAPOFF_EMBEDEXT)));
+       wd->embed_start = (extent >> 16) & 0xFFFF;
+       wd->embed_count = extent & 0xFFFF;
+
+       return 1;
+}
+
+static int hfsplus_get_last_session(struct super_block *sb,
+                                   sector_t *start, sector_t *size)
+{
+       struct cdrom_multisession ms_info;
+       struct cdrom_tocentry te;
+       int res;
+
+       /* default values */
+       *start = 0;
+       *size = sb->s_bdev->bd_inode->i_size >> 9;
+
+       if (HFSPLUS_SB(sb).session >= 0) {
+               te.cdte_track = HFSPLUS_SB(sb).session;
+               te.cdte_format = CDROM_LBA;
+               res = ioctl_by_bdev(sb->s_bdev, CDROMREADTOCENTRY, (unsigned long)&te);
+               if (!res && (te.cdte_ctrl & CDROM_DATA_TRACK) == 4) {
+                       *start = (sector_t)te.cdte_addr.lba << 2;
+                       return 0;
+               }
+               printk(KERN_ERR "HFS: Invalid session number or type of track\n");
+               return -EINVAL;
+       }
+       ms_info.addr_format = CDROM_LBA;
+       res = ioctl_by_bdev(sb->s_bdev, CDROMMULTISESSION, (unsigned long)&ms_info);
+       if (!res && ms_info.xa_flag)
+               *start = (sector_t)ms_info.addr.lba << 2;
+       return 0;
+}
+
+/* Find the volume header and fill in some minimum bits in superblock */
+/* Takes in super block, returns true if good data read */
+int hfsplus_read_wrapper(struct super_block *sb)
+{
+       struct buffer_head *bh;
+       struct hfsplus_vh *vhdr;
+       struct hfsplus_wd wd;
+       sector_t part_start, part_size;
+       u32 blocksize;
+
+       blocksize = sb_min_blocksize(sb, HFSPLUS_SECTOR_SIZE);
+       if (!blocksize)
+               return -EINVAL;
+
+       if (hfsplus_get_last_session(sb, &part_start, &part_size))
+               return -EINVAL;
+       while (1) {
+               bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
+               if (!bh)
+                       return -EIO;
+
+               if (vhdr->signature == cpu_to_be16(HFSP_WRAP_MAGIC)) {
+                       if (!hfsplus_read_mdb(vhdr, &wd))
+                               goto error;
+                       wd.ablk_size >>= HFSPLUS_SECTOR_SHIFT;
+                       part_start += wd.ablk_start + wd.embed_start * wd.ablk_size;
+                       part_size = wd.embed_count * wd.ablk_size;
+                       brelse(bh);
+                       bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
+                       if (!bh)
+                               return -EIO;
+               }
+               if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIG))
+                       break;
+               brelse(bh);
+
+               /* check for a partition block
+                * (should do this only for cdrom/loop though)
+                */
+               if (hfs_part_find(sb, &part_start, &part_size))
+                       return -EINVAL;
+       }
+
+       blocksize = be32_to_cpu(vhdr->blocksize);
+       brelse(bh);
+
+       /* block size must be at least as large as a sector
+        * and a multiple of 2
+        */
+       if (blocksize < HFSPLUS_SECTOR_SIZE ||
+           ((blocksize - 1) & blocksize))
+               return -EINVAL;
+       HFSPLUS_SB(sb).alloc_blksz = blocksize;
+       HFSPLUS_SB(sb).alloc_blksz_shift = 0;
+       while (blocksize >>= 1)
+               HFSPLUS_SB(sb).alloc_blksz_shift++;
+       blocksize = min(HFSPLUS_SB(sb).alloc_blksz, (u32)PAGE_SIZE);
+
+       /* align block size to block offset */
+       while (part_start & ((blocksize >> HFSPLUS_SECTOR_SHIFT) - 1))
+               blocksize >>= 1;
+
+       if (sb_set_blocksize(sb, blocksize) != blocksize) {
+               printk("HFS+: unable to blocksize to %u!\n", blocksize);
+               return -EINVAL;
+       }
+
+       HFSPLUS_SB(sb).blockoffset = part_start >>
+                       (sb->s_blocksize_bits - HFSPLUS_SECTOR_SHIFT);
+       HFSPLUS_SB(sb).sect_count = part_size;
+       HFSPLUS_SB(sb).fs_shift = HFSPLUS_SB(sb).alloc_blksz_shift -
+                       sb->s_blocksize_bits;
+
+       bh = sb_bread512(sb, part_start + HFSPLUS_VOLHEAD_SECTOR, vhdr);
+       if (!bh)
+               return -EIO;
+
+       /* should still be the same... */
+       if (be16_to_cpu(vhdr->signature) != HFSPLUS_VOLHEAD_SIG)
+               goto error;
+       HFSPLUS_SB(sb).s_vhbh = bh;
+       HFSPLUS_SB(sb).s_vhdr = vhdr;
+
+       return 0;
+ error:
+       brelse(bh);
+       return -EINVAL;
+}