Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

9
fs/hfsplus/Makefile Normal file
View File

@@ -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

209
fs/hfsplus/bfind.c Normal file
View File

@@ -0,0 +1,209 @@
/*
* linux/fs/hfsplus/bfind.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Search routines for btrees
*/
#include <linux/slab.h>
#include "hfsplus_fs.h"
int hfs_find_init(struct hfs_btree *tree, struct hfs_find_data *fd)
{
void *ptr;
fd->tree = tree;
fd->bnode = NULL;
ptr = kmalloc(tree->max_key_len * 2 + 4, GFP_KERNEL);
if (!ptr)
return -ENOMEM;
fd->search_key = ptr;
fd->key = ptr + tree->max_key_len + 2;
dprint(DBG_BNODE_REFS, "find_init: %d (%p)\n", tree->cnid, __builtin_return_address(0));
down(&tree->tree_lock);
return 0;
}
void hfs_find_exit(struct hfs_find_data *fd)
{
hfs_bnode_put(fd->bnode);
kfree(fd->search_key);
dprint(DBG_BNODE_REFS, "find_exit: %d (%p)\n", fd->tree->cnid, __builtin_return_address(0));
up(&fd->tree->tree_lock);
fd->tree = NULL;
}
/* Find the record in bnode that best matches key (not greater than...)*/
int __hfs_brec_find(struct hfs_bnode *bnode, struct hfs_find_data *fd)
{
int cmpval;
u16 off, len, keylen;
int rec;
int b, e;
int res;
b = 0;
e = bnode->num_recs - 1;
res = -ENOENT;
do {
rec = (e + b) / 2;
len = hfs_brec_lenoff(bnode, rec, &off);
keylen = hfs_brec_keylen(bnode, rec);
hfs_bnode_read(bnode, fd->key, off, keylen);
cmpval = bnode->tree->keycmp(fd->key, fd->search_key);
if (!cmpval) {
e = rec;
res = 0;
goto done;
}
if (cmpval < 0)
b = rec + 1;
else
e = rec - 1;
} while (b <= e);
if (rec != e && e >= 0) {
len = hfs_brec_lenoff(bnode, e, &off);
keylen = hfs_brec_keylen(bnode, e);
hfs_bnode_read(bnode, fd->key, off, keylen);
}
done:
fd->record = e;
fd->keyoffset = off;
fd->keylength = keylen;
fd->entryoffset = off + keylen;
fd->entrylength = len - keylen;
return res;
}
/* Traverse a B*Tree from the root to a leaf finding best fit to key */
/* Return allocated copy of node found, set recnum to best record */
int hfs_brec_find(struct hfs_find_data *fd)
{
struct hfs_btree *tree;
struct hfs_bnode *bnode;
u32 nidx, parent;
__be32 data;
int height, res;
tree = fd->tree;
if (fd->bnode)
hfs_bnode_put(fd->bnode);
fd->bnode = NULL;
nidx = tree->root;
if (!nidx)
return -ENOENT;
height = tree->depth;
res = 0;
parent = 0;
for (;;) {
bnode = hfs_bnode_find(tree, nidx);
if (IS_ERR(bnode)) {
res = PTR_ERR(bnode);
bnode = NULL;
break;
}
if (bnode->height != height)
goto invalid;
if (bnode->type != (--height ? HFS_NODE_INDEX : HFS_NODE_LEAF))
goto invalid;
bnode->parent = parent;
res = __hfs_brec_find(bnode, fd);
if (!height)
break;
if (fd->record < 0)
goto release;
parent = nidx;
hfs_bnode_read(bnode, &data, fd->entryoffset, 4);
nidx = be32_to_cpu(data);
hfs_bnode_put(bnode);
}
fd->bnode = bnode;
return res;
invalid:
printk(KERN_ERR "hfs: inconsistency in B*Tree (%d,%d,%d,%u,%u)\n",
height, bnode->height, bnode->type, nidx, parent);
res = -EIO;
release:
hfs_bnode_put(bnode);
return res;
}
int hfs_brec_read(struct hfs_find_data *fd, void *rec, int rec_len)
{
int res;
res = hfs_brec_find(fd);
if (res)
return res;
if (fd->entrylength > rec_len)
return -EINVAL;
hfs_bnode_read(fd->bnode, rec, fd->entryoffset, fd->entrylength);
return 0;
}
int hfs_brec_goto(struct hfs_find_data *fd, int cnt)
{
struct hfs_btree *tree;
struct hfs_bnode *bnode;
int idx, res = 0;
u16 off, len, keylen;
bnode = fd->bnode;
tree = bnode->tree;
if (cnt < 0) {
cnt = -cnt;
while (cnt > fd->record) {
cnt -= fd->record + 1;
fd->record = bnode->num_recs - 1;
idx = bnode->prev;
if (!idx) {
res = -ENOENT;
goto out;
}
hfs_bnode_put(bnode);
bnode = hfs_bnode_find(tree, idx);
if (IS_ERR(bnode)) {
res = PTR_ERR(bnode);
bnode = NULL;
goto out;
}
}
fd->record -= cnt;
} else {
while (cnt >= bnode->num_recs - fd->record) {
cnt -= bnode->num_recs - fd->record;
fd->record = 0;
idx = bnode->next;
if (!idx) {
res = -ENOENT;
goto out;
}
hfs_bnode_put(bnode);
bnode = hfs_bnode_find(tree, idx);
if (IS_ERR(bnode)) {
res = PTR_ERR(bnode);
bnode = NULL;
goto out;
}
}
fd->record += cnt;
}
len = hfs_brec_lenoff(bnode, fd->record, &off);
keylen = hfs_brec_keylen(bnode, fd->record);
fd->keyoffset = off;
fd->keylength = keylen;
fd->entryoffset = off + keylen;
fd->entrylength = len - keylen;
hfs_bnode_read(bnode, fd->key, off, keylen);
out:
fd->bnode = bnode;
return res;
}

220
fs/hfsplus/bitmap.c Normal file
View File

