summaryrefslogtreecommitdiff
path: root/libhfs_iso/btree.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
commit71dc8760ff4de5f365330d1bc571d934deb54af9 (patch)
tree7346d42a282562a3937d82307012b5857d642ce6 /libhfs_iso/btree.c
downloadcdrkit-71dc8760ff4de5f365330d1bc571d934deb54af9.tar.gz
Imported Upstream version 1.1.11upstream/1.1.11upstream
Diffstat (limited to 'libhfs_iso/btree.c')
-rw-r--r--libhfs_iso/btree.c740
1 files changed, 740 insertions, 0 deletions
diff --git a/libhfs_iso/btree.c b/libhfs_iso/btree.c
new file mode 100644
index 0000000..9023428
--- /dev/null
+++ b/libhfs_iso/btree.c
@@ -0,0 +1,740 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)btree.c 1.3 04/06/17 joerg */
+/*
+ * hfsutils - tools for reading and writing Macintosh HFS volumes
+ * Copyright (C) 1996, 1997 Robert Leslie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <mconfig.h>
+#include <stdxlib.h>
+#include <strdefs.h>
+#include <errno.h>
+
+#include "internal.h"
+#include "data.h"
+#include "block.h"
+#include "file.h"
+#include "btree.h"
+#include "node.h"
+
+/*
+ * NAME: btree->getnode()
+ * DESCRIPTION: retrieve a numbered node from a B*-tree file
+ */
+int bt_getnode(node *np)
+{
+ btree *bt = np->bt;
+ block *bp = &np->data;
+ unsigned char *ptr;
+ int i;
+
+ /* verify the node exists and is marked as in-use */
+
+ /*
+ * XXX This is the original code. As np->nnum is unsigned, the
+ * XXX comparison for < 0 makes no sense.
+ * XXX Thanks for a hint from Mike.Sullivan@Eng.Sun.COM
+ */
+/* if (np->nnum < 0 || (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes))*/
+
+ if (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes)
+ {
+ ERROR(EIO, "read nonexistent b*-tree node");
+ return -1;
+ }
+
+ if (bt->map && ! BMTST(bt->map, np->nnum))
+ {
+ ERROR(EIO, "read unallocated b*-tree node");
+ return -1;
+ }
+
+ if (f_getblock(&bt->f, np->nnum, bp) < 0)
+ return -1;
+
+ ptr = *bp;
+
+ d_fetchl(&ptr, (long *) &np->nd.ndFLink);
+ d_fetchl(&ptr, (long *) &np->nd.ndBLink);
+ d_fetchb(&ptr, (char *) &np->nd.ndType);
+ d_fetchb(&ptr, (char *) &np->nd.ndNHeight);
+ d_fetchw(&ptr, (short *) &np->nd.ndNRecs);
+ d_fetchw(&ptr, &np->nd.ndResv2);
+
+ if (np->nd.ndNRecs > HFS_MAXRECS)
+ {
+ ERROR(EIO, "too many b*-tree node records");
+ return -1;
+ }
+
+ i = np->nd.ndNRecs + 1;
+
+ ptr = *bp + HFS_BLOCKSZ - (2 * i);
+
+ while (i--)
+ d_fetchw(&ptr, (short *) &np->roff[i]);
+
+ return 0;
+}
+
+/*
+ * NAME: btree->putnode()
+ * DESCRIPTION: store a numbered node into a B*-tree file
+ */
+int bt_putnode(node *np)
+{
+ btree *bt = np->bt;
+ block *bp = &np->data;
+ unsigned char *ptr;
+ int i;
+
+ /* verify the node exists and is marked as in-use */
+
+ if (np->nnum && np->nnum >= bt->hdr.bthNNodes)
+ {
+ ERROR(EIO, "write nonexistent b*-tree node");
+ return -1;
+ }
+ else if (bt->map && ! BMTST(bt->map, np->nnum))
+ {
+ ERROR(EIO, "write unallocated b*-tree node");
+ return -1;
+ }
+
+ ptr = *bp;
+
+ d_storel(&ptr, np->nd.ndFLink);
+ d_storel(&ptr, np->nd.ndBLink);
+ d_storeb(&ptr, np->nd.ndType);
+ d_storeb(&ptr, np->nd.ndNHeight);
+ d_storew(&ptr, np->nd.ndNRecs);
+ d_storew(&ptr, np->nd.ndResv2);
+
+ if (np->nd.ndNRecs > HFS_MAXRECS)
+ {
+ ERROR(EIO, "too many b*-tree node records");
+ return -1;
+ }
+
+ i = np->nd.ndNRecs + 1;
+
+ ptr = *bp + HFS_BLOCKSZ - (2 * i);
+
+ while (i--)
+ d_storew(&ptr, np->roff[i]);
+
+ if (f_putblock(&bt->f, np->nnum, bp) < 0)
+ return -1;
+
+ return 0;
+}
+
+/*
+ * NAME: btree->readhdr()
+ * DESCRIPTION: read the header node of a B*-tree
+ */
+int bt_readhdr(btree *bt)
+{
+ unsigned char *ptr;
+ char *map;
+ int i;
+ unsigned long nnum;
+
+ bt->hdrnd.bt = bt;
+ bt->hdrnd.nnum = 0;
+
+ if (bt_getnode(&bt->hdrnd) < 0)
+ return -1;
+
+ if (bt->hdrnd.nd.ndType != ndHdrNode ||
+ bt->hdrnd.nd.ndNRecs != 3 ||
+ bt->hdrnd.roff[0] != 0x00e ||
+ bt->hdrnd.roff[1] != 0x078 ||
+ bt->hdrnd.roff[2] != 0x0f8 ||
+ bt->hdrnd.roff[3] != 0x1f8)
+ {
+ ERROR(EIO, "malformed b*-tree header node");
+ return -1;
+ }
+
+ /* read header record */
+
+ ptr = HFS_NODEREC(bt->hdrnd, 0);
+
+ d_fetchw(&ptr, (short *) &bt->hdr.bthDepth);
+ d_fetchl(&ptr, (long *) &bt->hdr.bthRoot);
+ d_fetchl(&ptr, (long *) &bt->hdr.bthNRecs);
+ d_fetchl(&ptr, (long *) &bt->hdr.bthFNode);
+ d_fetchl(&ptr, (long *) &bt->hdr.bthLNode);
+ d_fetchw(&ptr, (short *) &bt->hdr.bthNodeSize);
+ d_fetchw(&ptr, (short *) &bt->hdr.bthKeyLen);
+ d_fetchl(&ptr, (long *) &bt->hdr.bthNNodes);
+ d_fetchl(&ptr, (long *) &bt->hdr.bthFree);
+
+ for (i = 0; i < 76; ++i)
+ d_fetchb(&ptr, (char *) &bt->hdr.bthResv[i]);
+
+ if (bt->hdr.bthNodeSize != HFS_BLOCKSZ)
+ {
+ ERROR(EINVAL, "unsupported b*-tree node size");
+ return -1;
+ }
+
+ /* read map record; construct btree bitmap */
+ /* don't set bt->map until we're done, since getnode() checks it */
+
+ map = ALLOC(char, HFS_MAP1SZ);
+ if (map == 0)
+ {
+ ERROR(ENOMEM, 0);
+ return -1;
+ }
+
+ memcpy(map, HFS_NODEREC(bt->hdrnd, 2), HFS_MAP1SZ);
+ bt->mapsz = HFS_MAP1SZ;
+
+ /* read continuation map records, if any */
+
+ nnum = bt->hdrnd.nd.ndFLink;
+
+ while (nnum)
+ {
+ node n;
+ char *newmap;
+
+ n.bt = bt;
+ n.nnum = nnum;
+
+ if (bt_getnode(&n) < 0)
+ {
+ FREE(map);
+ return -1;
+ }
+
+ if (n.nd.ndType != ndMapNode ||
+ n.nd.ndNRecs != 1 ||
+ n.roff[0] != 0x00e ||
+ n.roff[1] != 0x1fa)
+ {
+ FREE(map);
+ ERROR(EIO, "malformed b*-tree map node");
+ return -1;
+ }
+
+ newmap = REALLOC(map, char, bt->mapsz + HFS_MAPXSZ);
+ if (newmap == 0)
+ {
+ FREE(map);
+ ERROR(ENOMEM, 0);
+ return -1;
+ }
+ map = newmap;
+
+ memcpy(map + bt->mapsz, HFS_NODEREC(n, 0), HFS_MAPXSZ);
+ bt->mapsz += HFS_MAPXSZ;
+
+ nnum = n.nd.ndFLink;
+ }
+
+ bt->map = map;
+
+ return 0;
+}
+
+/*
+ * NAME: btree->writehdr()
+ * DESCRIPTION: write the header node of a B*-tree
+ */
+int bt_writehdr(btree *bt)
+{
+ unsigned char *ptr;
+ char *map;
+ unsigned long mapsz, nnum;
+ int i;
+
+ if (bt->hdrnd.bt != bt ||
+ bt->hdrnd.nnum != 0 ||
+ bt->hdrnd.nd.ndType != ndHdrNode ||
+ bt->hdrnd.nd.ndNRecs != 3)
+ abort();
+
+ ptr = HFS_NODEREC(bt->hdrnd, 0);
+
+ d_storew(&ptr, bt->hdr.bthDepth);
+ d_storel(&ptr, bt->hdr.bthRoot);
+ d_storel(&ptr, bt->hdr.bthNRecs);
+ d_storel(&ptr, bt->hdr.bthFNode);
+ d_storel(&ptr, bt->hdr.bthLNode);
+ d_storew(&ptr, bt->hdr.bthNodeSize);
+ d_storew(&ptr, bt->hdr.bthKeyLen);
+ d_storel(&ptr, bt->hdr.bthNNodes);
+ d_storel(&ptr, bt->hdr.bthFree);
+
+ for (i = 0; i < 76; ++i)
+ d_storeb(&ptr, bt->hdr.bthResv[i]);
+
+ memcpy(HFS_NODEREC(bt->hdrnd, 2), bt->map, HFS_MAP1SZ);
+
+ if (bt_putnode(&bt->hdrnd) < 0)
+ return -1;
+
+ map = bt->map + HFS_MAP1SZ;
+ mapsz = bt->mapsz - HFS_MAP1SZ;
+
+ nnum = bt->hdrnd.nd.ndFLink;
+
+ while (mapsz)
+ {
+ node n;
+
+ if (nnum == 0)
+ {
+ ERROR(EIO, "truncated b*-tree map");
+ return -1;
+ }
+
+ n.bt = bt;
+ n.nnum = nnum;
+
+ if (bt_getnode(&n) < 0)
+ return -1;
+
+ if (n.nd.ndType != ndMapNode ||
+ n.nd.ndNRecs != 1 ||
+ n.roff[0] != 0x00e ||
+ n.roff[1] != 0x1fa)
+ {
+ ERROR(EIO, "malformed b*-tree map node");
+ return -1;
+ }
+
+ memcpy(HFS_NODEREC(n, 0), map, HFS_MAPXSZ);
+
+ if (bt_putnode(&n) < 0)
+ return -1;
+
+ map += HFS_MAPXSZ;
+ mapsz -= HFS_MAPXSZ;
+
+ nnum = n.nd.ndFLink;
+ }
+
+ bt->flags &= ~HFS_UPDATE_BTHDR;
+
+ return 0;
+}
+
+/* High-Level B*-Tree Routines ============================================= */
+
+/*
+ * NAME: btree->space()
+ * DESCRIPTION: assert space for new records, or extend the file
+ */
+int bt_space(btree *bt, unsigned int nrecs)
+{
+ unsigned int nnodes;
+ int space;
+
+ nnodes = nrecs * (bt->hdr.bthDepth + 1);
+
+ if (nnodes <= bt->hdr.bthFree)
+ return 0;
+
+ /* make sure the extents tree has room too */
+
+ if (bt != &bt->f.vol->ext)
+ {
+ if (bt_space(&bt->f.vol->ext, 1) < 0)
+ return -1;
+ }
+
+ space = f_alloc(&bt->f);
+ if (space < 0)
+ return -1;
+
+ nnodes = space * (bt->f.vol->mdb.drAlBlkSiz / bt->hdr.bthNodeSize);
+
+ bt->hdr.bthNNodes += nnodes;
+ bt->hdr.bthFree += nnodes;
+
+ bt->flags |= HFS_UPDATE_BTHDR;
+
+ bt->f.vol->flags |= HFS_UPDATE_ALTMDB;
+
+ while (bt->hdr.bthNNodes > bt->mapsz * 8)
+ {
+ char *newmap;
+ node mapnd;
+
+ /* extend tree map */
+
+ newmap = REALLOC(bt->map, char, bt->mapsz + HFS_MAPXSZ);
+ if (newmap == 0)
+ {
+ ERROR(ENOMEM, 0);
+ return -1;
+ }
+
+ memset(newmap + bt->mapsz, 0, HFS_MAPXSZ);
+
+ bt->map = newmap;
+ bt->mapsz += HFS_MAPXSZ;
+
+ n_init(&mapnd, bt, ndMapNode, 0);
+ if (n_new(&mapnd) < 0)
+ return -1;
+
+ /* link the new map node */
+
+ if (bt->hdrnd.nd.ndFLink == 0)
+ {
+ bt->hdrnd.nd.ndFLink = mapnd.nnum;
+ mapnd.nd.ndBLink = 0;
+ }
+ else
+ {
+ node n;
+
+ n.bt = bt;
+ n.nnum = bt->hdrnd.nd.ndFLink;
+
+ for (;;)
+ {
+ if (bt_getnode(&n) < 0)
+ return -1;
+
+ if (n.nd.ndFLink == 0)
+ break;
+
+ n.nnum = n.nd.ndFLink;
+ }
+
+ n.nd.ndFLink = mapnd.nnum;
+ mapnd.nd.ndBLink = n.nnum;
+
+ if (bt_putnode(&n) < 0)
+ return -1;
+ }
+
+ mapnd.nd.ndNRecs = 1;
+ mapnd.roff[1] = 0x1fa;
+
+ if (bt_putnode(&mapnd) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * NAME: btree->insertx()
+ * DESCRIPTION: recursively locate a node and insert a record
+ */
+int bt_insertx(node *np, unsigned char *record, int *reclen)
+{
+ node child;
+ unsigned char *rec;
+
+ if (n_search(np, record))
+ {
+ ERROR(EIO, "b*-tree record already exists");
+ return -1;
+ }
+
+ switch ((unsigned char) np->nd.ndType)
+ {
+ case ndIndxNode:
+ if (np->rnum < 0)
+ rec = HFS_NODEREC(*np, 0);
+ else
+ rec = HFS_NODEREC(*np, np->rnum);
+
+ child.bt = np->bt;
+ child.nnum = d_getl(HFS_RECDATA(rec));
+
+ if (bt_getnode(&child) < 0 ||
+ bt_insertx(&child, record, reclen) < 0)
+ return -1;
+
+ if (np->rnum < 0)
+ {
+ n_index(np->bt, HFS_NODEREC(child, 0), child.nnum, rec, 0);
+ if (*reclen == 0)
+ return bt_putnode(np);
+ }
+
+ return *reclen ? n_insert(np, record, reclen) : 0;
+
+ case ndLeafNode:
+ return n_insert(np, record, reclen);
+
+ default:
+ ERROR(EIO, "unexpected b*-tree node");
+ return -1;
+ }
+}
+
+/*
+ * NAME: btree->insert()
+ * DESCRIPTION: insert a new node record into a tree
+ */
+int bt_insert(btree *bt, unsigned char *record, int reclen)
+{
+ node root;
+
+ if (bt->hdr.bthRoot == 0)
+ {
+ /* create root node */
+
+ n_init(&root, bt, ndLeafNode, 1);
+ if (n_new(&root) < 0 ||
+ bt_putnode(&root) < 0)
+ return -1;
+
+ bt->hdr.bthDepth = 1;
+ bt->hdr.bthRoot = root.nnum;
+ bt->hdr.bthFNode = root.nnum;
+ bt->hdr.bthLNode = root.nnum;
+
+ bt->flags |= HFS_UPDATE_BTHDR;
+ }
+ else
+ {
+ root.bt = bt;
+ root.nnum = bt->hdr.bthRoot;
+
+ if (bt_getnode(&root) < 0)
+ return -1;
+ }
+
+ if (bt_insertx(&root, record, &reclen) < 0)
+ return -1;
+
+ if (reclen)
+ {
+ unsigned char oroot[HFS_MAXRECLEN];
+ int orootlen;
+
+ /* root node was split; create a new root */
+
+ n_index(bt, HFS_NODEREC(root, 0), root.nnum, oroot, &orootlen);
+
+ n_init(&root, bt, ndIndxNode, root.nd.ndNHeight + 1);
+ if (n_new(&root) < 0)
+ return -1;
+
+ ++bt->hdr.bthDepth;
+ bt->hdr.bthRoot = root.nnum;
+
+ bt->flags |= HFS_UPDATE_BTHDR;
+
+ /* insert index records for new root */
+
+ n_search(&root, oroot);
+ n_insertx(&root, oroot, orootlen);
+
+ n_search(&root, record);
+ n_insertx(&root, record, reclen);
+
+ if (bt_putnode(&root) < 0)
+ return -1;
+ }
+
+ ++bt->hdr.bthNRecs;
+ bt->flags |= HFS_UPDATE_BTHDR;
+
+ return 0;
+}
+
+/*
+ * NAME: btree->deletex()
+ * DESCRIPTION: recursively locate a node and delete a record
+ */
+int bt_deletex(node *np, unsigned char *key, unsigned char *record, int *flag)
+{
+ node child;
+ unsigned char *rec;
+ int found;
+
+ found = n_search(np, key);
+
+ switch ((unsigned char) np->nd.ndType)
+ {
+ case ndIndxNode:
+ if (np->rnum < 0)
+ {
+ ERROR(EIO, "b*-tree record not found");
+ return -1;
+ }
+
+ rec = HFS_NODEREC(*np, np->rnum);
+
+ child.bt = np->bt;
+ child.nnum = d_getl(HFS_RECDATA(rec));
+
+ if (bt_getnode(&child) < 0 ||
+ bt_deletex(&child, key, rec, flag) < 0)
+ return -1;
+
+ if (*flag)
+ {
+ *flag = 0;
+
+ if (HFS_RECKEYLEN(rec) == 0)
+ return n_delete(np, record, flag);
+
+ if (np->rnum == 0)
+ {
+ n_index(np->bt, HFS_NODEREC(*np, 0), np->nnum, record, 0);
+ *flag = 1;
+ }
+
+ return bt_putnode(np);
+ }
+
+ return 0;
+
+ case ndLeafNode:
+ if (found == 0)
+ {
+ ERROR(EIO, "b*-tree record not found");
+ return -1;
+ }
+
+ return n_delete(np, record, flag);
+
+ default:
+ ERROR(EIO, "unexpected b*-tree node");
+ return -1;
+ }
+}
+
+/*
+ * NAME: btree->delete()
+ * DESCRIPTION: remove a node record from a tree
+ */
+int bt_delete(btree *bt, unsigned char *key)
+{
+ node root;
+ unsigned char record[HFS_MAXRECLEN];
+ int flag = 0;
+
+ root.bt = bt;
+ root.nnum = bt->hdr.bthRoot;
+
+ if (root.nnum == 0)
+ {
+ ERROR(EIO, "empty b*-tree");
+ return -1;
+ }
+
+ if (bt_getnode(&root) < 0 ||
+ bt_deletex(&root, key, record, &flag) < 0)
+ return -1;
+
+ if (bt->hdr.bthDepth > 1 && root.nd.ndNRecs == 1)
+ {
+ unsigned char *rec;
+
+ /* chop the root */
+
+ rec = HFS_NODEREC(root, 0);
+
+ --bt->hdr.bthDepth;
+ bt->hdr.bthRoot = d_getl(HFS_RECDATA(rec));
+
+ n_free(&root);
+ }
+ else if (bt->hdr.bthDepth == 1 && root.nd.ndNRecs == 0)
+ {
+ /* delete the root node */
+
+ bt->hdr.bthDepth = 0;
+ bt->hdr.bthRoot = 0;
+ bt->hdr.bthFNode = 0;
+ bt->hdr.bthLNode = 0;
+
+ n_free(&root);
+ }
+
+ --bt->hdr.bthNRecs;
+ bt->flags |= HFS_UPDATE_BTHDR;
+
+ return 0;
+}
+
+/*
+ * NAME: btree->search()
+ * DESCRIPTION: locate a data record given a search key
+ */
+int bt_search(btree *bt, unsigned char *key, node *np)
+{
+ np->bt = bt;
+ np->nnum = bt->hdr.bthRoot;
+
+ if (np->nnum == 0)
+ {
+ ERROR(ENOENT, 0);
+ return 0;
+ }
+
+ for (;;)
+ {
+ int found;
+ unsigned char *rec;
+
+ if (bt_getnode(np) < 0)
+ return -1;
+
+ found = n_search(np, key);
+
+ switch ((unsigned char) np->nd.ndType)
+ {
+ case ndIndxNode:
+ if (np->rnum < 0)
+ {
+ ERROR(ENOENT, 0);
+ return 0;
+ }
+
+ rec = HFS_NODEREC(*np, np->rnum);
+ np->nnum = d_getl(HFS_RECDATA(rec));
+ break;
+
+ case ndLeafNode:
+ if (! found)
+ ERROR(ENOENT, 0);
+
+ return found;
+
+ default:
+ ERROR(EIO, "unexpected b*-tree node");
+ return -1;
+ }
+ }
+}