4 * Copyright (C) 1995-1997 Paul H. Hargrove
5 * This file may be distributed under the terms of the GNU Public License.
7 * This file contains the code to manipulate the B-tree structure.
8 * The catalog and extents files are both B-trees.
10 * "XXX" in a comment is a note to myself to consider changing something.
12 * In function preconditions the term "valid" applied to a pointer to
13 * a structure means that the pointer is non-NULL and the structure it
14 * points to has all fields initialized to consistent values.
16 * The code in this file initializes some structures which contain
17 * pointers by calling memset(&foo, 0, sizeof(foo)).
18 * This produces the desired behavior only due to the non-ANSI
19 * assumption that the machine representation of NULL is all zeros.
22 #include "hfs_btree.h"
24 /*================ File-local functions ================*/
30 * This function deletes an entire linked list of bnodes, so it
31 * does not need to keep the linked list consistent as
32 * hfs_bnode_delete() does.
33 * Called by hfs_btree_init() for error cleanup and by hfs_btree_free().
35 * struct hfs_bnode *bn: pointer to the first (struct hfs_bnode) in
36 * the linked list to be deleted.
42 * 'bn' is NULL or points to a "valid" (struct hfs_bnode) with a 'prev'
45 * 'bn' and all (struct hfs_bnode)s in the chain of 'next' pointers
46 * are deleted, freeing the associated memory and hfs_buffer_put()ing
47 * the associated buffer.
49 static void hfs_bnode_ditch(struct hfs_bnode
*bn
) {
50 struct hfs_bnode
*tmp
;
51 #if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
52 extern int bnode_count
;
57 #if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
58 hfs_warn("deleting node %d from tree %d with count %d\n",
59 bn
->node
, (int)ntohl(bn
->tree
->entry
.cnid
), bn
->count
);
62 hfs_buffer_put(bn
->buf
); /* safe: checks for NULL argument */
64 /* free all but the header */
72 /*================ Global functions ================*/
78 * This function frees a (struct hfs_btree) obtained from hfs_btree_init().
79 * Called by hfs_put_super().
81 * struct hfs_btree *bt: pointer to the (struct hfs_btree) to free
87 * 'bt' is NULL or points to a "valid" (struct hfs_btree)
89 * If 'bt' points to a "valid" (struct hfs_btree) then all (struct
90 * hfs_bnode)s associated with 'bt' are freed by calling
91 * hfs_bnode_ditch() and the memory associated with the (struct
92 * hfs_btree) is freed.
93 * If 'bt' is NULL or not "valid" an error is printed and nothing
96 void hfs_btree_free(struct hfs_btree
*bt
)
100 if (bt
&& (bt
->magic
== HFS_BTREE_MAGIC
)) {
101 hfs_extent_free(&bt
->entry
.u
.file
.data_fork
);
103 for (lcv
=0; lcv
<HFS_CACHELEN
; ++lcv
) {
104 #if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
105 hfs_warn("deleting nodes from bucket %d:\n", lcv
);
107 hfs_bnode_ditch(bt
->cache
[lcv
]);
110 #if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
111 hfs_warn("deleting header and bitmap nodes\n");
113 hfs_bnode_ditch(&bt
->head
);
115 #if defined(DEBUG_BNODES) || defined(DEBUG_ALL)
116 hfs_warn("deleting root node\n");
118 hfs_bnode_ditch(bt
->root
);
122 hfs_warn("hfs_btree_free: corrupted hfs_btree.\n");
130 * Given some vital information from the MDB (HFS superblock),
131 * initializes the fields of a (struct hfs_btree).
133 * struct hfs_mdb *mdb: pointer to the MDB
134 * ino_t cnid: the CNID (HFS_CAT_CNID or HFS_EXT_CNID) of the B-tree
135 * hfs_u32 tsize: the size, in bytes, of the B-tree
136 * hfs_u32 csize: the size, in bytes, of the clump size for the B-tree
137 * Output Variable(s):
140 * (struct hfs_btree *): pointer to the initialized hfs_btree on success,
143 * 'mdb' points to a "valid" (struct hfs_mdb)
145 * Assuming the inputs are what they claim to be, no errors occur
146 * reading from disk, and no inconsistencies are noticed in the data
147 * read from disk, the return value is a pointer to a "valid"
148 * (struct hfs_btree). If there are errors reading from disk or
149 * inconsistencies are noticed in the data read from disk, then and
150 * all resources that were allocated are released and NULL is
151 * returned. If the inputs are not what they claim to be or if they
152 * are unnoticed inconsistencies in the data read from disk then the
153 * returned hfs_btree is probably going to lead to errors when it is
154 * used in a non-trivial way.
156 struct hfs_btree
* hfs_btree_init(struct hfs_mdb
*mdb
, ino_t cnid
,
158 hfs_u32 tsize
, hfs_u32 csize
)
160 struct hfs_btree
* bt
;
161 struct BTHdrRec
* th
;
162 struct hfs_bnode
* tmp
;
164 #if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
165 unsigned char *p
, *q
;
168 if (!mdb
|| !ext
|| !HFS_NEW(bt
)) {
172 bt
->magic
= HFS_BTREE_MAGIC
;
173 bt
->sys_mdb
= mdb
->sys_mdb
;
176 hfs_init_waitqueue(&bt
->wait
);
178 memset(bt
->cache
, 0, sizeof(bt
->cache
));
180 #if 0 /* this is a fake entry. so we don't need to initialize it. */
181 memset(&bt
->entry
, 0, sizeof(bt
->entry
));
182 hfs_init_waitqueue(&bt
->entry
.wait
);
183 INIT_LIST_HEAD(&bt
->entry
.hash
);
184 INIT_LIST_HEAD(&bt
->entry
.list
);
188 bt
->entry
.cnid
= cnid
;
189 bt
->entry
.type
= HFS_CDR_FIL
;
190 bt
->entry
.u
.file
.magic
= HFS_FILE_MAGIC
;
191 bt
->entry
.u
.file
.clumpablks
= (csize
/ mdb
->alloc_blksz
)
192 >> HFS_SECTOR_SIZE_BITS
;
193 bt
->entry
.u
.file
.data_fork
.entry
= &bt
->entry
;
194 bt
->entry
.u
.file
.data_fork
.lsize
= tsize
;
195 bt
->entry
.u
.file
.data_fork
.psize
= tsize
>> HFS_SECTOR_SIZE_BITS
;
196 bt
->entry
.u
.file
.data_fork
.fork
= HFS_FK_DATA
;
197 hfs_extent_in(&bt
->entry
.u
.file
.data_fork
, ext
);
199 hfs_bnode_read(&bt
->head
, bt
, 0, HFS_STICKY
);
200 if (!hfs_buffer_ok(bt
->head
.buf
)) {
203 th
= (struct BTHdrRec
*)((char *)hfs_buffer_data(bt
->head
.buf
) +
204 sizeof(struct NodeDescriptor
));
206 /* read in the bitmap nodes (if any) */
208 while ((next
= tmp
->ndFLink
)) {
209 if (!HFS_NEW(tmp
->next
)) {
212 hfs_bnode_read(tmp
->next
, bt
, next
, HFS_STICKY
);
213 if (!hfs_buffer_ok(tmp
->next
->buf
)) {
216 tmp
->next
->prev
= tmp
;
220 if (hfs_get_ns(th
->bthNodeSize
) != htons(HFS_SECTOR_SIZE
)) {
221 hfs_warn("hfs_btree_init: bthNodeSize!=512 not supported\n");
225 if (cnid
== htonl(HFS_CAT_CNID
)) {
226 bt
->compare
= (hfs_cmpfn
)hfs_cat_compare
;
227 } else if (cnid
== htonl(HFS_EXT_CNID
)) {
228 bt
->compare
= (hfs_cmpfn
)hfs_ext_compare
;
232 bt
->bthDepth
= hfs_get_hs(th
->bthDepth
);
233 bt
->bthRoot
= hfs_get_hl(th
->bthRoot
);
234 bt
->bthNRecs
= hfs_get_hl(th
->bthNRecs
);
235 bt
->bthFNode
= hfs_get_hl(th
->bthFNode
);
236 bt
->bthLNode
= hfs_get_hl(th
->bthLNode
);
237 bt
->bthNNodes
= hfs_get_hl(th
->bthNNodes
);
238 bt
->bthFree
= hfs_get_hl(th
->bthFree
);
239 bt
->bthKeyLen
= hfs_get_hs(th
->bthKeyLen
);
241 #if defined(DEBUG_HEADER) || defined(DEBUG_ALL)
242 hfs_warn("bthDepth %d\n", bt
->bthDepth
);
243 hfs_warn("bthRoot %d\n", bt
->bthRoot
);
244 hfs_warn("bthNRecs %d\n", bt
->bthNRecs
);
245 hfs_warn("bthFNode %d\n", bt
->bthFNode
);
246 hfs_warn("bthLNode %d\n", bt
->bthLNode
);
247 hfs_warn("bthKeyLen %d\n", bt
->bthKeyLen
);
248 hfs_warn("bthNNodes %d\n", bt
->bthNNodes
);
249 hfs_warn("bthFree %d\n", bt
->bthFree
);
250 p
= (unsigned char *)hfs_buffer_data(bt
->head
.buf
);
251 q
= p
+ HFS_SECTOR_SIZE
;
253 hfs_warn("%02x %02x %02x %02x %02x %02x %02x %02x "
254 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
255 *p
++, *p
++, *p
++, *p
++, *p
++, *p
++, *p
++, *p
++,
256 *p
++, *p
++, *p
++, *p
++, *p
++, *p
++, *p
++, *p
++);
260 /* Read in the root if it exists.
261 The header always exists, but the root exists only if the
263 if (bt
->bthDepth
&& bt
->bthRoot
) {
264 if (!HFS_NEW(bt
->root
)) {
267 hfs_bnode_read(bt
->root
, bt
, bt
->bthRoot
, HFS_STICKY
);
268 if (!hfs_buffer_ok(bt
->root
->buf
)) {
278 hfs_bnode_ditch(bt
->root
);
280 hfs_bnode_ditch(&bt
->head
);
289 * Called to write a possibly dirty btree back to disk.
291 void hfs_btree_commit(struct hfs_btree
*bt
, hfs_byte_t ext
[12], hfs_lword_t size
)
295 th
= (struct BTHdrRec
*)((char *)hfs_buffer_data(bt
->head
.buf
) +
296 sizeof(struct NodeDescriptor
));
298 hfs_put_hs(bt
->bthDepth
, th
->bthDepth
);
299 hfs_put_hl(bt
->bthRoot
, th
->bthRoot
);
300 hfs_put_hl(bt
->bthNRecs
, th
->bthNRecs
);
301 hfs_put_hl(bt
->bthFNode
, th
->bthFNode
);
302 hfs_put_hl(bt
->bthLNode
, th
->bthLNode
);
303 hfs_put_hl(bt
->bthNNodes
, th
->bthNNodes
);
304 hfs_put_hl(bt
->bthFree
, th
->bthFree
);
305 hfs_buffer_dirty(bt
->head
.buf
);
308 * Commit the bnodes which are not cached.
309 * The map nodes don't need to be committed here because
310 * they are committed every time they are changed.
312 hfs_bnode_commit(&bt
->head
);
314 hfs_bnode_commit(bt
->root
);
318 hfs_put_hl(bt
->bthNNodes
<< HFS_SECTOR_SIZE_BITS
, size
);
319 hfs_extent_out(&bt
->entry
.u
.file
.data_fork
, ext
);
320 /* hfs_buffer_dirty(mdb->buf); (Done by caller) */