summaryrefslogtreecommitdiff
path: root/libhfs_iso/hfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'libhfs_iso/hfs.c')
-rw-r--r--libhfs_iso/hfs.c2098
1 files changed, 2098 insertions, 0 deletions
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 <mconfig.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <ctype.h>
+#include <statdefs.h>
+
+#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 */