From a119991b98b043ef5a2432a7a0e2c4f2cf484674 Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Fri, 1 Mar 2013 11:33:00 +0100 Subject: Imported Upstream version 1.1.11 --- libhfs_iso/CMakeLists.txt | 8 + libhfs_iso/README | 41 + libhfs_iso/block.c | 219 +++++ libhfs_iso/block.h | 37 + libhfs_iso/btree.c | 740 ++++++++++++++++ libhfs_iso/btree.h | 47 + libhfs_iso/data.c | 379 ++++++++ libhfs_iso/data.h | 57 ++ libhfs_iso/file.c | 470 ++++++++++ libhfs_iso/file.h | 48 ++ libhfs_iso/gdata.c | 20 + libhfs_iso/hfs.c | 2098 +++++++++++++++++++++++++++++++++++++++++++++ libhfs_iso/hfs.h | 208 +++++ libhfs_iso/hybrid.h | 67 ++ libhfs_iso/internal.h | 369 ++++++++ libhfs_iso/low.c | 529 ++++++++++++ libhfs_iso/low.h | 111 +++ libhfs_iso/node.c | 473 ++++++++++ libhfs_iso/node.h | 48 ++ libhfs_iso/record.c | 551 ++++++++++++ libhfs_iso/record.h | 52 ++ libhfs_iso/volume.c | 741 ++++++++++++++++ libhfs_iso/volume.h | 58 ++ 23 files changed, 7371 insertions(+) create mode 100644 libhfs_iso/CMakeLists.txt create mode 100644 libhfs_iso/README create mode 100644 libhfs_iso/block.c create mode 100644 libhfs_iso/block.h create mode 100644 libhfs_iso/btree.c create mode 100644 libhfs_iso/btree.h create mode 100644 libhfs_iso/data.c create mode 100644 libhfs_iso/data.h create mode 100644 libhfs_iso/file.c create mode 100644 libhfs_iso/file.h create mode 100644 libhfs_iso/gdata.c create mode 100644 libhfs_iso/hfs.c create mode 100644 libhfs_iso/hfs.h create mode 100644 libhfs_iso/hybrid.h create mode 100644 libhfs_iso/internal.h create mode 100644 libhfs_iso/low.c create mode 100644 libhfs_iso/low.h create mode 100644 libhfs_iso/node.c create mode 100644 libhfs_iso/node.h create mode 100644 libhfs_iso/record.c create mode 100644 libhfs_iso/record.h create mode 100644 libhfs_iso/volume.c create mode 100644 libhfs_iso/volume.h (limited to 'libhfs_iso') diff --git a/libhfs_iso/CMakeLists.txt b/libhfs_iso/CMakeLists.txt new file mode 100644 index 0000000..8558690 --- /dev/null +++ b/libhfs_iso/CMakeLists.txt @@ -0,0 +1,8 @@ +PROJECT (LIBhfs_iso C) +INCLUDE_DIRECTORIES(../include ../libhfs_iso ../wodim ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include) +ADD_DEFINITIONS(-DUSE_LARGEFILES -DABORT_DEEP_ISO_ONLY -DAPPLE_HYB -DUDF -DDVD_VIDEO -DSORTING -DHAVE_CONFIG_H -DUSE_LIBSCHILY -DUSE_SCG) + + +SET(LIBhfs_iso_SRCS block.c btree.c data.c file.c gdata.c hfs.c low.c node.c record.c volume.c) + +ADD_LIBRARY (hfs_iso STATIC ${LIBhfs_iso_SRCS}) diff --git a/libhfs_iso/README b/libhfs_iso/README new file mode 100644 index 0000000..5c6c00b --- /dev/null +++ b/libhfs_iso/README @@ -0,0 +1,41 @@ +# @(#)README 1.1 97/07/21 joerg + 06/09/11 christian + +Modified version of libhfs (v2.0) to work with mkhybrid. + +To complile for mkhybrid, use the #define -DAPPLE_HYB + +The libhfs.a created with this option must not be used with other +hfsutils routines. + +James Pearson 18/7/97 + +Copyright information from hfsutils: + + hfsutils - tools for reading and writing Macintosh HFS volumes + Copyright (C) 1996, 1997 Robert Leslie + + This describes the program as shipped with cdrkit, a spinoff from the + cdrtools project. However, the cdrtools developers are no longer + involved in the development of this spinoff and therefore shall not + be made responsible for any problem caused by it. Do not try to get + support for this program by contacting the original authors. + + 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. + + +Source: lihfs_iso/README from cdrtools package +Edited for cdrkit by Christian Fromme + diff --git a/libhfs_iso/block.c b/libhfs_iso/block.c new file mode 100644 index 0000000..bbca145 --- /dev/null +++ b/libhfs_iso/block.c @@ -0,0 +1,219 @@ +/* + * 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. + * + */ + +/* @(#)block.c 1.3 01/11/01 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 +#include +#include +#include + +#include "internal.h" +#include "data.h" +#include "block.h" +#include "low.h" + +#ifdef DEBUG +#include +#endif /* DEBUG */ + +/* + * NAME: block->readlb() + * DESCRIPTION: read a logical block from a volume + */ +int b_readlb(hfsvol *vol, unsigned long num, block *bp) +{ +#ifndef APPLE_HYB + int bytes; +#endif +#ifdef APPLE_HYB + block *b; + hce_mem *hce; + +#ifdef DEBUG + fprintf(stderr,"b_readlb: start block = %d\n", vol->vstart + num); +#endif /* DEBUG */ + + hce = vol->hce; + +/* Check to see if requested block is in the HFS header or catalog/exents + files. If it is, read info from memory copy. If not, then something + has gone horribly wrong ... */ + + if (num < hce->hfs_hdr_size) + b = (block *)hce->hfs_hdr + num; + else if (num < hce->hfs_hdr_size + hce->hfs_ce_size) + b = (block *)hce->hfs_ce + num - hce->hfs_hdr_size; + else + { + ERROR(EIO, "should not happen!"); + return -1; + } + + memcpy(bp, b, HFS_BLOCKSZ); + +#else + + if (lseek(vol->fd, (vol->vstart + num) * HFS_BLOCKSZ, SEEK_SET) < 0) + { + ERROR(errno, "error seeking device"); + return -1; + } + + bytes = read(vol->fd, bp, HFS_BLOCKSZ); + if (bytes < 0) + { + ERROR(errno, "error reading from device"); + return -1; + } + else if (bytes == 0) + { + ERROR(EIO, "read EOF on volume"); + return -1; + } + else if (bytes != HFS_BLOCKSZ) + { + ERROR(EIO, "read incomplete block"); + return -1; + } +#endif /* APPLE_HYB */ + return 0; +} + +/* + * NAME: block->writelb() + * DESCRIPTION: write a logical block to a volume + */ +int b_writelb(hfsvol *vol, unsigned long num, block *bp) +{ +#ifndef APPLE_HYB + int bytes; +#endif +#ifdef APPLE_HYB + block *b; + hce_mem *hce; + +#ifdef DEBUG + fprintf(stderr,"b_writelb: start block = %d\n", vol->vstart + num); +#endif /* DEBUG */ + + hce = vol->hce; + +/* Check to see if requested block is in the HFS header or catalog/exents + files. If it is, write info to memory copy. If not, then it's a block + for an ordinary file - and as we are writing the files later, then just + ignore and return OK */ + if (num < hce->hfs_hdr_size) + b = (block *)hce->hfs_hdr + num; + else if (num < hce->hfs_hdr_size + hce->hfs_ce_size) + b = (block *)hce->hfs_ce + num - hce->hfs_hdr_size; + else + { +#ifdef DEBUG + fprintf(stderr,"b_writelb: ignoring\n"); +#endif /* DEBUG */ + return 0; + } + + memcpy(b, bp, HFS_BLOCKSZ); + +#else + + if (lseek(vol->fd, (vol->vstart + num) * HFS_BLOCKSZ, SEEK_SET) < 0) + { + ERROR(errno, "error seeking device"); + return -1; + } + + bytes = write(vol->fd, bp, HFS_BLOCKSZ); + + if (bytes < 0) + { + ERROR(errno, "error writing to device"); + return -1; + } + else if (bytes != HFS_BLOCKSZ) + { + ERROR(EIO, "wrote incomplete block"); + return -1; + } +#endif /* APPLE_HYB */ + return 0; +} + +/* + * NAME: block->readab() + * DESCRIPTION: read a block from an allocation block from a volume + */ +int b_readab(hfsvol *vol, unsigned int anum, unsigned int idx, block *bp) +{ + /* verify the allocation block exists and is marked as in-use */ + + if (anum >= vol->mdb.drNmAlBlks) + { + ERROR(EIO, "read nonexistent block"); + return -1; + } + else if (vol->vbm && ! BMTST(vol->vbm, anum)) + { + ERROR(EIO, "read unallocated block"); + return -1; + } + + return b_readlb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + idx, bp); +} + +/* + * NAME: b->writeab() + * DESCRIPTION: write a block to an allocation block to a volume + */ +int b_writeab(hfsvol *vol, unsigned int anum, unsigned int idx, block *bp) +{ + /* verify the allocation block exists and is marked as in-use */ + + if (anum >= vol->mdb.drNmAlBlks) + { + ERROR(EIO, "write nonexistent block"); + return -1; + } + else if (vol->vbm && ! BMTST(vol->vbm, anum)) + { + ERROR(EIO, "write unallocated block"); + return -1; + } + + vol->mdb.drAtrb &= ~HFS_ATRB_UMOUNTED; + vol->mdb.drLsMod = d_tomtime(time(0)); + ++vol->mdb.drWrCnt; + + vol->flags |= HFS_UPDATE_MDB; + + return b_writelb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + idx, bp); +} diff --git a/libhfs_iso/block.h b/libhfs_iso/block.h new file mode 100644 index 0000000..ce3932d --- /dev/null +++ b/libhfs_iso/block.h @@ -0,0 +1,37 @@ +/* + * 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. + * + */ + +/* @(#)block.h 1.1 00/03/05 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. + */ + +int b_readlb(hfsvol *, unsigned long, block *); +int b_writelb(hfsvol *, unsigned long, block *); + +int b_readab(hfsvol *, unsigned int, unsigned int, block *); +int b_writeab(hfsvol *, unsigned int, unsigned int, block *); 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 +#include +#include +#include + +#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; + } + } +} diff --git a/libhfs_iso/btree.h b/libhfs_iso/btree.h new file mode 100644 index 0000000..c4b7629 --- /dev/null +++ b/libhfs_iso/btree.h @@ -0,0 +1,47 @@ +/* + * 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.h 1.1 00/03/05 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. + */ + +int bt_getnode(node *); +int bt_putnode(node *); + +int bt_readhdr(btree *); +int bt_writehdr(btree *); + +int bt_space(btree *, unsigned int); + +int bt_insertx(node *, unsigned char *, int *); +int bt_insert(btree *, unsigned char *, int); + +int bt_deletex(node *, unsigned char *, unsigned char *, int *); +int bt_delete(btree *, unsigned char *); + +int bt_search(btree *, unsigned char *, node *); diff --git a/libhfs_iso/data.c b/libhfs_iso/data.c new file mode 100644 index 0000000..3f503f4 --- /dev/null +++ b/libhfs_iso/data.c @@ -0,0 +1,379 @@ +/* + * 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. + * + */ + +/* @(#)data.c 1.5 02/02/10 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 +#include +#include + +#include "internal.h" +#include "data.h" +#include "btree.h" + +#ifdef APPLE_HYB +/* + * Depending on the version, AppleDouble/Single stores dates + * relative to 1st Jan 1904 (v1) or 1st Jan 2000 (v2) + * + * DUTDIFF is the difference between 1st Jan 2000 and + * 1st Jan 1970 (Unix epoch)). + */ + +#define DUTDIFF 946684800L +#endif /* APPLE_HYB */ +#define MUTDIFF 2082844800L +#define TZNONE 0x0FFFFFFFL /* A timezone diff that cannot occur */ + +static void calctzdiff(void); + +static +unsigned long tzdiff = TZNONE; + +static +unsigned char hfs_charorder[256] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + + 0x20, 0x22, 0x23, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, + + 0x47, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69, + 0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f, + 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1, + 0xa3, 0xa5, 0xa8, 0xaa, 0xab, 0xac, 0xad, 0xae, + + 0x54, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69, + 0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f, + 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1, + 0xa3, 0xa5, 0xa8, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + + 0x4c, 0x50, 0x5c, 0x62, 0x7d, 0x81, 0x9a, 0x55, + 0x4a, 0x56, 0x4c, 0x4e, 0x50, 0x5c, 0x62, 0x64, + 0x65, 0x66, 0x6f, 0x70, 0x71, 0x72, 0x7d, 0x89, + 0x8a, 0x8b, 0x81, 0x83, 0x9c, 0x9d, 0x9e, 0x9a, + + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0x95, + 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x52, 0x85, + 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0x57, 0x8c, 0xcc, 0x52, 0x85, + + 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0x26, + 0x27, 0xd4, 0x20, 0x4a, 0x4e, 0x83, 0x87, 0x87, + 0xd5, 0xd6, 0x24, 0x25, 0x2d, 0x2e, 0xd7, 0xd8, + 0xa7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; + +/* + * NAME: data->getb() + * DESCRIPTION: marshal 1 byte into local host format + */ +char d_getb(unsigned char *ptr) +{ + return (char) ptr[0]; +} + +/* + * NAME: data->getw() + * DESCRIPTION: marshal 2 bytes into local host format + */ +short d_getw(unsigned char *ptr) +{ + return (short) + ((ptr[0] << 8) | + (ptr[1] << 0)); +} + +/* + * NAME: data->getl() + * DESCRIPTION: marshal 4 bytes into local host format + */ +long d_getl(unsigned char *ptr) +{ + return (long) + ((ptr[0] << 24) | + (ptr[1] << 16) | + (ptr[2] << 8) | + (ptr[3] << 0)); +} + +/* + * NAME: data->putb() + * DESCRIPTION: marshal 1 byte out to Macintosh (big-endian) format + */ +#ifdef PROTOTYPES +void d_putb(unsigned char *ptr, char data) +#else +void d_putb(unsigned char *ptr, char data) +#endif +{ + ptr[0] = (unsigned char) data; +} + +/* + * NAME: data->putw() + * DESCRIPTION: marshal 2 bytes out to Macintosh (big-endian) format + */ +#ifdef PROTOTYPES +void d_putw(unsigned char *ptr, short data) +#else +void d_putw(unsigned char *ptr, short data) +#endif +{ + ptr[0] = ((unsigned) data & 0xff00) >> 8; + ptr[1] = ((unsigned) data & 0x00ff) >> 0; +} + +/* + * NAME: data->putl() + * DESCRIPTION: marshal 4 bytes out to Macintosh (big-endian) format + */ +void d_putl(unsigned char *ptr, long data) +{ + ptr[0] = ((unsigned long) data & 0xff000000) >> 24; + ptr[1] = ((unsigned long) data & 0x00ff0000) >> 16; + ptr[2] = ((unsigned long) data & 0x0000ff00) >> 8; + ptr[3] = ((unsigned long) data & 0x000000ff) >> 0; +} + +/* + * NAME: data->fetchb() + * DESCRIPTION: incrementally retrieve a byte of data + */ +void d_fetchb(unsigned char **ptr, char *dest) +{ + *dest = d_getb(*ptr); + *ptr += 1; +} + +/* + * NAME: data->fetchw() + * DESCRIPTION: incrementally retrieve a word of data + */ +void d_fetchw(unsigned char **ptr, short *dest) +{ + *dest = d_getw(*ptr); + *ptr += 2; +} + +/* + * NAME: data->fetchl() + * DESCRIPTION: incrementally retrieve a long word of data + */ +void d_fetchl(unsigned char **ptr, long *dest) +{ + *dest = d_getl(*ptr); + *ptr += 4; +} + +/* + * NAME: data->fetchs() + * DESCRIPTION: incrementally retrieve a string + */ +void d_fetchs(unsigned char **ptr, char *dest, int size) +{ + int len; + char blen; + + d_fetchb(ptr, &blen); + len = blen; + + if (len > 0 && len < size) + memcpy(dest, *ptr, len); + else + len = 0; + + dest[len] = 0; + + *ptr += size - 1; +} + +/* + * NAME: data->storeb() + * DESCRIPTION: incrementally store a byte of data + */ +#ifdef PROTOTYPES +void d_storeb(unsigned char **ptr, char data) +#else +void d_storeb(unsigned char **ptr, char data) +#endif +{ + d_putb(*ptr, data); + *ptr += 1; +} + +/* + * NAME: data->storew() + * DESCRIPTION: incrementally store a word of data + */ +#ifdef PROTOTYPES +void d_storew(unsigned char **ptr, short data) +#else +void d_storew(unsigned char **ptr, short data) +#endif +{ + d_putw(*ptr, data); + *ptr += 2; +} + +/* + * NAME: data->storel() + * DESCRIPTION: incrementally store a long word of data + */ +void d_storel(unsigned char **ptr, long data) +{ + d_putl(*ptr, data); + *ptr += 4; +} + +/* + * NAME: data->stores() + * DESCRIPTION: incrementally store a string + */ +void d_stores(unsigned char **ptr, char *src, int size) +{ + int len; + + len = strlen(src); + if (len > --size) + len = 0; + + d_storeb(ptr, (unsigned char) len); + + memcpy(*ptr, src, len); + memset(*ptr + len, 0, size - len); + + *ptr += size; +} + +/* + * NAME: calctzdiff() + * DESCRIPTION: calculate the timezone difference between local time and UTC + */ +static void calctzdiff() +{ + time_t t; + int isdst; + struct tm tm, *tmp; + + time(&t); + isdst = localtime(&t)->tm_isdst; + + tmp = gmtime(&t); + if (tmp) + { + tm = *tmp; + tm.tm_isdst = isdst; + + tzdiff = t - mktime(&tm); + } + else + tzdiff = 0; +} + +/* + * NAME: data->tomtime() + * DESCRIPTION: convert UNIX time to Macintosh time + */ +unsigned long d_tomtime(unsigned long secs) +{ + time_t utime = secs; + + if (tzdiff == TZNONE) + calctzdiff(); + + return utime + tzdiff + MUTDIFF; +} + +/* + * NAME: data->toutime() + * DESCRIPTION: convert Macintosh time to UNIX time + */ +unsigned long d_toutime(unsigned long secs) +{ + time_t utime = secs; + + if (tzdiff == TZNONE) + calctzdiff(); + + return utime - MUTDIFF - tzdiff; +} + +#ifdef APPLE_HYB +/* + * NAME: data->dtoutime() + * DESCRIPTION: convert Apple Double v2 time to UNIX time + */ +unsigned long d_dtoutime(long secs) +{ + time_t utime = secs; + + if (tzdiff == TZNONE) + calctzdiff(); + + return utime + DUTDIFF - tzdiff; +} +#endif /* APPLE_HYB */ + +/* + * NAME: data->relstring() + * DESCRIPTION: compare two strings as per MacOS for HFS + */ +int d_relstring(char *str1, char *str2) +{ + int diff; + + while (*str1 && *str2) + { + diff = hfs_charorder[(unsigned char) *str1] - + hfs_charorder[(unsigned char) *str2]; + + if (diff) + return diff; + + ++str1, ++str2; + } + + if (! *str1 && *str2) + return -1; + else if (*str1 && ! *str2) + return 1; + + return 0; +} diff --git a/libhfs_iso/data.h b/libhfs_iso/data.h new file mode 100644 index 0000000..b3a68a5 --- /dev/null +++ b/libhfs_iso/data.h @@ -0,0 +1,57 @@ +/* + * 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. + * + */ + +/* @(#)data.h 1.2 01/11/11 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. + */ + +char d_getb(unsigned char *); +short d_getw(unsigned char *); +long d_getl(unsigned char *); + +void d_putb(unsigned char *, char); +void d_putw(unsigned char *, short); +void d_putl(unsigned char *, long); + +void d_fetchb(unsigned char **, char *); +void d_fetchw(unsigned char **, short *); +void d_fetchl(unsigned char **, long *); +void d_fetchs(unsigned char **, char *, int); + +void d_storeb(unsigned char **, char); +void d_storew(unsigned char **, short); +void d_storel(unsigned char **, long); +void d_stores(unsigned char **, char *, int); + +unsigned long d_tomtime(unsigned long); +unsigned long d_toutime(unsigned long); +#ifdef APPLE_HYB +unsigned long d_dtoutime(long); +#endif /* APPLE_HYB */ + +int d_relstring(char *, char *); diff --git a/libhfs_iso/file.c b/libhfs_iso/file.c new file mode 100644 index 0000000..a0198c8 --- /dev/null +++ b/libhfs_iso/file.c @@ -0,0 +1,470 @@ +/* + * 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. + * + */ + +/* @(#)file.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 +#include +#include + +#include "internal.h" +#include "data.h" +#include "block.h" +#include "file.h" +#include "btree.h" +#include "record.h" +#include "volume.h" + +/* #include */ + + +/* + * NAME: file->selectfork() + * DESCRIPTION: choose a fork for file operations + */ +void f_selectfork(hfsfile *file, int ffork) +{ + if (ffork == 0) + { + file->fork = fkData; + memcpy(file->ext, file->cat.u.fil.filExtRec, sizeof(ExtDataRec)); + } + else + { + file->fork = fkRsrc; + memcpy(file->ext, file->cat.u.fil.filRExtRec, sizeof(ExtDataRec)); + } + + file->fabn = 0; + file->pos = 0; +} + +/* + * NAME: file->getptrs() + * DESCRIPTION: make pointers to the current fork's lengths and extents + */ +void f_getptrs(hfsfile *file, unsigned long **lglen, unsigned long **pylen, + ExtDataRec **extrec) +{ + if (file->fork == fkData) + { + if (lglen) + *lglen = &file->cat.u.fil.filLgLen; + if (pylen) + *pylen = &file->cat.u.fil.filPyLen; + if (extrec) + *extrec = &file->cat.u.fil.filExtRec; + } + else + { + if (lglen) + *lglen = &file->cat.u.fil.filRLgLen; + if (pylen) + *pylen = &file->cat.u.fil.filRPyLen; + if (extrec) + *extrec = &file->cat.u.fil.filRExtRec; + } +} + +/* + * NAME: file->doblock() + * DESCRIPTION: read or write a numbered block from a file + */ +int f_doblock(hfsfile *file, unsigned long number, block *bp, + int (*func)(hfsvol *, unsigned int, unsigned int, block *)) +{ + unsigned int abnum; + unsigned int blnum; + unsigned int fabn; + int i; + + abnum = number / file->vol->lpa; + blnum = number % file->vol->lpa; + + /* locate the appropriate extent record */ + + fabn = file->fabn; + + if (abnum < fabn) + { + ExtDataRec *extrec; + + f_getptrs(file, 0, 0, &extrec); + + fabn = file->fabn = 0; + memcpy(file->ext, extrec, sizeof(ExtDataRec)); + } + else + abnum -= fabn; + + for (;;) + { + unsigned int num; + + for (i = 0; i < 3; ++i) + { + num = file->ext[i].xdrNumABlks; + +#ifdef APPLE_HYB + if (i > 0) { +/* SHOULD NOT HAPPEN! - all the files should not be fragmented + if this happens, then a serious problem has occured, may be + a hard linked file? */ +#ifdef DEBUG + fprintf(stderr,"fragmented file: %s %d\n",file->name, i); */ +#endif /* DEBUG */ + ERROR(HCE_ERROR, "Possible Catalog file overflow - please report error"); + return -1; + } +#endif /* APPLE_HYB */ + if (abnum < num) + return func(file->vol, file->ext[i].xdrStABN + abnum, blnum, bp); + + fabn += num; + abnum -= num; + } + + if (v_extsearch(file, fabn, &file->ext, 0) <= 0) + return -1; + + file->fabn = fabn; + } +} + +/* + * NAME: file->alloc() + * DESCRIPTION: reserve disk blocks for a file + */ +int f_alloc(hfsfile *file) +{ + hfsvol *vol = file->vol; + ExtDescriptor blocks; + ExtDataRec *extrec; + unsigned long *pylen, clumpsz; + unsigned int start, end; + node n; + int i; + + clumpsz = file->clump; + if (clumpsz == 0) + clumpsz = vol->mdb.drClpSiz; + + blocks.xdrNumABlks = clumpsz / vol->mdb.drAlBlkSiz; + + if (v_allocblocks(vol, &blocks) < 0) + return -1; + + /* update the file's extents */ + + f_getptrs(file, 0, &pylen, &extrec); + + start = file->fabn; + end = *pylen / vol->mdb.drAlBlkSiz; + + n.nnum = 0; + i = -1; + + while (start < end) + { + for (i = 0; i < 3; ++i) + { + unsigned int num; + + num = file->ext[i].xdrNumABlks; + start += num; + + if (start == end) + break; + else if (start > end) + { + v_freeblocks(vol, &blocks); + ERROR(EIO, "file extents exceed file physical length"); + return -1; + } + else if (num == 0) + { + v_freeblocks(vol, &blocks); + ERROR(EIO, "empty file extent"); + return -1; + } + } + + if (start == end) + break; + + if (v_extsearch(file, start, &file->ext, &n) <= 0) + { + v_freeblocks(vol, &blocks); + return -1; + } + + file->fabn = start; + } + + if (i >= 0 && + file->ext[i].xdrStABN + file->ext[i].xdrNumABlks == blocks.xdrStABN) + file->ext[i].xdrNumABlks += blocks.xdrNumABlks; + else + { + /* create a new extent descriptor */ + + if (++i < 3) + file->ext[i] = blocks; + else + { + ExtKeyRec key; + unsigned char record[HFS_EXTRECMAXLEN]; + int reclen; + + /* record is full; create a new one */ + + file->ext[0] = blocks; + + for (i = 1; i < 3; ++i) + { + file->ext[i].xdrStABN = 0; + file->ext[i].xdrNumABlks = 0; + } + + file->fabn = start; + + r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, end); + r_packextkey(&key, record, &reclen); + r_packextdata(&file->ext, HFS_RECDATA(record), &reclen); + + if (bt_insert(&vol->ext, record, reclen) < 0) + { + v_freeblocks(vol, &blocks); + return -1; + } + + i = -1; + } + } + + if (i >= 0) + { + /* store the modified extent record */ + + if (file->fabn) + { + if ((n.nnum == 0 && + v_extsearch(file, file->fabn, 0, &n) <= 0) || + v_putextrec(&file->ext, &n) < 0) + { + v_freeblocks(vol, &blocks); + return -1; + } + } + else + memcpy(extrec, file->ext, sizeof(ExtDataRec)); + } + + *pylen += blocks.xdrNumABlks * vol->mdb.drAlBlkSiz; + + file->flags |= HFS_UPDATE_CATREC; + + return blocks.xdrNumABlks; +} + +/* + * NAME: file->trunc() + * DESCRIPTION: release disk blocks unneeded by a file + */ +int f_trunc(hfsfile *file) +{ + ExtDataRec *extrec; + unsigned long *lglen, *pylen, alblksz, newpylen; + unsigned int dlen, start, end; + node n; + int i; + + f_getptrs(file, &lglen, &pylen, &extrec); + + alblksz = file->vol->mdb.drAlBlkSiz; + newpylen = (*lglen / alblksz + (*lglen % alblksz != 0)) * alblksz; + + if (newpylen > *pylen) + { + ERROR(EIO, "file size exceeds physical length"); + return -1; + } + else if (newpylen == *pylen) + return 0; + + dlen = (*pylen - newpylen) / alblksz; + + start = file->fabn; + end = newpylen / alblksz; + + if (start >= end) + { + start = file->fabn = 0; + memcpy(file->ext, extrec, sizeof(ExtDataRec)); + } + + n.nnum = 0; + i = -1; + + while (start < end) + { + for (i = 0; i < 3; ++i) + { + unsigned int num; + + num = file->ext[i].xdrNumABlks; + start += num; + + if (start >= end) + break; + else if (num == 0) + { + ERROR(EIO, "empty file extent"); + return -1; + } + } + + if (start >= end) + break; + + if (v_extsearch(file, start, &file->ext, &n) <= 0) + return -1; + + file->fabn = start; + } + + if (start > end) + { + ExtDescriptor blocks; + + file->ext[i].xdrNumABlks -= start - end; + dlen -= start - end; + + blocks.xdrStABN = file->ext[i].xdrStABN + file->ext[i].xdrNumABlks; + blocks.xdrNumABlks = start - end; + + v_freeblocks(file->vol, &blocks); + } + + *pylen = newpylen; + + file->flags |= HFS_UPDATE_CATREC; + + do + { + while (dlen && ++i < 3) + { + unsigned int num; + + num = file->ext[i].xdrNumABlks; + start += num; + + if (num == 0) + { + ERROR(EIO, "empty file extent"); + return -1; + } + else if (num > dlen) + { + ERROR(EIO, "file extents exceed physical size"); + return -1; + } + + dlen -= num; + v_freeblocks(file->vol, &file->ext[i]); + + file->ext[i].xdrStABN = 0; + file->ext[i].xdrNumABlks = 0; + } + + if (file->fabn) + { + if (n.nnum == 0 && + v_extsearch(file, file->fabn, 0, &n) <= 0) + return -1; + + if (file->ext[0].xdrNumABlks) + { + if (v_putextrec(&file->ext, &n) < 0) + return -1; + } + else + { + if (bt_delete(&file->vol->ext, HFS_NODEREC(n, n.rnum)) < 0) + return -1; + + n.nnum = 0; + } + } + else + memcpy(extrec, file->ext, sizeof(ExtDataRec)); + + if (dlen) + { + if (v_extsearch(file, start, &file->ext, &n) <= 0) + return -1; + + file->fabn = start; + i = -1; + } + } + while (dlen); + + return 0; +} + +/* + * NAME: file->flush() + * DESCRIPTION: flush all pending changes to an open file + */ +int f_flush(hfsfile *file) +{ + hfsvol *vol = file->vol; + + if (! (vol->flags & HFS_READONLY)) + { + if (file->flags & HFS_UPDATE_CATREC) + { + node n; + + file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN; + file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN; + file->cat.u.fil.filClpSize = file->clump; + + if (v_catsearch(file->vol, file->parid, file->name, 0, 0, &n) <= 0 || + v_putcatrec(&file->cat, &n) < 0) + return -1; + + file->flags &= ~HFS_UPDATE_CATREC; + } + } + + return 0; +} diff --git a/libhfs_iso/file.h b/libhfs_iso/file.h new file mode 100644 index 0000000..9b95bd6 --- /dev/null +++ b/libhfs_iso/file.h @@ -0,0 +1,48 @@ +/* + * 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. + * + */ + +/* @(#)file.h 1.1 00/03/05 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. + */ + +enum { + fkData = 0x00, + fkRsrc = 0xff +}; + +void f_selectfork(hfsfile *, int); +void f_getptrs(hfsfile *, unsigned long **, unsigned long **, ExtDataRec **); + +int f_doblock(hfsfile *, unsigned long, block *, + int (*)(hfsvol *, unsigned int, unsigned int, block *)); + +# define f_getblock(file, num, bp) f_doblock(file, num, bp, b_readab) +# define f_putblock(file, num, bp) f_doblock(file, num, bp, b_writeab) + +int f_alloc(hfsfile *); +int f_flush(hfsfile *); diff --git a/libhfs_iso/gdata.c b/libhfs_iso/gdata.c new file mode 100644 index 0000000..7642083 --- /dev/null +++ b/libhfs_iso/gdata.c @@ -0,0 +1,20 @@ +/* + * 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. + * + */ + +/* @(#)gdata.c 1.1 01/01/21 Copyright 2001 J. Schilling */ + +#include +#include "internal.h" + +char *hfs_error = "no error"; /* static error string */ +hfsvol *hfs_mounts; /* linked list of mounted volumes */ +hfsvol *hfs_curvol; /* current volume */ diff --git a/libhfs_iso/hfs.c b/libhfs_iso/hfs.c new file mode 100644 index 0000000..fb63ef4 --- /dev/null +++ b/libhfs_iso/hfs.c @@ -0,0 +1,2098 @@ +/* + * 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. + * + */ + +/* @(#)hfs.c 1.9 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. + */ + +/* APPLE_HYB James Pearson j.pearson@ps.ucl.ac.uk 16/7/97 */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" +#include "data.h" +#include "block.h" +#include "low.h" +#include "file.h" +#include "btree.h" +#include "node.h" +#include "record.h" +#include "volume.h" +#include "hfs.h" + +/* High-Level Volume Routines ============================================== */ + +/* + * NAME: hfs->mount() + * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error) + */ +#ifdef APPLE_HYB +hfsvol *hfs_mount(hce_mem *hce, int pnum, int flags) +#else +hfsvol *hfs_mount(char *path, int pnum, int flags) +#endif /* APPLE_HYB */ +{ +#ifndef APPLE_HYB + struct stat dev; +#endif + hfsvol *vol = 0; + +#ifndef APPLE_HYB + /* see if the volume is already mounted */ + + if (stat(path, &dev) >= 0) + { + struct stat mdev; + hfsvol *check; + + for (check = hfs_mounts; check; check = check->next) + { + if (fstat(check->fd, &mdev) >= 0 && + mdev.st_dev == dev.st_dev && + mdev.st_ino == dev.st_ino && + (check->pnum == 0 || check->pnum == pnum)) + { + /* verify compatible read/write mode */ + + if (((check->flags & HFS_READONLY) && + ! (flags & O_WRONLY)) || + (! (check->flags & HFS_READONLY) && + (flags & (O_WRONLY | O_RDWR)))) + { + vol = check; + break; + } + } + } + } +#endif /* APPLE_HYB */ + if (vol == 0) + { + vol = ALLOC(hfsvol, 1); + if (vol == 0) + { + ERROR(ENOMEM, 0); + return 0; + } + + vol->flags = 0; + vol->pnum = pnum; + vol->vstart = 0; + vol->vlen = 0; + vol->lpa = 0; + vol->vbm = 0; + vol->cwd = HFS_CNID_ROOTDIR; + + vol->refs = 0; + vol->files = 0; + vol->dirs = 0; + vol->prev = 0; + vol->next = 0; + + vol->ext.map = 0; + vol->ext.mapsz = 0; + vol->ext.flags = 0; + vol->ext.compare = r_compareextkeys; + + vol->cat.map = 0; + vol->cat.mapsz = 0; + vol->cat.flags = 0; + vol->cat.compare = r_comparecatkeys; + + /* open and lock the device */ + +#ifdef APPLE_HYB + vol->fd = 3; /* any +ve number will do? */ + vol->hce = hce; /* store the extra with the vol info */ +#else + if (flags & (O_WRONLY | O_RDWR)) + { + vol->fd = open(path, O_RDWR); + if (vol->fd >= 0 && l_lockvol(vol) < 0) + { + close(vol->fd); + vol->fd = -2; + } + } + + if (! (flags & (O_WRONLY | O_RDWR)) || + (vol->fd < 0 && + (errno == EROFS || errno == EACCES || errno == EAGAIN) && + (flags & O_RDWR))) + { + vol->flags |= HFS_READONLY; + vol->fd = open(path, O_RDONLY); + if (vol->fd >= 0 && l_lockvol(vol) < 0) + { + close(vol->fd); + vol->fd = -2; + } + } + + if (vol->fd < 0) + { + if (vol->fd != -2) + ERROR(errno, "error opening device"); + + v_destruct(vol); + + return 0; + } +#endif /* APPLE_HYB */ + + /* find out what kind of media this is and read the MDB */ + + if (l_readblock0(vol) < 0 || + l_readmdb(vol) < 0) + { +#ifndef APPLE_HYB + close(vol->fd); + v_destruct(vol); +#endif /* APPLE_HYB */ + return 0; + } + + /* verify this is an HFS volume */ + + if (vol->mdb.drSigWord != 0x4244) + { +#ifndef APPLE_HYB + close(vol->fd); +#endif /* APPLE_HYB */ + v_destruct(vol); + + ERROR(EINVAL, "not a Macintosh HFS volume"); + return 0; + } + + /* do minimal consistency checks */ + + if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0) + { +#ifndef APPLE_HYB + close(vol->fd); +#endif /* APPLE_HYB */ + v_destruct(vol); + + ERROR(EINVAL, "bad volume allocation block size"); + return 0; + } + + if (vol->vlen == 0) + vol->vlen = vol->mdb.drAlBlSt + + vol->mdb.drNmAlBlks * (vol->mdb.drAlBlkSiz / HFS_BLOCKSZ) + 2; + + /* read the volume bitmap and extents/catalog B*-tree headers */ + + if (l_readvbm(vol) < 0 || + bt_readhdr(&vol->ext) < 0 || + bt_readhdr(&vol->cat) < 0) + { +#ifndef APPLE_HYB + close(vol->fd); +#endif /* APPLE_HYB */ + v_destruct(vol); + return 0; + } + + if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED)) + { + /* volume was not cleanly unmounted; scavenge free-space */ + + if (v_scavenge(vol) < 0) + { +#ifndef APPLE_HYB + close(vol->fd); +#endif /* APPLE_HYB */ + v_destruct(vol); + return 0; + } + } + + if (vol->flags & HFS_READONLY) + vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; + else + vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED; + + vol->prev = 0; + vol->next = hfs_mounts; + + if (hfs_mounts) + hfs_mounts->prev = vol; + + hfs_mounts = vol; + } + + ++vol->refs; + + return hfs_curvol = vol; +} + +/* + * NAME: hfs->flush() + * DESCRIPTION: flush all pending changes to an HFS volume + */ +int hfs_flush(hfsvol *vol) +{ + hfsfile *file; + + if (v_getvol(&vol) < 0) + return -1; + + for (file = vol->files; file; file = file->next) + { + if (f_flush(file) < 0) + return -1; + } + + if (v_flush(vol, 0) < 0) + return -1; + + return 0; +} + +/* + * NAME: hfs->flushall() + * DESCRIPTION: flush all pending changes to all mounted HFS volumes + */ +void hfs_flushall() +{ + hfsvol *vol; + + for (vol = hfs_mounts; vol; vol = vol->next) + hfs_flush(vol); +} + +/* + * NAME: hfs->umount() + * DESCRIPTION: close an HFS volume + */ +#ifdef APPLE_HYB +/* extra argument used to alter the position of the extents/catalog files */ +int hfs_umount(hfsvol *vol, long end, long locked) +#else +int hfs_umount(hfsvol *vol) +#endif /* APPLE_HYB */ +{ + int result = 0; + + if (v_getvol(&vol) < 0) + return -1; + + if (--vol->refs) + return v_flush(vol, 0); + + /* close all open files and directories */ + + while (vol->files) +#ifdef APPLE_HYB + hfs_close(vol->files, 0, 0); +#else + hfs_close(vol->files); +#endif /* APPLE_HYB */ + + while (vol->dirs) + hfs_closedir(vol->dirs); + +#ifdef APPLE_HYB + if (end) + { + /* move extents and catalog to end of volume ... */ + long vbmsz = (vol->vlen / vol->lpa + 4095) / 4096; + + /* we are adding this "files" to the end of the ISO volume, + so calculate this address in HFS speak ... */ +/* end -= vol->mdb.drAlBlSt; */ + end -= (vol->mdb.drAlBlSt + vol->hce->hfs_map_size); + end /= vol->lpa; + + /* catalog file ... */ + vol->ext.f.cat.u.fil.filExtRec[0].xdrStABN = end; + vol->mdb.drXTExtRec[0].xdrStABN = end; + + /* move postition to start of extents file */ + end += vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN; + + /* extents file ... */ + vol->cat.f.cat.u.fil.filExtRec[0].xdrStABN = end; + vol->mdb.drCTExtRec[0].xdrStABN = end; + + /* the volume bitmap is wrong as we have "moved" files + about - simple just set the whole lot (it's a readonly volume + anyway!) */ + memset(vol->vbm, 0xff, vbmsz*HFS_BLOCKSZ); + + /* set the free blocks to zero */ + vol->mdb.drFreeBks = 0; + + /* flag changes for flushing later */ + vol->flags |= HFS_UPDATE_VBM; + vol->flags |= HFS_UPDATE_MDB; + vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; + if (locked) { + vol->mdb.drAtrb |= HFS_ATRB_SLOCKED; + } + vol->ext.flags |= HFS_UPDATE_BTHDR; + vol->cat.flags |= HFS_UPDATE_BTHDR; + } +#endif /* APPLE_HYB */ + + if (v_flush(vol, 1) < 0) + result = -1; + +#ifndef APPLE_HYB + if (close(vol->fd) < 0 && result == 0) + { + ERROR(errno, "error closing device"); + result = -1; + } +#endif /* APPLE_HYB */ + + if (vol->prev) + vol->prev->next = vol->next; + if (vol->next) + vol->next->prev = vol->prev; + + if (vol == hfs_mounts) + hfs_mounts = vol->next; + if (vol == hfs_curvol) + hfs_curvol = 0; + + v_destruct(vol); + + return result; +} + +/* + * NAME: hfs->umountall() + * DESCRIPTION: unmount all mounted volumes + */ +void hfs_umountall() +{ + while (hfs_mounts) +#ifdef APPLE_HYB + continue; +#else + hfs_umount(hfs_mounts); +#endif /* APPLE_HYB */ +} + +/* + * NAME: hfs->getvol() + * DESCRIPTION: return a pointer to a mounted volume + */ +hfsvol *hfs_getvol(char *name) +{ + hfsvol *vol; + + if (name == 0) + return hfs_curvol; + + for (vol = hfs_mounts; vol; vol = vol->next) + { + if (d_relstring(name, vol->mdb.drVN) == 0) + return vol; + } + + return 0; +} + +/* + * NAME: hfs->setvol() + * DESCRIPTION: change the current volume + */ +void hfs_setvol(hfsvol *vol) +{ + hfs_curvol = vol; +} + +/* + * NAME: hfs->vstat() + * DESCRIPTION: return volume statistics + */ +int hfs_vstat(hfsvol *vol, hfsvolent *ent) +{ + if (v_getvol(&vol) < 0) + return -1; + + strcpy(ent->name, vol->mdb.drVN); + + ent->flags = (vol->flags & HFS_READONLY) ? HFS_ISLOCKED : 0; + ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz; + ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz; + ent->crdate = d_toutime(vol->mdb.drCrDate); + ent->mddate = d_toutime(vol->mdb.drLsMod); + + return 0; +} + +/* + * NAME: hfs->format() + * DESCRIPTION: write a new filesystem + */ +#ifdef APPLE_HYB +int hfs_format(hce_mem *hce, int pnum, char *vname) +#else +int hfs_format(char *path, int pnum, char *vname) +#endif /* APPLE_HYB */ +{ + hfsvol vol; + btree *ext = &vol.ext; + btree *cat = &vol.cat; + unsigned int vbmsz; + int i, result = 0; + block vbm[16]; + char *map; + + if (strchr(vname, ':')) + { + ERROR(EINVAL, "volume name may not contain colons"); + return -1; + } + + i = strlen(vname); + if (i < 1 || i > HFS_MAX_VLEN) + { + ERROR(EINVAL, "volume name must be 1-27 chars"); + return -1; + } + + vol.flags = 0; + vol.pnum = pnum; + vol.vstart = 0; + vol.vlen = 0; + vol.lpa = 0; + vol.vbm = vbm; + vol.cwd = HFS_CNID_ROOTDIR; + + vol.refs = 0; + vol.files = 0; + vol.dirs = 0; + vol.prev = 0; + vol.next = 0; + +#ifndef APPLE_HYB + vol.fd = open(path, O_RDWR); + if (vol.fd < 0) + { + ERROR(errno, "error opening device for writing"); + return -1; + } + + if (l_lockvol(&vol) < 0) + { + close(vol.fd); + return -1; + } +#endif /* APPLE_HYB */ + if (pnum > 0) + { + if (l_readpm(&vol) < 0) + { + close(vol.fd); + return -1; + } + } + else /* determine size of entire device */ + { +#ifdef APPLE_HYB + vol.vlen = hce->hfs_vol_size; +#else + unsigned long low, high, mid; + block b; + + for (low = 0, high = 2879; b_readlb(&vol, high, &b) >= 0; high *= 2) + low = high; + + while (low < high - 1) + { + mid = (low + high) / 2; + + if (b_readlb(&vol, mid, &b) < 0) + high = mid; + else + low = mid; + } + + vol.vlen = low + 1; +#endif /* APPLE_HYB */ + } + + if (vol.vlen < 800 * 1024 / HFS_BLOCKSZ) + { +#ifndef APPLE_HYB + close(vol.fd); +#endif /* APPLE_HYB */ + + ERROR(EINVAL, "volume size must be >= 800K"); + return -1; + } + + /* initialize volume geometry */ + +#ifdef APPLE_HYB + /* force lpa to be a multiple of 4 (i.e. 2048/512) - as calculated + earlier */ + vol.lpa = hce->Csize/HFS_BLOCKSZ; +#else + vol.lpa = 1 + vol.vlen / 65536; +#endif /* APPLE_HYB */ + + vbmsz = (vol.vlen / vol.lpa + 4095) / 4096; + + vol.mdb.drSigWord = 0x4244; + vol.mdb.drCrDate = d_tomtime(time(0)); + vol.mdb.drLsMod = vol.mdb.drCrDate; + vol.mdb.drAtrb = 0; + vol.mdb.drNmFls = 0; + vol.mdb.drVBMSt = 3; + vol.mdb.drAllocPtr = 0; + vol.mdb.drNmAlBlks = (vol.vlen - 5 - vbmsz) / vol.lpa; + vol.mdb.drAlBlkSiz = vol.lpa * HFS_BLOCKSZ; + vol.mdb.drClpSiz = vol.mdb.drAlBlkSiz * 4; + vol.mdb.drAlBlSt = 3 + vbmsz; +#ifdef APPLE_HYB + /* round up start block to a muliple of lpa - important later */ +/*vol.mdb.drAlBlSt = ((vol.mdb.drAlBlSt + vol.lpa - 1) / vol.lpa) * vol.lpa; +*/ + /* take in accout alignment of files wrt HFS volume start i.e we want + drAlBlSt plus hfs_map_size to me a multiple of lpa */ + vol.mdb.drAlBlSt = ((vol.mdb.drAlBlSt + hce->hfs_map_size + vol.lpa - 1) / vol.lpa) * vol.lpa; + vol.mdb.drAlBlSt -= hce->hfs_map_size; +#endif /* APPLE_HYB */ + vol.mdb.drNxtCNID = HFS_CNID_ROOTDIR; /* modified later */ + vol.mdb.drFreeBks = vol.mdb.drNmAlBlks; + + strcpy(vol.mdb.drVN, vname); + + vol.mdb.drVolBkUp = 0; + vol.mdb.drVSeqNum = 0; + vol.mdb.drWrCnt = 0; + vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz; +#ifdef APPLE_HYB + /* adjust size of extents/catalog upwards as we may have rounded up + allocation size */ + i = 1 + vol.vlen / 65536; + + vol.mdb.drXTClpSiz = (vol.mdb.drXTClpSiz * vol.lpa) / i; + + /* round up to lpa size */ + vol.mdb.drXTClpSiz = ((vol.mdb.drXTClpSiz + vol.mdb.drAlBlkSiz - 1) / + vol.mdb.drAlBlkSiz) * vol.mdb.drAlBlkSiz; + + /* ignore above, use what we have already calculated ... */ + vol.mdb.drXTClpSiz = hce->XTCsize; + + /* make Catalog file CTC (default twice) as big - prevents further allocation + later which we don't want - this seems to work OK ... */ +/*vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * CTC; */ + vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz * hce->ctc_size; + + /* we want to put things at the end of the volume later, so we'll + cheat here ... shouldn't matter, as we only need the volume read + only anyway (we won't be adding files later!) - leave some extra + space for the alternative MDB (in the last allocation block) */ + + vol.mdb.drNmAlBlks = vol.mdb.drFreeBks = vol.vlen / vol.lpa - 1; +#else + vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz; +#endif /* APPLE_HYB */ + vol.mdb.drNmRtDirs = 0; + vol.mdb.drFilCnt = 0; + vol.mdb.drDirCnt = -1; /* incremented when root folder is created */ + + for (i = 0; i < 8; ++i) + vol.mdb.drFndrInfo[i] = 0; + + vol.mdb.drVCSize = 0; + vol.mdb.drVBMCSize = 0; + vol.mdb.drCtlCSize = 0; + + vol.mdb.drXTFlSize = 0; + vol.mdb.drCTFlSize = 0; + + for (i = 0; i < 3; ++i) + { + vol.mdb.drXTExtRec[i].xdrStABN = 0; + vol.mdb.drXTExtRec[i].xdrNumABlks = 0; + + vol.mdb.drCTExtRec[i].xdrStABN = 0; + vol.mdb.drCTExtRec[i].xdrNumABlks = 0; + } + + /* initialize volume bitmap */ + + memset(vol.vbm, 0, sizeof(vbm)); + +#ifdef APPLE_HYB + /* We don't want to write anything out at the moment, so we allocate + memory to hold the HFS "header" info and extents/catalog files. + Any reads/writes from/to these parts of the volume are trapped and + stored in memory. */ + + /* blocks up to the first unallocated block == HFS "header" info + This will be placed in the first 32kb of the ISO volume later */ + hce->hfs_hdr_size = vol.mdb.drAlBlSt; + + /* size of the extents and catalog files. This will be added + to the end of the ISO volume later */ + hce->hfs_ce_size = vol.mdb.drXTClpSiz + vol.mdb.drCTClpSiz; + + /* we also allocate space for the Desktop file and the alternative + MDB while we're here */ + FREE(hce->hfs_ce); + hce->hfs_ce = ALLOC(unsigned char, (hce->hfs_ce_size + vol.mdb.drClpSiz + + vol.mdb.drAlBlkSiz)); + + /* allocate memory for the map and hdr */ + FREE(hce->hfs_map); + hce->hfs_map = ALLOC(unsigned char, ((hce->hfs_hdr_size + hce->hfs_map_size) + *HFS_BLOCKSZ)); + + if (hce->hfs_ce == 0 || hce->hfs_map == 0) + { + ERROR(ENOMEM, 0); + result = -1; + } + + /* hfs_hdr is immediately after the hfs_map */ + hce->hfs_hdr = hce->hfs_map + hce->hfs_map_size*HFS_BLOCKSZ; + + /* size needed in HFS_BLOCKSZ blocks for later use */ + hce->hfs_ce_size /= HFS_BLOCKSZ; + + /* note size of Desktop file */ + hce->hfs_dt_size = vol.mdb.drClpSiz/HFS_BLOCKSZ; + + /* total size of catalog/extents and desktop */ + hce->hfs_tot_size = hce->hfs_ce_size + hce->hfs_dt_size; + + /* alternative MDB in the last alocation block */ + hce->hfs_alt_mdb = hce->hfs_ce + hce->hfs_tot_size*HFS_BLOCKSZ; + + /* add the MDB to the total size */ + hce->hfs_tot_size += vol.lpa; + + /* store this info in the volume info */ + vol.hce = hce; + +#endif /* APPLE_HYB */ + + /* create extents overflow file */ + + ext->f.vol = &vol; + ext->f.parid = 0; + strcpy(ext->f.name, "extents overflow"); + + ext->f.cat.cdrType = cdrFilRec; + /* ext->f.cat.cdrResrv2 */ + ext->f.cat.u.fil.filFlags = 0; + ext->f.cat.u.fil.filTyp = 0; + /* ext->f.cat.u.fil.filUsrWds */ + ext->f.cat.u.fil.filFlNum = HFS_CNID_EXT; + ext->f.cat.u.fil.filStBlk = 0; + ext->f.cat.u.fil.filLgLen = 0; + ext->f.cat.u.fil.filPyLen = 0; + ext->f.cat.u.fil.filRStBlk = 0; + ext->f.cat.u.fil.filRLgLen = 0; + ext->f.cat.u.fil.filRPyLen = 0; + ext->f.cat.u.fil.filCrDat = vol.mdb.drCrDate; + ext->f.cat.u.fil.filMdDat = vol.mdb.drLsMod; + ext->f.cat.u.fil.filBkDat = 0; + /* ext->f.cat.u.fil.filFndrInfo */ + ext->f.cat.u.fil.filClpSize = 0; + + for (i = 0; i < 3; ++i) + { + ext->f.cat.u.fil.filExtRec[i].xdrStABN = 0; + ext->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0; + + ext->f.cat.u.fil.filRExtRec[i].xdrStABN = 0; + ext->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0; + } + /* ext->f.cat.u.fil.filResrv */ + f_selectfork(&ext->f, 0); + + ext->f.clump = vol.mdb.drXTClpSiz; + ext->f.flags = 0; + + ext->f.prev = ext->f.next = 0; + + n_init(&ext->hdrnd, ext, ndHdrNode, 0); + + ext->hdrnd.nnum = 0; + ext->hdrnd.nd.ndNRecs = 3; + ext->hdrnd.roff[1] = 0x078; + ext->hdrnd.roff[2] = 0x0f8; + ext->hdrnd.roff[3] = 0x1f8; + + memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128); + + ext->hdr.bthDepth = 0; + ext->hdr.bthRoot = 0; + ext->hdr.bthNRecs = 0; + ext->hdr.bthFNode = 0; + ext->hdr.bthLNode = 0; + ext->hdr.bthNodeSize = HFS_BLOCKSZ; + ext->hdr.bthKeyLen = 0x07; + ext->hdr.bthNNodes = 0; + ext->hdr.bthFree = 0; + for (i = 0; i < 76; ++i) + ext->hdr.bthResv[i] = 0; + + map = ALLOC(char, HFS_MAP1SZ); + if (map == 0) + { + if (result == 0) + { + ERROR(ENOMEM, 0); + result = -1; + } + } + else + { + memset(map, 0, HFS_MAP1SZ); + BMSET(map, 0); + } + + ext->map = map; + ext->mapsz = HFS_MAP1SZ; + ext->flags = HFS_UPDATE_BTHDR; + ext->compare = r_compareextkeys; + + if (result == 0 && bt_space(ext, 1) < 0) + result = -1; + + --ext->hdr.bthFree; + + /* create catalog file */ + + cat->f.vol = &vol; + cat->f.parid = 0; + strcpy(cat->f.name, "catalog"); + + cat->f.cat.cdrType = cdrFilRec; + /* cat->f.cat.cdrResrv2 */ + cat->f.cat.u.fil.filFlags = 0; + cat->f.cat.u.fil.filTyp = 0; + /* cat->f.cat.u.fil.filUsrWds */ + cat->f.cat.u.fil.filFlNum = HFS_CNID_CAT; + cat->f.cat.u.fil.filStBlk = 0; + cat->f.cat.u.fil.filLgLen = 0; + cat->f.cat.u.fil.filPyLen = 0; + cat->f.cat.u.fil.filRStBlk = 0; + cat->f.cat.u.fil.filRLgLen = 0; + cat->f.cat.u.fil.filRPyLen = 0; + cat->f.cat.u.fil.filCrDat = vol.mdb.drCrDate; + cat->f.cat.u.fil.filMdDat = vol.mdb.drLsMod; + cat->f.cat.u.fil.filBkDat = 0; + /* cat->f.cat.u.fil.filFndrInfo */ + cat->f.cat.u.fil.filClpSize = 0; + + for (i = 0; i < 3; ++i) + { + cat->f.cat.u.fil.filExtRec[i].xdrStABN = 0; + cat->f.cat.u.fil.filExtRec[i].xdrNumABlks = 0; + + cat->f.cat.u.fil.filRExtRec[i].xdrStABN = 0; + cat->f.cat.u.fil.filRExtRec[i].xdrNumABlks = 0; + } + /* cat->f.cat.u.fil.filResrv */ + f_selectfork(&cat->f, 0); + + cat->f.clump = vol.mdb.drCTClpSiz; + cat->f.flags = 0; + + cat->f.prev = cat->f.next = 0; + + n_init(&cat->hdrnd, cat, ndHdrNode, 0); + + cat->hdrnd.nnum = 0; + cat->hdrnd.nd.ndNRecs = 3; + cat->hdrnd.roff[1] = 0x078; + cat->hdrnd.roff[2] = 0x0f8; + cat->hdrnd.roff[3] = 0x1f8; + + memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128); + + cat->hdr.bthDepth = 0; + cat->hdr.bthRoot = 0; + cat->hdr.bthNRecs = 0; + cat->hdr.bthFNode = 0; + cat->hdr.bthLNode = 0; + cat->hdr.bthNodeSize = HFS_BLOCKSZ; + cat->hdr.bthKeyLen = 0x25; + cat->hdr.bthNNodes = 0; + cat->hdr.bthFree = 0; + for (i = 0; i < 76; ++i) + cat->hdr.bthResv[i] = 0; + + map = ALLOC(char, HFS_MAP1SZ); + if (map == 0) + { + if (result == 0) + { + ERROR(ENOMEM, 0); + result = -1; + } + } + else + { + memset(map, 0, HFS_MAP1SZ); + BMSET(map, 0); + } + + cat->map = map; + cat->mapsz = HFS_MAP1SZ; + cat->flags = HFS_UPDATE_BTHDR; + cat->compare = r_comparecatkeys; + + if (result == 0 && bt_space(cat, 1) < 0) + result = -1; + + --cat->hdr.bthFree; + + /* create root folder */ + + if (result == 0 && v_newfolder(&vol, HFS_CNID_ROOTPAR, vname) < 0) + result = -1; + + vol.mdb.drNxtCNID = 16; + + /* finish up */ + + if (result == 0) + { + block b; + + /* write boot blocks */ + + memset(&b, 0, sizeof(b)); + b_writelb(&vol, 0, &b); + b_writelb(&vol, 1, &b); + + /* flush other disk state */ + + vol.flags |= HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB | HFS_UPDATE_VBM; + + if (v_flush(&vol, 1) < 0) + result = -1; + } +#ifndef APPLE_HYB + if (close(vol.fd) < 0 && result == 0) + { + ERROR(errno, "error closing device"); + result = -1; + } +#endif /* APPLE_HYB */ + FREE(vol.ext.map); + FREE(vol.cat.map); + + return result; +} + +/* High-Level Directory Routines =========================================== */ + +/* + * NAME: hfs->chdir() + * DESCRIPTION: change current HFS directory + */ +int hfs_chdir(hfsvol *vol, char *path) +{ + CatDataRec data; + + if (v_getvol(&vol) < 0 || + v_resolve(&vol, path, &data, 0, 0, 0) <= 0) + return -1; + + if (data.cdrType != cdrDirRec) + { + ERROR(ENOTDIR, 0); + return -1; + } + + vol->cwd = data.u.dir.dirDirID; + + return 0; +} + +/* + * NAME: hfs->getcwd() + * DESCRIPTION: return the current working directory ID + */ +long hfs_getcwd(hfsvol *vol) +{ + if (v_getvol(&vol) < 0) + return 0; + + return vol->cwd; +} + +/* + * NAME: hfs->setcwd() + * DESCRIPTION: set the current working directory ID + */ +int hfs_setcwd(hfsvol *vol, long id) +{ + if (v_getvol(&vol) < 0) + return -1; + + if (id == vol->cwd) + return 0; + + /* make sure the directory exists */ + + if (v_getdthread(vol, id, 0, 0) <= 0) + return -1; + + vol->cwd = id; + + return 0; +} + +/* + * NAME: hfs->dirinfo() + * DESCRIPTION: given a directory ID, return its (name and) parent ID + */ +int hfs_dirinfo(hfsvol *vol, long *id, char *name) +{ + CatDataRec thread; + + if (v_getvol(&vol) < 0 || + v_getdthread(vol, *id, &thread, 0) <= 0) + return -1; + + *id = thread.u.dthd.thdParID; + + if (name) + strcpy(name, thread.u.dthd.thdCName); + + return 0; +} + +/* + * NAME: hfs->opendir() + * DESCRIPTION: prepare to read the contents of a directory + */ +hfsdir *hfs_opendir(hfsvol *vol, char *path) +{ + hfsdir *dir; + CatKeyRec key; + CatDataRec data; + unsigned char pkey[HFS_CATKEYLEN]; + + if (v_getvol(&vol) < 0) + return 0; + + dir = ALLOC(hfsdir, 1); + if (dir == 0) + { + ERROR(ENOMEM, 0); + return 0; + } + + dir->vol = vol; + + if (*path == 0) + { + /* meta-directory containing root dirs from all mounted volumes */ + + dir->dirid = 0; + dir->vptr = hfs_mounts; + } + else + { + if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0) + { + FREE(dir); + return 0; + } + + if (data.cdrType != cdrDirRec) + { + FREE(dir); + ERROR(ENOTDIR, 0); + return 0; + } + + dir->dirid = data.u.dir.dirDirID; + dir->vptr = 0; + + r_makecatkey(&key, dir->dirid, ""); + r_packcatkey(&key, pkey, 0); + + if (bt_search(&vol->cat, pkey, &dir->n) <= 0) + { + FREE(dir); + return 0; + } + } + + dir->prev = 0; + dir->next = vol->dirs; + + if (vol->dirs) + vol->dirs->prev = dir; + + vol->dirs = dir; + + return dir; +} + +/* + * NAME: hfs->readdir() + * DESCRIPTION: return the next entry in the directory + */ +int hfs_readdir(hfsdir *dir, hfsdirent *ent) +{ + CatKeyRec key; + CatDataRec data; + unsigned char *ptr; + + if (dir->dirid == 0) + { + hfsvol *vol; + char cname[HFS_MAX_FLEN + 1]; + + for (vol = hfs_mounts; vol; vol = vol->next) + { + if (vol == dir->vptr) + break; + } + + if (vol == 0) + { + ERROR(ENOENT, "no more entries"); + return -1; + } + + if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, 0) <= 0 || + v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName, + &data, cname, 0) < 0) + return -1; + + r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent); + + dir->vptr = vol->next; + + return 0; + } + + if (dir->n.rnum == -1) + { + ERROR(ENOENT, "no more entries"); + return -1; + } + + for (;;) + { + ++dir->n.rnum; + + while (dir->n.rnum >= (int)dir->n.nd.ndNRecs) + { + dir->n.nnum = dir->n.nd.ndFLink; + if (dir->n.nnum == 0) + { + dir->n.rnum = -1; + ERROR(ENOENT, "no more entries"); + return -1; + } + + if (bt_getnode(&dir->n) < 0) + { + dir->n.rnum = -1; + return -1; + } + + dir->n.rnum = 0; + } + + ptr = HFS_NODEREC(dir->n, dir->n.rnum); + + r_unpackcatkey(ptr, &key); + + if (key.ckrParID != dir->dirid) + { + dir->n.rnum = -1; + ERROR(ENOENT, "no more entries"); + return -1; + } + + r_unpackcatdata(HFS_RECDATA(ptr), &data); + + switch (data.cdrType) + { + case cdrDirRec: + case cdrFilRec: + r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent); + return 0; + + case cdrThdRec: + case cdrFThdRec: + break; + + default: + dir->n.rnum = -1; + + ERROR(EIO, "unexpected directory entry found"); + return -1; + } + } +} + +/* + * NAME: hfs->closedir() + * DESCRIPTION: stop reading a directory + */ +int hfs_closedir(hfsdir *dir) +{ + hfsvol *vol = dir->vol; + + if (dir->prev) + dir->prev->next = dir->next; + if (dir->next) + dir->next->prev = dir->prev; + if (dir == vol->dirs) + vol->dirs = dir->next; + + FREE(dir); + + return 0; +} + +/* High-Level File Routines ================================================ */ + +/* + * NAME: hfs->open() + * DESCRIPTION: prepare a file for I/O + */ +hfsfile *hfs_open(hfsvol *vol, char *path) +{ + hfsfile *file; + + if (v_getvol(&vol) < 0) + return 0; + + file = ALLOC(hfsfile, 1); + if (file == 0) + { + ERROR(ENOMEM, 0); + return 0; + } + + if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0) + { + FREE(file); + return 0; + } + + if (file->cat.cdrType != cdrFilRec) + { + FREE(file); + ERROR(EISDIR, 0); + return 0; + } + + file->vol = vol; + file->clump = file->cat.u.fil.filClpSize; + file->flags = 0; + + f_selectfork(file, 0); + + file->prev = 0; + file->next = vol->files; + + if (vol->files) + vol->files->prev = file; + + vol->files = file; + + return file; +} + +/* + * NAME: hfs->setfork() + * DESCRIPTION: select file fork for I/O operations + */ +int hfs_setfork(hfsfile *file, int ffork) +{ + int result = 0; + + if (! (file->vol->flags & HFS_READONLY) && + f_trunc(file) < 0) + result = -1; + + f_selectfork(file, ffork); + + return result; +} + +/* + * NAME: hfs->getfork() + * DESCRIPTION: return the current fork for I/O operations + */ +int hfs_getfork(hfsfile *file) +{ + return file->fork != fkData; +} + +/* + * NAME: hfs->read() + * DESCRIPTION: read from an open file + */ +long hfs_read(hfsfile *file, void *buf, unsigned long len) +{ + unsigned long *lglen, count; + unsigned char *ptr = buf; + + f_getptrs(file, &lglen, 0, 0); + + if (file->pos + len > *lglen) + len = *lglen - file->pos; + + count = len; + while (count) + { + block b; + unsigned long bnum, offs, chunk; + + bnum = file->pos / HFS_BLOCKSZ; + offs = file->pos % HFS_BLOCKSZ; + + chunk = HFS_BLOCKSZ - offs; + if (chunk > count) + chunk = count; + + if (f_getblock(file, bnum, &b) < 0) + return -1; + + memcpy(ptr, b + offs, chunk); + ptr += chunk; + + file->pos += chunk; + count -= chunk; + } + + return len; +} + +/* + * NAME: hfs->write() + * DESCRIPTION: write to an open file + */ +long hfs_write(hfsfile *file, void *buf, unsigned long len) +{ + unsigned long *lglen, *pylen, count; + unsigned char *ptr = buf; + + if (file->vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + f_getptrs(file, &lglen, &pylen, 0); + + count = len; + + /* set flag to update (at least) the modification time */ + + if (count) + { + file->cat.u.fil.filMdDat = d_tomtime(time(0)); + file->flags |= HFS_UPDATE_CATREC; + } + + while (count) + { + block b; + unsigned long bnum, offs, chunk; + + bnum = file->pos / HFS_BLOCKSZ; + offs = file->pos % HFS_BLOCKSZ; + + chunk = HFS_BLOCKSZ - offs; + if (chunk > count) + chunk = count; + + if (file->pos + chunk > *pylen) + { + if (bt_space(&file->vol->ext, 1) < 0 || + f_alloc(file) < 0) + return -1; + } +#ifndef APPLE_HYB + /* Ignore this part as we are always writing new files to an empty disk + i.e. offs will always be 0 */ + + if (offs > 0 || chunk < HFS_BLOCKSZ) + { + if (f_getblock(file, bnum, &b) < 0) + return -1; + } +#endif /* APPLE_HYB */ + memcpy(b + offs, ptr, chunk); + ptr += chunk; + + if (f_putblock(file, bnum, &b) < 0) + return -1; + + file->pos += chunk; + count -= chunk; + + if (file->pos > *lglen) + *lglen = file->pos; + } + + return len; +} + +/* + * NAME: hfs->truncate() + * DESCRIPTION: truncate an open file + */ +int hfs_truncate(hfsfile *file, unsigned long len) +{ + unsigned long *lglen; + + f_getptrs(file, &lglen, 0, 0); + + if (*lglen > len) + { + if (file->vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + *lglen = len; + + file->cat.u.fil.filMdDat = d_tomtime(time(0)); + file->flags |= HFS_UPDATE_CATREC; + + if (file->pos > len) + file->pos = len; + } + + return 0; +} + +/* + * NAME: hfs->lseek() + * DESCRIPTION: change file seek pointer + */ +long hfs_lseek(hfsfile *file, long offset, int from) +{ + unsigned long *lglen; + long newpos; + + f_getptrs(file, &lglen, 0, 0); + + switch (from) + { + case SEEK_SET: + newpos = offset; + break; + + case SEEK_CUR: + newpos = file->pos + offset; + break; + + case SEEK_END: + newpos = *lglen + offset; + break; + + default: + ERROR(EINVAL, 0); + return -1; + } + + if (newpos < 0) + newpos = 0; + else if (newpos > *lglen) + newpos = *lglen; + + file->pos = newpos; + + return newpos; +} + +/* + * NAME: hfs->close() + * DESCRIPTION: close a file + */ +#ifdef APPLE_HYB +/* extra args are used to set the start of the forks in the ISO volume */ +int hfs_close(hfsfile *file, long dext, long rext) +{ + int offset; +#else +int hfs_close(hfsfile *file) +{ +#endif /* APPLE_HYB */ + hfsvol *vol = file->vol; + int result = 0; + + if (f_trunc(file) < 0 || + f_flush(file) < 0) + result = -1; + +#ifdef APPLE_HYB + /* "start" of file is relative to the first available block */ + offset = vol->hce->hfs_hdr_size + vol->hce->hfs_map_size; + /* update the "real" starting extent and re-flush the file */ + if (dext) + file->cat.u.fil.filExtRec[0].xdrStABN = (dext - offset)/vol->lpa; + + if (rext) + file->cat.u.fil.filRExtRec[0].xdrStABN = (rext - offset)/vol->lpa; + + if (dext || rext) + file->flags |= HFS_UPDATE_CATREC; + + if (f_flush(file) < 0) + result = -1; +#endif /*APPLE_HYB */ + + if (file->prev) + file->prev->next = file->next; + if (file->next) + file->next->prev = file->prev; + if (file == vol->files) + vol->files = file->next; + + FREE(file); + + return result; +} + +/* High-Level Catalog Routines ============================================= */ + +/* + * NAME: hfs->stat() + * DESCRIPTION: return catalog information for an arbitrary path + */ +int hfs_stat(hfsvol *vol, char *path, hfsdirent *ent) +{ + CatDataRec data; + long parid; + char name[HFS_MAX_FLEN + 1]; + + if (v_getvol(&vol) < 0 || + v_resolve(&vol, path, &data, &parid, name, 0) <= 0) + return -1; + + r_unpackdirent(parid, name, &data, ent); + + return 0; +} + +/* + * NAME: hfs->fstat() + * DESCRIPTION: return catalog information for an open file + */ +int hfs_fstat(hfsfile *file, hfsdirent *ent) +{ + r_unpackdirent(file->parid, file->name, &file->cat, ent); + + return 0; +} + +/* + * NAME: hfs->setattr() + * DESCRIPTION: change a file's attributes + */ +int hfs_setattr(hfsvol *vol, char *path, hfsdirent *ent) +{ + CatDataRec data; + node n; + + if (v_getvol(&vol) < 0 || + v_resolve(&vol, path, &data, 0, 0, &n) <= 0) + return -1; + + if (vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + r_packdirent(&data, ent); + + if (v_putcatrec(&data, &n) < 0) + return -1; + + return 0; +} + +/* + * NAME: hfs->fsetattr() + * DESCRIPTION: change an open file's attributes + */ +int hfs_fsetattr(hfsfile *file, hfsdirent *ent) +{ + if (file->vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + r_packdirent(&file->cat, ent); + + file->flags |= HFS_UPDATE_CATREC; + + return 0; +} + +/* + * NAME: hfs->mkdir() + * DESCRIPTION: create a new directory + */ +int hfs_mkdir(hfsvol *vol, char *path) +{ + CatDataRec data; + long parid; + char name[HFS_MAX_FLEN + 1]; + int found; + + if (v_getvol(&vol) < 0) + return -1; + + found = v_resolve(&vol, path, &data, &parid, name, 0); + if (found < 0 || parid == 0) + return -1; + else if (found) + { + ERROR(EEXIST, 0); + return -1; + } + + if (parid == HFS_CNID_ROOTPAR) + { + ERROR(EINVAL, 0); + return -1; + } + + if (vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + if (v_newfolder(vol, parid, name) < 0) + return -1; + + return 0; +} + +/* + * NAME: hfs->rmdir() + * DESCRIPTION: delete an empty directory + */ +int hfs_rmdir(hfsvol *vol, char *path) +{ + CatKeyRec key; + CatDataRec data; + long parid; + char name[HFS_MAX_FLEN + 1]; + unsigned char pkey[HFS_CATKEYLEN]; + + if (v_getvol(&vol) < 0 || + v_resolve(&vol, path, &data, &parid, name, 0) <= 0) + return -1; + + if (data.cdrType != cdrDirRec) + { + ERROR(ENOTDIR, 0); + return -1; + } + + if (data.u.dir.dirVal != 0) + { + ERROR(ENOTEMPTY, 0); + return -1; + } + + if (parid == HFS_CNID_ROOTPAR) + { + ERROR(EINVAL, 0); + return -1; + } + + if (vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + /* delete directory record */ + + r_makecatkey(&key, parid, name); + r_packcatkey(&key, pkey, 0); + + if (bt_delete(&vol->cat, pkey) < 0) + return -1; + + /* delete thread record */ + + r_makecatkey(&key, data.u.dir.dirDirID, ""); + r_packcatkey(&key, pkey, 0); + + if (bt_delete(&vol->cat, pkey) < 0 || + v_adjvalence(vol, parid, 1, -1) < 0) + return -1; + + return 0; +} + +/* + * NAME: hfs->create() + * DESCRIPTION: create a new file + */ +int hfs_create(hfsvol *vol, char *path, char *type, char *creator) +{ + CatKeyRec key; + CatDataRec data; + long id, parid; + char name[HFS_MAX_FLEN + 1]; + unsigned char record[HFS_CATRECMAXLEN]; + int found, i, reclen; + + if (v_getvol(&vol) < 0) + return -1; + + found = v_resolve(&vol, path, &data, &parid, name, 0); + if (found < 0 || parid == 0) + return -1; + else if (found) + { + ERROR(EEXIST, 0); + return -1; + } + + if (parid == HFS_CNID_ROOTPAR) + { + ERROR(EINVAL, 0); + return -1; + } + + if (vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + /* create file `name' in parent `parid' */ + + if (bt_space(&vol->cat, 1) < 0) + return -1; + + id = vol->mdb.drNxtCNID++; + vol->flags |= HFS_UPDATE_MDB; + + /* create file record */ + + data.cdrType = cdrFilRec; + data.cdrResrv2 = 0; + + data.u.fil.filFlags = 0; + data.u.fil.filTyp = 0; + + memset(&data.u.fil.filUsrWds, 0, sizeof(data.u.fil.filUsrWds)); + + data.u.fil.filUsrWds.fdType = d_getl((unsigned char *) type); + data.u.fil.filUsrWds.fdCreator = d_getl((unsigned char *) creator); + + data.u.fil.filFlNum = id; + data.u.fil.filStBlk = 0; + data.u.fil.filLgLen = 0; + data.u.fil.filPyLen = 0; + data.u.fil.filRStBlk = 0; + data.u.fil.filRLgLen = 0; + data.u.fil.filRPyLen = 0; + data.u.fil.filCrDat = d_tomtime(time(0)); + data.u.fil.filMdDat = data.u.fil.filCrDat; + data.u.fil.filBkDat = 0; + + memset(&data.u.fil.filFndrInfo, 0, sizeof(data.u.fil.filFndrInfo)); + + data.u.fil.filClpSize = 0; + + for (i = 0; i < 3; ++i) + { + data.u.fil.filExtRec[i].xdrStABN = 0; + data.u.fil.filExtRec[i].xdrNumABlks = 0; + + data.u.fil.filRExtRec[i].xdrStABN = 0; + data.u.fil.filRExtRec[i].xdrNumABlks = 0; + } + + data.u.fil.filResrv = 0; + + r_makecatkey(&key, parid, name); + r_packcatkey(&key, record, &reclen); + r_packcatdata(&data, HFS_RECDATA(record), &reclen); + + if (bt_insert(&vol->cat, record, reclen) < 0 || + v_adjvalence(vol, parid, 0, 1) < 0) + return -1; + + return 0; +} + +/* + * NAME: hfs->delete() + * DESCRIPTION: remove both forks of a file + */ +int hfs_delete(hfsvol *vol, char *path) +{ + hfsfile file; + CatKeyRec key; + unsigned char pkey[HFS_CATKEYLEN]; + int found; + + if (v_getvol(&vol) < 0 || + v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0) + return -1; + + if (file.cat.cdrType != cdrFilRec) + { + ERROR(EISDIR, 0); + return -1; + } + + if (file.parid == HFS_CNID_ROOTPAR) + { + ERROR(EINVAL, 0); + return -1; + } + + if (vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + /* free disk blocks */ + + file.vol = vol; + file.flags = 0; + + file.cat.u.fil.filLgLen = 0; + file.cat.u.fil.filRLgLen = 0; + + f_selectfork(&file, 0); + if (f_trunc(&file) < 0) + return -1; + + f_selectfork(&file, 1); + if (f_trunc(&file) < 0) + return -1; + + /* delete file record */ + + r_makecatkey(&key, file.parid, file.name); + r_packcatkey(&key, pkey, 0); + + if (bt_delete(&vol->cat, pkey) < 0 || + v_adjvalence(vol, file.parid, 0, -1) < 0) + return -1; + + /* delete file thread, if any */ + + found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0); + if (found < 0) + return -1; + + if (found) + { + r_makecatkey(&key, file.cat.u.fil.filFlNum, ""); + r_packcatkey(&key, pkey, 0); + + if (bt_delete(&vol->cat, pkey) < 0) + return -1; + } + + return 0; +} + +/* + * NAME: hfs->rename() + * DESCRIPTION: change the name of and/or move a file or directory + */ +int hfs_rename(hfsvol *vol, char *srcpath, char *dstpath) +{ + hfsvol *srcvol; + CatDataRec src, dst; + long srcid, dstid; + CatKeyRec key; + char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1]; + unsigned char record[HFS_CATRECMAXLEN]; + int found, isdir, moving, reclen; + node n; + + if (v_getvol(&vol) < 0 || + v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0) + return -1; + + isdir = (src.cdrType == cdrDirRec); + srcvol = vol; + + found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0); + if (found < 0) + return -1; + + if (vol != srcvol) + { + ERROR(EINVAL, "can't move across volumes"); + return -1; + } + + if (dstid == 0) + { + ERROR(ENOENT, "bad destination path"); + return -1; + } + + if (found && + dst.cdrType == cdrDirRec && + dst.u.dir.dirDirID != src.u.dir.dirDirID) + { + dstid = dst.u.dir.dirDirID; + strcpy(dstname, srcname); + + found = v_catsearch(vol, dstid, dstname, 0, 0, 0); + if (found < 0) + return -1; + } + + moving = (srcid != dstid); + + if (found) + { + char *ptr; + + ptr = strrchr(dstpath, ':'); + if (ptr == 0) + ptr = dstpath; + else + ++ptr; + + if (*ptr) + strcpy(dstname, ptr); + + if (! moving && strcmp(srcname, dstname) == 0) + return 0; /* source and destination are the same */ + + if (moving || d_relstring(srcname, dstname)) + { + ERROR(EEXIST, "can't use destination name"); + return -1; + } + } + + /* can't move anything into the root directory's parent */ + + if (moving && dstid == HFS_CNID_ROOTPAR) + { + ERROR(EINVAL, "can't move above root directory"); + return -1; + } + + if (moving && isdir) + { + long id; + + /* can't move root directory anywhere */ + + if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR) + { + ERROR(EINVAL, "can't move root directory"); + return -1; + } + + /* make sure we aren't trying to move a directory inside itself */ + + for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID) + { + if (id == src.u.dir.dirDirID) + { + ERROR(EINVAL, "can't move directory inside itself"); + return -1; + } + + if (v_getdthread(vol, id, &dst, 0) <= 0) + return -1; + } + } + + if (vol->flags & HFS_READONLY) + { + ERROR(EROFS, 0); + return -1; + } + + /* change volume name */ + + if (dstid == HFS_CNID_ROOTPAR) + { + if (strlen(dstname) > HFS_MAX_VLEN) + { + ERROR(ENAMETOOLONG, 0); + return -1; + } + + strcpy(vol->mdb.drVN, dstname); + vol->flags |= HFS_UPDATE_MDB; + } + + /* remove source record */ + + r_makecatkey(&key, srcid, srcname); + r_packcatkey(&key, record, 0); + + if (bt_delete(&vol->cat, record) < 0) + return -1; + + /* insert destination record */ + + r_makecatkey(&key, dstid, dstname); + r_packcatkey(&key, record, &reclen); + r_packcatdata(&src, HFS_RECDATA(record), &reclen); + + if (bt_insert(&vol->cat, record, reclen) < 0) + return -1; + + /* update thread record */ + + if (isdir) + { + if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0) + return -1; + + dst.u.dthd.thdParID = dstid; + strcpy(dst.u.dthd.thdCName, dstname); + + if (v_putcatrec(&dst, &n) < 0) + return -1; + } + else + { + found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n); + if (found < 0) + return -1; + + if (found) + { + dst.u.fthd.fthdParID = dstid; + strcpy(dst.u.fthd.fthdCName, dstname); + + if (v_putcatrec(&dst, &n) < 0) + return -1; + } + } + + /* update directory valences */ + + if (moving) + { + if (v_adjvalence(vol, srcid, isdir, -1) < 0 || + v_adjvalence(vol, dstid, isdir, 1) < 0) + return -1; + } + + return 0; +} +#ifdef APPLE_HYB +/* + * NAME: hfs->hfs_get_drAllocPtr() + * DESCRIPTION: get the current start of next allocation search + */ +unsigned short +hfs_get_drAllocPtr(hfsfile *file) +{ + return(file->vol->mdb.drAllocPtr); +} + +/* + * NAME: hfs->hfs_set_drAllocPtr() + * DESCRIPTION: set the current start of next allocation search + */ +#ifdef PROTOTYPES +int +hfs_set_drAllocPtr(hfsfile *file, unsigned short drAllocPtr, int size) +#else +int +hfs_set_drAllocPtr(hfsfile *file, unsigned short drAllocPtr, int size) +#endif +{ + hfsvol *vol = file->vol; + int result = 0; + + /* truncate the current fork */ + if (f_trunc(file) < 0 || + f_flush(file) < 0) + result = -1; + + /* convert the fork size into allocation blocks */ + size = (size + vol->mdb.drAlBlkSiz - 1)/vol->mdb.drAlBlkSiz; + + /* set the start of next allocation search to be after this fork */ + vol->mdb.drAllocPtr = drAllocPtr + size; + + vol->flags |= HFS_UPDATE_MDB; + + return result; +} + +/* + * NAME: hfs->vsetbless() + * DESCRIPTION: set blessed folder + * + * adapted from vsetattr() from v3.2.6 + */ +#ifdef PROTOTYPES +void +hfs_vsetbless(hfsvol *vol, unsigned long cnid) +#else +void +hfs_vsetbless(hfsvol *vol, unsigned long cnid) +#endif +{ + vol->mdb.drFndrInfo[0] = cnid; + + vol->flags |= HFS_UPDATE_MDB; +} +#endif /* APPLE_HYB */ diff --git a/libhfs_iso/hfs.h b/libhfs_iso/hfs.h new file mode 100644 index 0000000..f2c9d96 --- /dev/null +++ b/libhfs_iso/hfs.h @@ -0,0 +1,208 @@ +/* + * 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. + * + */ + +/* @(#)hfs.h 1.4 01/11/01 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. + */ +#ifndef HFS_H +#define HFS_H + +#include +#include + +#ifdef APPLE_HYB +#include "hybrid.h" + +/* don't need device locking for mkhybrid */ +#ifndef NODEVLOCKS +#define NODEVLOCKS +#endif /* NODEVLOCKS */ + +#endif /* APPLE_HYB */ + +# define HFS_BLOCKSZ 512 +# define HFS_MAX_FLEN 31 +# define HFS_MAX_VLEN 27 + +typedef struct _hfsvol_ hfsvol; +typedef struct _hfsfile_ hfsfile; +typedef struct _hfsdir_ hfsdir; + +typedef struct { + char name[HFS_MAX_VLEN + 1]; /* name of volume */ + int flags; /* volume flags */ + unsigned long totbytes; /* total bytes on volume */ + unsigned long freebytes; /* free bytes on volume */ + time_t crdate; /* volume creation date */ + time_t mddate; /* last volume modification date */ +} hfsvolent; + +/* taken from v3.2.6 */ +typedef struct { + char name[HFS_MAX_FLEN + 1]; /* catalog name (MacOS Standard Roman) */ + int flags; /* bit flags */ + unsigned long cnid; /* catalog node id (CNID) */ + unsigned long parid; /* CNID of parent directory */ + + time_t crdate; /* date of creation */ + time_t mddate; /* date of last modification */ + time_t bkdate; /* date of last backup */ + + short fdflags; /* Macintosh Finder flags */ + + struct { + signed short v; /* Finder icon vertical coordinate */ + signed short h; /* horizontal coordinate */ + } fdlocation; + + union { + struct { + unsigned long dsize; /* size of data fork */ + unsigned long rsize; /* size of resource fork */ + + char type[5]; /* file type code (plus null) */ + char creator[5]; /* file creator code (plus null) */ + } file; + + struct { + unsigned short valence; /* number of items in directory */ + + struct { + signed short top; /* top edge of folder's rectangle */ + signed short left; /* left edge */ + signed short bottom; /* bottom edge */ + signed short right; /* right edge */ + } rect; + + /* mkhybrid extra */ + signed short view; /* Folder's view */ + + struct { + signed short v; /* Scoll vertical position */ + signed short h; /* Scoll horizontal position */ + } frscroll; + } dir; + } u; +} hfsdirent; + + +# define HFS_ISDIR 0x01 +# define HFS_ISLOCKED 0x02 + +# define HFS_CNID_ROOTPAR 1 +# define HFS_CNID_ROOTDIR 2 +# define HFS_CNID_EXT 3 +# define HFS_CNID_CAT 4 +# define HFS_CNID_BADALLOC 5 + +# define HFS_FNDR_ISONDESK (1 << 0) +# define HFS_FNDR_COLOR 0x0e +# define HFS_FNDR_COLORRESERVED (1 << 4) +# define HFS_FNDR_REQUIRESSWITCHLAUNCH (1 << 5) +# define HFS_FNDR_ISSHARED (1 << 6) +# define HFS_FNDR_HASNOINITS (1 << 7) +# define HFS_FNDR_HASBEENINITED (1 << 8) +# define HFS_FNDR_RESERVED (1 << 9) +# define HFS_FNDR_HASCUSTOMICON (1 << 10) +# define HFS_FNDR_ISSTATIONERY (1 << 11) +# define HFS_FNDR_NAMELOCKED (1 << 12) +# define HFS_FNDR_HASBUNDLE (1 << 13) +# define HFS_FNDR_ISINVISIBLE (1 << 14) +# define HFS_FNDR_ISALIAS (1 << 15) + +extern char *hfs_error; +/*extern unsigned char hfs_charorder[];*/ + +#ifdef APPLE_HYB +hfsvol *hfs_mount(hce_mem *, int, int); +#else +hfsvol *hfs_mount(char *, int, int); +#endif /* APPLE_HYB */ + +int hfs_flush(hfsvol *); +void hfs_flushall(void); +#ifdef APPLE_HYB +int hfs_umount(hfsvol *, long, long); +#else +int hfs_umount(hfsvol *); +#endif /* APPLE_HYB */ +void hfs_umountall(void); +hfsvol *hfs_getvol(char *); +void hfs_setvol(hfsvol *); + +int hfs_vstat(hfsvol *, hfsvolent *); +#ifdef APPLE_HYB +int hfs_format(hce_mem *, int, char *); +#else +int hfs_format(char *, int, char *); +#endif /* APPLE_HYB */ + +int hfs_chdir(hfsvol *, char *); +long hfs_getcwd(hfsvol *); +int hfs_setcwd(hfsvol *, long); +int hfs_dirinfo(hfsvol *, long *, char *); + +hfsdir *hfs_opendir(hfsvol *, char *); +int hfs_readdir(hfsdir *, hfsdirent *); +int hfs_closedir(hfsdir *); + +hfsfile *hfs_open(hfsvol *, char *); +int hfs_setfork(hfsfile *, int); +int hfs_getfork(hfsfile *); +long hfs_read(hfsfile *, void *, unsigned long); +long hfs_write(hfsfile *, void *, unsigned long); +int hfs_truncate(hfsfile *, unsigned long); +long hfs_lseek(hfsfile *, long, int); +#ifdef APPLE_HYB +int hfs_close(hfsfile *, long, long); +#else +int hfs_close(hfsfile *); +#endif /* APPLE_HYB */ + +int hfs_stat(hfsvol *, char *, hfsdirent *); +int hfs_fstat(hfsfile *, hfsdirent *); +int hfs_setattr(hfsvol *, char *, hfsdirent *); +int hfs_fsetattr(hfsfile *, hfsdirent *); + +int hfs_mkdir(hfsvol *, char *); +int hfs_rmdir(hfsvol *, char *); + +int hfs_create(hfsvol *, char *, char *, char *); +int hfs_delete(hfsvol *, char *); + +int hfs_rename(hfsvol *, char *, char *); + +int f_trunc(hfsfile *file); + +#ifdef APPLE_HYB +unsigned short hfs_get_drAllocPtr(hfsfile *); +int hfs_set_drAllocPtr(hfsfile *, unsigned short, int size); +void hfs_vsetbless(hfsvol *, unsigned long); +#endif /* APPLE_HYB */ + +#endif diff --git a/libhfs_iso/hybrid.h b/libhfs_iso/hybrid.h new file mode 100644 index 0000000..01b83a6 --- /dev/null +++ b/libhfs_iso/hybrid.h @@ -0,0 +1,67 @@ +/* + * 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. + * + */ + +/* @(#)hybrid.h 1.3 02/10/04 joerg */ +/* +** hybrid.h: extra info needed by libhfs and mkisofs +** +** James Pearson 15/9/97 +*/ + +#ifndef _HYBRID_H + +/* + * The following three variables can be overridden at run time + * by using the -hfs-parms option i.e. to use the following defaults + * you could use: -hfs-parms CTC=2,CTC_LOOP=4,MAX_XTCSIZE=4194304 + * i.e. to change just MAX_XTCSIZE to 2Mb use: + * -hfs-parms MAX_XTCSIZE=2097152 + */ + +#define CTC 2 /* factor to increase initial Catalog file + size to prevent the file growing */ +#define CTC_LOOP 4 /* number of attemps before we give up + trying to create the volume */ + +#define MAX_XTCSIZE 4*1024*1024 /* the maximum size of the Catalog file - + as the size of the Catalog file is linked + to the size of the volume, multi-gigabyte + volumes create very large Catalog and + Extent files - 4Mb is the size calculated + for a 1Gb volume - which is probably + overkill, but seems to be OK */ + +#define HCE_ERROR -9999 /* dummy errno value for Catalog file + size problems */ + +#define HFS_MAP_SIZE 16 /* size of HFS partition maps (8Kb) */ + +typedef struct { + int hfs_ce_size; /* extents/catalog size in HFS blks */ + int hfs_hdr_size; /* vol header size in HFS blks */ + int hfs_dt_size; /* Desktop file size in HFS blks */ + int hfs_tot_size; /* extents/catalog/dt size in HFS blks */ + int hfs_map_size; /* size of partition maps in HFS blks */ + unsigned long hfs_vol_size; /* size of volume in HFS blks */ + unsigned char *hfs_ce; /* mem copy of extents/catalog files */ + unsigned char *hfs_hdr; /* mem copy of vol header */ + unsigned char *hfs_alt_mdb; /* location of alternate MDB */ + unsigned char *hfs_map; /* location of partiton_maps */ + int Csize; /* size of allocation unit (bytes) */ + int XTCsize; /* def size of catalog/extents files (bytes) */ + int max_XTCsize; /* max size of catalog/extents files (bytes) */ + int ctc_size; /* factor to increase Catalog file size */ + char *error; /* HFS error message */ +} hce_mem; + +#define _HYBRID_H +#endif /* _HYBRID_H */ diff --git a/libhfs_iso/internal.h b/libhfs_iso/internal.h new file mode 100644 index 0000000..ee79b0c --- /dev/null +++ b/libhfs_iso/internal.h @@ -0,0 +1,369 @@ +/* + * 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. + * + */ + +/* @(#)internal.h 1.2 01/11/01 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 +#include + +# include "hfs.h" + +# define ERROR(code, str) (hfs_error = (str), errno = (code)) + +# define SIZE(type, n) ((size_t) (sizeof(type) * (n))) +# define ALLOC(type, n) ((type *) malloc(SIZE(type, n))) +# define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type *) 0) +# define FREE(ptr) {if (ptr) free((void *) ptr) ;} + +# define REALLOC(ptr, type, n) \ + ((type *) ((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n)))) +# define REALLOCX(ptr, type, n) \ + ((n) ? REALLOC(type, n) : (FREE(ptr), (type *) 0)) + +# define BMTST(bm, num) \ + (((char *) (bm))[(num) >> 3] & (0x80 >> ((num) & 0x07))) +# define BMSET(bm, num) \ + (((char *) (bm))[(num) >> 3] |= (0x80 >> ((num) & 0x07))) +# define BMCLR(bm, num) \ + (((char *) (bm))[(num) >> 3] &= ~(0x80 >> ((num) & 0x07))) + +typedef unsigned char block[HFS_BLOCKSZ]; + +typedef signed char Char; +typedef unsigned char UChar; +typedef signed char SignedByte; +typedef signed short Integer; +typedef unsigned short UInteger; +typedef signed long LongInt; +typedef unsigned long ULongInt; +typedef char Str15[16]; +typedef char Str31[32]; +typedef long OSType; + +typedef struct { + UInteger xdrStABN; /* first allocation block */ + UInteger xdrNumABlks; /* number of allocation blocks */ +} ExtDescriptor; + +typedef ExtDescriptor ExtDataRec[3]; + +typedef struct { + SignedByte xkrKeyLen; /* key length */ + SignedByte xkrFkType; /* fork type (0x00/0xff == data/resource */ + ULongInt xkrFNum; /* file number */ + UInteger xkrFABN; /* starting file allocation block */ +} ExtKeyRec; + +typedef struct { + SignedByte ckrKeyLen; /* key length */ + SignedByte ckrResrv1; /* reserved */ + ULongInt ckrParID; /* parent directory ID */ + Str31 ckrCName; /* catalog node name */ +} CatKeyRec; + +# define HFS_MAP1SZ 256 +# define HFS_MAPXSZ 492 + +# define HFS_NODEREC(nd, rnum) ((nd).data + (nd).roff[rnum]) + +# define HFS_RECKEYLEN(ptr) (*(unsigned char *) (ptr)) +# define HFS_RECKEYSKIP(ptr) ((1 + HFS_RECKEYLEN(ptr) + 1) & ~1) +# define HFS_RECDATA(ptr) ((ptr) + HFS_RECKEYSKIP(ptr)) + +# define HFS_CATDATALEN sizeof(CatDataRec) +# define HFS_EXTDATALEN sizeof(ExtDataRec) + +# define HFS_CATKEYLEN sizeof(CatKeyRec) +# define HFS_EXTKEYLEN sizeof(ExtKeyRec) + +# define HFS_CATRECMAXLEN (HFS_CATKEYLEN + HFS_CATDATALEN) +# define HFS_EXTRECMAXLEN (HFS_EXTKEYLEN + HFS_EXTDATALEN) + +# define HFS_MAXRECLEN HFS_CATRECMAXLEN + +typedef struct { + Integer v; /* vertical coordinate */ + Integer h; /* horizontal coordinate */ +} Point; + +typedef struct { + Integer top; /* top edge of rectangle */ + Integer left; /* left edge */ + Integer bottom; /* bottom edge */ + Integer right; /* rightmost edge */ +} Rect; + +typedef struct { + Rect frRect; /* folder's rectangle */ + Integer frFlags; /* flags */ + Point frLocation; /* folder's location */ + Integer frView; /* folder's view */ +} DInfo; + +typedef struct { + Point frScroll; /* scroll position */ + LongInt frOpenChain; /* directory ID chain of open folders */ + Integer frUnused; /* reserved */ + Integer frComment; /* comment ID */ + LongInt frPutAway; /* directory ID */ +} DXInfo; + +typedef struct { + OSType fdType; /* file type */ + OSType fdCreator; /* file's creator */ + Integer fdFlags; /* flags */ + Point fdLocation; /* file's location */ + Integer fdFldr; /* file's window */ +} FInfo; + +typedef struct { + Integer fdIconID; /* icon ID */ + Integer fdUnused[4]; /* reserved */ + Integer fdComment; /* comment ID */ + LongInt fdPutAway; /* home directory ID */ +} FXInfo; + +typedef struct { + Integer drSigWord; /* volume signature (0x4244 for HFS) */ + LongInt drCrDate; /* date and time of volume creation */ + LongInt drLsMod; /* date and time of last modification */ + Integer drAtrb; /* volume attributes */ + UInteger drNmFls; /* number of files in root directory */ + UInteger drVBMSt; /* first block of volume bit map (always 3) */ + UInteger drAllocPtr; /* start of next allocation search */ + UInteger drNmAlBlks; /* number of allocation blocks in volume */ + ULongInt drAlBlkSiz; /* size (in bytes) of allocation blocks */ + ULongInt drClpSiz; /* default clump size */ + UInteger drAlBlSt; /* first allocation block in volume */ + LongInt drNxtCNID; /* next unused catalog node ID (dir/file ID) */ + UInteger drFreeBks; /* number of unused allocation blocks */ + char drVN[28]; /* volume name (1-27 chars) */ + LongInt drVolBkUp; /* date and time of last backup */ + Integer drVSeqNum; /* volume backup sequence number */ + ULongInt drWrCnt; /* volume write count */ + ULongInt drXTClpSiz; /* clump size for extents overflow file */ + ULongInt drCTClpSiz; /* clump size for catalog file */ + UInteger drNmRtDirs; /* number of directories in root directory */ + ULongInt drFilCnt; /* number of files in volume */ + ULongInt drDirCnt; /* number of directories in volume */ + LongInt drFndrInfo[8]; /* information used by the Finder */ + UInteger drVCSize; /* size (in blocks) of volume cache */ + UInteger drVBMCSize; /* size (in blocks) of volume bitmap cache */ + UInteger drCtlCSize; /* size (in blocks) of common volume cache */ + ULongInt drXTFlSize; /* size (in bytes) of extents overflow file */ + ExtDataRec drXTExtRec; /* first extent record for extents file */ + ULongInt drCTFlSize; /* size (in bytes) of catalog file */ + ExtDataRec drCTExtRec; /* first extent record for catalog file */ +} MDB; + +# define HFS_ATRB_BUSY (1 << 6) +# define HFS_ATRB_HLOCKED (1 << 7) +# define HFS_ATRB_UMOUNTED (1 << 8) +# define HFS_ATRB_BBSPARED (1 << 9) +# define HFS_ATRB_COPYPROT (1 << 14) +# define HFS_ATRB_SLOCKED (1 << 15) + +typedef enum { + cdrDirRec = 1, + cdrFilRec = 2, + cdrThdRec = 3, + cdrFThdRec = 4 +} CatDataType; + +typedef struct { + SignedByte cdrType; /* record type */ + SignedByte cdrResrv2; /* reserved */ + union { + struct { /* cdrDirRec */ + Integer dirFlags; /* directory flags */ + UInteger dirVal; /* directory valence */ + ULongInt dirDirID; /* directory ID */ + LongInt dirCrDat; /* date and time of creation */ + LongInt dirMdDat; /* date and time of last modification */ + LongInt dirBkDat; /* date and time of last backup */ + DInfo dirUsrInfo; /* Finder information */ + DXInfo dirFndrInfo; /* additional Finder information */ + LongInt dirResrv[4]; /* reserved */ + } dir; + struct { /* cdrFilRec */ + SignedByte + filFlags; /* file flags */ + SignedByte + filTyp; /* file type */ + FInfo filUsrWds; /* Finder information */ + ULongInt filFlNum; /* file ID */ + UInteger filStBlk; /* first alloc block of data fork */ + ULongInt filLgLen; /* logical EOF of data fork */ + ULongInt filPyLen; /* physical EOF of data fork */ + UInteger filRStBlk; /* first alloc block of resource fork */ + ULongInt filRLgLen; /* logical EOF of resource fork */ + ULongInt filRPyLen; /* physical EOF of resource fork */ + LongInt filCrDat; /* date and time of creation */ + LongInt filMdDat; /* date and time of last modification */ + LongInt filBkDat; /* date and time of last backup */ + FXInfo filFndrInfo; /* additional Finder information */ + UInteger filClpSize; /* file clump size */ + ExtDataRec + filExtRec; /* first data fork extent record */ + ExtDataRec + filRExtRec; /* first resource fork extent record */ + LongInt filResrv; /* reserved */ + } fil; + struct { /* cdrThdRec */ + LongInt thdResrv[2]; /* reserved */ + ULongInt thdParID; /* parent ID for this directory */ + Str31 thdCName; /* name of this directory */ + } dthd; + struct { /* cdrFThdRec */ + LongInt fthdResrv[2]; /* reserved */ + ULongInt fthdParID; /* parent ID for this file */ + Str31 fthdCName; /* name of this file */ + } fthd; + } u; +} CatDataRec; + +struct _hfsfile_ { + struct _hfsvol_ *vol; /* pointer to volume descriptor */ + long parid; /* parent directory ID of this file */ + char name[HFS_MAX_FLEN + 1]; /* catalog name of this file */ + CatDataRec cat; /* catalog information */ + ExtDataRec ext; /* current extent record */ + unsigned int fabn; /* starting file allocation block number */ + int fork; /* current selected fork for I/O */ + unsigned long pos; /* current file seek pointer */ + unsigned long clump; /* file's clump size, for allocation */ + int flags; /* bit flags */ + + struct _hfsfile_ *prev; + struct _hfsfile_ *next; +}; + +# define HFS_UPDATE_CATREC 0x01 + +typedef struct { + ULongInt ndFLink; /* forward link */ + ULongInt ndBLink; /* backward link */ + SignedByte ndType; /* node type */ + SignedByte ndNHeight; /* node level */ + UInteger ndNRecs; /* number of records in node */ + Integer ndResv2; /* reserved */ +} NodeDescriptor; + +# define HFS_MAXRECS 35 /* maximum based on minimum record size */ + +typedef struct _node_ { + struct _btree_ *bt; /* btree to which this node belongs */ + unsigned long nnum; /* node index */ + NodeDescriptor nd; /* node descriptor */ + int rnum; /* current record index */ + UInteger roff[HFS_MAXRECS + 1]; /* record offsets */ + block data; /* raw contents of node */ +} node; + +enum { + ndIndxNode = 0x00, + ndHdrNode = 0x01, + ndMapNode = 0x02, + ndLeafNode = 0xff +}; + +struct _hfsdir_ { + struct _hfsvol_ *vol; /* associated volume */ + long dirid; /* directory ID of interest (or 0) */ + + node n; /* current B*-tree node */ + struct _hfsvol_ *vptr; /* current volume pointer */ + + struct _hfsdir_ *prev; + struct _hfsdir_ *next; +}; + +typedef struct { + UInteger bthDepth; /* current depth of tree */ + ULongInt bthRoot; /* number of root node */ + ULongInt bthNRecs; /* number of leaf records in tree */ + ULongInt bthFNode; /* number of first leaf node */ + ULongInt bthLNode; /* number of last leaf node */ + UInteger bthNodeSize; /* size of a node */ + UInteger bthKeyLen; /* maximum length of a key */ + ULongInt bthNNodes; /* total number of nodes in tree */ + ULongInt bthFree; /* number of free nodes */ + SignedByte bthResv[76]; /* reserved */ +} BTHdrRec; + +typedef struct _btree_ { + hfsfile f; /* subset file information */ + node hdrnd; /* header node */ + BTHdrRec hdr; /* header record */ + char *map; /* usage bitmap */ + unsigned long mapsz; /* number of bytes in bitmap */ + int flags; /* bit flags */ + + int (*compare)(unsigned char *, unsigned char *); + /* key comparison function */ +} btree; + +# define HFS_UPDATE_BTHDR 0x01 + +struct _hfsvol_ { + int fd; /* volume's open file descriptor */ + int flags; /* bit flags */ + +#ifdef APPLE_HYB + hce_mem *hce; /* Extras needed by libhfs/mkisofs */ +#endif /* APPLE_HYB */ + + int pnum; /* ordinal HFS partition number */ + unsigned long vstart; /* logical block offset to start of volume */ + unsigned long vlen; /* number of logical blocks in volume */ + unsigned int lpa; /* number of logical blocks per allocation block */ + + MDB mdb; /* master directory block */ + block *vbm; /* volume bit map */ + btree ext; /* B*-tree control block for extents overflow file */ + btree cat; /* B*-tree control block for catalog file */ + long cwd; /* directory id of current working directory */ + + int refs; /* number of external references to this volume */ + hfsfile *files; /* list of open files */ + hfsdir *dirs; /* list of open directories */ + + struct _hfsvol_ *prev; + struct _hfsvol_ *next; +}; + +# define HFS_READONLY 0x01 + +# define HFS_UPDATE_MDB 0x10 +# define HFS_UPDATE_ALTMDB 0x20 +# define HFS_UPDATE_VBM 0x40 + +extern hfsvol *hfs_mounts; +extern hfsvol *hfs_curvol; diff --git a/libhfs_iso/low.c b/libhfs_iso/low.c new file mode 100644 index 0000000..94cba83 --- /dev/null +++ b/libhfs_iso/low.c @@ -0,0 +1,529 @@ +/* + * 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. + * + */ + +/* @(#)low.c 1.4 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 +#include +#include +#include +#include +#include + +#include "internal.h" +#include "data.h" +#include "block.h" +#include "low.h" +#include "file.h" + +/* + * NAME: low->lockvol() + * DESCRIPTION: prevent destructive simultaneous access + */ +int l_lockvol(hfsvol *vol) +{ +# ifndef NODEVLOCKS + + struct flock lock; + + lock.l_type = (vol->flags & HFS_READONLY) ? F_RDLCK : F_WRLCK; + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + + if (fcntl(vol->fd, F_SETLK, &lock) < 0) + { + ERROR(errno, "unable to obtain lock for device"); + return -1; + } + +# endif + + return 0; +} + +/* + * NAME: low->readblock0() + * DESCRIPTION: read the first sector and get bearings + */ +int l_readblock0(hfsvol *vol) +{ + block b; + unsigned char *ptr = b; + Block0 rec; + + if (b_readlb(vol, 0, &b) < 0) + return -1; + + d_fetchw(&ptr, &rec.sbSig); + d_fetchw(&ptr, &rec.sbBlkSize); + d_fetchl(&ptr, &rec.sbBlkCount); + d_fetchw(&ptr, &rec.sbDevType); + d_fetchw(&ptr, &rec.sbDevId); + d_fetchl(&ptr, &rec.sbData); + d_fetchw(&ptr, &rec.sbDrvrCount); + d_fetchl(&ptr, &rec.ddBlock); + d_fetchw(&ptr, &rec.ddSize); + d_fetchw(&ptr, &rec.ddType); + + switch (rec.sbSig) + { + case 0x4552: /* block device with a partition table */ + { + if (rec.sbBlkSize != HFS_BLOCKSZ) + { + ERROR(EINVAL, "unsupported block size"); + return -1; + } + + vol->vlen = rec.sbBlkCount; + + if (l_readpm(vol) < 0) + return -1; + } + break; + + case 0x4c4b: /* bootable floppy */ + vol->pnum = 0; + break; + + default: /* non-bootable floppy or something else */ + + /* some miscreant media may also be partitioned; + we attempt to read a partition map, but ignore any failure */ + + if (l_readpm(vol) < 0) + vol->pnum = 0; + } + + return 0; +} + +/* + * NAME: low->readpm() + * DESCRIPTION: read the partition map and locate an HFS volume + */ +int l_readpm(hfsvol *vol) +{ + block b; + unsigned char *ptr; + Partition map; + unsigned long bnum; + int pnum; + + bnum = 1; + pnum = vol->pnum; + + for (;;) + { + if (b_readlb(vol, bnum, &b) < 0) + return -1; + + ptr = b; + + d_fetchw(&ptr, &map.pmSig); + d_fetchw(&ptr, &map.pmSigPad); + d_fetchl(&ptr, &map.pmMapBlkCnt); + d_fetchl(&ptr, &map.pmPyPartStart); + d_fetchl(&ptr, &map.pmPartBlkCnt); + + memcpy(map.pmPartName, ptr, 32); + map.pmPartName[32] = 0; + ptr += 32; + + memcpy(map.pmParType, ptr, 32); + map.pmParType[32] = 0; + ptr += 32; + + d_fetchl(&ptr, &map.pmLgDataStart); + d_fetchl(&ptr, &map.pmDataCnt); + d_fetchl(&ptr, &map.pmPartStatus); + d_fetchl(&ptr, &map.pmLgBootStart); + d_fetchl(&ptr, &map.pmBootSize); + d_fetchl(&ptr, &map.pmBootAddr); + d_fetchl(&ptr, &map.pmBootAddr2); + d_fetchl(&ptr, &map.pmBootEntry); + d_fetchl(&ptr, &map.pmBootEntry2); + d_fetchl(&ptr, &map.pmBootCksum); + + memcpy(map.pmProcessor, ptr, 16); + map.pmProcessor[16] = 0; + ptr += 16; + + if (map.pmSig == 0x5453) + { + /* old partition map sig */ + + ERROR(EINVAL, "unsupported partition map signature"); + return -1; + } + + if (map.pmSig != 0x504d) + { + ERROR(EINVAL, "bad partition map"); + return -1; + } + + if (strcmp((char *) map.pmParType, "Apple_HFS") == 0 && --pnum == 0) + { + if (map.pmLgDataStart != 0) + { + ERROR(EINVAL, "unsupported start of partition logical data"); + return -1; + } + + vol->vstart = map.pmPyPartStart; + vol->vlen = map.pmPartBlkCnt; + + return 0; + } + + if (bnum >= map.pmMapBlkCnt) + { + ERROR(EINVAL, "can't find HFS partition"); + return -1; + } + + ++bnum; + } +} + +/* + * NAME: low->readmdb() + * DESCRIPTION: read the master directory block into memory + */ +int l_readmdb(hfsvol *vol) +{ + block b; + unsigned char *ptr = b; + MDB *mdb = &vol->mdb; + hfsfile *ext = &vol->ext.f; + hfsfile *cat = &vol->cat.f; + int i; + + if (b_readlb(vol, 2, &b) < 0) + return -1; + + d_fetchw(&ptr, &mdb->drSigWord); + d_fetchl(&ptr, &mdb->drCrDate); + d_fetchl(&ptr, &mdb->drLsMod); + d_fetchw(&ptr, &mdb->drAtrb); + d_fetchw(&ptr, (short *) &mdb->drNmFls); + d_fetchw(&ptr, (short *) &mdb->drVBMSt); + d_fetchw(&ptr, (short *) &mdb->drAllocPtr); + d_fetchw(&ptr, (short *) &mdb->drNmAlBlks); + d_fetchl(&ptr, (long *) &mdb->drAlBlkSiz); + d_fetchl(&ptr, (long *) &mdb->drClpSiz); + d_fetchw(&ptr, (short *) &mdb->drAlBlSt); + d_fetchl(&ptr, &mdb->drNxtCNID); + d_fetchw(&ptr, (short *) &mdb->drFreeBks); + + d_fetchs(&ptr, mdb->drVN, sizeof(mdb->drVN)); + + if (ptr - b != 64) + abort(); + + d_fetchl(&ptr, &mdb->drVolBkUp); + d_fetchw(&ptr, &mdb->drVSeqNum); + d_fetchl(&ptr, (long *) &mdb->drWrCnt); + d_fetchl(&ptr, (long *) &mdb->drXTClpSiz); + d_fetchl(&ptr, (long *) &mdb->drCTClpSiz); + d_fetchw(&ptr, (short *) &mdb->drNmRtDirs); + d_fetchl(&ptr, (long *) &mdb->drFilCnt); + d_fetchl(&ptr, (long *) &mdb->drDirCnt); + + for (i = 0; i < 8; ++i) + d_fetchl(&ptr, &mdb->drFndrInfo[i]); + + if (ptr - b != 124) + abort(); + + d_fetchw(&ptr, (short *) &mdb->drVCSize); + d_fetchw(&ptr, (short *) &mdb->drVBMCSize); + d_fetchw(&ptr, (short *) &mdb->drCtlCSize); + + d_fetchl(&ptr, (long *) &mdb->drXTFlSize); + + for (i = 0; i < 3; ++i) + { + d_fetchw(&ptr, (short *) &mdb->drXTExtRec[i].xdrStABN); + d_fetchw(&ptr, (short *) &mdb->drXTExtRec[i].xdrNumABlks); + } + + if (ptr - b != 146) + abort(); + + d_fetchl(&ptr, (long *) &mdb->drCTFlSize); + + for (i = 0; i < 3; ++i) + { + d_fetchw(&ptr, (short *) &mdb->drCTExtRec[i].xdrStABN); + d_fetchw(&ptr, (short *) &mdb->drCTExtRec[i].xdrNumABlks); + } + + if (ptr - b != 162) + abort(); + + vol->lpa = mdb->drAlBlkSiz / HFS_BLOCKSZ; + + /* extents pseudo-file structs */ + + ext->vol = vol; + ext->parid = 0; + strcpy(ext->name, "extents overflow"); + + ext->cat.cdrType = cdrFilRec; + /* ext->cat.cdrResrv2 */ + ext->cat.u.fil.filFlags = 0; + ext->cat.u.fil.filTyp = 0; + /* ext->cat.u.fil.filUsrWds */ + ext->cat.u.fil.filFlNum = HFS_CNID_EXT; + ext->cat.u.fil.filStBlk = mdb->drXTExtRec[0].xdrStABN; + ext->cat.u.fil.filLgLen = mdb->drXTFlSize; + ext->cat.u.fil.filPyLen = mdb->drXTFlSize; + ext->cat.u.fil.filRStBlk = 0; + ext->cat.u.fil.filRLgLen = 0; + ext->cat.u.fil.filRPyLen = 0; + ext->cat.u.fil.filCrDat = mdb->drCrDate; + ext->cat.u.fil.filMdDat = mdb->drLsMod; + ext->cat.u.fil.filBkDat = 0; + /* ext->cat.u.fil.filFndrInfo */ + ext->cat.u.fil.filClpSize = 0; + + memcpy(ext->cat.u.fil.filExtRec, mdb->drXTExtRec, sizeof(ExtDataRec)); + for (i = 0; i < 3; ++i) + { + ext->cat.u.fil.filRExtRec[i].xdrStABN = 0; + ext->cat.u.fil.filRExtRec[i].xdrNumABlks = 0; + } + f_selectfork(ext, 0); + + ext->clump = mdb->drXTClpSiz; + ext->flags = 0; + + ext->prev = ext->next = 0; + + /* catalog pseudo-file structs */ + + cat->vol = vol; + cat->parid = 0; + strcpy(cat->name, "catalog"); + + cat->cat.cdrType = cdrFilRec; + /* cat->cat.cdrResrv2 */ + cat->cat.u.fil.filFlags = 0; + cat->cat.u.fil.filTyp = 0; + /* cat->cat.u.fil.filUsrWds */ + cat->cat.u.fil.filFlNum = HFS_CNID_CAT; + cat->cat.u.fil.filStBlk = mdb->drCTExtRec[0].xdrStABN; + cat->cat.u.fil.filLgLen = mdb->drCTFlSize; + cat->cat.u.fil.filPyLen = mdb->drCTFlSize; + cat->cat.u.fil.filRStBlk = 0; + cat->cat.u.fil.filRLgLen = 0; + cat->cat.u.fil.filRPyLen = 0; + cat->cat.u.fil.filCrDat = mdb->drCrDate; + cat->cat.u.fil.filMdDat = mdb->drLsMod; + cat->cat.u.fil.filBkDat = 0; + /* cat->cat.u.fil.filFndrInfo */ + cat->cat.u.fil.filClpSize = 0; + + memcpy(cat->cat.u.fil.filExtRec, mdb->drCTExtRec, sizeof(ExtDataRec)); + for (i = 0; i < 3; ++i) + { + cat->cat.u.fil.filRExtRec[i].xdrStABN = 0; + cat->cat.u.fil.filRExtRec[i].xdrNumABlks = 0; + } + f_selectfork(cat, 0); + + cat->clump = mdb->drCTClpSiz; + cat->flags = 0; + + cat->prev = cat->next = 0; + + return 0; +} + +/* + * NAME: low->writemdb() + * DESCRIPTION: write the master directory block to disk + */ +int l_writemdb(hfsvol *vol) +{ + block b; + unsigned char *ptr = b; + MDB *mdb = &vol->mdb; + hfsfile *ext = &vol->ext.f; + hfsfile *cat = &vol->cat.f; + int i; + + memset(&b, 0, sizeof(b)); + + mdb->drXTFlSize = ext->cat.u.fil.filPyLen; + mdb->drXTClpSiz = ext->clump; + memcpy(mdb->drXTExtRec, ext->cat.u.fil.filExtRec, sizeof(ExtDataRec)); + + mdb->drCTFlSize = cat->cat.u.fil.filPyLen; + mdb->drCTClpSiz = cat->clump; + memcpy(mdb->drCTExtRec, cat->cat.u.fil.filExtRec, sizeof(ExtDataRec)); + + d_storew(&ptr, mdb->drSigWord); + d_storel(&ptr, mdb->drCrDate); + d_storel(&ptr, mdb->drLsMod); + d_storew(&ptr, mdb->drAtrb); + d_storew(&ptr, mdb->drNmFls); + d_storew(&ptr, mdb->drVBMSt); + d_storew(&ptr, mdb->drAllocPtr); + d_storew(&ptr, mdb->drNmAlBlks); + d_storel(&ptr, mdb->drAlBlkSiz); + d_storel(&ptr, mdb->drClpSiz); + d_storew(&ptr, mdb->drAlBlSt); + d_storel(&ptr, mdb->drNxtCNID); + d_storew(&ptr, mdb->drFreeBks); + d_stores(&ptr, mdb->drVN, sizeof(mdb->drVN)); + + if (ptr - b != 64) + abort(); + + d_storel(&ptr, mdb->drVolBkUp); + d_storew(&ptr, mdb->drVSeqNum); + d_storel(&ptr, mdb->drWrCnt); + d_storel(&ptr, mdb->drXTClpSiz); + d_storel(&ptr, mdb->drCTClpSiz); + d_storew(&ptr, mdb->drNmRtDirs); + d_storel(&ptr, mdb->drFilCnt); + d_storel(&ptr, mdb->drDirCnt); + + for (i = 0; i < 8; ++i) + d_storel(&ptr, mdb->drFndrInfo[i]); + + if (ptr - b != 124) + abort(); + + d_storew(&ptr, mdb->drVCSize); + d_storew(&ptr, mdb->drVBMCSize); + d_storew(&ptr, mdb->drCtlCSize); + d_storel(&ptr, mdb->drXTFlSize); + + for (i = 0; i < 3; ++i) + { + d_storew(&ptr, mdb->drXTExtRec[i].xdrStABN); + d_storew(&ptr, mdb->drXTExtRec[i].xdrNumABlks); + } + + if (ptr - b != 146) + abort(); + + d_storel(&ptr, mdb->drCTFlSize); + + for (i = 0; i < 3; ++i) + { + d_storew(&ptr, mdb->drCTExtRec[i].xdrStABN); + d_storew(&ptr, mdb->drCTExtRec[i].xdrNumABlks); + } + + if (ptr - b != 162) + abort(); + + if (b_writelb(vol, 2, &b) < 0) + return -1; + if (vol->flags & HFS_UPDATE_ALTMDB) + { +#ifdef APPLE_HYB + /* "write" alternative MDB to memory copy */ + memcpy(vol->hce->hfs_alt_mdb, &b, sizeof(b)); +#else + if (b_writelb(vol, vol->vlen - 2, &b) < 0) + return -1; +#endif /* APPLE_HYB */ + } + vol->flags &= ~(HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB); + + return 0; +} + +/* + * NAME: low->readvbm() + * DESCRIPTION: read the volume bit map into memory + */ +int l_readvbm(hfsvol *vol) +{ + int vbmst = vol->mdb.drVBMSt; + int vbmsz = (vol->mdb.drNmAlBlks + 4095) / (unsigned)4096; + block *bp; + + if ((int)(vol->mdb.drAlBlSt - vbmst) < vbmsz) + { + ERROR(EIO, "volume bitmap collides with volume data"); + return -1; + } + + bp = ALLOC(block, vbmsz); + if (bp == 0) + { + ERROR(ENOMEM, 0); + return -1; + } + + vol->vbm = bp; + + while (vbmsz--) + { + if (b_readlb(vol, vbmst++, bp++) < 0) + { + FREE(vol->vbm); + vol->vbm = 0; + + return -1; + } + } + + return 0; +} + +/* + * NAME: low->writevbm() + * DESCRIPTION: write the volume bit map to disk + */ +int l_writevbm(hfsvol *vol) +{ + int vbmst = vol->mdb.drVBMSt; + int vbmsz = (vol->mdb.drNmAlBlks + 4095) / (unsigned)4096; + block *bp = vol->vbm; + + while (vbmsz--) + { + if (b_writelb(vol, vbmst++, bp++) < 0) + return -1; + } + + vol->flags &= ~HFS_UPDATE_VBM; + + return 0; +} diff --git a/libhfs_iso/low.h b/libhfs_iso/low.h new file mode 100644 index 0000000..4434444 --- /dev/null +++ b/libhfs_iso/low.h @@ -0,0 +1,111 @@ +/* + * 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. + * + */ + +/* @(#)low.h 1.1 00/03/05 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. + */ + +typedef struct { + Integer sbSig; /* device signature (should be 0x4552) */ + Integer sbBlkSize; /* block size of the device (in bytes) */ + LongInt sbBlkCount; /* number of blocks on the device */ + Integer sbDevType; /* reserved */ + Integer sbDevId; /* reserved */ + LongInt sbData; /* reserved */ + Integer sbDrvrCount; /* number of driver descriptor entries */ + LongInt ddBlock; /* first driver's starting block */ + Integer ddSize; /* size of the driver, in 512-byte blocks */ + Integer ddType; /* driver operating system type (MacOS = 1) */ + Integer ddPad[243]; /* additional drivers, if any */ +} Block0; + +typedef struct { + Integer bbID; /* boot blocks signature */ + LongInt bbEntry; /* entry point to boot code */ + Integer bbVersion; /* boot blocks version number */ + Integer bbPageFlags; /* used internally */ + Str15 bbSysName; /* System filename */ + Str15 bbShellName; /* Finder filename */ + Str15 bbDbg1Name; /* debugger filename */ + Str15 bbDbg2Name; /* debugger filename */ + Str15 bbScreenName; /* name of startup screen */ + Str15 bbHelloName; /* name of startup program */ + Str15 bbScrapName; /* name of system scrap file */ + Integer bbCntFCBs; /* number of FCBs to allocate */ + Integer bbCntEvts; /* number of event queue elements */ + LongInt bb128KSHeap; /* system heap size on 128K Mac */ + LongInt bb256KSHeap; /* used internally */ + LongInt bbSysHeapSize; /* system heap size on all machines */ + Integer filler; /* reserved */ + LongInt bbSysHeapExtra; /* additional system heap space */ + LongInt bbSysHeapFract; /* fraction of RAM for system heap */ +} BootBlkHdr; + +typedef struct { + Integer pmSig; /* partition signature (0x504d or 0x5453) */ + Integer pmSigPad; /* reserved */ + LongInt pmMapBlkCnt; /* number of blocks in partition map */ + LongInt pmPyPartStart; /* first physical block of partition */ + LongInt pmPartBlkCnt; /* number of blocks in partition */ + Char pmPartName[33]; /* partition name */ + Char pmParType[33]; /* partition type */ + /* + * Apple_partition_map partition map + * Apple_Driver device driver + * Apple_Driver43 SCSI Manager 4.3 device driver + * Apple_MFS Macintosh 64K ROM filesystem + * Apple_HFS Macintosh hierarchical filesystem + * Apple_Unix_SVR2 Unix filesystem + * Apple_PRODOS ProDOS filesystem + * Apple_Free unused + * Apple_Scratch empty + */ + LongInt pmLgDataStart; /* first logical block of data area */ + LongInt pmDataCnt; /* number of blocks in data area */ + LongInt pmPartStatus; /* partition status information */ + LongInt pmLgBootStart; /* first logical block of boot code */ + LongInt pmBootSize; /* size of boot code, in bytes */ + LongInt pmBootAddr; /* boot code load address */ + LongInt pmBootAddr2; /* reserved */ + LongInt pmBootEntry; /* boot code entry point */ + LongInt pmBootEntry2; /* reserved */ + LongInt pmBootCksum; /* boot code checksum */ + Char pmProcessor[17];/* processor type */ + Integer pmPad[188]; /* reserved */ +} Partition; + +int l_lockvol(hfsvol *); + +int l_readblock0(hfsvol *); +int l_readpm(hfsvol *); + +int l_readmdb(hfsvol *); +int l_writemdb(hfsvol *); + +int l_readvbm(hfsvol *); +int l_writevbm(hfsvol *); diff --git a/libhfs_iso/node.c b/libhfs_iso/node.c new file mode 100644 index 0000000..8fce735 --- /dev/null +++ b/libhfs_iso/node.c @@ -0,0 +1,473 @@ +/* + * 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. + * + */ + +/* @(#)node.c 1.2 02/02/10 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 +#include +#include +#include + +#include "internal.h" +#include "data.h" +#include "btree.h" +#include "node.h" + +# define NODESPACE(n) \ + (HFS_BLOCKSZ - (n).roff[(n).nd.ndNRecs] - 2 * ((n).nd.ndNRecs + 1)) + +/* + * NAME: node->init() + * DESCRIPTION: construct an empty node + */ +void n_init(node *np, btree *bt, int type, int height) +{ + np->bt = bt; + np->nnum = -1; + + np->nd.ndFLink = 0; + np->nd.ndBLink = 0; + np->nd.ndType = type; + np->nd.ndNHeight = height; + np->nd.ndNRecs = 0; + np->nd.ndResv2 = 0; + + np->rnum = -1; + np->roff[0] = 0x00e; + + memset(np->data, 0, sizeof(np->data)); +} + +/* + * NAME: node->new() + * DESCRIPTION: allocate a new b*-tree node + */ +int n_new(node *np) +{ + btree *bt = np->bt; + unsigned long num; + + if (bt->hdr.bthFree == 0) + { + ERROR(EIO, "b*-tree full"); + return -1; + } + + num = 0; + while (num < bt->hdr.bthNNodes && BMTST(bt->map, num)) + ++num; + + if (num == bt->hdr.bthNNodes) + { + ERROR(EIO, "free b*-tree node not found"); + return -1; + } + + np->nnum = num; + + BMSET(bt->map, num); + --bt->hdr.bthFree; + + bt->flags |= HFS_UPDATE_BTHDR; + + return 0; +} + +/* + * NAME: node->free() + * DESCRIPTION: deallocate a b*-tree node + */ +void n_free(node *np) +{ + btree *bt = np->bt; + + BMCLR(bt->map, np->nnum); + ++bt->hdr.bthFree; + + bt->flags |= HFS_UPDATE_BTHDR; +} + +/* + * NAME: node->compact() + * DESCRIPTION: clean up a node, removing deleted records + */ +void n_compact(node *np) +{ + unsigned char *ptr; + int offset, nrecs, i; + + offset = 0x00e; + ptr = np->data + offset; + nrecs = 0; + + for (i = 0; i < (int)np->nd.ndNRecs; ++i) + { + unsigned char *rec; + int reclen; + + rec = HFS_NODEREC(*np, i); + reclen = np->roff[i + 1] - np->roff[i]; + + if (HFS_RECKEYLEN(rec) > 0) + { + np->roff[nrecs++] = offset; + offset += reclen; + + if (ptr == rec) + ptr += reclen; + else + { + while (reclen--) + *ptr++ = *rec++; + } + } + } + + np->roff[nrecs] = offset; + np->nd.ndNRecs = nrecs; +} + +/* + * NAME: node->search() + * DESCRIPTION: locate a record in a node, or the record it should follow + */ +int n_search(node *np, unsigned char *key) +{ + btree *bt = np->bt; + int i, comp = -1; + + for (i = np->nd.ndNRecs; i--; ) + { + unsigned char *rec; + + rec = HFS_NODEREC(*np, i); + + if (HFS_RECKEYLEN(rec) == 0) + continue; /* deleted record */ + + comp = bt->compare(rec, key); + + if (comp <= 0) + break; + } + + np->rnum = i; + + return comp == 0; +} + +/* + * NAME: node->index() + * DESCRIPTION: create an index record from a key and node pointer + */ +void n_index(btree *bt, unsigned char *key, unsigned long nnum, + unsigned char *record, int *reclen) +{ + if (bt == &bt->f.vol->cat) + { + /* force the key length to be 0x25 */ + + HFS_RECKEYLEN(record) = 0x25; + memset(record + 1, 0, 0x25); + memcpy(record + 1, key + 1, HFS_RECKEYLEN(key)); + } + else + memcpy(record, key, HFS_RECKEYSKIP(key)); + + d_putl(HFS_RECDATA(record), nnum); + + if (reclen) + *reclen = HFS_RECKEYSKIP(record) + 4; +} + +/* + * NAME: node->split() + * DESCRIPTION: divide a node into two and insert a record + */ +int n_split(node *left, unsigned char *record, int *reclen) +{ + node right; + int nrecs, i, mid; + unsigned char *rec; + + right = *left; + right.nd.ndBLink = left->nnum; + + if (n_new(&right) < 0) + return -1; + + left->nd.ndFLink = right.nnum; + nrecs = left->nd.ndNRecs; + + /* + * Ensure node split leaves enough room for new record. + * The size calculations used are based on the NODESPACE() macro, but + * I don't know what the extra 2's and 1's are needed for. + * John Witford + */ + n_search(&right, record); + mid = nrecs/2; + for(;;) + { + if (right.rnum < mid) + { + if ( mid > 0 + && (int)left->roff[mid] + *reclen + 2 > HFS_BLOCKSZ - 2 * (mid + 1)) + { + --mid; + if (mid > 0) + continue; + } + } + else + { + if ( mid < nrecs + && (int)right.roff[nrecs] - (int)right.roff[mid] + (int)left->roff[0] + *reclen + 2 > HFS_BLOCKSZ - 2 * (mid + 1)) + { + ++mid; + if (mid < nrecs) + continue; + } + } + break; + } + + for (i = 0; i < nrecs; ++i) + { + if (i < mid) + rec = HFS_NODEREC(right, i); + else + rec = HFS_NODEREC(*left, i); + + HFS_RECKEYLEN(rec) = 0; + } + +/* original code ... + for (i = 0; i < nrecs; ++i) + { + if (i < nrecs / 2) + rec = HFS_NODEREC(right, i); + else + rec = HFS_NODEREC(*left, i); + + HFS_RECKEYLEN(rec) = 0; + } +*/ + n_compact(left); + n_compact(&right); + + n_search(&right, record); + if (right.rnum >= 0) + n_insertx(&right, record, *reclen); + else + { + n_search(left, record); + n_insertx(left, record, *reclen); + } + + /* store the new/modified nodes */ + + if (bt_putnode(left) < 0 || + bt_putnode(&right) < 0) + return -1; + + /* create an index record for the new node in the parent */ + + n_index(right.bt, HFS_NODEREC(right, 0), right.nnum, record, reclen); + + /* update link pointers */ + + if (left->bt->hdr.bthLNode == left->nnum) + { + left->bt->hdr.bthLNode = right.nnum; + left->bt->flags |= HFS_UPDATE_BTHDR; + } + + if (right.nd.ndFLink) + { + node n; + + n.bt = right.bt; + n.nnum = right.nd.ndFLink; + + if (bt_getnode(&n) < 0) + return -1; + + n.nd.ndBLink = right.nnum; + + if (bt_putnode(&n) < 0) + return -1; + } + + return 0; +} + +/* + * NAME: node->insertx() + * DESCRIPTION: insert a record into a node (which must already have room) + */ +void n_insertx(node *np, unsigned char *record, int reclen) +{ + int rnum, i; + unsigned char *ptr; + + rnum = np->rnum + 1; + + /* push other records down to make room */ + + for (ptr = HFS_NODEREC(*np, np->nd.ndNRecs) + reclen; + ptr > HFS_NODEREC(*np, rnum) + reclen; --ptr) + *(ptr - 1) = *(ptr - 1 - reclen); + + ++np->nd.ndNRecs; + + for (i = np->nd.ndNRecs; i > rnum; --i) + np->roff[i] = np->roff[i - 1] + reclen; + + /* write the new record */ + + memcpy(HFS_NODEREC(*np, rnum), record, reclen); +} + +/* + * NAME: node->insert() + * DESCRIPTION: insert a new record into a node; return a record for parent + */ +int n_insert(node *np, unsigned char *record, int *reclen) +{ + n_compact(np); + + /* check for free space */ + + if (np->nd.ndNRecs >= HFS_MAXRECS || + *reclen + 2 > (int)NODESPACE(*np)) + return n_split(np, record, reclen); + + n_insertx(np, record, *reclen); + *reclen = 0; + + return bt_putnode(np); +} + +/* + * NAME: node->merge() + * DESCRIPTION: combine two nodes into a single node + */ +int n_merge(node *right, node *left, unsigned char *record, int *flag) +{ + int i, offset; + + /* copy records and offsets */ + + memcpy(HFS_NODEREC(*left, left->nd.ndNRecs), HFS_NODEREC(*right, 0), + right->roff[right->nd.ndNRecs] - right->roff[0]); + + offset = left->roff[left->nd.ndNRecs] - right->roff[0]; + + for (i = 1; i <= (int)right->nd.ndNRecs; ++i) + left->roff[++left->nd.ndNRecs] = offset + right->roff[i]; + + /* update link pointers */ + + left->nd.ndFLink = right->nd.ndFLink; + + if (bt_putnode(left) < 0) + return -1; + + if (right->bt->hdr.bthLNode == right->nnum) + { + right->bt->hdr.bthLNode = left->nnum; + right->bt->flags |= HFS_UPDATE_BTHDR; + } + + if (right->nd.ndFLink) + { + node n; + + n.bt = right->bt; + n.nnum = right->nd.ndFLink; + + if (bt_getnode(&n) < 0) + return -1; + + n.nd.ndBLink = left->nnum; + + if (bt_putnode(&n) < 0) + return -1; + } + + n_free(right); + + HFS_RECKEYLEN(record) = 0; + *flag = 1; + + return 0; +} + +/* + * NAME: node->delete() + * DESCRIPTION: remove a record from a node + */ +int n_delete(node *np, unsigned char *record, int *flag) +{ + node left; + unsigned char *rec; + + rec = HFS_NODEREC(*np, np->rnum); + + HFS_RECKEYLEN(rec) = 0; + n_compact(np); + + /* see if we can merge with our left sibling */ + + left.bt = np->bt; + left.nnum = np->nd.ndBLink; + + if (left.nnum > 0) + { + if (bt_getnode(&left) < 0) + return -1; + + if ((int)(np->nd.ndNRecs + left.nd.ndNRecs) <= HFS_MAXRECS && + (int)(np->roff[np->nd.ndNRecs] - np->roff[0] + + 2 * np->nd.ndNRecs) <= (int)NODESPACE(left)) + return n_merge(np, &left, record, flag); + } + + if (np->rnum == 0) + { + /* special case: first record changed; update parent record key */ + + n_index(np->bt, HFS_NODEREC(*np, 0), np->nnum, record, 0); + *flag = 1; + } + + return bt_putnode(np); +} diff --git a/libhfs_iso/node.h b/libhfs_iso/node.h new file mode 100644 index 0000000..39d3227 --- /dev/null +++ b/libhfs_iso/node.h @@ -0,0 +1,48 @@ +/* + * 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. + * + */ + +/* @(#)node.h 1.1 00/03/05 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. + */ + +void n_init(node *, btree *, int, int); + +int n_new(node *); +void n_free(node *); + +void n_compact(node *); +int n_search(node *, unsigned char *); + +void n_index(btree *, unsigned char *, unsigned long, unsigned char *, int *); +int n_split(node *, unsigned char *, int *); + +void n_insertx(node *, unsigned char *, int); +int n_insert(node *, unsigned char *, int *); + +int n_merge(node *, node *, unsigned char *, int *); +int n_delete(node *, unsigned char *, int *); diff --git a/libhfs_iso/record.c b/libhfs_iso/record.c new file mode 100644 index 0000000..e202158 --- /dev/null +++ b/libhfs_iso/record.c @@ -0,0 +1,551 @@ +/* + * 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. + * + */ + +/* @(#)record.c 1.1 00/04/26 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 +#include +#include +#include + +#include "internal.h" +#include "data.h" +#include "record.h" + +/* + * NAME: record->packcatkey() + * DESCRIPTION: pack a catalog record key + */ +void r_packcatkey(CatKeyRec *key, unsigned char *pkey, int *len) +{ + unsigned char *start = pkey; + + d_storeb(&pkey, key->ckrKeyLen); + d_storeb(&pkey, key->ckrResrv1); + d_storel(&pkey, key->ckrParID); + d_stores(&pkey, key->ckrCName, sizeof(key->ckrCName)); + + if (len) + *len = HFS_RECKEYSKIP(start); +} + +/* + * NAME: record->unpackcatkey() + * DESCRIPTION: unpack a catalog record key + */ +void r_unpackcatkey(unsigned char *pkey, CatKeyRec *key) +{ + d_fetchb(&pkey, (char *) &key->ckrKeyLen); + d_fetchb(&pkey, (char *) &key->ckrResrv1); + d_fetchl(&pkey, (long *) &key->ckrParID); + d_fetchs(&pkey, key->ckrCName, sizeof(key->ckrCName)); +} + +/* + * NAME: record->packextkey() + * DESCRIPTION: pack an extents record key + */ +void r_packextkey(ExtKeyRec *key, unsigned char *pkey, int *len) +{ + unsigned char *start = pkey; + + d_storeb(&pkey, key->xkrKeyLen); + d_storeb(&pkey, key->xkrFkType); + d_storel(&pkey, key->xkrFNum); + d_storew(&pkey, key->xkrFABN); + + if (len) + *len = HFS_RECKEYSKIP(start); +} + +/* + * NAME: record->unpackextkey() + * DESCRIPTION: unpack an extents record key + */ +void r_unpackextkey(unsigned char *pkey, ExtKeyRec *key) +{ + d_fetchb(&pkey, (char *) &key->xkrKeyLen); + d_fetchb(&pkey, (char *) &key->xkrFkType); + d_fetchl(&pkey, (long *) &key->xkrFNum); + d_fetchw(&pkey, (short *) &key->xkrFABN); +} + +/* + * NAME: record->comparecatkeys() + * DESCRIPTION: compare two (packed) catalog record keys + */ +int r_comparecatkeys(unsigned char *pkey1, unsigned char *pkey2) +{ + CatKeyRec key1; + CatKeyRec key2; + int diff; + + r_unpackcatkey(pkey1, &key1); + r_unpackcatkey(pkey2, &key2); + + diff = key1.ckrParID - key2.ckrParID; + if (diff) + return diff; + + return d_relstring(key1.ckrCName, key2.ckrCName); +} + +/* + * NAME: record->compareextkeys() + * DESCRIPTION: compare two (packed) extents record keys + */ +int r_compareextkeys(unsigned char *pkey1, unsigned char *pkey2) +{ + ExtKeyRec key1; + ExtKeyRec key2; + int diff; + + r_unpackextkey(pkey1, &key1); + r_unpackextkey(pkey2, &key2); + + diff = key1.xkrFNum - key2.xkrFNum; + if (diff) + return diff; + + diff = (unsigned char) key1.xkrFkType - + (unsigned char) key2.xkrFkType; + if (diff) + return diff; + + return key1.xkrFABN - key2.xkrFABN; +} + +/* + * NAME: record->packcatdata() + * DESCRIPTION: pack catalog record data + */ +void r_packcatdata(CatDataRec *data, unsigned char *pdata, int *len) +{ + unsigned char *start = pdata; + int i; + + d_storeb(&pdata, data->cdrType); + d_storeb(&pdata, data->cdrResrv2); + + switch (data->cdrType) + { + case cdrDirRec: + d_storew(&pdata, data->u.dir.dirFlags); + d_storew(&pdata, data->u.dir.dirVal); + d_storel(&pdata, data->u.dir.dirDirID); + d_storel(&pdata, data->u.dir.dirCrDat); + d_storel(&pdata, data->u.dir.dirMdDat); + d_storel(&pdata, data->u.dir.dirBkDat); + + d_storew(&pdata, data->u.dir.dirUsrInfo.frRect.top); + d_storew(&pdata, data->u.dir.dirUsrInfo.frRect.left); + d_storew(&pdata, data->u.dir.dirUsrInfo.frRect.bottom); + d_storew(&pdata, data->u.dir.dirUsrInfo.frRect.right); + d_storew(&pdata, data->u.dir.dirUsrInfo.frFlags); + d_storew(&pdata, data->u.dir.dirUsrInfo.frLocation.v); + d_storew(&pdata, data->u.dir.dirUsrInfo.frLocation.h); + d_storew(&pdata, data->u.dir.dirUsrInfo.frView); + + d_storew(&pdata, data->u.dir.dirFndrInfo.frScroll.v); + d_storew(&pdata, data->u.dir.dirFndrInfo.frScroll.h); + d_storel(&pdata, data->u.dir.dirFndrInfo.frOpenChain); + d_storew(&pdata, data->u.dir.dirFndrInfo.frUnused); + d_storew(&pdata, data->u.dir.dirFndrInfo.frComment); + d_storel(&pdata, data->u.dir.dirFndrInfo.frPutAway); + + for (i = 0; i < 4; ++i) + d_storel(&pdata, data->u.dir.dirResrv[i]); + + break; + + case cdrFilRec: + d_storeb(&pdata, data->u.fil.filFlags); + d_storeb(&pdata, data->u.fil.filTyp); + + d_storel(&pdata, data->u.fil.filUsrWds.fdType); + d_storel(&pdata, data->u.fil.filUsrWds.fdCreator); + d_storew(&pdata, data->u.fil.filUsrWds.fdFlags); + d_storew(&pdata, data->u.fil.filUsrWds.fdLocation.v); + d_storew(&pdata, data->u.fil.filUsrWds.fdLocation.h); + d_storew(&pdata, data->u.fil.filUsrWds.fdFldr); + + d_storel(&pdata, data->u.fil.filFlNum); + + d_storew(&pdata, data->u.fil.filStBlk); + d_storel(&pdata, data->u.fil.filLgLen); + d_storel(&pdata, data->u.fil.filPyLen); + + d_storew(&pdata, data->u.fil.filRStBlk); + d_storel(&pdata, data->u.fil.filRLgLen); + d_storel(&pdata, data->u.fil.filRPyLen); + + d_storel(&pdata, data->u.fil.filCrDat); + d_storel(&pdata, data->u.fil.filMdDat); + d_storel(&pdata, data->u.fil.filBkDat); + + d_storew(&pdata, data->u.fil.filFndrInfo.fdIconID); + for (i = 0; i < 4; ++i) + d_storew(&pdata, data->u.fil.filFndrInfo.fdUnused[i]); + d_storew(&pdata, data->u.fil.filFndrInfo.fdComment); + d_storel(&pdata, data->u.fil.filFndrInfo.fdPutAway); + + d_storew(&pdata, data->u.fil.filClpSize); + + for (i = 0; i < 3; ++i) + { + d_storew(&pdata, data->u.fil.filExtRec[i].xdrStABN); + d_storew(&pdata, data->u.fil.filExtRec[i].xdrNumABlks); + } + + for (i = 0; i < 3; ++i) + { + d_storew(&pdata, data->u.fil.filRExtRec[i].xdrStABN); + d_storew(&pdata, data->u.fil.filRExtRec[i].xdrNumABlks); + } + + d_storel(&pdata, data->u.fil.filResrv); + break; + + case cdrThdRec: + for (i = 0; i < 2; ++i) + d_storel(&pdata, data->u.dthd.thdResrv[i]); + + d_storel(&pdata, data->u.dthd.thdParID); + d_stores(&pdata, data->u.dthd.thdCName, sizeof(data->u.dthd.thdCName)); + break; + + case cdrFThdRec: + for (i = 0; i < 2; ++i) + d_storel(&pdata, data->u.fthd.fthdResrv[i]); + + d_storel(&pdata, data->u.fthd.fthdParID); + d_stores(&pdata, data->u.fthd.fthdCName, sizeof(data->u.fthd.fthdCName)); + break; + + default: + abort(); + } + + if (len) + *len += pdata - start; +} + +/* + * NAME: record->unpackcatdata() + * DESCRIPTION: unpack catalog record data + */ +void r_unpackcatdata(unsigned char *pdata, CatDataRec *data) +{ + int i; + + d_fetchb(&pdata, (char *) &data->cdrType); + d_fetchb(&pdata, (char *) &data->cdrResrv2); + + switch (data->cdrType) + { + case cdrDirRec: + d_fetchw(&pdata, &data->u.dir.dirFlags); + d_fetchw(&pdata, (short *) &data->u.dir.dirVal); + d_fetchl(&pdata, (long *) &data->u.dir.dirDirID); + d_fetchl(&pdata, &data->u.dir.dirCrDat); + d_fetchl(&pdata, &data->u.dir.dirMdDat); + d_fetchl(&pdata, &data->u.dir.dirBkDat); + + d_fetchw(&pdata, &data->u.dir.dirUsrInfo.frRect.top); + d_fetchw(&pdata, &data->u.dir.dirUsrInfo.frRect.left); + d_fetchw(&pdata, &data->u.dir.dirUsrInfo.frRect.bottom); + d_fetchw(&pdata, &data->u.dir.dirUsrInfo.frRect.right); + d_fetchw(&pdata, &data->u.dir.dirUsrInfo.frFlags); + d_fetchw(&pdata, &data->u.dir.dirUsrInfo.frLocation.v); + d_fetchw(&pdata, &data->u.dir.dirUsrInfo.frLocation.h); + d_fetchw(&pdata, &data->u.dir.dirUsrInfo.frView); + + d_fetchw(&pdata, &data->u.dir.dirFndrInfo.frScroll.v); + d_fetchw(&pdata, &data->u.dir.dirFndrInfo.frScroll.h); + d_fetchl(&pdata, &data->u.dir.dirFndrInfo.frOpenChain); + d_fetchw(&pdata, &data->u.dir.dirFndrInfo.frUnused); + d_fetchw(&pdata, &data->u.dir.dirFndrInfo.frComment); + d_fetchl(&pdata, &data->u.dir.dirFndrInfo.frPutAway); + + for (i = 0; i < 4; ++i) + d_fetchl(&pdata, &data->u.dir.dirResrv[i]); + + break; + + case cdrFilRec: + d_fetchb(&pdata, (char *) &data->u.fil.filFlags); + d_fetchb(&pdata, (char *) &data->u.fil.filTyp); + + d_fetchl(&pdata, &data->u.fil.filUsrWds.fdType); + d_fetchl(&pdata, &data->u.fil.filUsrWds.fdCreator); + d_fetchw(&pdata, &data->u.fil.filUsrWds.fdFlags); + d_fetchw(&pdata, &data->u.fil.filUsrWds.fdLocation.v); + d_fetchw(&pdata, &data->u.fil.filUsrWds.fdLocation.h); + d_fetchw(&pdata, &data->u.fil.filUsrWds.fdFldr); + + d_fetchl(&pdata, (long *) &data->u.fil.filFlNum); + + d_fetchw(&pdata, (short *) &data->u.fil.filStBlk); + d_fetchl(&pdata, (long *) &data->u.fil.filLgLen); + d_fetchl(&pdata, (long *) &data->u.fil.filPyLen); + + d_fetchw(&pdata, (short *) &data->u.fil.filRStBlk); + d_fetchl(&pdata, (long *) &data->u.fil.filRLgLen); + d_fetchl(&pdata, (long *) &data->u.fil.filRPyLen); + + d_fetchl(&pdata, &data->u.fil.filCrDat); + d_fetchl(&pdata, &data->u.fil.filMdDat); + d_fetchl(&pdata, &data->u.fil.filBkDat); + + d_fetchw(&pdata, &data->u.fil.filFndrInfo.fdIconID); + for (i = 0; i < 4; ++i) + d_fetchw(&pdata, &data->u.fil.filFndrInfo.fdUnused[i]); + d_fetchw(&pdata, &data->u.fil.filFndrInfo.fdComment); + d_fetchl(&pdata, &data->u.fil.filFndrInfo.fdPutAway); + + d_fetchw(&pdata, (short *) &data->u.fil.filClpSize); + + for (i = 0; i < 3; ++i) + { + d_fetchw(&pdata, (short *) &data->u.fil.filExtRec[i].xdrStABN); + d_fetchw(&pdata, (short *) &data->u.fil.filExtRec[i].xdrNumABlks); + } + + for (i = 0; i < 3; ++i) + { + d_fetchw(&pdata, (short *) &data->u.fil.filRExtRec[i].xdrStABN); + d_fetchw(&pdata, (short *) &data->u.fil.filRExtRec[i].xdrNumABlks); + } + + d_fetchl(&pdata, &data->u.fil.filResrv); + break; + + case cdrThdRec: + for (i = 0; i < 2; ++i) + d_fetchl(&pdata, &data->u.dthd.thdResrv[i]); + + d_fetchl(&pdata, (long *) &data->u.dthd.thdParID); + d_fetchs(&pdata, data->u.dthd.thdCName, sizeof(data->u.dthd.thdCName)); + break; + + case cdrFThdRec: + for (i = 0; i < 2; ++i) + d_fetchl(&pdata, &data->u.fthd.fthdResrv[i]); + + d_fetchl(&pdata, (long *) &data->u.fthd.fthdParID); + d_fetchs(&pdata, data->u.fthd.fthdCName, sizeof(data->u.fthd.fthdCName)); + break; + + default: + abort(); + } +} + +/* + * NAME: record->packextdata() + * DESCRIPTION: pack extent record data + */ +void r_packextdata(ExtDataRec *data, unsigned char *pdata, int *len) +{ + unsigned char *start = pdata; + int i; + + for (i = 0; i < 3; ++i) + { + d_storew(&pdata, (*data)[i].xdrStABN); + d_storew(&pdata, (*data)[i].xdrNumABlks); + } + + if (len) + *len += pdata - start; +} + +/* + * NAME: record->unpackextdata() + * DESCRIPTION: unpack extent record data + */ +void r_unpackextdata(unsigned char *pdata, ExtDataRec *data) +{ + int i; + + for (i = 0; i < 3; ++i) + { + d_fetchw(&pdata, (short *) &(*data)[i].xdrStABN); + d_fetchw(&pdata, (short *) &(*data)[i].xdrNumABlks); + } +} + +/* + * NAME: record->makecatkey() + * DESCRIPTION: construct a catalog record key + */ +void r_makecatkey(CatKeyRec *key, long parid, char *name) +{ + int len; + + len = strlen(name) + 1; + + key->ckrKeyLen = 0x05 + len + (len & 1); + key->ckrResrv1 = 0; + key->ckrParID = parid; + + strcpy(key->ckrCName, name); +} + +/* + * NAME: record->makeextkey() + * DESCRIPTION: construct an extents record key + */ +void r_makeextkey(ExtKeyRec *key, int ffork, long fnum, unsigned int fabn) +{ + key->xkrKeyLen = 0x07; + key->xkrFkType = ffork; + key->xkrFNum = fnum; + key->xkrFABN = fabn; +} + +/* + * NAME: record->unpackdirent() + * DESCRIPTION: unpack catalog information into hfsdirent structure + * + * Taken fron v3.2.6 + */ +void r_unpackdirent(long parid, char *name, CatDataRec *data, hfsdirent *ent) +{ + strcpy(ent->name, name); + ent->parid = parid; + + switch (data->cdrType) + { + case cdrDirRec: + ent->flags = HFS_ISDIR; + ent->cnid = data->u.dir.dirDirID; + + ent->crdate = d_toutime(data->u.dir.dirCrDat); + ent->mddate = d_toutime(data->u.dir.dirMdDat); + ent->bkdate = d_toutime(data->u.dir.dirBkDat); + + ent->fdflags = data->u.dir.dirUsrInfo.frFlags; + ent->fdlocation.v = data->u.dir.dirUsrInfo.frLocation.v; + ent->fdlocation.h = data->u.dir.dirUsrInfo.frLocation.h; + + ent->u.dir.valence = data->u.dir.dirVal; + + ent->u.dir.rect.top = data->u.dir.dirUsrInfo.frRect.top; + ent->u.dir.rect.left = data->u.dir.dirUsrInfo.frRect.left; + ent->u.dir.rect.bottom = data->u.dir.dirUsrInfo.frRect.bottom; + ent->u.dir.rect.right = data->u.dir.dirUsrInfo.frRect.right; + + /* mkhybrid extra */ + ent->u.dir.frscroll.v = data->u.dir.dirFndrInfo.frScroll.v; + ent->u.dir.frscroll.h = data->u.dir.dirFndrInfo.frScroll.h; + ent->u.dir.view = data->u.dir.dirUsrInfo.frView; + + break; + + case cdrFilRec: + ent->flags = (data->u.fil.filFlags & (1 << 0)) ? HFS_ISLOCKED : 0; + ent->cnid = data->u.fil.filFlNum; + + ent->crdate = d_toutime(data->u.fil.filCrDat); + ent->mddate = d_toutime(data->u.fil.filMdDat); + ent->bkdate = d_toutime(data->u.fil.filBkDat); + + ent->fdflags = data->u.fil.filUsrWds.fdFlags; + ent->fdlocation.v = data->u.fil.filUsrWds.fdLocation.v; + ent->fdlocation.h = data->u.fil.filUsrWds.fdLocation.h; + + ent->u.file.dsize = data->u.fil.filLgLen; + ent->u.file.rsize = data->u.fil.filRLgLen; + + d_putl((unsigned char *) ent->u.file.type, + data->u.fil.filUsrWds.fdType); + d_putl((unsigned char *) ent->u.file.creator, + data->u.fil.filUsrWds.fdCreator); + + ent->u.file.type[4] = ent->u.file.creator[4] = 0; + + break; + } +} + +/* + * NAME: record->packdirent() + * DESCRIPTION: make changes to a catalog record + * + * Taken fron v3.2.6 + */ +void r_packdirent(CatDataRec *data, hfsdirent *ent) +{ + switch (data->cdrType) + { + case cdrDirRec: + data->u.dir.dirCrDat = d_tomtime(ent->crdate); + data->u.dir.dirMdDat = d_tomtime(ent->mddate); + data->u.dir.dirBkDat = d_tomtime(ent->bkdate); + + data->u.dir.dirUsrInfo.frFlags = ent->fdflags; + data->u.dir.dirUsrInfo.frLocation.v = ent->fdlocation.v; + data->u.dir.dirUsrInfo.frLocation.h = ent->fdlocation.h; + + data->u.dir.dirUsrInfo.frRect.top = ent->u.dir.rect.top; + data->u.dir.dirUsrInfo.frRect.left = ent->u.dir.rect.left; + data->u.dir.dirUsrInfo.frRect.bottom = ent->u.dir.rect.bottom; + data->u.dir.dirUsrInfo.frRect.right = ent->u.dir.rect.right; + + /* mkhybrid extra */ + data->u.dir.dirFndrInfo.frScroll.v = ent->u.dir.frscroll.v; + data->u.dir.dirFndrInfo.frScroll.h = ent->u.dir.frscroll.h; + data->u.dir.dirUsrInfo.frView = ent->u.dir.view; + + break; + + case cdrFilRec: + if (ent->flags & HFS_ISLOCKED) + data->u.fil.filFlags |= (1 << 0); + else + data->u.fil.filFlags &= ~(1 << 0); + + data->u.fil.filCrDat = d_tomtime(ent->crdate); + data->u.fil.filMdDat = d_tomtime(ent->mddate); + data->u.fil.filBkDat = d_tomtime(ent->bkdate); + + data->u.fil.filUsrWds.fdFlags = ent->fdflags; + data->u.fil.filUsrWds.fdLocation.v = ent->fdlocation.v; + data->u.fil.filUsrWds.fdLocation.h = ent->fdlocation.h; + + data->u.fil.filUsrWds.fdType = + d_getl((unsigned char *) ent->u.file.type); + data->u.fil.filUsrWds.fdCreator = + d_getl((unsigned char *) ent->u.file.creator); + + break; + } +} diff --git a/libhfs_iso/record.h b/libhfs_iso/record.h new file mode 100644 index 0000000..90934eb --- /dev/null +++ b/libhfs_iso/record.h @@ -0,0 +1,52 @@ +/* + * 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. + * + */ + +/* @(#)record.h 1.1 00/03/05 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. + */ + +void r_packcatkey(CatKeyRec *, unsigned char *, int *); +void r_unpackcatkey(unsigned char *, CatKeyRec *); + +void r_packextkey(ExtKeyRec *, unsigned char *, int *); +void r_unpackextkey(unsigned char *, ExtKeyRec *); + +int r_comparecatkeys(unsigned char *, unsigned char *); +int r_compareextkeys(unsigned char *, unsigned char *); + +void r_packcatdata(CatDataRec *, unsigned char *, int *); +void r_unpackcatdata(unsigned char *, CatDataRec *); + +void r_packextdata(ExtDataRec *, unsigned char *, int *); +void r_unpackextdata(unsigned char *, ExtDataRec *); + +void r_makecatkey(CatKeyRec *, long, char *); +void r_makeextkey(ExtKeyRec *, int, long, unsigned int); + +void r_unpackdirent(long, char *, CatDataRec *, hfsdirent *); +void r_packdirent(CatDataRec *, hfsdirent *); diff --git a/libhfs_iso/volume.c b/libhfs_iso/volume.c new file mode 100644 index 0000000..b352735 --- /dev/null +++ b/libhfs_iso/volume.c @@ -0,0 +1,741 @@ +/* + * 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. + * + */ + +/* @(#)volume.c 1.4 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 +#include +#include +#include + +#include "internal.h" +#include "data.h" +#include "low.h" +#include "btree.h" +#include "record.h" +#include "volume.h" + +static void markexts(block *vbm, ExtDataRec *exts); + + +/* + * NAME: vol->catsearch() + * DESCRIPTION: search catalog tree + */ +int v_catsearch(hfsvol *vol, long parid, char *name, CatDataRec *data, + char *cname, node *np) +{ + CatKeyRec key; + unsigned char pkey[HFS_CATKEYLEN]; + node n; + unsigned char *ptr; + int found; + + if (np == 0) + np = &n; + + r_makecatkey(&key, parid, name); + r_packcatkey(&key, pkey, 0); + + found = bt_search(&vol->cat, pkey, np); + if (found <= 0) + return found; + + ptr = HFS_NODEREC(*np, np->rnum); + + if (cname) + { + r_unpackcatkey(ptr, &key); + strcpy(cname, key.ckrCName); + } + + if (data) + r_unpackcatdata(HFS_RECDATA(ptr), data); + + return 1; +} + +/* + * NAME: vol->extsearch() + * DESCRIPTION: search extents tree + */ +int v_extsearch(hfsfile *file, unsigned int fabn, ExtDataRec *data, node *np) +{ + ExtKeyRec key; + ExtDataRec extsave; + unsigned int fabnsave; + unsigned char pkey[HFS_EXTKEYLEN]; + node n; + unsigned char *ptr; + int found; + + if (np == 0) + np = &n; + + r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn); + r_packextkey(&key, pkey, 0); + + /* in case bt_search() clobbers these */ + + memcpy(&extsave, &file->ext, sizeof(ExtDataRec)); + fabnsave = file->fabn; + + found = bt_search(&file->vol->ext, pkey, np); + + memcpy(&file->ext, &extsave, sizeof(ExtDataRec)); + file->fabn = fabnsave; + + if (found <= 0) + return found; + + if (data) + { + ptr = HFS_NODEREC(*np, np->rnum); + r_unpackextdata(HFS_RECDATA(ptr), data); + } + + return 1; +} + +/* + * NAME: vol->getthread() + * DESCRIPTION: retrieve catalog thread information for a file or directory + */ +int v_getthread(hfsvol *vol, long id, CatDataRec *thread, node *np, int type) +{ + CatDataRec rec; + int found; + + if (thread == 0) + thread = &rec; + + found = v_catsearch(vol, id, "", thread, 0, np); + if (found <= 0) + return found; + + if (thread->cdrType != type) + { + ERROR(EIO, "bad thread record"); + return -1; + } + + return 1; +} + +/* + * NAME: vol->putcatrec() + * DESCRIPTION: store catalog information + */ +int v_putcatrec(CatDataRec *data, node *np) +{ + unsigned char pdata[HFS_CATDATALEN], *ptr; + int len = 0; + + r_packcatdata(data, pdata, &len); + + ptr = HFS_NODEREC(*np, np->rnum); + memcpy(HFS_RECDATA(ptr), pdata, len); + + return bt_putnode(np); +} + +/* + * NAME: vol->putextrec() + * DESCRIPTION: store extent information + */ +int v_putextrec(ExtDataRec *data, node *np) +{ + unsigned char pdata[HFS_EXTDATALEN], *ptr; + int len = 0; + + r_packextdata(data, pdata, &len); + + ptr = HFS_NODEREC(*np, np->rnum); + memcpy(HFS_RECDATA(ptr), pdata, len); + + return bt_putnode(np); +} + +/* + * NAME: vol->allocblocks() + * DESCRIPTION: allocate a contiguous range of blocks + */ +int v_allocblocks(hfsvol *vol, ExtDescriptor *blocks) +{ + unsigned int request, found, foundat, start, end, pt; + block *vbm; + int wrap = 0; + + if (vol->mdb.drFreeBks == 0) + { + ERROR(ENOSPC, "volume full"); + return -1; + } + + request = blocks->xdrNumABlks; + found = 0; + foundat = 0; + start = vol->mdb.drAllocPtr; + end = vol->mdb.drNmAlBlks; + pt = start; + vbm = vol->vbm; + + if (request == 0) + abort(); + + for (;;) + { + unsigned int mark; + + /* skip blocks in use */ + + while (pt < end && BMTST(vbm, pt)) + ++pt; + + if (wrap && pt >= start) + break; + + /* count blocks not in use */ + + mark = pt; + while (pt < end && pt - mark < request && ! BMTST(vbm, pt)) + ++pt; + + if (pt - mark > found) + { + found = pt - mark; + foundat = mark; + } + + if (pt == end) + pt = 0, wrap = 1; + + if (found == request) + break; + } + + if (found == 0 || found > vol->mdb.drFreeBks) + { + ERROR(EIO, "bad volume bitmap or free block count"); + return -1; + } + + blocks->xdrStABN = foundat; + blocks->xdrNumABlks = found; + + vol->mdb.drAllocPtr = pt; + vol->mdb.drFreeBks -= found; + + for (pt = foundat; pt < foundat + found; ++pt) + BMSET(vbm, pt); + + vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM; + + return 0; +} + +/* + * NAME: vol->freeblocks() + * DESCRIPTION: deallocate a contiguous range of blocks + */ +void v_freeblocks(hfsvol *vol, ExtDescriptor *blocks) +{ + unsigned int start, len, pt; + block *vbm; + + start = blocks->xdrStABN; + len = blocks->xdrNumABlks; + vbm = vol->vbm; + + vol->mdb.drFreeBks += len; + + for (pt = start; pt < start + len; ++pt) + BMCLR(vbm, pt); + + vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM; +} + +/* + * NAME: vol->resolve() + * DESCRIPTION: translate a pathname; return catalog information + */ +int v_resolve(hfsvol **vol, char *path, CatDataRec *data, long *parid, + char *fname, node *np) +{ + long dirid; + char name[HFS_MAX_FLEN + 1], *nptr; + int found; + + if (*path == 0) + { + ERROR(ENOENT, "empty path"); + return -1; + } + + if (parid) + *parid = 0; + + nptr = strchr(path, ':'); + + if (*path == ':' || nptr == 0) + { + dirid = (*vol)->cwd; /* relative path */ + + if (*path == ':') + ++path; + + if (*path == 0) + { + found = v_getdthread(*vol, dirid, data, 0); + if (found <= 0) + return found; + + if (parid) + *parid = data->u.dthd.thdParID; + + return v_catsearch(*vol, data->u.dthd.thdParID, + data->u.dthd.thdCName, data, fname, np); + } + } + else + { + hfsvol *check; + + dirid = HFS_CNID_ROOTPAR; /* absolute path */ + + if (nptr - path > HFS_MAX_VLEN) + { + ERROR(ENAMETOOLONG, 0); + return -1; + } + + strncpy(name, path, nptr - path); + name[nptr - path] = 0; + + for (check = hfs_mounts; check; check = check->next) + { + if (d_relstring(check->mdb.drVN, name) == 0) + { + *vol = check; + break; + } + } + } + + for (;;) + { + while (*path == ':') + { + ++path; + + found = v_getdthread(*vol, dirid, data, 0); + if (found <= 0) + return found; + + dirid = data->u.dthd.thdParID; + } + + if (*path == 0) + { + found = v_getdthread(*vol, dirid, data, 0); + if (found <= 0) + return found; + + if (parid) + *parid = data->u.dthd.thdParID; + + return v_catsearch(*vol, data->u.dthd.thdParID, + data->u.dthd.thdCName, data, fname, np); + } + + nptr = name; + while (nptr < name + sizeof(name) - 1 && *path && *path != ':') + *nptr++ = *path++; + + if (*path && *path != ':') + { + ERROR(ENAMETOOLONG, 0); + return -1; + } + + *nptr = 0; + if (*path == ':') + ++path; + + if (parid) + *parid = dirid; + + found = v_catsearch(*vol, dirid, name, data, fname, np); + if (found < 0) + return -1; + + if (found == 0) + { + if (*path && parid) + *parid = 0; + + if (*path == 0 && fname) + strcpy(fname, name); + + return 0; + } + + switch (data->cdrType) + { + case cdrDirRec: + if (*path == 0) + return 1; + + dirid = data->u.dir.dirDirID; + break; + + case cdrFilRec: + if (*path == 0) + return 1; + + ERROR(ENOTDIR, "invalid pathname"); + return -1; + + default: + ERROR(EIO, "unexpected catalog record"); + return -1; + } + } +} + +/* + * NAME: vol->destruct() + * DESCRIPTION: free memory consumed by a volume descriptor + */ +void v_destruct(hfsvol *vol) +{ + FREE(vol->vbm); + + FREE(vol->ext.map); + FREE(vol->cat.map); + + FREE(vol); +} + +/* + * NAME: vol->getvol() + * DESCRIPTION: validate a volume reference + */ +int v_getvol(hfsvol **vol) +{ + if (*vol == 0) + { + if (hfs_curvol == 0) + { + ERROR(EINVAL, "no volume is current"); + return -1; + } + + *vol = hfs_curvol; + } + + return 0; +} + +/* + * NAME: vol->flush() + * DESCRIPTION: flush all pending changes (B*-tree, MDB, VBM) to disk + */ +int v_flush(hfsvol *vol, int umounting) +{ + if (! (vol->flags & HFS_READONLY)) + { + if ((vol->ext.flags & HFS_UPDATE_BTHDR) && + bt_writehdr(&vol->ext) < 0) + return -1; + + if ((vol->cat.flags & HFS_UPDATE_BTHDR) && + bt_writehdr(&vol->cat) < 0) + return -1; + + if ((vol->flags & HFS_UPDATE_VBM) && + l_writevbm(vol) < 0) + return -1; + + if (umounting && + ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED)) + { + vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED; + vol->flags |= HFS_UPDATE_MDB; + } + + if ((vol->flags & (HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB)) && + l_writemdb(vol) < 0) + return -1; + } + + return 0; +} + +/* + * NAME: vol->adjvalence() + * DESCRIPTION: update a volume's valence counts + */ +int v_adjvalence(hfsvol *vol, long parid, int isdir, int adj) +{ + node n; + CatDataRec data; + + if (isdir) + vol->mdb.drDirCnt += adj; + else + vol->mdb.drFilCnt += adj; + + vol->flags |= HFS_UPDATE_MDB; + + if (parid == HFS_CNID_ROOTDIR) + { + if (isdir) + vol->mdb.drNmRtDirs += adj; + else + vol->mdb.drNmFls += adj; + } + else if (parid == HFS_CNID_ROOTPAR) + return 0; + + if (v_getdthread(vol, parid, &data, 0) <= 0 || + v_catsearch(vol, data.u.dthd.thdParID, data.u.dthd.thdCName, + &data, 0, &n) <= 0 || + data.cdrType != cdrDirRec) + { + ERROR(EIO, "can't find parent directory"); + return -1; + } + + data.u.dir.dirVal += adj; + data.u.dir.dirMdDat = d_tomtime(time(0)); + + return v_putcatrec(&data, &n); +} + +/* + * NAME: vol->newfolder() + * DESCRIPTION: create a new HFS folder + */ +int v_newfolder(hfsvol *vol, long parid, char *name) +{ + CatKeyRec key; + CatDataRec data; + long id; + unsigned char record[HFS_CATRECMAXLEN]; + int i, reclen; + + if (bt_space(&vol->cat, 2) < 0) + return -1; + + id = vol->mdb.drNxtCNID++; + vol->flags |= HFS_UPDATE_MDB; + + /* create directory record */ + + data.cdrType = cdrDirRec; + data.cdrResrv2 = 0; + + data.u.dir.dirFlags = 0; + data.u.dir.dirVal = 0; + data.u.dir.dirDirID = id; + data.u.dir.dirCrDat = d_tomtime(time(0)); + data.u.dir.dirMdDat = data.u.dir.dirCrDat; + data.u.dir.dirBkDat = 0; + + memset(&data.u.dir.dirUsrInfo, 0, sizeof(data.u.dir.dirUsrInfo)); + memset(&data.u.dir.dirFndrInfo, 0, sizeof(data.u.dir.dirFndrInfo)); + for (i = 0; i < 4; ++i) + data.u.dir.dirResrv[i] = 0; + + r_makecatkey(&key, parid, name); + r_packcatkey(&key, record, &reclen); + r_packcatdata(&data, HFS_RECDATA(record), &reclen); + + if (bt_insert(&vol->cat, record, reclen) < 0) + return -1; + + /* create thread record */ + + data.cdrType = cdrThdRec; + data.cdrResrv2 = 0; + + data.u.dthd.thdResrv[0] = 0; + data.u.dthd.thdResrv[1] = 0; + data.u.dthd.thdParID = parid; + strcpy(data.u.dthd.thdCName, name); + + r_makecatkey(&key, id, ""); + r_packcatkey(&key, record, &reclen); + r_packcatdata(&data, HFS_RECDATA(record), &reclen); + + if (bt_insert(&vol->cat, record, reclen) < 0 || + v_adjvalence(vol, parid, 1, 1) < 0) + return -1; + + return 0; +} + +/* + * NAME: markexts() + * DESCRIPTION: set bits from an extent record in the volume bitmap + */ +static +void markexts(block *vbm, ExtDataRec *exts) +{ + int i; + unsigned int start, len; + + for (i = 0; i < 3; ++i) + { + for (start = (*exts)[i].xdrStABN, + len = (*exts)[i].xdrNumABlks; len--; ++start) + BMSET(vbm, start); + } +} + +/* + * NAME: vol->scavenge() + * DESCRIPTION: safeguard blocks in the volume bitmap + */ +int v_scavenge(hfsvol *vol) +{ + block *vbm = vol->vbm; + node n; + unsigned int pt, blks; + + if (vbm == 0) + return 0; + + markexts(vbm, &vol->mdb.drXTExtRec); + markexts(vbm, &vol->mdb.drCTExtRec); + + vol->flags |= HFS_UPDATE_VBM; + + /* scavenge the extents overflow file */ + + n.bt = &vol->ext; + n.nnum = vol->ext.hdr.bthFNode; + + if (n.nnum > 0) + { + if (bt_getnode(&n) < 0) + return -1; + + n.rnum = 0; + + for (;;) + { + ExtDataRec data; + unsigned char *ptr; + + while (n.rnum >= (int)n.nd.ndNRecs) + { + n.nnum = n.nd.ndFLink; + if (n.nnum == 0) + break; + + if (bt_getnode(&n) < 0) + return -1; + + n.rnum = 0; + } + + if (n.nnum == 0) + break; + + ptr = HFS_NODEREC(n, n.rnum); + r_unpackextdata(HFS_RECDATA(ptr), &data); + + markexts(vbm, &data); + + ++n.rnum; + } + } + + /* scavenge the catalog file */ + + n.bt = &vol->cat; + n.nnum = vol->cat.hdr.bthFNode; + + if (n.nnum > 0) + { + if (bt_getnode(&n) < 0) + return -1; + + n.rnum = 0; + + for (;;) + { + CatDataRec data; + unsigned char *ptr; + + while (n.rnum >= (int)n.nd.ndNRecs) + { + n.nnum = n.nd.ndFLink; + if (n.nnum == 0) + break; + + if (bt_getnode(&n) < 0) + return -1; + + n.rnum = 0; + } + + if (n.nnum == 0) + break; + + ptr = HFS_NODEREC(n, n.rnum); + r_unpackcatdata(HFS_RECDATA(ptr), &data); + + if (data.cdrType == cdrFilRec) + { + markexts(vbm, &data.u.fil.filExtRec); + markexts(vbm, &data.u.fil.filRExtRec); + } + + ++n.rnum; + } + } + + for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; ) + { + if (! BMTST(vbm, pt)) + ++blks; + } + + if (vol->mdb.drFreeBks != blks) + { + vol->mdb.drFreeBks = blks; + vol->flags |= HFS_UPDATE_MDB; + } + + return 0; +} diff --git a/libhfs_iso/volume.h b/libhfs_iso/volume.h new file mode 100644 index 0000000..2cc6005 --- /dev/null +++ b/libhfs_iso/volume.h @@ -0,0 +1,58 @@ +/* + * 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. + * + */ + +/* @(#)volume.h 1.1 00/03/05 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. + */ + +int v_catsearch(hfsvol *, long, char *, CatDataRec *, char *, node *); +int v_extsearch(hfsfile *, unsigned int, ExtDataRec *, node *); + +int v_getthread(hfsvol *, long, CatDataRec *, node *, int); + +# define v_getdthread(vol, id, thread, np) \ + v_getthread(vol, id, thread, np, cdrThdRec) +# define v_getfthread(vol, id, thread, np) \ + v_getthread(vol, id, thread, np, cdrFThdRec) + +int v_putcatrec(CatDataRec *, node *); +int v_putextrec(ExtDataRec *, node *); + +int v_allocblocks(hfsvol *, ExtDescriptor *); +void v_freeblocks(hfsvol *, ExtDescriptor *); + +int v_resolve(hfsvol **, char *, CatDataRec *, long *, char *, node *); + +void v_destruct(hfsvol *); +int v_getvol(hfsvol **); +int v_flush(hfsvol *, int); + +int v_adjvalence(hfsvol *, long, int, int); +int v_newfolder(hfsvol *, long, char *); + +int v_scavenge(hfsvol *); -- cgit v1.2.3