DESC HFS+ support EDESC From: Roman Zippel This driver adds full read/write support for HFS+ and is based on the readonly driver by Brad Broyer. Thanks to Ethan Benson for a number of patches to make the driver more compliant with the spec. --- fs/Kconfig | 12 fs/Makefile | 1 fs/hfsplus/Makefile | 9 fs/hfsplus/bfind.c | 209 ++++++++++++++ fs/hfsplus/bitmap.c | 220 +++++++++++++++ fs/hfsplus/bnode.c | 661 +++++++++++++++++++++++++++++++++++++++++++++++ fs/hfsplus/brec.c | 487 ++++++++++++++++++++++++++++++++++ fs/hfsplus/btree.c | 319 ++++++++++++++++++++++ fs/hfsplus/catalog.c | 344 ++++++++++++++++++++++++ fs/hfsplus/dir.c | 471 +++++++++++++++++++++++++++++++++ fs/hfsplus/extents.c | 502 +++++++++++++++++++++++++++++++++++ fs/hfsplus/hfsplus_fs.h | 412 +++++++++++++++++++++++++++++ fs/hfsplus/hfsplus_raw.h | 326 +++++++++++++++++++++++ fs/hfsplus/inode.c | 549 +++++++++++++++++++++++++++++++++++++++ fs/hfsplus/ioctl.c | 82 +++++ fs/hfsplus/options.c | 127 +++++++++ fs/hfsplus/part_tbl.c | 133 +++++++++ fs/hfsplus/super.c | 485 ++++++++++++++++++++++++++++++++++ fs/hfsplus/tables.c | 408 +++++++++++++++++++++++++++++ fs/hfsplus/unicode.c | 140 +++++++++ fs/hfsplus/wrapper.c | 171 ++++++++++++ 21 files changed, 6068 insertions(+) diff -puN /dev/null fs/hfsplus/bfind.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/bfind.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,209 @@ +/* + * linux/fs/hfsplus/bfind.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Search routines for btrees + */ + +#include +#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 -puN /dev/null fs/hfsplus/bitmap.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/bitmap.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,220 @@ +/* + * linux/fs/hfsplus/bitmap.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Handling of allocation file + */ + +#include + +#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 -puN /dev/null fs/hfsplus/bnode.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/bnode.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,661 @@ +/* + * linux/fs/hfsplus/bnode.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Handle basic btree node operations + */ + +#include +#include +#include +#include +#include +#include + +#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 -puN /dev/null fs/hfsplus/brec.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/brec.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,487 @@ +/* + * linux/fs/hfsplus/brec.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * 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 -puN /dev/null fs/hfsplus/btree.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/btree.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,319 @@ +/* + * linux/fs/hfsplus/btree.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Handle opening/closing btree + */ + +#include +#include + +#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 -puN /dev/null fs/hfsplus/catalog.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/catalog.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,344 @@ +/* + * linux/fs/hfsplus/catalog.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Handling of catalog records + */ + +#include + +#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 -puN /dev/null fs/hfsplus/dir.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/dir.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,471 @@ +/* + * linux/fs/hfsplus/dir.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Handling of directories + */ + +#include +#include +#include +#include +#include +#include + +#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 -puN /dev/null fs/hfsplus/extents.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/extents.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,502 @@ +/* + * linux/fs/hfsplus/extents.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Handling of Extents both in catalog and extents overflow trees + */ + +#include +#include +#include +#include + +#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 -puN /dev/null fs/hfsplus/hfsplus_fs.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/hfsplus_fs.h 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,412 @@ +/* + * linux/include/linux/hfsplus_fs.h + * + * Copyright (C) 1999 + * Brad Boyer (flar@pants.nu) + * (C) 2003 Ardis Technologies + * + */ + +#ifndef _LINUX_HFSPLUS_FS_H +#define _LINUX_HFSPLUS_FS_H + +#include +#include +#include +#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 -puN /dev/null fs/hfsplus/hfsplus_raw.h --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/hfsplus_raw.h 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,326 @@ +/* + * linux/include/linux/hfsplus_raw.h + * + * Copyright (C) 1999 + * Brad Boyer (flar@pants.nu) + * (C) 2003 Ardis Technologies + * + * 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 + +#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 -puN /dev/null fs/hfsplus/inode.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/inode.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,549 @@ +/* + * linux/fs/hfsplus/inode.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Inode handling routines + */ + +#include +#include +#include +#include +#include + +#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 -puN /dev/null fs/hfsplus/ioctl.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/ioctl.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,82 @@ +/* + * linux/fs/hfsplus/ioctl.c + * + * Copyright (C) 2003 + * Ethan Benson + * 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 +#include +#include +#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 -puN /dev/null fs/hfsplus/Makefile --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/Makefile 2004-02-23 00:31:35.000000000 -0800 @@ -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 -puN /dev/null fs/hfsplus/options.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/options.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,127 @@ +/* + * linux/fs/hfsplus/options.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Option parsing + */ + +#include +#include +#include +#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 -puN /dev/null fs/hfsplus/part_tbl.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/part_tbl.c 2004-02-23 00:31:35.000000000 -0800 @@ -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 -puN /dev/null fs/hfsplus/super.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/super.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,485 @@ +/* + * linux/fs/hfsplus/super.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 -puN /dev/null fs/hfsplus/tables.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/tables.c 2004-02-23 00:31:35.000000000 -0800 @@ -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 -puN /dev/null fs/hfsplus/unicode.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/unicode.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,140 @@ +/* + * linux/fs/hfsplus/unicode.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Handler routines for unicode strings + */ + +#include +#include +#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 -puN /dev/null fs/hfsplus/wrapper.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/fs/hfsplus/wrapper.c 2004-02-23 00:31:35.000000000 -0800 @@ -0,0 +1,171 @@ +/* + * linux/fs/hfsplus/wrapper.c + * + * Copyright (C) 2001 + * Brad Boyer (flar@allandria.com) + * (C) 2003 Ardis Technologies + * + * Handling of HFS wrappers around HFS+ volumes + */ + +#include +#include +#include +#include +#include +#include + +#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; +} diff -puN fs/Kconfig~hfsplus-support fs/Kconfig --- 25/fs/Kconfig~hfsplus-support 2004-02-23 00:31:35.000000000 -0800 +++ 25-akpm/fs/Kconfig 2004-02-23 00:31:35.000000000 -0800 @@ -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 diff -puN fs/Makefile~hfsplus-support fs/Makefile --- 25/fs/Makefile~hfsplus-support 2004-02-23 00:31:35.000000000 -0800 +++ 25-akpm/fs/Makefile 2004-02-23 00:31:35.000000000 -0800 @@ -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/ _