@@ -0,0 +1,220 @@
/*
* linux/fs/hfsplus/bitmap.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handling of allocation file
*/
#include <linux/pagemap.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
#define PAGE_CACHE_BITS (PAGE_CACHE_SIZE * 8)
int hfsplus_block_allocate(struct super_block *sb, u32 size, u32 offset, u32 *max)
{
struct page *page;
struct address_space *mapping;
__be32 *pptr, *curr, *end;
u32 mask, start, len, n;
__be32 val;
int i;
len = *max;
if (!len)
return size;
dprint(DBG_BITMAP, "block_allocate: %u,%u,%u\n", size, offset, len);
mutex_lock(&HFSPLUS_SB(sb).alloc_file->i_mutex);
mapping = HFSPLUS_SB(sb).alloc_file->i_mapping;
page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS, NULL);
pptr = kmap(page);
curr = pptr + (offset & (PAGE_CACHE_BITS - 1)) / 32;
i = offset % 32;
offset &= ~(PAGE_CACHE_BITS - 1);
if ((size ^ offset) / PAGE_CACHE_BITS)
end = pptr + PAGE_CACHE_BITS / 32;
else
end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
/* scan the first partial u32 for zero bits */
val = *curr;
if (~val) {
n = be32_to_cpu(val);
mask = (1U << 31) >> i;
for (; i < 32; mask >>= 1, i++) {
if (!(n & mask))
goto found;
}
}
curr++;
/* scan complete u32s for the first zero bit */
while (1) {
while (curr < end) {
val = *curr;
if (~val) {
n = be32_to_cpu(val);
mask = 1 << 31;
for (i = 0; i < 32; mask >>= 1, i++) {
if (!(n & mask))
goto found;
}
}
curr++;
}
kunmap(page);
offset += PAGE_CACHE_BITS;
if (offset >= size)
break;
page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS,
NULL);
curr = pptr = kmap(page);
if ((size ^ offset) / PAGE_CACHE_BITS)
end = pptr + PAGE_CACHE_BITS / 32;
else
end = pptr + ((size + 31) & (PAGE_CACHE_BITS - 1)) / 32;
}
dprint(DBG_BITMAP, "bitmap full\n");
start = size;
goto out;
found:
start = offset + (curr - pptr) * 32 + i;
if (start >= size) {
dprint(DBG_BITMAP, "bitmap full\n");
goto out;
}
/* do any partial u32 at the start */
len = min(size - start, len);
while (1) {
n |= mask;
if (++i >= 32)
break;
mask >>= 1;
if (!--len || n & mask)
goto done;
}
if (!--len)
goto done;
*curr++ = cpu_to_be32(n);
/* do full u32s */
while (1) {
while (curr < end) {
n = be32_to_cpu(*curr);
if (len < 32)
goto last;
if (n) {
len = 32;
goto last;
}
*curr++ = cpu_to_be32(0xffffffff);
len -= 32;
}
set_page_dirty(page);
kunmap(page);
offset += PAGE_CACHE_BITS;
page = read_mapping_page(mapping, offset / PAGE_CACHE_BITS,
NULL);
pptr = kmap(page);
curr = pptr;
end = pptr + PAGE_CACHE_BITS / 32;
}
last:
/* do any partial u32 at end */
mask = 1U << 31;
for (i = 0; i < len; i++) {
if (n & mask)
break;
n |= mask;
mask >>= 1;
}
done:
*curr = cpu_to_be32(n);
set_page_dirty(page);
kunmap(page);
*max = offset + (curr - pptr) * 32 + i - start;
HFSPLUS_SB(sb).free_blocks -= *max;
sb->s_dirt = 1;
dprint(DBG_BITMAP, "-> %u,%u\n", start, *max);
out:
mutex_unlock(&HFSPLUS_SB(sb).alloc_file->i_mutex);
return start;
}
int hfsplus_block_free(struct super_block *sb, u32 offset, u32 count)
{
struct page *page;
struct address_space *mapping;
__be32 *pptr, *curr, *end;
u32 mask, len, pnr;
int i;
/* is there any actual work to be done? */
if (!count)
return 0;
dprint(DBG_BITMAP, "block_free: %u,%u\n", offset, count);
/* are all of the bits in range? */
if ((offset + count) > HFSPLUS_SB(sb).total_blocks)
return -2;
mutex_lock(&HFSPLUS_SB(sb).alloc_file->i_mutex);
mapping = HFSPLUS_SB(sb).alloc_file->i_mapping;
pnr = offset / PAGE_CACHE_BITS;
page = read_mapping_page(mapping, pnr, 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_mapping_page(mapping, ++pnr, 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;
mutex_unlock(&HFSPLUS_SB(sb).alloc_file->i_mutex);
return 0;
}

642
fs/hfsplus/bnode.c Normal file
View File

@@ -0,0 +1,642 @@
/*
* linux/fs/hfsplus/bnode.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handle basic btree node operations
*/
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/fs.h>
#include <linux/swap.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
/* Copy a specified range of bytes from the raw data of a node */
void hfs_bnode_read(struct hfs_bnode *node, void *buf, int off, int len)
{
struct page **pagep;
int l;
off += node->page_offset;
pagep = node->page + (off >> PAGE_CACHE_SHIFT);
off &= ~PAGE_CACHE_MASK;
l = min(len, (int)PAGE_CACHE_SIZE - off);
memcpy(buf, kmap(*pagep) + off, l);
kunmap(*pagep);
while ((len -= l) != 0) {
buf += l;
l = min(len, (int)PAGE_CACHE_SIZE);
memcpy(buf, kmap(*++pagep), l);
kunmap(*pagep);
}
}
u16 hfs_bnode_read_u16(struct hfs_bnode *node, int off)
{
__be16 data;
// optimize later...
hfs_bnode_read(node, &data, off, 2);
return be16_to_cpu(data);
}
u8 hfs_bnode_read_u8(struct hfs_bnode *node, int off)
{
u8 data;
// optimize later...
hfs_bnode_read(node, &data, off, 1);
return data;
}
void hfs_bnode_read_key(struct hfs_bnode *node, void *key, int off)
{
struct hfs_btree *tree;
int key_len;
tree = node->tree;
if (node->type == HFS_NODE_LEAF ||
tree->attributes & HFS_TREE_VARIDXKEYS)
key_len = hfs_bnode_read_u16(node, off) + 2;
else
key_len = tree->max_key_len + 2;
hfs_bnode_read(node, key, off, key_len);
}
void hfs_bnode_write(struct hfs_bnode *node, void *buf, int off, int len)
{
struct page **pagep;
int l;
off += node->page_offset;
pagep = node->page + (off >> PAGE_CACHE_SHIFT);
off &= ~PAGE_CACHE_MASK;
l = min(len, (int)PAGE_CACHE_SIZE - off);
memcpy(kmap(*pagep) + off, buf, l);
set_page_dirty(*pagep);
kunmap(*pagep);
while ((len -= l) != 0) {
buf += l;
l = min(len, (int)PAGE_CACHE_SIZE);
memcpy(kmap(*++pagep), buf, l);
set_page_dirty(*pagep);
kunmap(*pagep);
}
}
void hfs_bnode_write_u16(struct hfs_bnode *node, int off, u16 data)
{
__be16 v = cpu_to_be16(data);
// optimize later...
hfs_bnode_write(node, &v, off, 2);
}
void hfs_bnode_clear(struct hfs_bnode *node, int off, int len)
{
struct page **pagep;
int l;
off += node->page_offset;
pagep = node->page + (off >> PAGE_CACHE_SHIFT);
off &= ~PAGE_CACHE_MASK;
l = min(len, (int)PAGE_CACHE_SIZE - off);
memset(kmap(*pagep) + off, 0, l);
set_page_dirty(*pagep);
kunmap(*pagep);
while ((len -= l) != 0) {
l = min(len, (int)PAGE_CACHE_SIZE);
memset(kmap(*++pagep), 0, l);
set_page_dirty(*pagep);
kunmap(*pagep);
}
}
void hfs_bnode_copy(struct hfs_bnode *dst_node, int dst,
struct hfs_bnode *src_node, int src, int len)
{
struct hfs_btree *tree;
struct page **src_page, **dst_page;
int l;
dprint(DBG_BNODE_MOD, "copybytes: %u,%u,%u\n", dst, src, len);
if (!len)
return;
tree = src_node->tree;
src += src_node->page_offset;
dst += dst_node->page_offset;
src_page = src_node->page + (src >> PAGE_CACHE_SHIFT);
src &= ~PAGE_CACHE_MASK;
dst_page = dst_node->page + (dst >> PAGE_CACHE_SHIFT);
dst &= ~PAGE_CACHE_MASK;
if (src == dst) {
l = min(len, (int)PAGE_CACHE_SIZE - src);
memcpy(kmap(*dst_page) + src, kmap(*src_page) + src, l);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
while ((len -= l) != 0) {
l = min(len, (int)PAGE_CACHE_SIZE);
memcpy(kmap(*++dst_page), kmap(*++src_page), l);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
}
} else {
void *src_ptr, *dst_ptr;
do {
src_ptr = kmap(*src_page) + src;
dst_ptr = kmap(*dst_page) + dst;
if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) {
l = PAGE_CACHE_SIZE - src;
src = 0;
dst += l;
} else {
l = PAGE_CACHE_SIZE - dst;
src += l;
dst = 0;
}
l = min(len, l);
memcpy(dst_ptr, src_ptr, l);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
if (!dst)
dst_page++;
else
src_page++;
} while ((len -= l));
}
}
void hfs_bnode_move(struct hfs_bnode *node, int dst, int src, int len)
{
struct page **src_page, **dst_page;
int l;
dprint(DBG_BNODE_MOD, "movebytes: %u,%u,%u\n", dst, src, len);
if (!len)
return;
src += node->page_offset;
dst += node->page_offset;
if (dst > src) {
src += len - 1;
src_page = node->page + (src >> PAGE_CACHE_SHIFT);
src = (src & ~PAGE_CACHE_MASK) + 1;
dst += len - 1;
dst_page = node->page + (dst >> PAGE_CACHE_SHIFT);
dst = (dst & ~PAGE_CACHE_MASK) + 1;
if (src == dst) {
while (src < len) {
memmove(kmap(*dst_page), kmap(*src_page), src);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
len -= src;
src = PAGE_CACHE_SIZE;
src_page--;
dst_page--;
}
src -= len;
memmove(kmap(*dst_page) + src, kmap(*src_page) + src, len);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
} else {
void *src_ptr, *dst_ptr;
do {
src_ptr = kmap(*src_page) + src;
dst_ptr = kmap(*dst_page) + dst;
if (src < dst) {
l = src;
src = PAGE_CACHE_SIZE;
dst -= l;
} else {
l = dst;
src -= l;
dst = PAGE_CACHE_SIZE;
}
l = min(len, l);
memmove(dst_ptr - l, src_ptr - l, l);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
if (dst == PAGE_CACHE_SIZE)
dst_page--;
else
src_page--;
} while ((len -= l));
}
} else {
src_page = node->page + (src >> PAGE_CACHE_SHIFT);
src &= ~PAGE_CACHE_MASK;
dst_page = node->page + (dst >> PAGE_CACHE_SHIFT);
dst &= ~PAGE_CACHE_MASK;
if (src == dst) {
l = min(len, (int)PAGE_CACHE_SIZE - src);
memmove(kmap(*dst_page) + src, kmap(*src_page) + src, l);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
while ((len -= l) != 0) {
l = min(len, (int)PAGE_CACHE_SIZE);
memmove(kmap(*++dst_page), kmap(*++src_page), l);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
}
} else {
void *src_ptr, *dst_ptr;
do {
src_ptr = kmap(*src_page) + src;
dst_ptr = kmap(*dst_page) + dst;
if (PAGE_CACHE_SIZE - src < PAGE_CACHE_SIZE - dst) {
l = PAGE_CACHE_SIZE - src;
src = 0;
dst += l;
} else {
l = PAGE_CACHE_SIZE - dst;
src += l;
dst = 0;
}
l = min(len, l);
memmove(dst_ptr, src_ptr, l);
kunmap(*src_page);
set_page_dirty(*dst_page);
kunmap(*dst_page);
if (!dst)
dst_page++;
else
src_page++;
} while ((len -= l));
}
}
}
void hfs_bnode_dump(struct hfs_bnode *node)
{
struct hfs_bnode_desc desc;
__be32 cnid;
int i, off, key_off;
dprint(DBG_BNODE_MOD, "bnode: %d\n", node->this);
hfs_bnode_read(node, &desc, 0, sizeof(desc));
dprint(DBG_BNODE_MOD, "%d, %d, %d, %d, %d\n",
be32_to_cpu(desc.next), be32_to_cpu(desc.prev),
desc.type, desc.height, be16_to_cpu(desc.num_recs));
off = node->tree->node_size - 2;
for (i = be16_to_cpu(desc.num_recs); i >= 0; off -= 2, i--) {
key_off = hfs_bnode_read_u16(node, off);
dprint(DBG_BNODE_MOD, " %d", key_off);
if (i && node->type == HFS_NODE_INDEX) {
int tmp;
if (node->tree->attributes & HFS_TREE_VARIDXKEYS)
tmp = hfs_bnode_read_u16(node, key_off) + 2;
else
tmp = node->tree->max_key_len + 2;
dprint(DBG_BNODE_MOD, " (%d", tmp);
hfs_bnode_read(node, &cnid, key_off + tmp, 4);
dprint(DBG_BNODE_MOD, ",%d)", be32_to_cpu(cnid));
} else if (i && node->type == HFS_NODE_LEAF) {
int tmp;
tmp = hfs_bnode_read_u16(node, key_off);
dprint(DBG_BNODE_MOD, " (%d)", tmp);
}
}
dprint(DBG_BNODE_MOD, "\n");
}
void hfs_bnode_unlink(struct hfs_bnode *node)
{
struct hfs_btree *tree;
struct hfs_bnode *tmp;
__be32 cnid;
tree = node->tree;
if (node->prev) {
tmp = hfs_bnode_find(tree, node->prev);
if (IS_ERR(tmp))
return;
tmp->next = node->next;
cnid = cpu_to_be32(tmp->next);
hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, next), 4);
hfs_bnode_put(tmp);
} else if (node->type == HFS_NODE_LEAF)
tree->leaf_head = node->next;
if (node->next) {
tmp = hfs_bnode_find(tree, node->next);
if (IS_ERR(tmp))
return;
tmp->prev = node->prev;
cnid = cpu_to_be32(tmp->prev);
hfs_bnode_write(tmp, &cnid, offsetof(struct hfs_bnode_desc, prev), 4);
hfs_bnode_put(tmp);
} else if (node->type == HFS_NODE_LEAF)
tree->leaf_tail = node->prev;
// move down?
if (!node->prev && !node->next) {
printk(KERN_DEBUG "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(KERN_ERR "hfs: request for non-existent node %d in B*Tree\n", cnid);
return NULL;
}
for (node = tree->node_hash[hfs_bnode_hash(cnid)];
node; node = node->next_hash) {
if (node->this == cnid) {
return node;
}
}
return NULL;
}
static struct hfs_bnode *__hfs_bnode_create(struct hfs_btree *tree, u32 cnid)
{
struct super_block *sb;
struct hfs_bnode *node, *node2;
struct address_space *mapping;
struct page *page;
int size, block, i, hash;
loff_t off;
if (cnid >= tree->node_count) {
printk(KERN_ERR "hfs: request for non-existent node %d in B*Tree\n", cnid);
return NULL;
}
sb = tree->inode->i_sb;
size = sizeof(struct hfs_bnode) + tree->pages_per_bnode *
sizeof(struct page *);
node = kzalloc(size, GFP_KERNEL);
if (!node)
return NULL;
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_mapping_page(mapping, block, NULL);
if (IS_ERR(page))
goto fail;
if (PageError(page)) {
page_cache_release(page);
goto fail;
}
page_cache_release(page);
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)
;
BUG_ON(!*p);
*p = node->next_hash;
node->tree->node_hash_cnt--;
}
/* Load a particular node out of a tree */
struct hfs_bnode *hfs_bnode_find(struct hfs_btree *tree, u32 num)
{
struct hfs_bnode *node;
struct hfs_bnode_desc *desc;
int i, rec_off, off, next_off;
int entry_size, key_size;
spin_lock(&tree->hash_lock);
node = hfs_bnode_findhash(tree, num);
if (node) {
hfs_bnode_get(node);
spin_unlock(&tree->hash_lock);
wait_event(node->lock_wq, !test_bit(HFS_BNODE_NEW, &node->flags));
if (test_bit(HFS_BNODE_ERROR, &node->flags))
goto node_error;
return node;
}
spin_unlock(&tree->hash_lock);
node = __hfs_bnode_create(tree, num);
if (!node)
return ERR_PTR(-ENOMEM);
if (test_bit(HFS_BNODE_ERROR, &node->flags))
goto node_error;
if (!test_bit(HFS_BNODE_NEW, &node->flags))
return node;
desc = (struct hfs_bnode_desc *)(kmap(node->page[0]) + node->page_offset);
node->prev = be32_to_cpu(desc->prev);
node->next = be32_to_cpu(desc->next);
node->num_recs = be16_to_cpu(desc->num_recs);
node->type = desc->type;
node->height = desc->height;
kunmap(node->page[0]);
switch (node->type) {
case HFS_NODE_HEADER:
case HFS_NODE_MAP:
if (node->height != 0)
goto node_error;
break;
case HFS_NODE_LEAF:
if (node->height != 1)
goto node_error;
break;
case HFS_NODE_INDEX:
if (node->height <= 1 || node->height > tree->depth)
goto node_error;
break;
default:
goto node_error;
}
rec_off = tree->node_size - 2;
off = hfs_bnode_read_u16(node, rec_off);
if (off != sizeof(struct hfs_bnode_desc))
goto node_error;
for (i = 1; i <= node->num_recs; off = next_off, i++) {
rec_off -= 2;
next_off = hfs_bnode_read_u16(node, rec_off);
if (next_off <= off ||
next_off > tree->node_size ||
next_off & 1)
goto node_error;
entry_size = next_off - off;
if (node->type != HFS_NODE_INDEX &&
node->type != HFS_NODE_LEAF)
continue;
key_size = hfs_bnode_read_u16(node, off) + 2;
if (key_size >= entry_size || key_size & 1)
goto node_error;
}
clear_bit(HFS_BNODE_NEW, &node->flags);
wake_up(&node->lock_wq);
return node;
node_error:
set_bit(HFS_BNODE_ERROR, &node->flags);
clear_bit(HFS_BNODE_NEW, &node->flags);
wake_up(&node->lock_wq);
hfs_bnode_put(node);
return ERR_PTR(-EIO);
}
void hfs_bnode_free(struct hfs_bnode *node)
{
//int i;
//for (i = 0; i < node->tree->pages_per_bnode; i++)
// if (node->page[i])
// page_cache_release(node->page[i]);
kfree(node);
}
struct hfs_bnode *hfs_bnode_create(struct hfs_btree *tree, u32 num)
{
struct hfs_bnode *node;
struct page **pagep;
int i;
spin_lock(&tree->hash_lock);
node = hfs_bnode_findhash(tree, num);
spin_unlock(&tree->hash_lock);
if (node) {
printk(KERN_CRIT "new node %u already hashed?\n", num);
WARN_ON(1);
return node;
}
node = __hfs_bnode_create(tree, num);
if (!node)
return ERR_PTR(-ENOMEM);
if (test_bit(HFS_BNODE_ERROR, &node->flags)) {
hfs_bnode_put(node);
return ERR_PTR(-EIO);
}
pagep = node->page;
memset(kmap(*pagep) + node->page_offset, 0,
min((int)PAGE_CACHE_SIZE, (int)tree->node_size));
set_page_dirty(*pagep);
kunmap(*pagep);
for (i = 1; i < tree->pages_per_bnode; i++) {
memset(kmap(*++pagep), 0, PAGE_CACHE_SIZE);
set_page_dirty(*pagep);
kunmap(*pagep);
}
clear_bit(HFS_BNODE_NEW, &node->flags);
wake_up(&node->lock_wq);
return node;
}
void hfs_bnode_get(struct hfs_bnode *node)
{
if (node) {
atomic_inc(&node->refcnt);
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));
BUG_ON(!atomic_read(&node->refcnt));
if (!atomic_dec_and_lock(&node->refcnt, &tree->hash_lock))
return;
for (i = 0; i < tree->pages_per_bnode; i++) {
if (!node->page[i])
continue;
mark_page_accessed(node->page[i]);
}
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);
}
}

