2 * libhfs - library for reading and writing Macintosh HFS volumes
3 * The fucntions are used to handle the various forms of btrees
4 * found on HFS+ volumes.
6 * The fucntions are used to handle the various forms of btrees
7 * found on HFS+ volumes.
9 * Copyright (C) 2000 Klaus Halfmann <khalfmann@libra.de>
10 * Original 1996-1998 Robert Leslie <rob@mars.org>
11 * Additional work by Brad Boyer (flar@pants.nu)
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
28 * $Id: btree.c,v 1.14 2000/10/25 05:43:04 hasi Exp $
38 /* Read the node from the given buffer and swap the bytes.
40 * return pointer after reading the structure
42 static void* btree_readnode(btree_node_desc
* node
, void *p
)
44 node
->next
= bswabU32_inc(p
);
45 node
->prev
= bswabU32_inc(p
);
46 node
->kind
= bswabU8_inc(p
);
47 node
->height
= bswabU8_inc(p
);
48 node
->num_rec
= bswabU16_inc(p
);
49 node
->reserved
= bswabU16_inc(p
);
53 /* read a btree header from the given buffer and swap the bytes.
55 * return pointer after reading the structure
57 static void* btree_readhead(btree_head
* head
, void *p
)
60 head
->depth
= bswabU16_inc(p
);
61 head
->root
= bswabU32_inc(p
);
62 head
->leaf_count
= bswabU32_inc(p
);
63 head
->leaf_head
= bswabU32_inc(p
);
64 head
->leaf_tail
= bswabU32_inc(p
);
65 head
->node_size
= bswabU16_inc(p
);
66 head
->max_key_len
= bswabU16_inc(p
);
67 head
->node_count
= bswabU32_inc(p
);
68 head
->free_nodes
= bswabU32_inc(p
);
69 head
->reserved1
= bswabU16_inc(p
);
70 head
->clump_size
= bswabU32_inc(p
);
71 head
->btree_type
= bswabU8_inc(p
);
72 head
->reserved2
= bswabU8_inc(p
);
73 head
->attributes
= bswabU32_inc(p
);
74 // skip reserved bytes
76 // ((UInt32*) p) += 16;
81 /* Priority of the depth of the node compared to LRU value.
82 * Should be the average number of keys per node but these vary. */
83 #define DEPTH_FACTOR 1000
85 /* Cache size is height of tree + this value
86 * Really big numbers wont help in case of ls -R
88 #define EXTRA_CACHESIZE 3
90 /* Not in use by now ... */
91 #define CACHE_DIRTY 0x0001
93 /* Intialize cache with default cache Size,
94 * must call node_cache_close to deallocate memory */
95 static int node_cache_init(node_cache
* cache
, btree
* tree
, int size
)
101 cache
->currindex
= 0;
102 nodebufsize
= tree
->head
.node_size
+ sizeof(node_buf
);
103 buf
= malloc(size
*(sizeof(node_entry
) + nodebufsize
));
106 cache
-> nodebufsize
= nodebufsize
;
107 cache
-> entries
= (node_entry
*) buf
;
108 cache
-> buffers
= (char*) &cache
->entries
[size
];
109 bzero(cache
->entries
, size
*sizeof(node_entry
));
113 /* Like cache->buffers[i], since size of node_buf is variable */
114 static inline node_buf
* node_buf_get(node_cache
* cache
, int i
)
116 return (node_buf
*) (cache
->buffers
+ (i
* cache
->nodebufsize
));
119 /* flush the node at index */
120 static void node_cache_flush_node(node_cache
* cache
, int index
)
123 cache
-> entries
[index
].index
= 0; // invalidate entry
126 static void node_cache_close(node_cache
* cache
)
128 if (!cache
->entries
) // not (fully) intialized ?
130 free(cache
->entries
);
133 /* Load the cach node indentified by index with
134 * the node identified by node_index */
136 static node_buf
* node_cache_load_buf
137 (btree
* bt
, node_cache
* cache
, int index
, UInt16 node_index
)
139 node_buf
*result
= node_buf_get(cache
,index
);
140 UInt32 blkpernode
= bt
->blkpernode
;
141 UInt32 block
= node_index
* blkpernode
;
142 void* p
= volume_readfromfork(bt
->vol
, result
->node
, bt
->fork
,
143 block
, blkpernode
, HFSP_EXTENT_DATA
, bt
->cnid
);
144 node_entry
*e
= &cache
->entries
[index
];
147 return NULL
; // evil ...
149 result
->index
= node_index
;
150 btree_readnode(&result
->desc
, p
);
152 e
-> priority
= result
->desc
.height
* DEPTH_FACTOR
;
153 e
-> index
= node_index
;
157 /* Read node at given index, using cache.
159 node_buf
* btree_node_by_index(btree
* bt
, UInt16 index
)
161 node_cache
* cache
= &bt
->cache
;
162 int oldindex
, lruindex
;
163 int currindex
= cache
->currindex
;
167 // Shortcut acces to current node, will not change priorities
168 if (cache
->entries
[currindex
].index
== index
)
169 return node_buf_get(cache
,currindex
);
170 oldindex
= currindex
;
172 currindex
= cache
->size
;
174 lruindex
= oldindex
; // entry to be flushed when needed
175 prio
= 0; // current priority
176 while (currindex
!= oldindex
) // round robin
178 e
= &cache
->entries
[currindex
];
179 if (e
->index
== index
) // got it
181 if (e
->priority
!= 0) // already top, uuh
183 cache
->currindex
= currindex
;
184 return node_buf_get(cache
,currindex
);
190 lruindex
= currindex
;
191 break; // empty entry, load it
193 if (e
->priority
!= UINT_MAX
) // already least, uuh
196 if (prio
< e
->priority
)
198 lruindex
= currindex
;
202 currindex
= cache
->size
;
205 e
= &cache
->entries
[lruindex
];
206 cache
->currindex
= lruindex
;
207 if (e
->flags
& CACHE_DIRTY
)
208 node_cache_flush_node( cache
, lruindex
);
209 return node_cache_load_buf (bt
, cache
, lruindex
, index
);
212 /** intialize the btree with the first entry in the fork */
213 static int btree_init(btree
* bt
, volume
* vol
, hfsp_fork_raw
* fork
)
216 char buf
[vol
->blksize
];
218 btree_node_desc node
;
222 p
= volume_readfromfork(vol
, buf
, fork
, 0, 1,
223 HFSP_EXTENT_DATA
, bt
->cnid
);
226 p
= btree_readnode(&node
, p
);
227 if (node
.kind
!= HFSP_NODE_HEAD
)
228 return -1; // should not happen ?
229 btree_readhead(&bt
->head
, p
);
231 node_size
= bt
->head
.node_size
;
232 bt
->blkpernode
= node_size
/ vol
->blksize
;
234 if (bt
->blkpernode
== 0 || vol
->blksize
*
235 bt
->blkpernode
!= node_size
)
236 return -1; // should never happen ...
238 node_cache_init(&bt
->cache
, bt
, bt
->head
.depth
+ EXTRA_CACHESIZE
);
241 // bt->buf = malloc(node_size);
248 /** Intialize catalog btree, so that btree_close can safely be called. */
249 void btree_reset(btree
* bt
)
251 bt
->cache
.entries
= NULL
;
254 /** Intialize catalog btree */
255 int btree_init_cat(btree
* bt
, volume
* vol
, hfsp_fork_raw
* fork
)
257 int result
= btree_init(bt
,vol
,fork
); // super (...)
258 bt
->cnid
= HFSP_CAT_CNID
;
259 bt
->kcomp
= record_key_compare
;
260 bt
->kread
= record_readkey
;
264 /** Intialize catalog btree */
265 int btree_init_extent(btree
* bt
, volume
* vol
, hfsp_fork_raw
* fork
)
267 int result
= btree_init(bt
,vol
,fork
); // super (...)
268 bt
->cnid
= HFSP_EXT_CNID
;
269 bt
->kcomp
= record_extent_key_compare
;
270 bt
->kread
= record_extent_readkey
;
274 /** close the btree and free any resources */
275 void btree_close(btree
* bt
)
277 node_cache_close(&bt
->cache
);
281 /* returns pointer to key given by index in current node.
283 * Assumes that current node is not NODE_HEAD ...
285 void* btree_key_by_index(btree
* bt
, node_buf
* buf
, UInt16 index
)
287 UInt16 node_size
= bt
->head
.node_size
;
288 // The offsets are found at the end of the node ...
289 UInt16 off_pos
= node_size
- (index
+1) * sizeof(btree_record_offset
);
290 // position of offset at end of node
291 btree_record_offset
* offset
=
292 (btree_record_offset
*) (buf
->node
+ off_pos
);
294 // now we have the offset and can read the key ...
295 #ifdef CONFIG_LITTLE_ENDIAN
296 return buf
->node
+ bswabU16(*offset
);
298 return buf
->node
+ *offset
;
305 /* print btree header node information */
306 void btree_printhead(btree_head
* head
)
309 printf(" depth : %#X\n", head
->depth
);
310 printf(" root : %#lX\n", head
->root
);
311 printf(" leaf_count : %#lX\n", head
->leaf_count
);
312 printf(" leaf_head : %#lX\n", head
->leaf_head
);
313 printf(" leaf_tail : %#lX\n", head
->leaf_tail
);
314 printf(" node_size : %#X\n", head
->node_size
);
315 printf(" max_key_len : %#X\n", head
->max_key_len
);
316 printf(" node_count : %#lX\n", head
->node_count
);
317 printf(" free_nodes : %#lX\n", head
->free_nodes
);
318 printf(" reserved1 : %#X\n", head
->reserved1
);
319 printf(" clump_size : %#lX\n", head
->clump_size
);
320 printf(" btree_type : %#X\n", head
->btree_type
);
321 attr
= head
->attributes
;
322 printf(" reserved2 : %#X\n", head
->reserved2
);
323 if (attr
& HFSPLUS_BAD_CLOSE
)
324 printf(" HFSPLUS_BAD_CLOSE *** ");
326 printf(" !HFSPLUS_BAD_CLOSE");
327 if (attr
& HFSPLUS_TREE_BIGKEYS
)
328 printf(" HFSPLUS_TREE_BIGKEYS ");
330 printf(" !HFSPLUS_TREE_BIGKEYS");
331 if (attr
& HFSPLUS_TREE_VAR_NDXKEY_SIZE
)
332 printf(" HFSPLUS_TREE_VAR_NDXKEY_SIZE");
334 printf(" !HFSPLUS_TREE_VAR_NDXKEY_SIZE");
335 if (attr
& HFSPLUS_TREE_UNUSED
)
336 printf(" HFSPLUS_TREE_UNUSED ***\n");
340 /* Dump all the node information to stdout */
341 void btree_print(btree
* bt
)
343 btree_node_desc
* node
;
345 btree_printhead(&bt
->head
);
348 printf("next : %#lX\n", node
->next
);
349 printf("prev : %#lX\n", node
->prev
);
350 printf("height : %#X\n", node
->height
);
351 printf("num_rec : %#X\n", node
->num_rec
);
352 printf("reserved : %#X\n", node
->reserved
);
353 printf("height : %#X\n", node
->height
); switch(node
->kind
)
356 printf("HFSP_NODE_NDX\n");
358 case HFSP_NODE_HEAD
:
359 printf("HFSP_NODE_HEAD\n");
362 printf("HFSP_NODE_MAP\n");
364 case HFSP_NODE_LEAF
:
365 printf("HFSP_NODE_LEAF\n");
368 printf("*** Unknown Node type ***\n");