diff options
Diffstat (limited to 'libhfs_iso/volume.c')
-rw-r--r-- | libhfs_iso/volume.c | 741 |
1 files changed, 741 insertions, 0 deletions
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 <mconfig.h> +#include <stdxlib.h> +#include <strdefs.h> +#include <errno.h> + +#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; +} |