491
fs/hfsplus/brec.c Normal file
View File

@@ -0,0 +1,491 @@
/*
* linux/fs/hfsplus/brec.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handle individual btree records
*/
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
static struct hfs_bnode *hfs_bnode_split(struct hfs_find_data *fd);
static int hfs_brec_update_parent(struct hfs_find_data *fd);
static int hfs_btree_inc_height(struct hfs_btree *);
/* 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)
{
__be16 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 + 2;
} 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;
__be32 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 = cpu_to_be16(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;
}
static 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;
}
static 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(KERN_DEBUG "hfs: 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) {
__be32 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;
}
static 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;
__be32 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;
}

318
fs/hfsplus/btree.c Normal file
View File

@@ -0,0 +1,318 @@
/*
* linux/fs/hfsplus/btree.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handle opening/closing btree
*/
#include <linux/slab.h>
#include <linux/pagemap.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
/* Get a reference to a B*Tree and do some initial checks */
struct hfs_btree *hfs_btree_open(struct super_block *sb, u32 id)
{
struct hfs_btree *tree;
struct hfs_btree_header_rec *head;
struct address_space *mapping;
struct page *page;
unsigned int size;
tree = kzalloc(sizeof(*tree), GFP_KERNEL);
if (!tree)
return NULL;
init_MUTEX(&tree->tree_lock);
spin_lock_init(&tree->hash_lock);
tree->sb = sb;
tree->cnid = id;
tree->inode = iget(sb, id);
if (!tree->inode)
goto free_tree;
mapping = tree->inode->i_mapping;
page = read_mapping_page(mapping, 0, 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);
/* Set the correct compare function */
if (id == HFSPLUS_EXT_CNID) {
tree->keycmp = hfsplus_ext_cmp_key;
} else if (id == HFSPLUS_CAT_CNID) {
if ((HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX) &&
(head->key_type == HFSPLUS_KEY_BINARY))
tree->keycmp = hfsplus_cat_bin_cmp_key;
else
tree->keycmp = hfsplus_cat_case_cmp_key;
} else {
printk(KERN_ERR "hfs: unknown B*Tree requested\n");
goto fail_page;
}
size = tree->node_size;
if (!size || size & (size - 1))
goto fail_page;
if (!tree->node_count)
goto fail_page;
tree->node_size_shift = ffs(size) - 1;
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(KERN_CRIT "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;
__be32 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);
HFSPLUS_I(inode).phys_size = inode->i_size =
(loff_t)HFSPLUS_I(inode).alloc_blocks <<
HFSPLUS_SB(tree->sb).alloc_blksz_shift;
HFSPLUS_I(inode).fs_blocks = HFSPLUS_I(inode).alloc_blocks <<
HFSPLUS_SB(tree->sb).fs_shift;
inode_set_bytes(inode, inode->i_size);
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);
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(KERN_DEBUG "hfs: 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);
BUG_ON(!node->this);
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(KERN_CRIT "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(KERN_CRIT "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(KERN_CRIT "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);
}

383
fs/hfsplus/catalog.c Normal file
View File

@@ -0,0 +1,383 @@
/*
* linux/fs/hfsplus/catalog.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handling of catalog records
*/
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
int hfsplus_cat_case_cmp_key(const hfsplus_btree_key *k1,
const hfsplus_btree_key *k2)
{
__be32 k1p, k2p;
k1p = k1->cat.parent;
k2p = k2->cat.parent;
if (k1p != k2p)
return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
return hfsplus_strcasecmp(&k1->cat.name, &k2->cat.name);
}
int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *k1,
const hfsplus_btree_key *k2)
{
__be32 k1p, k2p;
k1p = k1->cat.parent;
k2p = k2->cat.parent;
if (k1p != k2p)
return be32_to_cpu(k1p) < be32_to_cpu(k2p) ? -1 : 1;
return hfsplus_strcmp(&k1->cat.name, &k2->cat.name);
}
void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *key,
u32 parent, struct qstr *str)
{
int len;
key->cat.parent = cpu_to_be32(parent);
if (str) {
hfsplus_asc2uni(sb, &key->cat.name, str->name, str->len);
len = be16_to_cpu(key->cat.name.length);
} else {
key->cat.name.length = 0;
len = 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);
HFSPLUS_I(inode).create_date =
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);
HFSPLUS_I(inode).create_date =
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);
if (S_ISLNK(inode->i_mode)) {
file->user_info.fdType = cpu_to_be32(HFSP_SYMLINK_TYPE);
file->user_info.fdCreator = cpu_to_be32(HFSP_SYMLINK_CREATOR);
} else {
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->create_date = HFSPLUS_I(HFSPLUS_SB(inode->i_sb).hidden_dir).create_date;
file->permissions.dev = cpu_to_be32(HFSPLUS_I(inode).dev);
}
return sizeof(*file);
}
}
static int hfsplus_fill_cat_thread(struct super_block *sb,
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(sb, &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(sb, 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(KERN_ERR "hfs: 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(sb, fd.search_key, cnid, NULL);
entry_size = hfsplus_fill_cat_thread(sb, &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 err2;
}
err = hfs_brec_insert(&fd, &entry, entry_size);
if (err)
goto err2;
hfsplus_cat_build_key(sb, 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 err1;
}
err = hfs_brec_insert(&fd, &entry, entry_size);
if (err)
goto err1;
dir->i_size++;
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(dir);
hfs_find_exit(&fd);
return 0;
err1:
hfsplus_cat_build_key(sb, fd.search_key, cnid, NULL);
if (!hfs_brec_find(&fd))
hfs_brec_remove(&fd);
err2:
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(sb, 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(sb, 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(sb, 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--;
dir->i_mtime = dir->i_ctime = CURRENT_TIME_SEC;
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(sb, 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(sb, 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++;
dst_dir->i_mtime = dst_dir->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(dst_dir);
/* finally remove the old entry */
hfsplus_cat_build_key(sb, 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--;
src_dir->i_mtime = src_dir->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(src_dir);
/* remove old thread entry */
hfsplus_cat_build_key(sb, 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(sb, dst_fd.search_key, cnid, NULL);
entry_size = hfsplus_fill_cat_thread(sb, &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;
}

491
fs/hfsplus/dir.c Normal file
View File

@@ -0,0 +1,491 @@
/*
* linux/fs/hfsplus/dir.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handling of directories
*/
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/random.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
static inline void hfsplus_instantiate(struct dentry *dentry,
struct inode *inode, u32 cnid)
{
dentry->d_fsdata = (void *)(unsigned long)cnid;
d_instantiate(dentry, inode);
}
/* 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(sb, 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);
dentry->d_fsdata = (void *)(unsigned long)cnid;
} 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) &&
(entry.file.create_date == HFSPLUS_I(HFSPLUS_SB(sb).hidden_dir).create_date ||
entry.file.create_date == HFSPLUS_I(sb->s_root->d_inode).create_date) &&
HFSPLUS_SB(sb).hidden_dir) {
struct qstr str;
char name[32];
if (dentry->d_fsdata) {
/*
* We found a link pointing to another link,
* so ignore it and treat it as regular file.
*/
cnid = (unsigned long)dentry->d_fsdata;
linkid = 0;
} else {
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(sb, 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(KERN_ERR "hfs: invalid 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_path.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(sb, 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(KERN_ERR "hfs: bad catalog folder thread\n");
err = -EIO;
goto out;
}
if (fd.entrylength < HFSPLUS_MIN_THREAD_SZ) {
printk(KERN_ERR "hfs: 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(KERN_ERR "hfs: 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(sb, &fd.key->cat.name, strbuf, &len);
if (err)
goto out;
if (type == HFSPLUS_FOLDER) {
if (fd.entrylength < sizeof(struct hfsplus_cat_folder)) {
printk(KERN_ERR "hfs: 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(KERN_ERR "hfs: 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(KERN_ERR "hfs: 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;
}
static 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;
hfsplus_delete_inode(inode);
iput(inode);
return res;
}
hfsplus_instantiate(dentry, inode, inode->i_ino);
mark_inode_dirty(inode);
return 0;
}
static 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;
inc_nlink(inode);
hfsplus_instantiate(dst_dentry, inode, cnid);
atomic_inc(&inode->i_count);
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
HFSPLUS_SB(sb).file_count++;
sb->s_dirt = 1;
return 0;
}
static 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;
if (inode->i_nlink > 0)
drop_nlink(inode);
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;
} else
clear_nlink(inode);
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
return res;
}
static 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;
hfsplus_delete_inode(inode);
iput(inode);
return res;
}
hfsplus_instantiate(dentry, inode, inode->i_ino);
mark_inode_dirty(inode);
return 0;
}
static 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;
clear_nlink(inode);
inode->i_ctime = CURRENT_TIME_SEC;
hfsplus_delete_inode(inode);
mark_inode_dirty(inode);
return 0;
}
static 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;
hfsplus_delete_inode(inode);
iput(inode);
return res;
}
mark_inode_dirty(inode);
res = hfsplus_create_cat(inode->i_ino, dir, &dentry->d_name, inode);
if (!res) {
hfsplus_instantiate(dentry, inode, inode->i_ino);
mark_inode_dirty(inode);
}
return res;
}
static 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;
hfsplus_delete_inode(inode);
iput(inode);
return res;
}
init_special_inode(inode, mode, rdev);
hfsplus_instantiate(dentry, inode, inode->i_ino);
mark_inode_dirty(inode);
return 0;
}
static 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;
}
const 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,
};
const struct file_operations hfsplus_dir_operations = {
.read = generic_read_dir,
.readdir = hfsplus_readdir,
.ioctl = hfsplus_ioctl,
.llseek = generic_file_llseek,
.release = hfsplus_dir_release,
};

506
fs/hfsplus/extents.c Normal file
View File

@@ -0,0 +1,506 @@
/*
* linux/fs/hfsplus/extents.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handling of Extents both in catalog and extents overflow trees
*/
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
/* Compare two extents keys, returns 0 on same, pos/neg for difference */
int hfsplus_ext_cmp_key(const hfsplus_btree_key *k1,
const hfsplus_btree_key *k2)
{
__be32 k1id, k2id;
__be32 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;
}
static 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 >= HFSPLUS_I(inode).fs_blocks) {
if (iblock > HFSPLUS_I(inode).fs_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;
HFSPLUS_I(inode).fs_blocks++;
inode_add_bytes(inode, sb->s_blocksize);
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;
}
static 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(KERN_ERR "hfs: 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;
}
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;
} else if (inode->i_size == HFSPLUS_I(inode).phys_size)
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;
HFSPLUS_I(inode).fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
inode_set_bytes(inode, HFSPLUS_I(inode).fs_blocks << sb->s_blocksize_bits);
mark_inode_dirty(inode);
}

412
fs/hfsplus/hfsplus_fs.h Normal file
View File

@@ -0,0 +1,412 @@
/*
* linux/include/linux/hfsplus_fs.h
*
* Copyright (C) 1999
* Brad Boyer (flar@pants.nu)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
*/
#ifndef _LINUX_HFSPLUS_FS_H
#define _LINUX_HFSPLUS_FS_H
#include <linux/fs.h>
#include <linux/buffer_head.h>
#include "hfsplus_raw.h"
#define DBG_BNODE_REFS 0x00000001
#define DBG_BNODE_MOD 0x00000002
#define DBG_CAT_MOD 0x00000004
#define DBG_INODE 0x00000008
#define DBG_SUPER 0x00000010
#define DBG_EXTENT 0x00000020
#define DBG_BITMAP 0x00000040
//#define DBG_MASK (DBG_EXTENT|DBG_INODE|DBG_BNODE_MOD)
//#define DBG_MASK (DBG_BNODE_MOD|DBG_CAT_MOD|DBG_INODE)
//#define DBG_MASK (DBG_CAT_MOD|DBG_BNODE_REFS|DBG_INODE|DBG_EXTENT)
#define DBG_MASK (0)
#define dprint(flg, fmt, args...) \
if (flg & DBG_MASK) printk(fmt , ## args)
/* Runtime config options */
#define HFSPLUS_DEF_CR_TYPE 0x3F3F3F3F /* '????' */
#define HFSPLUS_TYPE_DATA 0x00
#define HFSPLUS_TYPE_RSRC 0xFF
typedef int (*btree_keycmp)(const hfsplus_btree_key *, const 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;
struct nls_table *nls;
/* 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;
struct hlist_head rsrc_inodes;
};
#define HFSPLUS_SB_WRITEBACKUP 0x0001
#define HFSPLUS_SB_NODECOMPOSE 0x0002
#define HFSPLUS_SB_FORCE 0x0004
#define HFSPLUS_SB_HFSX 0x0008
struct hfsplus_inode_info {
struct semaphore extents_lock;
u32 clump_blocks, alloc_blocks;
sector_t fs_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;
__be32 create_date;
/* 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_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 FS_IOC_GETFLAGS
#define HFSPLUS_IOC_EXT2_SETFLAGS FS_IOC_SETFLAGS
/*
* 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 *);
/* 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_case_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *);
int hfsplus_cat_bin_cmp_key(const hfsplus_btree_key *, const hfsplus_btree_key *);
void hfsplus_cat_build_key(struct super_block *sb, hfsplus_btree_key *, u32, struct qstr *);
int hfsplus_find_cat(struct super_block *, u32, struct hfs_find_data *);
int hfsplus_create_cat(u32, struct inode *, struct qstr *, struct inode *);
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(const hfsplus_btree_key *, const hfsplus_btree_key *);
void hfsplus_ext_write_extent(struct inode *);
int hfsplus_get_block(struct inode *, sector_t, struct buffer_head *, int);
int hfsplus_free_fork(struct super_block *, u32, struct hfsplus_fork_raw *, int);
int hfsplus_file_extend(struct inode *);
void hfsplus_file_truncate(struct inode *);
/* inode.c */
extern const struct address_space_operations hfsplus_aops;
extern const 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 *);
int 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);
int hfsplus_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags);
ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
void *value, size_t size);
ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size);
/* options.c */
int hfsplus_parse_options(char *, struct hfsplus_sb_info *);
void hfsplus_fill_defaults(struct hfsplus_sb_info *);
int hfsplus_show_options(struct seq_file *, struct vfsmount *);
/* tables.c */
extern u16 hfsplus_case_fold_table[];
extern u16 hfsplus_decompose_table[];
extern u16 hfsplus_compose_table[];
/* unicode.c */
int hfsplus_strcasecmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *);
int hfsplus_strcmp(const struct hfsplus_unistr *, const struct hfsplus_unistr *);
int hfsplus_uni2asc(struct super_block *, const struct hfsplus_unistr *, char *, int *);
int hfsplus_asc2uni(struct super_block *, struct hfsplus_unistr *, const char *, int);
/* 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

335
fs/hfsplus/hfsplus_raw.h Normal file
View File

@@ -0,0 +1,335 @@
/*
* linux/include/linux/hfsplus_raw.h
*
* Copyright (C) 1999
* Brad Boyer (flar@pants.nu)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Format of structures on disk
* Information taken from Apple Technote #1150 (HFS Plus Volume Format)
*
*/
#ifndef _LINUX_HFSPLUS_RAW_H
#define _LINUX_HFSPLUS_RAW_H
#include <linux/types.h>
/* Some constants */
#define HFSPLUS_SECTOR_SIZE 512
#define HFSPLUS_SECTOR_SHIFT 9
#define HFSPLUS_VOLHEAD_SECTOR 2
#define HFSPLUS_VOLHEAD_SIG 0x482b
#define HFSPLUS_VOLHEAD_SIGX 0x4858
#define HFSPLUS_SUPER_MAGIC 0x482b
#define HFSPLUS_MIN_VERSION 4
#define HFSPLUS_CURRENT_VERSION 5
#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_SYMLINK_TYPE 0x736c6e6b /* 'slnk' */
#define HFSP_SYMLINK_CREATOR 0x72686170 /* 'rhap' */
#define HFSP_MOUNT_VERSION 0x482b4c78 /* 'H+Lx' */
/* Structures used on disk */
typedef __be32 hfsplus_cnid;
typedef __be16 hfsplus_unichr;
/* A "string" as used in filenames, etc. */
struct hfsplus_unistr {
__be16 length;
hfsplus_unichr unicode[255];
} __packed;
#define HFSPLUS_MAX_STRLEN 255
/* POSIX permissions */
struct hfsplus_perm {
__be32 owner;
__be32 group;
u8 rootflags;
u8 userflags;
__be16 mode;
__be32 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 {
__be32 start_block;
__be32 block_count;
} __packed;
typedef struct hfsplus_extent hfsplus_extent_rec[8];
/* Information for a "Fork" in a file */
struct hfsplus_fork_raw {
__be64 total_size;
__be32 clump_size;
__be32 total_blocks;
hfsplus_extent_rec extents;
} __packed;
/* HFS+ Volume Header */
struct hfsplus_vh {
__be16 signature;
__be16 version;
__be32 attributes;
__be32 last_mount_vers;
u32 reserved;
__be32 create_date;
__be32 modify_date;
__be32 backup_date;
__be32 checked_date;
__be32 file_count;
__be32 folder_count;
__be32 blocksize;
__be32 total_blocks;
__be32 free_blocks;
__be32 next_alloc;
__be32 rsrc_clump_sz;
__be32 data_clump_sz;
hfsplus_cnid next_cnid;
__be32 write_count;
__be64 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_NODEID_REUSED (1 << 12)
#define HFSPLUS_VOL_JOURNALED (1 << 13)
#define HFSPLUS_VOL_SOFTLOCK (1 << 15)
/* HFS+ BTree node descriptor */
struct hfs_bnode_desc {
__be32 next;
__be32 prev;
s8 type;
u8 height;
__be16 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 {
__be16 depth;
__be32 root;
__be32 leaf_count;
__be32 leaf_head;
__be32 leaf_tail;
__be16 node_size;
__be16 max_key_len;
__be32 node_count;
__be32 free_nodes;
u16 reserved1;
__be32 clump_size;
u8 btree_type;
u8 key_type;
__be32 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 */
/* btree key type */
#define HFSPLUS_KEY_CASEFOLDING 0xCF /* case-insensitive */
#define HFSPLUS_KEY_BINARY 0xBC /* case-sensitive */
/* HFS+ catalog entry key */
struct hfsplus_cat_key {
__be16 key_len;
hfsplus_cnid parent;
struct hfsplus_unistr name;
} __packed;
/* Structs from hfs.h */
struct hfsp_point {
__be16 v;
__be16 h;
} __packed;
struct hfsp_rect {
__be16 top;
__be16 left;
__be16 bottom;
__be16 right;
} __packed;
/* HFS directory info (stolen from hfs.h */
struct DInfo {
struct hfsp_rect frRect;
__be16 frFlags;
struct hfsp_point frLocation;
__be16 frView;
} __packed;
struct DXInfo {
struct hfsp_point frScroll;
__be32 frOpenChain;
__be16 frUnused;
__be16 frComment;
__be32 frPutAway;
} __packed;
/* HFS+ folder data (part of an hfsplus_cat_entry) */
struct hfsplus_cat_folder {
__be16 type;
__be16 flags;
__be32 valence;
hfsplus_cnid id;
__be32 create_date;
__be32 content_mod_date;
__be32 attribute_mod_date;
__be32 access_date;
__be32 backup_date;
struct hfsplus_perm permissions;
struct DInfo user_info;
struct DXInfo finder_info;
__be32 text_encoding;
u32 reserved;
} __packed;
/* HFS file info (stolen from hfs.h) */
struct FInfo {
__be32 fdType;
__be32 fdCreator;
__be16 fdFlags;
struct hfsp_point fdLocation;
__be16 fdFldr;
} __packed;
struct FXInfo {
__be16 fdIconID;
u8 fdUnused[8];
__be16 fdComment;
__be32 fdPutAway;
} __packed;
/* HFS+ file data (part of a cat_entry) */
struct hfsplus_cat_file {
__be16 type;
__be16 flags;
u32 reserved1;
hfsplus_cnid id;
__be32 create_date;
__be32 content_mod_date;
__be32 attribute_mod_date;
__be32 access_date;
__be32 backup_date;
struct hfsplus_perm permissions;
struct FInfo user_info;
struct FXInfo finder_info;
__be32 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 {
__be16 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 {
__be16 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 {
__be16 key_len;
u8 fork_type;
u8 pad;
hfsplus_cnid cnid;
__be32 start_block;
} __packed;
#define HFSPLUS_EXT_KEYLEN 12
/* HFS+ generic BTree key */
typedef union {
__be16 key_len;
struct hfsplus_cat_key cat;
struct hfsplus_ext_key ext;
} __packed hfsplus_btree_key;
#endif

532
fs/hfsplus/inode.c Normal file
View File

@@ -0,0 +1,532 @@
/*
* linux/fs/hfsplus/inode.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Inode handling routines
*/
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/pagemap.h>
#include <linux/mpage.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
static int hfsplus_readpage(struct file *file, struct page *page)
{
return block_read_full_page(page, hfsplus_get_block);
}
static int hfsplus_writepage(struct page *page, struct writeback_control *wbc)
{
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);
}
static int hfsplus_releasepage(struct page *page, gfp_t 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;
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 && nidx < tree->node_count);
spin_unlock(&tree->hash_lock);
}
return res ? try_to_free_buffers(page) : 0;
}
static ssize_t 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_path.dentry->d_inode->i_mapping->host;
return blockdev_direct_IO(rw, iocb, inode, inode->i_sb->s_bdev, iov,
offset, nr_segs, hfsplus_get_block, NULL);
}
static int hfsplus_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
return mpage_writepages(mapping, wbc, hfsplus_get_block);
}
const 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,
};
const 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);
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 generic_permission(inode, mask, NULL);
}
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)) {
mutex_lock(&inode->i_mutex);
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);
}
mutex_unlock(&inode->i_mutex);
}
return 0;
}
extern const struct inode_operations hfsplus_dir_inode_operations;
extern struct file_operations hfsplus_dir_operations;
static const struct inode_operations hfsplus_file_inode_operations = {
.lookup = hfsplus_file_lookup,
.truncate = hfsplus_file_truncate,
.permission = hfsplus_permission,
.setxattr = hfsplus_setxattr,
.getxattr = hfsplus_getxattr,
.listxattr = hfsplus_listxattr,
};
static const struct file_operations hfsplus_file_operations = {
.llseek = generic_file_llseek,
.read = do_sync_read,
.aio_read = generic_file_aio_read,
.write = do_sync_write,
.aio_write = generic_file_aio_write,
.mmap = generic_file_mmap,
.sendfile = generic_file_sendfile,
.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;
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_SEC;
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;
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).fs_blocks = 0;
HFSPLUS_I(inode).rsrc_inode = NULL;
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;
} 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);
HFSPLUS_I(inode).fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
inode_set_bytes(inode, HFSPLUS_I(inode).fs_blocks << 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;
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 = hfsp_mt2ut(folder->attribute_mod_date);
HFSPLUS_I(inode).create_date = folder->create_date;
HFSPLUS_I(inode).fs_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 = hfsp_mt2ut(file->attribute_mod_date);
HFSPLUS_I(inode).create_date = file->create_date;
} else {
printk(KERN_ERR "hfs: bad catalog entry used to create inode\n");
res = -EIO;
}
return res;
}
int hfsplus_cat_write_inode(struct inode *inode)
{
struct inode *main_inode = inode;
struct hfs_find_data fd;
hfsplus_cat_entry entry;
if (HFSPLUS_IS_RSRC(inode))
main_inode = HFSPLUS_I(inode).rsrc_inode;
if (!main_inode->i_nlink)
return 0;
if (hfs_find_init(HFSPLUS_SB(main_inode->i_sb).cat_tree, &fd))
/* panic? */
return -EIO;
if (hfsplus_find_cat(main_inode->i_sb, main_inode->i_ino, &fd))
/* panic? */
goto out;
if (S_ISDIR(main_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 if (HFSPLUS_IS_RSRC(inode)) {
struct hfsplus_cat_file *file = &entry.file;
hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file));
hfsplus_inode_write_fork(inode, &file->rsrc_fork);
hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file));
} 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 (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);
return 0;
}

