summaryrefslogtreecommitdiffstats
path: root/fs/hfs
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/hfs
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'fs/hfs')
-rw-r--r--fs/hfs/Makefile10
-rw-r--r--fs/hfs/attr.c121
-rw-r--r--fs/hfs/bfind.c210
-rw-r--r--fs/hfs/bitmap.c243
-rw-r--r--fs/hfs/bnode.c498
-rw-r--r--fs/hfs/brec.c496
-rw-r--r--fs/hfs/btree.c327
-rw-r--r--fs/hfs/btree.h168
-rw-r--r--fs/hfs/catalog.c347
-rw-r--r--fs/hfs/dir.c330
-rw-r--r--fs/hfs/extent.c527
-rw-r--r--fs/hfs/hfs.h287
-rw-r--r--fs/hfs/hfs_fs.h286
-rw-r--r--fs/hfs/inode.c636
-rw-r--r--fs/hfs/mdb.c343
-rw-r--r--fs/hfs/part_tbl.c117
-rw-r--r--fs/hfs/string.c115
-rw-r--r--fs/hfs/super.c395
-rw-r--r--fs/hfs/sysdep.c40
-rw-r--r--fs/hfs/trans.c72
20 files changed, 5568 insertions, 0 deletions
diff --git a/fs/hfs/Makefile b/fs/hfs/Makefile
new file mode 100644
index 000000000000..c41f5a85f42d
--- /dev/null
+++ b/fs/hfs/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux hfs filesystem routines.
+#
+
+obj-$(CONFIG_HFS_FS) += hfs.o
+
+hfs-objs := bitmap.o bfind.o bnode.o brec.o btree.o \
+ catalog.o dir.o extent.o inode.o attr.o mdb.o \
+ part_tbl.o string.o super.o sysdep.o trans.o
+
diff --git a/fs/hfs/attr.c b/fs/hfs/attr.c
new file mode 100644
index 000000000000..e057ec542a6a
--- /dev/null
+++ b/fs/hfs/attr.c
@@ -0,0 +1,121 @@
+/*
+ * linux/fs/hfs/attr.c
+ *
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Export hfs data via xattr
+ */
+
+
+#include <linux/fs.h>
+#include <linux/xattr.h>
+
+#include "hfs_fs.h"
+#include "btree.h"
+
+int hfs_setxattr(struct dentry *dentry, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct inode *inode = dentry->d_inode;
+ struct hfs_find_data fd;
+ hfs_cat_rec rec;
+ struct hfs_cat_file *file;
+ int res;
+
+ if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode))
+ return -EOPNOTSUPP;
+
+ res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd);
+ if (res)
+ return res;
+ fd.search_key->cat = HFS_I(inode)->cat_key;
+ res = hfs_brec_find(&fd);
+ if (res)
+ goto out;
+ hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
+ sizeof(struct hfs_cat_file));
+ file = &rec.file;
+
+ if (!strcmp(name, "hfs.type")) {
+ if (size == 4)
+ memcpy(&file->UsrWds.fdType, value, 4);
+ else
+ res = -ERANGE;
+ } else if (!strcmp(name, "hfs.creator")) {
+ if (size == 4)
+ memcpy(&file->UsrWds.fdCreator, value, 4);
+ else
+ res = -ERANGE;
+ } else
+ res = -EOPNOTSUPP;
+ if (!res)
+ hfs_bnode_write(fd.bnode, &rec, fd.entryoffset,
+ sizeof(struct hfs_cat_file));
+out:
+ hfs_find_exit(&fd);
+ return res;
+}
+
+ssize_t hfs_getxattr(struct dentry *dentry, const char *name,
+ void *value, size_t size)
+{
+ struct inode *inode = dentry->d_inode;
+ struct hfs_find_data fd;
+ hfs_cat_rec rec;
+ struct hfs_cat_file *file;
+ ssize_t res = 0;
+
+ if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode))
+ return -EOPNOTSUPP;
+
+ if (size) {
+ res = hfs_find_init(HFS_SB(inode->i_sb)->cat_tree, &fd);
+ if (res)
+ return res;
+ fd.search_key->cat = HFS_I(inode)->cat_key;
+ res = hfs_brec_find(&fd);
+ if (res)
+ goto out;
+ hfs_bnode_read(fd.bnode, &rec, fd.entryoffset,
+ sizeof(struct hfs_cat_file));
+ }
+ file = &rec.file;
+
+ if (!strcmp(name, "hfs.type")) {
+ if (size >= 4) {
+ memcpy(value, &file->UsrWds.fdType, 4);
+ res = 4;
+ } else
+ res = size ? -ERANGE : 4;
+ } else if (!strcmp(name, "hfs.creator")) {
+ if (size >= 4) {
+ memcpy(value, &file->UsrWds.fdCreator, 4);
+ res = 4;
+ } else
+ res = size ? -ERANGE : 4;
+ } else
+ res = -ENODATA;
+out:
+ if (size)
+ hfs_find_exit(&fd);
+ return res;
+}
+
+#define HFS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
+
+ssize_t hfs_listxattr(struct dentry *dentry, char *buffer, size_t size)
+{
+ struct inode *inode = dentry->d_inode;
+
+ if (!S_ISREG(inode->i_mode) || HFS_IS_RSRC(inode))
+ return -EOPNOTSUPP;
+
+ if (!buffer || !size)
+ return HFS_ATTRLIST_SIZE;
+ if (size < HFS_ATTRLIST_SIZE)
+ return -ERANGE;
+ strcpy(buffer, "hfs.type");
+ strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
+
+ return HFS_ATTRLIST_SIZE;
+}
diff --git a/fs/hfs/bfind.c b/fs/hfs/bfind.c
new file mode 100644
index 000000000000..89450ae32228
--- /dev/null
+++ b/fs/hfs/bfind.c
@@ -0,0 +1,210 @@
+/*
+ * linux/fs/hfs/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 "btree.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 nidx, parent;
+ __be32 data;
+ 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: 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/hfs/bitmap.c b/fs/hfs/bitmap.c
new file mode 100644
index 000000000000..24e75798ddf0
--- /dev/null
+++ b/fs/hfs/bitmap.c
@@ -0,0 +1,243 @@
+/*
+ * linux/fs/hfs/bitmap.c
+ *
+ * Copyright (C) 1996-1997 Paul H. Hargrove
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ * This file may be distributed under the terms of the GNU General Public License.
+ *
+ * Based on GPLed code Copyright (C) 1995 Michael Dreher
+ *
+ * This file contains the code to modify the volume bitmap:
+ * search/set/clear bits.
+ */
+
+#include "hfs_fs.h"
+
+/*
+ * hfs_find_zero_bit()
+ *
+ * Description:
+ * Given a block of memory, its length in bits, and a starting bit number,
+ * determine the number of the first zero bits (in left-to-right ordering)
+ * in that range.
+ *
+ * Returns >= 'size' if no zero bits are found in the range.
+ *
+ * Accesses memory in 32-bit aligned chunks of 32-bits and thus
+ * may read beyond the 'size'th bit.
+ */
+static u32 hfs_find_set_zero_bits(__be32 *bitmap, u32 size, u32 offset, u32 *max)
+{
+ __be32 *curr, *end;
+ u32 mask, start, len, n;
+ __be32 val;
+ int i;
+
+ len = *max;
+ if (!len)
+ return size;
+
+ curr = bitmap + (offset / 32);
+ end = bitmap + ((size + 31) / 32);
+
+ /* scan the first partial u32 for zero bits */
+ val = *curr;
+ if (~val) {
+ n = be32_to_cpu(val);
+ i = offset % 32;
+ mask = (1U << 31) >> i;
+ for (; i < 32; mask >>= 1, i++) {
+ if (!(n & mask))
+ goto found;
+ }
+ }
+
+ /* scan complete u32s for the first zero bit */
+ while (++curr < end) {
+ val = *curr;
+ if (~val) {
+ n = be32_to_cpu(val);
+ mask = 1 << 31;
+ for (i = 0; i < 32; mask >>= 1, i++) {
+ if (!(n & mask))
+ goto found;
+ }
+ }
+ }
+ return size;
+
+found:
+ start = (curr - bitmap) * 32 + i;
+ if (start >= size)
+ return start;
+ /* do any partial u32 at the start */
+ len = min(size - start, len);
+ while (1) {
+ n |= mask;
+ if (++i >= 32)
+ break;
+ mask >>= 1;
+ if (!--len || n & mask)
+ goto done;
+ }
+ if (!--len)
+ goto done;
+ *curr++ = cpu_to_be32(n);
+ /* do full u32s */
+ while (1) {
+ n = be32_to_cpu(*curr);
+ if (len < 32)
+ break;
+ if (n) {
+ len = 32;
+ break;
+ }
+ *curr++ = cpu_to_be32(0xffffffff);
+ len -= 32;
+ }
+ /* do any partial u32 at end */
+ mask = 1U << 31;
+ for (i = 0; i < len; i++) {
+ if (n & mask)
+ break;
+ n |= mask;
+ mask >>= 1;
+ }
+done:
+ *curr = cpu_to_be32(n);
+ *max = (curr - bitmap) * 32 + i - start;
+ return start;
+}
+
+/*
+ * hfs_vbm_search_free()
+ *
+ * Description:
+ * Search for 'num_bits' consecutive cleared bits in the bitmap blocks of
+ * the hfs MDB. 'mdb' had better be locked or the returned range
+ * may be no longer free, when this functions returns!
+ * XXX Currently the search starts from bit 0, but it should start with
+ * the bit number stored in 's_alloc_ptr' of the MDB.
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * u16 *num_bits: Pointer to the number of cleared bits
+ * to search for
+ * Output Variable(s):
+ * u16 *num_bits: The number of consecutive clear bits of the
+ * returned range. If the bitmap is fragmented, this will be less than
+ * requested and it will be zero, when the disk is full.
+ * Returns:
+ * The number of the first bit of the range of cleared bits which has been
+ * found. When 'num_bits' is zero, this is invalid!
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * 'num_bits' points to a variable of type (u16), which contains
+ * the number of cleared bits to find.
+ * Postconditions:
+ * 'num_bits' is set to the length of the found sequence.
+ */
+u32 hfs_vbm_search_free(struct super_block *sb, u32 goal, u32 *num_bits)
+{
+ void *bitmap;
+ u32 pos;
+
+ /* make sure we have actual work to perform */
+ if (!*num_bits)
+ return 0;
+
+ down(&HFS_SB(sb)->bitmap_lock);
+ bitmap = HFS_SB(sb)->bitmap;
+
+ pos = hfs_find_set_zero_bits(bitmap, HFS_SB(sb)->fs_ablocks, goal, num_bits);
+ if (pos >= HFS_SB(sb)->fs_ablocks) {
+ if (goal)
+ pos = hfs_find_set_zero_bits(bitmap, goal, 0, num_bits);
+ if (pos >= HFS_SB(sb)->fs_ablocks) {
+ *num_bits = pos = 0;
+ goto out;
+ }
+ }
+
+ dprint(DBG_BITMAP, "alloc_bits: %u,%u\n", pos, *num_bits);
+ HFS_SB(sb)->free_ablocks -= *num_bits;
+ hfs_bitmap_dirty(sb);
+out:
+ up(&HFS_SB(sb)->bitmap_lock);
+ return pos;
+}
+
+
+/*
+ * hfs_clear_vbm_bits()
+ *
+ * Description:
+ * Clear the requested bits in the volume bitmap of the hfs filesystem
+ * Input Variable(s):
+ * struct hfs_mdb *mdb: Pointer to the hfs MDB
+ * u16 start: The offset of the first bit
+ * u16 count: The number of bits
+ * Output Variable(s):
+ * None
+ * Returns:
+ * 0: no error
+ * -1: One of the bits was already clear. This is a strange
+ * error and when it happens, the filesystem must be repaired!
+ * -2: One or more of the bits are out of range of the bitmap.
+ * Preconditions:
+ * 'mdb' points to a "valid" (struct hfs_mdb).
+ * Postconditions:
+ * Starting with bit number 'start', 'count' bits in the volume bitmap
+ * are cleared. The affected bitmap blocks are marked "dirty", the free
+ * block count of the MDB is updated and the MDB is marked dirty.
+ */
+int hfs_clear_vbm_bits(struct super_block *sb, u16 start, u16 count)
+{
+ __be32 *curr;
+ u32 mask;
+ int i, len;
+
+ /* is there any actual work to be done? */
+ if (!count)
+ return 0;
+
+ dprint(DBG_BITMAP, "clear_bits: %u,%u\n", start, count);
+ /* are all of the bits in range? */
+ if ((start + count) > HFS_SB(sb)->fs_ablocks)
+ return -2;
+
+ down(&HFS_SB(sb)->bitmap_lock);
+ /* bitmap is always on a 32-bit boundary */
+ curr = HFS_SB(sb)->bitmap + (start / 32);
+ len = count;
+
+ /* do any partial u32 at the start */
+ i = start % 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 (count >= 32) {
+ *curr++ = 0;
+ count -= 32;
+ }
+ /* do any partial u32 at end */
+ if (count) {
+ mask = 0xffffffffU >> count;
+ *curr &= cpu_to_be32(mask);
+ }
+out:
+ HFS_SB(sb)->free_ablocks += len;
+ up(&HFS_SB(sb)->bitmap_lock);
+ hfs_bitmap_dirty(sb);
+
+ return 0;
+}
diff --git a/fs/hfs/bnode.c b/fs/hfs/bnode.c
new file mode 100644
index 000000000000..6ad1211f84ed
--- /dev/null
+++ b/fs/hfs/bnode.c
@@ -0,0 +1,498 @@
+/*
+ * linux/fs/hfs/bnode.c
+ *
+ * Copyright (C) 2001
+ * Brad Boyer (flar@allandria.com)
+ * (C) 2003 Ardis Technologies <roman@ardistech.com>
+ *
+ * Handle basic btree node operations
+ */
+
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+
+#include "btree.h"
+
+#define REF_PAGES 0
+
+void hfs_bnode_read(struct hfs_bnode *node, void *buf,
+ int off, int len)
+{
+ struct page *page;
+
+ off += node->page_offset;
+ page = node->page[0];
+
+ memcpy(buf, kmap(page) + off, len);
+ kunmap(page);
+}
+
+u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
+{
+ __be16 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 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_u8(node, off) + 1;
+ else
+ key_len = tree->max_key_len + 1;
+
+ hfs_bnode_read(node, key, off, key_len);
+}
+
+void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
+{
+ struct page *page;
+
+ off += node->page_offset;
+ page = node->page[0];
+
+ memcpy(kmap(page) + off, buf, len);
+ kunmap(page);
+ set_page_dirty(page);
+}
+
+void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
+{
+ __be16 v = cpu_to_be16(data);
+ // optimize later...
+ hfs_bnode_write(node, &v, off, 2);
+}
+
+void hfs_bnode_write_u8(struct hfs_bnode *node, int off, u8 data)
+{
+ // optimize later...
+ hfs_bnode_write(node, &data, off, 1);
+}
+
+void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
+{
+ struct page *page;
+
+ off += node->page_offset;
+ page = node->page[0];
+
+ memset(kmap(page) + off, 0, len);
+ kunmap(page);
+ set_page_dirty(page);
+}
+
+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;
+
+ 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[0];
+ dst_page = dst_node->page[0];
+
+ memcpy(kmap(dst_page) + dst, kmap(src_page) + src, len);
+ kunmap(src_page);
+ kunmap(dst_page);
+ set_page_dirty(dst_page);
+}
+
+void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
+{
+ struct page *page;
+ void *ptr;
+
+ dprint(DBG_BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
+ if (!len)
+ return;
+ src += node->page_offset;
+ dst += node->page_offset;
+ page = node->page[0];
+ ptr = kmap(page);
+ memmove(ptr + dst, ptr + src, len);
+ kunmap(page);
+ set_page_dirty(page);
+}
+
+void hfs_bnode_dump(struct hfs_bnode *node)
+{
+ struct hfs_bnode_desc desc;
+ __be32 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_u8(node, key_off) | 1) + 1;
+ else
+ tmp = node->tree->max_key_len + 1;
+ dprint(DBG_BNODE_MOD, " (%d,%d", tmp, hfs_bnode_read_u8(node, key_off));
+ 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_u8(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;
+ __be32 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: 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: 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;
+ block = off >> PAGE_CACHE_SHIFT;
+ node->page_offset = off & ~PAGE_CACHE_MASK;
+ for (i = 0; i < tree->pages_per_bnode; i++) {
+ page = read_cache_page(mapping, block++, (filler_t *)mapping->a_ops->readpage, NULL);
+ if (IS_ERR(page))
+ goto fail;
+ if (PageError(page)) {
+ page_cache_release(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));
+ if (test_bit(HFS_BNODE_ERROR, &node->flags))
+ goto node_error;
+ return node;
+ }
+ spin_unlock(&tree->hash_lock);
+ node = __hfs_bnode_create(tree, num);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+ if (test_bit(HFS_BNODE_ERROR, &node->flags))
+ goto node_error;
+ 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_u8(node, off) + 1;
+ 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)
+ BUG();
+ node = __hfs_bnode_create(tree, num);
+ if (!node)
+ return ERR_PTR(-ENOMEM);
+ if (test_bit(HFS_BNODE_ERROR, &node->flags)) {
+ hfs_bnode_put(node);
+ return ERR_PTR(-EIO);
+ }
+
+ 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->p