2 * linux/fs/hfsplus/btree.c
5 * Brad Boyer (flar@allandria.com)
6 * (C) 2003 Ardis Technologies <roman@ardistech.com>
8 * Handle opening/closing btree
11 #include <linux/slab.h>
12 #include <linux/pagemap.h>
13 #include <asm/div64.h>
15 #include "hfsplus_fs.h"
16 #include "hfsplus_raw.h"
18 /* Release resources used by a btree */
19 void hfsplus_close_btree(struct hfsplus_btree *tree)
27 for (i = 0; i < NODE_HASH_SIZE; i++) {
28 while ((node = tree->node_hash[i])) {
29 tree->node_hash[i] = node->next_hash;
30 if (atomic_read(&node->refcnt))
31 printk("HFS+: node %d:%d still has %d user(s)!\n",
32 node->tree->cnid, node->this, atomic_read(&node->refcnt));
33 hfsplus_bnode_free(node);
34 tree->node_hash_cnt--;
41 /* Fill in extra data in tree structure from header node */
42 static void hfsplus_read_treeinfo(hfsplus_btree *tree, hfsplus_btree_head *hdr)
44 unsigned int shift, size;
49 tree->root = be32_to_cpu(hdr->root);
50 tree->leaf_count = be32_to_cpu(hdr->leaf_count);
51 tree->leaf_head = be32_to_cpu(hdr->leaf_head);
52 tree->leaf_tail = be32_to_cpu(hdr->leaf_tail);
53 tree->node_count = be32_to_cpu(hdr->node_count);
54 tree->free_nodes = be32_to_cpu(hdr->free_nodes);
55 tree->attributes = be32_to_cpu(hdr->attributes);
56 tree->node_size = be16_to_cpu(hdr->node_size);
57 tree->max_key_len = be16_to_cpu(hdr->max_key_len);
58 tree->depth = be16_to_cpu(hdr->depth);
60 size = tree->node_size;
61 if (size & (size - 1))
63 for (shift = 0; size >>= 1; shift += 1)
65 tree->node_size_shift = shift;
67 tree->pages_per_bnode = (tree->node_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
70 static void hfsplus_write_treeinfo(hfsplus_btree *tree, hfsplus_btree_head *hdr)
72 hdr->root = cpu_to_be32(tree->root);
73 hdr->leaf_count = cpu_to_be32(tree->leaf_count);
74 hdr->leaf_head = cpu_to_be32(tree->leaf_head);
75 hdr->leaf_tail = cpu_to_be32(tree->leaf_tail);
76 hdr->node_count = cpu_to_be32(tree->node_count);
77 hdr->free_nodes = cpu_to_be32(tree->free_nodes);
78 hdr->attributes = cpu_to_be32(tree->attributes);
79 hdr->depth = cpu_to_be16(tree->depth);
82 /* Get a reference to a B*Tree and do some initial checks */
83 hfsplus_btree *hfsplus_open_btree(struct super_block *sb, u32 id)
86 hfsplus_btree_head *head;
87 struct address_space *mapping;
90 tree = kmalloc(sizeof(struct hfsplus_btree), GFP_KERNEL);
93 memset(tree, 0, sizeof(struct hfsplus_btree));
95 init_MUTEX(&tree->tree_lock);
96 spin_lock_init(&tree->hash_lock);
97 /* Set the correct compare function */
100 if (id == HFSPLUS_EXT_CNID) {
101 tree->keycmp = hfsplus_cmp_ext_key;
102 } else if (id == HFSPLUS_CAT_CNID) {
103 tree->keycmp = hfsplus_cmp_cat_key;
105 printk("HFS+-fs: unknown B*Tree requested\n");
108 tree->inode = iget(sb, id);
112 mapping = tree->inode->i_mapping;
113 page = grab_cache_page(mapping, 0);
116 if (!PageUptodate(page)) {
117 if (mapping->a_ops->readpage(NULL, page))
119 wait_on_page_locked(page);
120 if (!PageUptodate(page))
125 /* Load the header */
126 head = (hfsplus_btree_head *)(kmap(page) + sizeof(hfsplus_btree_node_desc));
127 hfsplus_read_treeinfo(tree, head);
129 page_cache_release(page);
133 page_cache_release(page);
140 void hfsplus_write_btree(struct hfsplus_btree *tree)
142 hfsplus_btree_head *head;
146 node = hfsplus_find_bnode(tree, 0);
150 /* Load the header */
151 page = node->page[0];
152 head = (hfsplus_btree_head *)(kmap(page) + sizeof(hfsplus_btree_node_desc));
153 hfsplus_write_treeinfo(tree, head);
155 set_page_dirty(page);
156 hfsplus_put_bnode(node);
159 hfsplus_bnode *hfsplus_btree_alloc_node(hfsplus_btree *tree)
168 while (!tree->free_nodes) {
172 res = hfsplus_extend_file(tree->inode);
175 HFSPLUS_I(tree->inode).total_blocks = HFSPLUS_I(tree->inode).alloc_blocks;
176 size = HFSPLUS_I(tree->inode).total_blocks;
177 size <<= tree->sb->s_blocksize_bits;
178 tree->inode->i_size = size;
179 do_div(size, (u32)tree->node_size);
180 tree->free_nodes = (u32)size - tree->node_count;
181 tree->node_count = size;
185 node = hfsplus_find_bnode(tree, nidx);
186 len = hfsplus_brec_lenoff(node, 2, &off);
188 off += node->page_offset;
189 pagep = node->page + (off >> PAGE_CACHE_SHIFT);
190 data = hfsplus_kmap(*pagep);
191 off &= ~PAGE_CACHE_MASK;
198 for (m = 0x80, i = 0; i < 8; m >>= 1, i++) {
202 set_page_dirty(*pagep);
203 hfsplus_kunmap(*pagep);
205 mark_inode_dirty(tree->inode);
206 hfsplus_put_bnode(node);
207 return hfsplus_create_bnode(tree, idx);
211 if (++off >= PAGE_CACHE_SIZE) {
212 hfsplus_kunmap(*pagep++);
213 data = hfsplus_kmap(*pagep);
220 hfsplus_put_bnode(node);
222 printk("need new bmap node...\n");
223 hfsplus_kunmap(*pagep);
224 return ERR_PTR(-ENOSPC);
226 node = hfsplus_find_bnode(tree, nidx);
227 len = hfsplus_brec_lenoff(node, 0, &off);
229 off += node->page_offset;
230 pagep = node->page + (off >> PAGE_CACHE_SHIFT);
231 data = hfsplus_kmap(*pagep);
232 off &= ~PAGE_CACHE_MASK;
236 void hfsplus_btree_remove_node(hfsplus_bnode *node)
244 tmp = hfsplus_find_bnode(tree, node->prev);
245 tmp->next = node->next;
246 cnid = cpu_to_be32(tmp->next);
247 hfsplus_bnode_writebytes(tmp, &cnid, offsetof(hfsplus_btree_node_desc, next), 4);
248 hfsplus_put_bnode(tmp);
249 } else if (node->kind == HFSPLUS_NODE_LEAF)
250 tree->leaf_head = node->next;
253 tmp = hfsplus_find_bnode(tree, node->next);
254 tmp->prev = node->prev;
255 cnid = cpu_to_be32(tmp->prev);
256 hfsplus_bnode_writebytes(tmp, &cnid, offsetof(hfsplus_btree_node_desc, prev), 4);
257 hfsplus_put_bnode(tmp);
258 } else if (node->kind == HFSPLUS_NODE_LEAF)
259 tree->leaf_tail = node->prev;
262 if (!node->prev && !node->next) {
263 printk("hfsplus_btree_del_level\n");
269 set_bit(HFSPLUS_BNODE_DELETED, &node->flags);
272 void hfsplus_btree_free_node(hfsplus_bnode *node)
280 dprint(DBG_BNODE_MOD, "btree_free_node: %u\n", node->this);
283 node = hfsplus_find_bnode(tree, 0);
284 len = hfsplus_brec_lenoff(node, 2, &off);
285 while (nidx >= len * 8) {
290 hfsplus_put_bnode(node);
293 node = hfsplus_find_bnode(tree, nidx);
294 if (node->kind != HFSPLUS_NODE_MAP)
296 len = hfsplus_brec_lenoff(node, 0, &off);
298 off += node->page_offset + nidx / 8;
299 page = node->page[off >> PAGE_CACHE_SHIFT];
300 data = hfsplus_kmap(page);
301 off &= ~PAGE_CACHE_MASK;
302 m = 1 << (~nidx & 7);
307 hfsplus_kunmap(page);
308 hfsplus_put_bnode(node);
311 data[off] = byte & ~m;
312 set_page_dirty(page);
313 hfsplus_kunmap(page);
314 hfsplus_put_bnode(node);
316 mark_inode_dirty(tree->inode);