188
fs/hfsplus/ioctl.c Normal file
View File

@@ -0,0 +1,188 @@
/*
* linux/fs/hfsplus/ioctl.c
*
* Copyright (C) 2003
* Ethan Benson <erbenson@alaska.net>
* partially derived from linux/fs/ext2/ioctl.c
* Copyright (C) 1993, 1994, 1995
* Remy Card (card@masi.ibp.fr)
* Laboratoire MASI - Institut Blaise Pascal
* Universite Pierre et Marie Curie (Paris VI)
*
* hfsplus ioctls
*/
#include <linux/capability.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/xattr.h>
#include <asm/uaccess.h>
#include "hfsplus_fs.h"
int hfsplus_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
unsigned long arg)
{
unsigned int flags;
switch (cmd) {
case HFSPLUS_IOC_EXT2_GETFLAGS:
flags = 0;
if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_IMMUTABLE)
flags |= FS_IMMUTABLE_FL; /* EXT2_IMMUTABLE_FL */
if (HFSPLUS_I(inode).rootflags & HFSPLUS_FLG_APPEND)
flags |= FS_APPEND_FL; /* EXT2_APPEND_FL */
if (HFSPLUS_I(inode).userflags & HFSPLUS_FLG_NODUMP)
flags |= FS_NODUMP_FL; /* EXT2_NODUMP_FL */
return put_user(flags, (int __user *)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 __user *)arg))
return -EFAULT;
if (flags & (FS_IMMUTABLE_FL|FS_APPEND_FL) ||
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 & ~(FS_IMMUTABLE_FL|FS_APPEND_FL|FS_NODUMP_FL))
return -EOPNOTSUPP;
if (flags & FS_IMMUTABLE_FL) { /* 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 & FS_APPEND_FL) { /* 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 & FS_NODUMP_FL) /* EXT2_NODUMP_FL */
HFSPLUS_I(inode).userflags |= HFSPLUS_FLG_NODUMP;
else
HFSPLUS_I(inode).userflags &= ~HFSPLUS_FLG_NODUMP;
inode->i_ctime = CURRENT_TIME_SEC;
mark_inode_dirty(inode);
return 0;
}
default:
return -ENOTTY;
}
}
int hfsplus_setxattr(struct dentry *dentry, const char *name,
const void *value, size_t size, int flags)
{
struct inode *inode = dentry->d_inode;
struct hfs_find_data fd;
hfsplus_cat_entry entry;
struct hfsplus_cat_file *file;
int res;
if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
return -EOPNOTSUPP;
res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
if (res)
return res;
res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
if (res)
goto out;
hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file));
file = &entry.file;
if (!strcmp(name, "hfs.type")) {
if (size == 4)
memcpy(&file->user_info.fdType, value, 4);
else
res = -ERANGE;
} else if (!strcmp(name, "hfs.creator")) {
if (size == 4)
memcpy(&file->user_info.fdCreator, value, 4);
else
res = -ERANGE;
} else
res = -EOPNOTSUPP;
if (!res)
hfs_bnode_write(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file));
out:
hfs_find_exit(&fd);
return res;
}
ssize_t hfsplus_getxattr(struct dentry *dentry, const char *name,
void *value, size_t size)
{
struct inode *inode = dentry->d_inode;
struct hfs_find_data fd;
hfsplus_cat_entry entry;
struct hfsplus_cat_file *file;
ssize_t res = 0;
if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
return -EOPNOTSUPP;
if (size) {
res = hfs_find_init(HFSPLUS_SB(inode->i_sb).cat_tree, &fd);
if (res)
return res;
res = hfsplus_find_cat(inode->i_sb, inode->i_ino, &fd);
if (res)
goto out;
hfs_bnode_read(fd.bnode, &entry, fd.entryoffset,
sizeof(struct hfsplus_cat_file));
}
file = &entry.file;
if (!strcmp(name, "hfs.type")) {
if (size >= 4) {
memcpy(value, &file->user_info.fdType, 4);
res = 4;
} else
res = size ? -ERANGE : 4;
} else if (!strcmp(name, "hfs.creator")) {
if (size >= 4) {
memcpy(value, &file->user_info.fdCreator, 4);
res = 4;
} else
res = size ? -ERANGE : 4;
} else
res = -ENODATA;
out:
if (size)
hfs_find_exit(&fd);
return res;
}
#define HFSPLUS_ATTRLIST_SIZE (sizeof("hfs.creator")+sizeof("hfs.type"))
ssize_t hfsplus_listxattr(struct dentry *dentry, char *buffer, size_t size)
{
struct inode *inode = dentry->d_inode;
if (!S_ISREG(inode->i_mode) || HFSPLUS_IS_RSRC(inode))
return -EOPNOTSUPP;
if (!buffer || !size)
return HFSPLUS_ATTRLIST_SIZE;
if (size < HFSPLUS_ATTRLIST_SIZE)
return -ERANGE;
strcpy(buffer, "hfs.type");
strcpy(buffer + sizeof("hfs.type"), "hfs.creator");
return HFSPLUS_ATTRLIST_SIZE;
}

