| /* |
| * libhfs - library for reading and writing Macintosh HFS volumes |
| * Copyright (C) 1996-1998 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., 51 Franklin Street - Fifth Floor, Boston, |
| * MA 02110-1301, USA. |
| * |
| * $Id: btree.c,v 1.10 1998/11/02 22:08:54 rob Exp $ |
| */ |
| |
| #include "config.h" |
| |
| #include "libhfs.h" |
| #include "btree.h" |
| #include "data.h" |
| #include "file.h" |
| #include "block.h" |
| #include "node.h" |
| |
| /* |
| * NAME: btree->getnode() |
| * DESCRIPTION: retrieve a numbered node from a B*-tree file |
| */ |
| int bt_getnode(node *np, btree *bt, unsigned long nnum) |
| { |
| block *bp = &np->data; |
| const byte *ptr; |
| int i; |
| |
| np->bt = bt; |
| np->nnum = nnum; |
| |
| # if 0 |
| fprintf(stderr, "BTREE: GET vol \"%s\" btree \"%s\" node %lu\n", |
| bt->f.vol->mdb.drVN, bt->f.name, np->nnum); |
| # endif |
| |
| /* verify the node exists and is marked as in-use */ |
| |
| if (nnum > 0 && nnum >= bt->hdr.bthNNodes) |
| ERROR(EIO, "read nonexistent b*-tree node"); |
| else if (bt->map && ! BMTST(bt->map, nnum)) |
| ERROR(EIO, "read unallocated b*-tree node"); |
| |
| if (f_getblock(&bt->f, nnum, bp) == -1) |
| goto fail; |
| |
| ptr = *bp; |
| |
| d_fetchul(&ptr, &np->nd.ndFLink); |
| d_fetchul(&ptr, &np->nd.ndBLink); |
| d_fetchsb(&ptr, &np->nd.ndType); |
| d_fetchsb(&ptr, &np->nd.ndNHeight); |
| d_fetchuw(&ptr, &np->nd.ndNRecs); |
| d_fetchsw(&ptr, &np->nd.ndResv2); |
| |
| if (np->nd.ndNRecs > HFS_MAX_NRECS) |
| ERROR(EIO, "too many b*-tree node records"); |
| |
| i = np->nd.ndNRecs + 1; |
| |
| ptr = *bp + HFS_BLOCKSZ - (2 * i); |
| |
| while (i--) |
| d_fetchuw(&ptr, &np->roff[i]); |
| |
| return 0; |
| |
| fail: |
| return -1; |
| } |
| |
| |
| /* |
| * NAME: btree->readhdr() |
| * DESCRIPTION: read the header node of a B*-tree |
| */ |
| int bt_readhdr(btree *bt) |
| { |
| const byte *ptr; |
| byte *map = NULL; |
| int i; |
| unsigned long nnum; |
| |
| if (bt_getnode(&bt->hdrnd, bt, 0) == -1) |
| goto fail; |
| |
| 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"); |
| |
| /* read header record */ |
| |
| ptr = HFS_NODEREC(bt->hdrnd, 0); |
| |
| d_fetchuw(&ptr, &bt->hdr.bthDepth); |
| d_fetchul(&ptr, &bt->hdr.bthRoot); |
| d_fetchul(&ptr, &bt->hdr.bthNRecs); |
| d_fetchul(&ptr, &bt->hdr.bthFNode); |
| d_fetchul(&ptr, &bt->hdr.bthLNode); |
| d_fetchuw(&ptr, &bt->hdr.bthNodeSize); |
| d_fetchuw(&ptr, &bt->hdr.bthKeyLen); |
| d_fetchul(&ptr, &bt->hdr.bthNNodes); |
| d_fetchul(&ptr, &bt->hdr.bthFree); |
| |
| for (i = 0; i < 76; ++i) |
| d_fetchsb(&ptr, &bt->hdr.bthResv[i]); |
| |
| if (bt->hdr.bthNodeSize != HFS_BLOCKSZ) |
| ERROR(EINVAL, "unsupported b*-tree node size"); |
| |
| /* read map record; construct btree bitmap */ |
| /* don't set bt->map until we're done, since getnode() checks it */ |
| |
| map = ALLOC(byte, HFS_MAP1SZ); |
| if (map == NULL) |
| ERROR(ENOMEM, NULL); |
| |
| 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; |
| byte *newmap; |
| |
| if (bt_getnode(&n, bt, nnum) == -1) |
| goto fail; |
| |
| if (n.nd.ndType != ndMapNode || |
| n.nd.ndNRecs != 1 || |
| n.roff[0] != 0x00e || |
| n.roff[1] != 0x1fa) |
| ERROR(EIO, "malformed b*-tree map node"); |
| |
| newmap = REALLOC(map, byte, bt->mapsz + HFS_MAPXSZ); |
| if (newmap == NULL) |
| ERROR(ENOMEM, NULL); |
| |
| 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; |
| |
| fail: |
| FREE(map); |
| return -1; |
| } |
| |
| |
| /* |
| * NAME: btree->search() |
| * DESCRIPTION: locate a data record given a search key |
| */ |
| int bt_search(btree *bt, const byte *key, node *np) |
| { |
| int found = 0; |
| unsigned long nnum; |
| |
| nnum = bt->hdr.bthRoot; |
| |
| if (nnum == 0) |
| ERROR(ENOENT, NULL); |
| |
| while (1) |
| { |
| const byte *rec; |
| |
| if (bt_getnode(np, bt, nnum) == -1) |
| { |
| found = -1; |
| goto fail; |
| } |
| |
| found = n_search(np, key); |
| |
| switch (np->nd.ndType) |
| { |
| case ndIndxNode: |
| if (np->rnum == -1) |
| ERROR(ENOENT, NULL); |
| |
| rec = HFS_NODEREC(*np, np->rnum); |
| nnum = d_getul(HFS_RECDATA(rec)); |
| |
| break; |
| |
| case ndLeafNode: |
| if (! found) |
| ERROR(ENOENT, NULL); |
| |
| goto done; |
| |
| default: |
| found = -1; |
| ERROR(EIO, "unexpected b*-tree node"); |
| } |
| } |
| |
| done: |
| fail: |
| return found; |
| } |