2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)btree.c 1.3 04/06/17 joerg */
15 * hfsutils - tools for reading and writing Macintosh HFS volumes
16 * Copyright (C) 1996, 1997 Robert Leslie
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
46 * NAME: btree->getnode()
47 * DESCRIPTION: retrieve a numbered node from a B*-tree file
49 int bt_getnode(node *np)
52 block *bp = &np->data;
56 /* verify the node exists and is marked as in-use */
59 * XXX This is the original code. As np->nnum is unsigned, the
60 * XXX comparison for < 0 makes no sense.
61 * XXX Thanks for a hint from Mike.Sullivan@Eng.Sun.COM
63 /* if (np->nnum < 0 || (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes))*/
65 if (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes)
67 ERROR(EIO, "read nonexistent b*-tree node");
71 if (bt->map && ! BMTST(bt->map, np->nnum))
73 ERROR(EIO, "read unallocated b*-tree node");
77 if (f_getblock(&bt->f, np->nnum, bp) < 0)
82 d_fetchl(&ptr, (long *) &np->nd.ndFLink);
83 d_fetchl(&ptr, (long *) &np->nd.ndBLink);
84 d_fetchb(&ptr, (char *) &np->nd.ndType);
85 d_fetchb(&ptr, (char *) &np->nd.ndNHeight);
86 d_fetchw(&ptr, (short *) &np->nd.ndNRecs);
87 d_fetchw(&ptr, &np->nd.ndResv2);
89 if (np->nd.ndNRecs > HFS_MAXRECS)
91 ERROR(EIO, "too many b*-tree node records");
95 i = np->nd.ndNRecs + 1;
97 ptr = *bp + HFS_BLOCKSZ - (2 * i);
100 d_fetchw(&ptr, (short *) &np->roff[i]);
106 * NAME: btree->putnode()
107 * DESCRIPTION: store a numbered node into a B*-tree file
109 int bt_putnode(node *np)
112 block *bp = &np->data;
116 /* verify the node exists and is marked as in-use */
118 if (np->nnum && np->nnum >= bt->hdr.bthNNodes)
120 ERROR(EIO, "write nonexistent b*-tree node");
123 else if (bt->map && ! BMTST(bt->map, np->nnum))
125 ERROR(EIO, "write unallocated b*-tree node");
131 d_storel(&ptr, np->nd.ndFLink);
132 d_storel(&ptr, np->nd.ndBLink);
133 d_storeb(&ptr, np->nd.ndType);
134 d_storeb(&ptr, np->nd.ndNHeight);
135 d_storew(&ptr, np->nd.ndNRecs);
136 d_storew(&ptr, np->nd.ndResv2);
138 if (np->nd.ndNRecs > HFS_MAXRECS)
140 ERROR(EIO, "too many b*-tree node records");
144 i = np->nd.ndNRecs + 1;
146 ptr = *bp + HFS_BLOCKSZ - (2 * i);
149 d_storew(&ptr, np->roff[i]);
151 if (f_putblock(&bt->f, np->nnum, bp) < 0)
158 * NAME: btree->readhdr()
159 * DESCRIPTION: read the header node of a B*-tree
161 int bt_readhdr(btree *bt)
171 if (bt_getnode(&bt->hdrnd) < 0)
174 if (bt->hdrnd.nd.ndType != ndHdrNode ||
175 bt->hdrnd.nd.ndNRecs != 3 ||
176 bt->hdrnd.roff[0] != 0x00e ||
177 bt->hdrnd.roff[1] != 0x078 ||
178 bt->hdrnd.roff[2] != 0x0f8 ||
179 bt->hdrnd.roff[3] != 0x1f8)
181 ERROR(EIO, "malformed b*-tree header node");
185 /* read header record */
187 ptr = HFS_NODEREC(bt->hdrnd, 0);
189 d_fetchw(&ptr, (short *) &bt->hdr.bthDepth);
190 d_fetchl(&ptr, (long *) &bt->hdr.bthRoot);
191 d_fetchl(&ptr, (long *) &bt->hdr.bthNRecs);
192 d_fetchl(&ptr, (long *) &bt->hdr.bthFNode);
193 d_fetchl(&ptr, (long *) &bt->hdr.bthLNode);
194 d_fetchw(&ptr, (short *) &bt->hdr.bthNodeSize);
195 d_fetchw(&ptr, (short *) &bt->hdr.bthKeyLen);
196 d_fetchl(&ptr, (long *) &bt->hdr.bthNNodes);
197 d_fetchl(&ptr, (long *) &bt->hdr.bthFree);
199 for (i = 0; i < 76; ++i)
200 d_fetchb(&ptr, (char *) &bt->hdr.bthResv[i]);
202 if (bt->hdr.bthNodeSize != HFS_BLOCKSZ)
204 ERROR(EINVAL, "unsupported b*-tree node size");
208 /* read map record; construct btree bitmap */
209 /* don't set bt->map until we're done, since getnode() checks it */
211 map = ALLOC(char, HFS_MAP1SZ);
218 memcpy(map, HFS_NODEREC(bt->hdrnd, 2), HFS_MAP1SZ);
219 bt->mapsz = HFS_MAP1SZ;
221 /* read continuation map records, if any */
223 nnum = bt->hdrnd.nd.ndFLink;
233 if (bt_getnode(&n) < 0)
239 if (n.nd.ndType != ndMapNode ||
241 n.roff[0] != 0x00e ||
245 ERROR(EIO, "malformed b*-tree map node");
249 newmap = REALLOC(map, char, bt->mapsz + HFS_MAPXSZ);
258 memcpy(map + bt->mapsz, HFS_NODEREC(n, 0), HFS_MAPXSZ);
259 bt->mapsz += HFS_MAPXSZ;
270 * NAME: btree->writehdr()
271 * DESCRIPTION: write the header node of a B*-tree
273 int bt_writehdr(btree *bt)
277 unsigned long mapsz, nnum;
280 if (bt->hdrnd.bt != bt ||
281 bt->hdrnd.nnum != 0 ||
282 bt->hdrnd.nd.ndType != ndHdrNode ||
283 bt->hdrnd.nd.ndNRecs != 3)
286 ptr = HFS_NODEREC(bt->hdrnd, 0);
288 d_storew(&ptr, bt->hdr.bthDepth);
289 d_storel(&ptr, bt->hdr.bthRoot);
290 d_storel(&ptr, bt->hdr.bthNRecs);
291 d_storel(&ptr, bt->hdr.bthFNode);
292 d_storel(&ptr, bt->hdr.bthLNode);
293 d_storew(&ptr, bt->hdr.bthNodeSize);
294 d_storew(&ptr, bt->hdr.bthKeyLen);
295 d_storel(&ptr, bt->hdr.bthNNodes);
296 d_storel(&ptr, bt->hdr.bthFree);
298 for (i = 0; i < 76; ++i)
299 d_storeb(&ptr, bt->hdr.bthResv[i]);
301 memcpy(HFS_NODEREC(bt->hdrnd, 2), bt->map, HFS_MAP1SZ);
303 if (bt_putnode(&bt->hdrnd) < 0)
306 map = bt->map + HFS_MAP1SZ;
307 mapsz = bt->mapsz - HFS_MAP1SZ;
309 nnum = bt->hdrnd.nd.ndFLink;
317 ERROR(EIO, "truncated b*-tree map");
324 if (bt_getnode(&n) < 0)
327 if (n.nd.ndType != ndMapNode ||
329 n.roff[0] != 0x00e ||
332 ERROR(EIO, "malformed b*-tree map node");
336 memcpy(HFS_NODEREC(n, 0), map, HFS_MAPXSZ);
338 if (bt_putnode(&n) < 0)
347 bt->flags &= ~HFS_UPDATE_BTHDR;
352 /* High-Level B*-Tree Routines ============================================= */
355 * NAME: btree->space()
356 * DESCRIPTION: assert space for new records, or extend the file
358 int bt_space(btree *bt, unsigned int nrecs)
363 nnodes = nrecs * (bt->hdr.bthDepth + 1);
365 if (nnodes <= bt->hdr.bthFree)
368 /* make sure the extents tree has room too */
370 if (bt != &bt->f.vol->ext)
372 if (bt_space(&bt->f.vol->ext, 1) < 0)
376 space = f_alloc(&bt->f);
380 nnodes = space * (bt->f.vol->mdb.drAlBlkSiz / bt->hdr.bthNodeSize);
382 bt->hdr.bthNNodes += nnodes;
383 bt->hdr.bthFree += nnodes;
385 bt->flags |= HFS_UPDATE_BTHDR;
387 bt->f.vol->flags |= HFS_UPDATE_ALTMDB;
389 while (bt->hdr.bthNNodes > bt->mapsz * 8)
394 /* extend tree map */
396 newmap = REALLOC(bt->map, char, bt->mapsz + HFS_MAPXSZ);
403 memset(newmap + bt->mapsz, 0, HFS_MAPXSZ);
406 bt->mapsz += HFS_MAPXSZ;
408 n_init(&mapnd, bt, ndMapNode, 0);
409 if (n_new(&mapnd) < 0)
412 /* link the new map node */
414 if (bt->hdrnd.nd.ndFLink == 0)
416 bt->hdrnd.nd.ndFLink = mapnd.nnum;
417 mapnd.nd.ndBLink = 0;
424 n.nnum = bt->hdrnd.nd.ndFLink;
428 if (bt_getnode(&n) < 0)
431 if (n.nd.ndFLink == 0)
434 n.nnum = n.nd.ndFLink;
437 n.nd.ndFLink = mapnd.nnum;
438 mapnd.nd.ndBLink = n.nnum;
440 if (bt_putnode(&n) < 0)
444 mapnd.nd.ndNRecs = 1;
445 mapnd.roff[1] = 0x1fa;
447 if (bt_putnode(&mapnd) < 0)
455 * NAME: btree->insertx()
456 * DESCRIPTION: recursively locate a node and insert a record
458 int bt_insertx(node *np, unsigned char *record, int *reclen)
463 if (n_search(np, record))
465 ERROR(EIO, "b*-tree record already exists");
469 switch ((unsigned char) np->nd.ndType)
473 rec = HFS_NODEREC(*np, 0);
475 rec = HFS_NODEREC(*np, np->rnum);
478 child.nnum = d_getl(HFS_RECDATA(rec));
480 if (bt_getnode(&child) < 0 ||
481 bt_insertx(&child, record, reclen) < 0)
486 n_index(np->bt, HFS_NODEREC(child, 0), child.nnum, rec, 0);
488 return bt_putnode(np);
491 return *reclen ? n_insert(np, record, reclen) : 0;
494 return n_insert(np, record, reclen);
497 ERROR(EIO, "unexpected b*-tree node");
503 * NAME: btree->insert()
504 * DESCRIPTION: insert a new node record into a tree
506 int bt_insert(btree *bt, unsigned char *record, int reclen)
510 if (bt->hdr.bthRoot == 0)
512 /* create root node */
514 n_init(&root, bt, ndLeafNode, 1);
515 if (n_new(&root) < 0 ||
516 bt_putnode(&root) < 0)
519 bt->hdr.bthDepth = 1;
520 bt->hdr.bthRoot = root.nnum;
521 bt->hdr.bthFNode = root.nnum;
522 bt->hdr.bthLNode = root.nnum;
524 bt->flags |= HFS_UPDATE_BTHDR;
529 root.nnum = bt->hdr.bthRoot;
531 if (bt_getnode(&root) < 0)
535 if (bt_insertx(&root, record, &reclen) < 0)
540 unsigned char oroot[HFS_MAXRECLEN];
543 /* root node was split; create a new root */
545 n_index(bt, HFS_NODEREC(root, 0), root.nnum, oroot, &orootlen);
547 n_init(&root, bt, ndIndxNode, root.nd.ndNHeight + 1);
548 if (n_new(&root) < 0)
552 bt->hdr.bthRoot = root.nnum;
554 bt->flags |= HFS_UPDATE_BTHDR;
556 /* insert index records for new root */
558 n_search(&root, oroot);
559 n_insertx(&root, oroot, orootlen);
561 n_search(&root, record);
562 n_insertx(&root, record, reclen);
564 if (bt_putnode(&root) < 0)
569 bt->flags |= HFS_UPDATE_BTHDR;
575 * NAME: btree->deletex()
576 * DESCRIPTION: recursively locate a node and delete a record
578 int bt_deletex(node *np, unsigned char *key, unsigned char *record, int *flag)
584 found = n_search(np, key);
586 switch ((unsigned char) np->nd.ndType)
591 ERROR(EIO, "b*-tree record not found");
595 rec = HFS_NODEREC(*np, np->rnum);
598 child.nnum = d_getl(HFS_RECDATA(rec));
600 if (bt_getnode(&child) < 0 ||
601 bt_deletex(&child, key, rec, flag) < 0)
608 if (HFS_RECKEYLEN(rec) == 0)
609 return n_delete(np, record, flag);
613 n_index(np->bt, HFS_NODEREC(*np, 0), np->nnum, record, 0);
617 return bt_putnode(np);
625 ERROR(EIO, "b*-tree record not found");
629 return n_delete(np, record, flag);
632 ERROR(EIO, "unexpected b*-tree node");
638 * NAME: btree->delete()
639 * DESCRIPTION: remove a node record from a tree
641 int bt_delete(btree *bt, unsigned char *key)
644 unsigned char record[HFS_MAXRECLEN];
648 root.nnum = bt->hdr.bthRoot;
652 ERROR(EIO, "empty b*-tree");
656 if (bt_getnode(&root) < 0 ||
657 bt_deletex(&root, key, record, &flag) < 0)
660 if (bt->hdr.bthDepth > 1 && root.nd.ndNRecs == 1)
666 rec = HFS_NODEREC(root, 0);
669 bt->hdr.bthRoot = d_getl(HFS_RECDATA(rec));
673 else if (bt->hdr.bthDepth == 1 && root.nd.ndNRecs == 0)
675 /* delete the root node */
677 bt->hdr.bthDepth = 0;
679 bt->hdr.bthFNode = 0;
680 bt->hdr.bthLNode = 0;
686 bt->flags |= HFS_UPDATE_BTHDR;
692 * NAME: btree->search()
693 * DESCRIPTION: locate a data record given a search key
695 int bt_search(btree *bt, unsigned char *key, node *np)
698 np->nnum = bt->hdr.bthRoot;
711 if (bt_getnode(np) < 0)
714 found = n_search(np, key);
716 switch ((unsigned char) np->nd.ndType)
725 rec = HFS_NODEREC(*np, np->rnum);
726 np->nnum = d_getl(HFS_RECDATA(rec));
736 ERROR(EIO, "unexpected b*-tree node");