188
fs/hfsplus/options.c Normal file
View File

@@ -0,0 +1,188 @@
/*
* linux/fs/hfsplus/options.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Option parsing
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/parser.h>
#include <linux/nls.h>
#include <linux/mount.h>
#include <linux/seq_file.h>
#include "hfsplus_fs.h"
enum {
opt_creator, opt_type,
opt_umask, opt_uid, opt_gid,
opt_part, opt_session, opt_nls,
opt_nodecompose, opt_decompose,
opt_force, opt_err
};
static match_table_t tokens = {
{ opt_creator, "creator=%s" },
{ opt_type, "type=%s" },
{ opt_umask, "umask=%o" },
{ opt_uid, "uid=%u" },
{ opt_gid, "gid=%u" },
{ opt_part, "part=%u" },
{ opt_session, "session=%u" },
{ opt_nls, "nls=%s" },
{ opt_decompose, "decompose" },
{ opt_nodecompose, "nodecompose" },
{ opt_force, "force" },
{ opt_err, NULL }
};
/* Initialize an options object to reasonable defaults */
void hfsplus_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 inline int match_fourchar(substring_t *arg, u32 *result)
{
if (arg->to - arg->from != 4)
return -EINVAL;
memcpy(result, arg->from, 4);
return 0;
}
/* Parse options from mount. Returns 0 on failure */
/* input is the options passed to mount() as a string */
int hfsplus_parse_options(char *input, struct hfsplus_sb_info *sbi)
{
char *p;
substring_t args[MAX_OPT_ARGS];
int tmp, token;
if (!input)
goto done;
while ((p = strsep(&input, ",")) != NULL) {
if (!*p)
continue;
token = match_token(p, tokens, args);
switch (token) {
case opt_creator:
if (match_fourchar(&args[0], &sbi->creator)) {
printk(KERN_ERR "hfs: creator requires a 4 character value\n");
return 0;
}
break;
case opt_type:
if (match_fourchar(&args[0], &sbi->type)) {
printk(KERN_ERR "hfs: type requires a 4 character value\n");
return 0;
}
break;
case opt_umask:
if (match_octal(&args[0], &tmp)) {
printk(KERN_ERR "hfs: umask requires a value\n");
return 0;
}
sbi->umask = (umode_t)tmp;
break;
case opt_uid:
if (match_int(&args[0], &tmp)) {
printk(KERN_ERR "hfs: uid requires an argument\n");
return 0;
}
sbi->uid = (uid_t)tmp;
break;
case opt_gid:
if (match_int(&args[0], &tmp)) {
printk(KERN_ERR "hfs: gid requires an argument\n");
return 0;
}
sbi->gid = (gid_t)tmp;
break;
case opt_part:
if (match_int(&args[0], &sbi->part)) {
printk(KERN_ERR "hfs: part requires an argument\n");
return 0;
}
break;
case opt_session:
if (match_int(&args[0], &sbi->session)) {
printk(KERN_ERR "hfs: session requires an argument\n");
return 0;
}
break;
case opt_nls:
if (sbi->nls) {
printk(KERN_ERR "hfs: unable to change nls mapping\n");
return 0;
}
p = match_strdup(&args[0]);
sbi->nls = load_nls(p);
if (!sbi->nls) {
printk(KERN_ERR "hfs: unable to load nls mapping \"%s\"\n", p);
kfree(p);
return 0;
}
kfree(p);
break;
case opt_decompose:
sbi->flags &= ~HFSPLUS_SB_NODECOMPOSE;
break;
case opt_nodecompose:
sbi->flags |= HFSPLUS_SB_NODECOMPOSE;
break;
case opt_force:
sbi->flags |= HFSPLUS_SB_FORCE;
break;
default:
return 0;
}
}
done:
if (!sbi->nls) {
/* try utf8 first, as this is the old default behaviour */
sbi->nls = load_nls("utf8");
if (!sbi->nls)
sbi->nls = load_nls_default();
if (!sbi->nls)
return 0;
}
return 1;
}
int hfsplus_show_options(struct seq_file *seq, struct vfsmount *mnt)
{
struct hfsplus_sb_info *sbi = &HFSPLUS_SB(mnt->mnt_sb);
if (sbi->creator != HFSPLUS_DEF_CR_TYPE)
seq_printf(seq, ",creator=%.4s", (char *)&sbi->creator);
if (sbi->type != HFSPLUS_DEF_CR_TYPE)
seq_printf(seq, ",type=%.4s", (char *)&sbi->type);
seq_printf(seq, ",umask=%o,uid=%u,gid=%u", sbi->umask, sbi->uid, sbi->gid);
if (sbi->part >= 0)
seq_printf(seq, ",part=%u", sbi->part);
if (sbi->session >= 0)
seq_printf(seq, ",session=%u", sbi->session);
if (sbi->nls)
seq_printf(seq, ",nls=%s", sbi->nls->charset);
if (sbi->flags & HFSPLUS_SB_NODECOMPOSE)
seq_printf(seq, ",nodecompose");
return 0;
}

