aboutsummaryrefslogtreecommitdiff
path: root/fs/hfsplus
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/hfsplus
Linux-2.6.12-rc2v2.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/hfsplus')
-rw-r--r--fs/hfsplus/Makefile9
-rw-r--r--fs/hfsplus/bfind.c210
-rw-r--r--fs/hfsplus/bitmap.c221
-rw-r--r--fs/hfsplus/bnode.c662
-rw-r--r--fs/hfsplus/brec.c491
-rw-r--r--fs/hfsplus/btree.c319
-rw-r--r--fs/hfsplus/catalog.c358
-rw-r--r--fs/hfsplus/dir.c484
-rw-r--r--fs/hfsplus/extents.c505
-rw-r--r--fs/hfsplus/hfsplus_fs.h414
-rw-r--r--fs/hfsplus/hfsplus_raw.h326
-rw-r--r--fs/hfsplus/inode.c555
-rw-r--r--fs/hfsplus/ioctl.c188
-rw-r--r--fs/hfsplus/options.c162
-rw-r--r--fs/hfsplus/part_tbl.c133
-rw-r--r--fs/hfsplus/super.c502
-rw-r--r--fs/hfsplus/tables.c3245
-rw-r--r--fs/hfsplus/unicode.c271
-rw-r--r--fs/hfsplus/wrapper.c171
19 files changed, 9226 insertions, 0 deletions
diff --git a/fs/hfsplus/Makefile b/fs/hfsplus/Makefile
new file mode 100644
index 00000000000..3cc0df73015
--- /dev/null
+++ b/fs/hfsplus/Makefile
@@ -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
index 00000000000..257cdde0514
--- /dev/null
+++ b/fs/hfsplus/bfind.c
@@ -0,0 +1,210 @@
+/*
+ * 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 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+-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
index 00000000000..c7d316455fa
--- /dev/null
+++ b/fs/hfsplus/bitmap.c
@@ -0,0 +1,221 @@
+/*
+ * 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;
+ __be32 *pptr, *curr, *end;
+ u32 mask, start, len, n;
+ __be32 val;
+ 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) {
+ n = be32_to_cpu(val);
+ mask = (1U << 31) >> i;
+ for (; i < 32; mask >>= 1, i++) {
+ if (!(n & mask))
+ goto found;
+ }
+ }
+ curr++;
+
+ /* scan complete u32s for the first zero bit */
+ while (1) {
+ 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;
+ }
+ }
+ 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) {
+ 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) {
+ while (curr < end) {
+ n = be32_to_cpu(*curr);
+ if (len < 32)
+ goto last;
+ if (n) {
+ len = 32;
+ goto last;
+ }
+ *curr++ = cpu_to_be32(0xffffffff);
+ 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 (n & mask)
+ break;
+ n |= mask;
+ mask >>= 1;
+ }
+done:
+ *curr = cpu_to_be32(n);
+ 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;
+ __be32 *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
index 00000000000..267872e84d7
--- /dev/null
+++ b/fs/hfsplus/bnode.c
@@ -0,0 +1,662 @@
+/*
+ * 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) != 0) {
+ 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)
+{
+ __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_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) != 0) {
+ 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)
+{
+ __be16 v = cpu_to_be16(data);
+ // optimize later...
+ hfs_bnode_write(node, &v, 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) != 0) {
+ 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) != 0) {
+ 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) != 0) {
+ 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;
+ __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_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;
+ __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+-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 (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_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);
+ 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->page[i]);
+ }
+#endif
+ dprint(DBG_BNODE_REFS, "get_node(%d:%d): %d\n",
+ node->tree->cnid, node->this, atomic_read(&node->refcnt));