patch-2.4.22 linux-2.4.22/fs/hfsplus/btree.c

Next file: linux-2.4.22/fs/hfsplus/catalog.c
Previous file: linux-2.4.22/fs/hfsplus/brec.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.21/fs/hfsplus/btree.c linux-2.4.22/fs/hfsplus/btree.c
@@ -0,0 +1,317 @@
+/*
+ *  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 <asm/div64.h>
+
+#include "hfsplus_fs.h"
+#include "hfsplus_raw.h"
+
+/* Release resources used by a btree */
+void hfsplus_close_btree(struct hfsplus_btree *tree)
+{
+	hfsplus_bnode *node;
+	int i;
+
+	if (!tree)
+		return;
+
+	for (i = 0; i < NODE_HASH_SIZE; i++) {
+		while ((node = tree->node_hash[i])) {
+			tree->node_hash[i] = node->next_hash;
+			if (atomic_read(&node->refcnt))
+				printk("HFS+: node %d:%d still has %d user(s)!\n",
+					node->tree->cnid, node->this, atomic_read(&node->refcnt));
+			hfsplus_bnode_free(node);
+			tree->node_hash_cnt--;
+		}
+	}
+	iput(tree->inode);
+	kfree(tree);
+}
+
+/* Fill in extra data in tree structure from header node */
+static void hfsplus_read_treeinfo(hfsplus_btree *tree, hfsplus_btree_head *hdr)
+{
+	unsigned int shift, size;
+
+	if (!tree || !hdr)
+		return;
+
+	tree->root = be32_to_cpu(hdr->root);
+	tree->leaf_count = be32_to_cpu(hdr->leaf_count);
+	tree->leaf_head = be32_to_cpu(hdr->leaf_head);
+	tree->leaf_tail = be32_to_cpu(hdr->leaf_tail);
+	tree->node_count = be32_to_cpu(hdr->node_count);
+	tree->free_nodes = be32_to_cpu(hdr->free_nodes);
+	tree->attributes = be32_to_cpu(hdr->attributes);
+	tree->node_size = be16_to_cpu(hdr->node_size);
+	tree->max_key_len = be16_to_cpu(hdr->max_key_len);
+	tree->depth = be16_to_cpu(hdr->depth);
+
+	size = tree->node_size;
+	if (size & (size - 1))
+		/* panic */;
+	for (shift = 0; size >>= 1; shift += 1)
+		;
+	tree->node_size_shift = shift;
+
+	tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+}
+
+static void hfsplus_write_treeinfo(hfsplus_btree *tree, hfsplus_btree_head *hdr)
+{
+	hdr->root = cpu_to_be32(tree->root);
+	hdr->leaf_count = cpu_to_be32(tree->leaf_count);
+	hdr->leaf_head = cpu_to_be32(tree->leaf_head);
+	hdr->leaf_tail = cpu_to_be32(tree->leaf_tail);
+	hdr->node_count = cpu_to_be32(tree->node_count);
+	hdr->free_nodes = cpu_to_be32(tree->free_nodes);
+	hdr->attributes = cpu_to_be32(tree->attributes);
+	hdr->depth = cpu_to_be16(tree->depth);
+}
+
+/* Get a reference to a B*Tree and do some initial checks */
+hfsplus_btree *hfsplus_open_btree(struct super_block *sb, u32 id)
+{
+	hfsplus_btree *tree;
+	hfsplus_btree_head *head;
+	struct address_space *mapping;
+	struct page *page;
+
+	tree = kmalloc(sizeof(struct hfsplus_btree), GFP_KERNEL);
+	if (!tree)
+		return NULL;
+	memset(tree, 0, sizeof(struct hfsplus_btree));
+
+	init_MUTEX(&tree->tree_lock);
+	spin_lock_init(&tree->hash_lock);
+	/* Set the correct compare function */
+	tree->sb = sb;
+	tree->cnid = id;
+	if (id == HFSPLUS_EXT_CNID) {
+		tree->keycmp = hfsplus_cmp_ext_key;
+	} else if (id == HFSPLUS_CAT_CNID) {
+		tree->keycmp = hfsplus_cmp_cat_key;
+	} else {
+		printk("HFS+-fs: unknown B*Tree requested\n");
+		goto free_tree;
+	}
+	tree->inode = iget(sb, id);
+	if (!tree->inode)
+		goto free_tree;
+
+	mapping = tree->inode->i_mapping;
+	page = grab_cache_page(mapping, 0);
+	if (!page)
+		goto free_tree;
+	if (!PageUptodate(page)) {
+		if (mapping->a_ops->readpage(NULL, page))
+			goto fail_page;
+		wait_on_page_locked(page);
+		if (!PageUptodate(page))
+			goto fail_page;
+	} else
+		unlock_page(page);
+
+	/* Load the header */
+	head = (hfsplus_btree_head *)(kmap(page) + sizeof(hfsplus_btree_node_desc));
+	hfsplus_read_treeinfo(tree, head);
+	kunmap(page);
+	page_cache_release(page);
+	return tree;
+
+ fail_page:
+	page_cache_release(page);
+ free_tree:
+	iput(tree->inode);
+	kfree(tree);
+	return NULL;
+}
+
+void hfsplus_write_btree(struct hfsplus_btree *tree)
+{
+	hfsplus_btree_head *head;
+	hfsplus_bnode *node;
+	struct page *page;
+
+	node = hfsplus_find_bnode(tree, 0);
+	if (!node)
+		/* panic? */
+		return;
+	/* Load the header */
+	page = node->page[0];
+	head = (hfsplus_btree_head *)(kmap(page) + sizeof(hfsplus_btree_node_desc));
+	hfsplus_write_treeinfo(tree, head);
+	kunmap(page);
+	set_page_dirty(page);
+	hfsplus_put_bnode(node);
+}
+
+hfsplus_bnode *hfsplus_btree_alloc_node(hfsplus_btree *tree)
+{
+	hfsplus_bnode *node;
+	struct page **pagep;
+	u32 nidx;
+	u16 idx, off, len;
+	u8 *data, byte, m;
+	int i;
+
+	while (!tree->free_nodes) {
+		loff_t size;
+		int res;
+
+		res = hfsplus_extend_file(tree->inode);
+		if (res)
+			return ERR_PTR(res);
+		HFSPLUS_I(tree->inode).total_blocks = HFSPLUS_I(tree->inode).alloc_blocks;
+		size = HFSPLUS_I(tree->inode).total_blocks;
+		size <<= tree->sb->s_blocksize_bits;
+		tree->inode->i_size = size;
+		do_div(size, (u32)tree->node_size);
+		tree->free_nodes = (u32)size - tree->node_count;
+		tree->node_count = size;
+	}
+
+	nidx = 0;
+	node = hfsplus_find_bnode(tree, nidx);
+	len = hfsplus_brec_lenoff(node, 2, &off);
+
+	off += node->page_offset;
+	pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+	data = hfsplus_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);
+						hfsplus_kunmap(*pagep);
+						tree->free_nodes--;
+						mark_inode_dirty(tree->inode);
+						hfsplus_put_bnode(node);
+						return hfsplus_create_bnode(tree, idx);
+					}
+				}
+			}
+			if (++off >= PAGE_CACHE_SIZE) {
+				hfsplus_kunmap(*pagep++);
+				data = hfsplus_kmap(*pagep);
+				off = 0;
+			}
+			idx += 8;
+			len--;
+		}
+		nidx = node->next;
+		hfsplus_put_bnode(node);
+		if (!nidx) {
+			printk("need new bmap node...\n");
+			hfsplus_kunmap(*pagep);
+			return ERR_PTR(-ENOSPC);
+		}
+		node = hfsplus_find_bnode(tree, nidx);
+		len = hfsplus_brec_lenoff(node, 0, &off);
+
+		off += node->page_offset;
+		pagep = node->page + (off >> PAGE_CACHE_SHIFT);
+		data = hfsplus_kmap(*pagep);
+		off &= ~PAGE_CACHE_MASK;
+	}
+}
+
+void hfsplus_btree_remove_node(hfsplus_bnode *node)
+{
+	hfsplus_btree *tree;
+	hfsplus_bnode *tmp;
+	u32 cnid;
+
+	tree = node->tree;
+	if (node->prev) {
+		tmp = hfsplus_find_bnode(tree, node->prev);
+		tmp->next = node->next;
+		cnid = cpu_to_be32(tmp->next);
+		hfsplus_bnode_writebytes(tmp, &cnid, offsetof(hfsplus_btree_node_desc, next), 4);
+		hfsplus_put_bnode(tmp);
+	} else if (node->kind == HFSPLUS_NODE_LEAF)
+		tree->leaf_head = node->next;
+
+	if (node->next) {
+		tmp = hfsplus_find_bnode(tree, node->next);
+		tmp->prev = node->prev;
+		cnid = cpu_to_be32(tmp->prev);
+		hfsplus_bnode_writebytes(tmp, &cnid, offsetof(hfsplus_btree_node_desc, prev), 4);
+		hfsplus_put_bnode(tmp);
+	} else if (node->kind == HFSPLUS_NODE_LEAF)
+		tree->leaf_tail = node->prev;
+
+	// move down?
+	if (!node->prev && !node->next) {
+		printk("hfsplus_btree_del_level\n");
+	}
+	if (!node->parent) {
+		tree->root = 0;
+		tree->depth = 0;
+	}
+	set_bit(HFSPLUS_BNODE_DELETED, &node->flags);
+}
+
+void hfsplus_btree_free_node(hfsplus_bnode *node)
+{
+	hfsplus_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);
+	tree = node->tree;
+	nidx = node->this;
+	node = hfsplus_find_bnode(tree, 0);
+	len = hfsplus_brec_lenoff(node, 2, &off);
+	while (nidx >= len * 8) {
+		u32 i;
+
+		nidx -= len * 8;
+		i = node->next;
+		hfsplus_put_bnode(node);
+		if (!nidx)
+			/* panic */;
+		node = hfsplus_find_bnode(tree, nidx);
+		if (node->kind != HFSPLUS_NODE_MAP)
+			/* panic */;
+		len = hfsplus_brec_lenoff(node, 0, &off);
+	}
+	off += node->page_offset + nidx / 8;
+	page = node->page[off >> PAGE_CACHE_SHIFT];
+	data = hfsplus_kmap(page);
+	off &= ~PAGE_CACHE_MASK;
+	m = 1 << (~nidx & 7);
+	byte = data[off];
+	if (!(byte & m)) {
+		BUG();
+		/* panic */
+		hfsplus_kunmap(page);
+		hfsplus_put_bnode(node);
+		return;
+	}
+	data[off] = byte & ~m;
+	set_page_dirty(page);
+	hfsplus_kunmap(page);
+	hfsplus_put_bnode(node);
+	tree->free_nodes++;
+	mark_inode_dirty(tree->inode);
+}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)