133
fs/hfsplus/part_tbl.c Normal file
View File

@@ -0,0 +1,133 @@
/*
* linux/fs/hfsplus/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 {
__be16 pmSig; /* signature */
__be16 reSigPad; /* padding */
__be32 pmMapBlkCnt; /* partition blocks count */
__be32 pmPyPartStart; /* physical block start of partition */
__be32 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 {
__be16 pdSig; /* Signature bytes */
struct old_pmap_entry {
__be32 pdStart;
__be32 pdSize;
__be32 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;
__be16 *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;
}

499
fs/hfsplus/super.c Normal file
View File

@@ -0,0 +1,499 @@
/*
* linux/fs/hfsplus/super.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/vfs.h>
#include <linux/nls.h>
static struct inode *hfsplus_alloc_inode(struct super_block *sb);
static void hfsplus_destroy_inode(struct inode *inode);
#include "hfsplus_fs.h"
static void hfsplus_read_inode(struct inode *inode)
{
struct hfs_find_data fd;
struct hfsplus_vh *vhdr;
int err;
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;
atomic_set(&HFSPLUS_I(inode).opencnt, 0);
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);
}
static int hfsplus_write_inode(struct inode *inode, int unused)
{
struct hfsplus_vh *vhdr;
int ret = 0;
dprint(DBG_INODE, "hfsplus_write_inode: %lu\n", inode->i_ino);
hfsplus_ext_write_extent(inode);
if (inode->i_ino >= HFSPLUS_FIRSTUSER_CNID) {
return hfsplus_cat_write_inode(inode);
}
vhdr = HFSPLUS_SB(inode->i_sb).s_vhdr;
switch (inode->i_ino) {
case HFSPLUS_ROOT_CNID:
ret = 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;
}
return ret;
}
static void hfsplus_clear_inode(struct inode *inode)
{
dprint(DBG_INODE, "hfsplus_clear_inode: %lu\n", inode->i_ino);
if (HFSPLUS_IS_RSRC(inode)) {
HFSPLUS_I(HFSPLUS_I(inode).rsrc_inode).rsrc_inode = NULL;
iput(HFSPLUS_I(inode).rsrc_inode);
}
}
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(KERN_DEBUG "hfs: 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(KERN_WARNING "hfs: 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_fs_info)
return;
if (!(sb->s_flags & MS_RDONLY) && HFSPLUS_SB(sb).s_vhdr) {
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);
sync_dirty_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);
if (HFSPLUS_SB(sb).nls)
unload_nls(HFSPLUS_SB(sb).nls);
kfree(sb->s_fs_info);
sb->s_fs_info = NULL;
}
static int hfsplus_statfs(struct dentry *dentry, struct kstatfs *buf)
{
struct super_block *sb = dentry->d_sb;
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;
}
static 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;
struct hfsplus_sb_info sbi;
memset(&sbi, 0, sizeof(struct hfsplus_sb_info));
sbi.nls = HFSPLUS_SB(sb).nls;
if (!hfsplus_parse_options(data, &sbi))
return -EINVAL;
if (!(vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_UNMNT))) {
printk(KERN_WARNING "hfs: filesystem was not cleanly unmounted, "
"running fsck.hfsplus is recommended. leaving read-only.\n");
sb->s_flags |= MS_RDONLY;
*flags |= MS_RDONLY;
} else if (sbi.flags & HFSPLUS_SB_FORCE) {
/* nothing */
} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
printk(KERN_WARNING "hfs: filesystem is marked locked, leaving read-only.\n");
sb->s_flags |= MS_RDONLY;
*flags |= MS_RDONLY;
} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
printk(KERN_WARNING "hfs: filesystem is marked journaled, leaving read-only.\n");
sb->s_flags |= MS_RDONLY;
*flags |= MS_RDONLY;
}
}
return 0;
}
static const 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,
.show_options = hfsplus_show_options,
};
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 inode *root;
struct qstr str;
struct nls_table *nls = NULL;
int err = -EINVAL;
sbi = kmalloc(sizeof(struct hfsplus_sb_info), GFP_KERNEL);
if (!sbi)
return -ENOMEM;
memset(sbi, 0, sizeof(HFSPLUS_SB(sb)));
sb->s_fs_info = sbi;
INIT_HLIST_HEAD(&sbi->rsrc_inodes);
hfsplus_fill_defaults(sbi);
if (!hfsplus_parse_options(data, sbi)) {
printk(KERN_ERR "hfs: unable to parse mount options\n");
err = -EINVAL;
goto cleanup;
}
/* temporarily use utf8 to correctly find the hidden dir below */
nls = sbi->nls;
sbi->nls = load_nls("utf8");
if (!sbi->nls) {
printk(KERN_ERR "hfs: unable to load nls for utf8\n");
err = -EINVAL;
goto cleanup;
}
/* Grab the volume header */
if (hfsplus_read_wrapper(sb)) {
if (!silent)
printk(KERN_WARNING "hfs: unable to find HFS+ superblock\n");
err = -EINVAL;
goto cleanup;
}
vhdr = HFSPLUS_SB(sb).s_vhdr;
/* Copy parts of the volume header into the superblock */
sb->s_magic = HFSPLUS_VOLHEAD_SIG;
if (be16_to_cpu(vhdr->version) < HFSPLUS_MIN_VERSION ||
be16_to_cpu(vhdr->version) > HFSPLUS_CURRENT_VERSION) {
printk(KERN_ERR "hfs: wrong filesystem version\n");
goto cleanup;
}
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_UNMNT))) {
printk(KERN_WARNING "hfs: Filesystem was not cleanly unmounted, "
"running fsck.hfsplus is recommended. mounting read-only.\n");
sb->s_flags |= MS_RDONLY;
} else if (sbi->flags & HFSPLUS_SB_FORCE) {
/* nothing */
} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_SOFTLOCK)) {
printk(KERN_WARNING "hfs: Filesystem is marked locked, mounting read-only.\n");
sb->s_flags |= MS_RDONLY;
} else if (vhdr->attributes & cpu_to_be32(HFSPLUS_VOL_JOURNALED)) {
printk(KERN_WARNING "hfs: write access to a jounaled filesystem is not supported, "
"use the force option at your own risk, mounting read-only.\n");
sb->s_flags |= MS_RDONLY;
}
sbi->flags &= ~HFSPLUS_SB_FORCE;
/* Load metadata objects (B*Trees) */
HFSPLUS_SB(sb).ext_tree = hfs_btree_open(sb, HFSPLUS_EXT_CNID);
if (!HFSPLUS_SB(sb).ext_tree) {
printk(KERN_ERR "hfs: 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) {
printk(KERN_ERR "hfs: failed to load catalog file\n");
goto cleanup;
}
HFSPLUS_SB(sb).alloc_file = iget(sb, HFSPLUS_ALLOC_CNID);
if (!HFSPLUS_SB(sb).alloc_file) {
printk(KERN_ERR "hfs: failed to load allocation file\n");
goto cleanup;
}
/* Load the root directory */
root = iget(sb, HFSPLUS_ROOT_CNID);
sb->s_root = d_alloc_root(root);
if (!sb->s_root) {
printk(KERN_ERR "hfs: failed to load root directory\n");
iput(root);
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(sb, 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);
sync_dirty_buffer(HFSPLUS_SB(sb).s_vhbh);
if (!HFSPLUS_SB(sb).hidden_dir) {
printk(KERN_DEBUG "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:
unload_nls(sbi->nls);
sbi->nls = nls;
return 0;
cleanup:
hfsplus_put_super(sb);
if (nls)
unload_nls(nls);
return err;
}
MODULE_AUTHOR("Brad Boyer");
MODULE_DESCRIPTION("Extended Macintosh Filesystem");
MODULE_LICENSE("GPL");
static struct kmem_cache *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, GFP_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 int hfsplus_get_sb(struct file_system_type *fs_type,
int flags, const char *dev_name, void *data,
struct vfsmount *mnt)
{
return get_sb_bdev(fs_type, flags, dev_name, data, hfsplus_fill_super,
mnt);
}
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, struct kmem_cache *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);
kmem_cache_destroy(hfsplus_inode_cachep);
}
module_init(init_hfsplus_fs)
module_exit(exit_hfsplus_fs)

3245
fs/hfsplus/tables.c Normal file

File diff suppressed because it is too large Load Diff

299
fs/hfsplus/unicode.c Normal file
View File

@@ -0,0 +1,299 @@
/*
* linux/fs/hfsplus/unicode.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handler routines for unicode strings
*/
#include <linux/types.h>
#include <linux/nls.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
/* Fold the case of a unicode char, given the 16 bit value */
/* Returns folded char, or 0 if ignorable */
static inline u16 case_fold(u16 c)
{
u16 tmp;
tmp = hfsplus_case_fold_table[c >> 8];
if (tmp)
tmp = hfsplus_case_fold_table[tmp + (c & 0xff)];
else
tmp = c;
return tmp;
}
/* Compare unicode strings, return values like normal strcmp */
int hfsplus_strcasecmp(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;
}
}
/* Compare names as a sequence of 16-bit unsigned integers */
int hfsplus_strcmp(const struct hfsplus_unistr *s1,
const struct hfsplus_unistr *s2)
{
u16 len1, len2, c1, c2;
const hfsplus_unichr *p1, *p2;
int len;
len1 = be16_to_cpu(s1->length);
len2 = be16_to_cpu(s2->length);
p1 = s1->unicode;
p2 = s2->unicode;
for (len = min(len1, len2); len > 0; len--) {
c1 = be16_to_cpu(*p1);
c2 = be16_to_cpu(*p2);
if (c1 != c2)
return c1 < c2 ? -1 : 1;
p1++;
p2++;
}
return len1 < len2 ? -1 :
len1 > len2 ? 1 : 0;
}
#define Hangul_SBase 0xac00
#define Hangul_LBase 0x1100
#define Hangul_VBase 0x1161
#define Hangul_TBase 0x11a7
#define Hangul_SCount 11172
#define Hangul_LCount 19
#define Hangul_VCount 21
#define Hangul_TCount 28
#define Hangul_NCount (Hangul_VCount * Hangul_TCount)
static u16 *hfsplus_compose_lookup(u16 *p, u16 cc)
{
int i, s, e;
s = 1;
e = p[1];
if (!e || cc < p[s * 2] || cc > p[e * 2])
return NULL;
do {
i = (s + e) / 2;
if (cc > p[i * 2])
s = i + 1;
else if (cc < p[i * 2])
e = i - 1;
else
return hfsplus_compose_table + p[i * 2 + 1];
} while (s <= e);
return NULL;
}
int hfsplus_uni2asc(struct super_block *sb, const struct hfsplus_unistr *ustr, char *astr, int *len_p)
{
const hfsplus_unichr *ip;
struct nls_table *nls = HFSPLUS_SB(sb).nls;
u8 *op;
u16 cc, c0, c1;
u16 *ce1, *ce2;
int i, len, ustrlen, res, compose;
op = astr;
ip = ustr->unicode;
ustrlen = be16_to_cpu(ustr->length);
len = *len_p;
ce1 = NULL;
compose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE);
while (ustrlen > 0) {
c0 = be16_to_cpu(*ip++);
ustrlen--;
/* search for single decomposed char */
if (likely(compose))
ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c0);
if (ce1 && (cc = ce1[0])) {
/* start of a possibly decomposed Hangul char */
if (cc != 0xffff)
goto done;
if (!ustrlen)
goto same;
c1 = be16_to_cpu(*ip) - Hangul_VBase;
if (c1 < Hangul_VCount) {
/* compose the Hangul char */
cc = (c0 - Hangul_LBase) * Hangul_VCount;
cc = (cc + c1) * Hangul_TCount;
cc += Hangul_SBase;
ip++;
ustrlen--;
if (!ustrlen)
goto done;
c1 = be16_to_cpu(*ip) - Hangul_TBase;
if (c1 > 0 && c1 < Hangul_TCount) {
cc += c1;
ip++;
ustrlen--;
}
goto done;
}
}
while (1) {
/* main loop for common case of not composed chars */
if (!ustrlen)
goto same;
c1 = be16_to_cpu(*ip);
if (likely(compose))
ce1 = hfsplus_compose_lookup(hfsplus_compose_table, c1);
if (ce1)
break;
switch (c0) {
case 0:
c0 = 0x2400;
break;
case '/':
c0 = ':';
break;
}
res = nls->uni2char(c0, op, len);
if (res < 0) {
if (res == -ENAMETOOLONG)
goto out;
*op = '?';
res = 1;
}
op += res;
len -= res;
c0 = c1;
ip++;
ustrlen--;
}
ce2 = hfsplus_compose_lookup(ce1, c0);
if (ce2) {
i = 1;
while (i < ustrlen) {
ce1 = hfsplus_compose_lookup(ce2, be16_to_cpu(ip[i]));
if (!ce1)
break;
i++;
ce2 = ce1;
}
if ((cc = ce2[0])) {
ip += i;
ustrlen -= i;
goto done;
}
}
same:
switch (c0) {
case 0:
cc = 0x2400;
break;
case '/':
cc = ':';
break;
default:
cc = c0;
}
done:
res = nls->uni2char(cc, op, len);
if (res < 0) {
if (res == -ENAMETOOLONG)
goto out;
*op = '?';
res = 1;
}
op += res;
len -= res;
}
res = 0;
out:
*len_p = (char *)op - astr;
return res;
}
int hfsplus_asc2uni(struct super_block *sb, struct hfsplus_unistr *ustr, const char *astr, int len)
{
struct nls_table *nls = HFSPLUS_SB(sb).nls;
int size, off, decompose;
wchar_t c;
u16 outlen = 0;
decompose = !(HFSPLUS_SB(sb).flags & HFSPLUS_SB_NODECOMPOSE);
while (outlen < HFSPLUS_MAX_STRLEN && len > 0) {
size = nls->char2uni(astr, len, &c);
if (size <= 0) {
c = '?';
size = 1;
}
astr += size;
len -= size;
switch (c) {
case 0x2400:
c = 0;
break;
case ':':
c = '/';
break;
}
if (c >= 0xc0 && decompose) {
off = hfsplus_decompose_table[(c >> 12) & 0xf];
if (!off)
goto done;
if (off == 0xffff) {
goto done;
}
off = hfsplus_decompose_table[off + ((c >> 8) & 0xf)];
if (!off)
goto done;
off = hfsplus_decompose_table[off + ((c >> 4) & 0xf)];
if (!off)
goto done;
off = hfsplus_decompose_table[off + (c & 0xf)];
size = off & 3;
if (!size)
goto done;
off /= 4;
if (outlen + size > HFSPLUS_MAX_STRLEN)
break;
do {
ustr->unicode[outlen++] = cpu_to_be16(hfsplus_decompose_table[off++]);
} while (--size > 0);
continue;
}
done:
ustr->unicode[outlen++] = cpu_to_be16(c);
}
ustr->length = cpu_to_be16(outlen);
if (len > 0)
return -ENAMETOOLONG;
return 0;
}

179
fs/hfsplus/wrapper.c Normal file
View File

@@ -0,0 +1,179 @@
/*
* linux/fs/hfsplus/wrapper.c
*
* Copyright (C) 2001
* Brad Boyer (flar@allandria.com)
* (C) 2003 Ardis Technologies <roman@ardistech.com>
*
* Handling of HFS wrappers around HFS+ volumes
*/
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/cdrom.h>
#include <linux/genhd.h>
#include <asm/unaligned.h>
#include "hfsplus_fs.h"
#include "hfsplus_raw.h"
struct hfsplus_wd {
u32 ablk_size;
u16 ablk_start;
u16 embed_start;
u16 embed_count;
};
static int hfsplus_read_mdb(void *bufptr, struct hfsplus_wd *wd)
{
u32 extent;
u16 attrib;
__be16 sig;
sig = *(__be16 *)(bufptr + HFSP_WRAPOFF_EMBEDSIG);
if (sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIG) &&
sig != cpu_to_be16(HFSPLUS_VOLHEAD_SIGX))
return 0;
attrib = be16_to_cpu(*(__be16 *)(bufptr + HFSP_WRAPOFF_ATTRIB));
if (!(attrib & HFSP_WRAP_ATTRIB_SLOCK) ||
!(attrib & HFSP_WRAP_ATTRIB_SPARED))
return 0;
wd->ablk_size = be32_to_cpu(*(__be32 *)(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(*(__be16 *)(bufptr + HFSP_WRAPOFF_ABLKSTART));
extent = be32_to_cpu(get_unaligned((__be32 *)(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;
if (vhdr->signature == cpu_to_be16(HFSPLUS_VOLHEAD_SIGX)) {
HFSPLUS_SB(sb).flags |= HFSPLUS_SB_HFSX;
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) != 0)
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(KERN_ERR "hfs: unable to set 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 (vhdr->signature != (HFSPLUS_SB(sb).flags & HFSPLUS_SB_HFSX ?
cpu_to_be16(HFSPLUS_VOLHEAD_SIGX) :
cpu_to_be16(HFSPLUS_VOLHEAD_SIG)))
goto error;
HFSPLUS_SB(sb).s_vhbh = bh;
HFSPLUS_SB(sb).s_vhdr = vhdr;
return 0;
error:
brelse(bh);
return -EINVAL;
}