/* * libhfs - library for reading and writing Macintosh HFS volumes * Copyright (C) 1996-1998 Robert Leslie * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: volume.c,v 1.12 1998/11/02 22:09:10 rob Exp $ */ # include # include # include # include # include "libhfs.h" # include "volume.h" # include "data.h" # include "block.h" # include "low.h" # include "medium.h" # include "file.h" # include "btree.h" # include "record.h" # include "os.h" /* * NAME: vol->init() * DESCRIPTION: initialize volume structure */ void v_init(hfsvol *vol, int flags) { btree *ext = &vol->ext; btree *cat = &vol->cat; vol->priv = 0; vol->flags = flags & HFS_VOL_OPT_MASK; vol->pnum = -1; vol->vstart = 0; vol->vlen = 0; vol->lpa = 0; vol->cache = 0; vol->vbm = 0; vol->vbmsz = 0; f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow"); ext->map = 0; ext->mapsz = 0; ext->flags = 0; ext->keyunpack = (keyunpackfunc) r_unpackextkey; ext->keycompare = (keycomparefunc) r_compareextkeys; f_init(&cat->f, vol, HFS_CNID_CAT, "catalog"); cat->map = 0; cat->mapsz = 0; cat->flags = 0; cat->keyunpack = (keyunpackfunc) r_unpackcatkey; cat->keycompare = (keycomparefunc) r_comparecatkeys; vol->cwd = HFS_CNID_ROOTDIR; vol->refs = 0; vol->files = 0; vol->dirs = 0; vol->prev = 0; vol->next = 0; } /* * NAME: vol->open() * DESCRIPTION: open volume source and lock against concurrent updates */ int v_open(hfsvol *vol, const char *path, int mode) { if (vol->flags & HFS_VOL_OPEN) ERROR(EINVAL, "volume already open"); if (os_open(&vol->priv, path, mode) == -1) goto fail; vol->flags |= HFS_VOL_OPEN; /* initialize volume block cache (OK to fail) */ if (! (vol->flags & HFS_OPT_NOCACHE) && b_init(vol) != -1) vol->flags |= HFS_VOL_USINGCACHE; return 0; fail: return -1; } /* * NAME: flushvol() * DESCRIPTION: flush all pending changes (B*-tree, MDB, VBM) to volume */ static int flushvol(hfsvol *vol, int umount) { if (vol->flags & HFS_VOL_READONLY) goto done; if ((vol->ext.flags & HFS_BT_UPDATE_HDR) && bt_writehdr(&vol->ext) == -1) goto fail; if ((vol->cat.flags & HFS_BT_UPDATE_HDR) && bt_writehdr(&vol->cat) == -1) goto fail; if ((vol->flags & HFS_VOL_UPDATE_VBM) && v_writevbm(vol) == -1) goto fail; if (umount && ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED)) { vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED; vol->flags |= HFS_VOL_UPDATE_MDB; } if ((vol->flags & (HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB)) && v_writemdb(vol) == -1) goto fail; done: return 0; fail: return -1; } /* * NAME: vol->flush() * DESCRIPTION: commit all pending changes to volume device */ int v_flush(hfsvol *vol) { if (flushvol(vol, 0) == -1) goto fail; if ((vol->flags & HFS_VOL_USINGCACHE) && b_flush(vol) == -1) goto fail; return 0; fail: return -1; } /* * NAME: vol->close() * DESCRIPTION: close access path to volume source */ int v_close(hfsvol *vol) { int result = 0; if (! (vol->flags & HFS_VOL_OPEN)) goto done; if ((vol->flags & HFS_VOL_MOUNTED) && flushvol(vol, 1) == -1) result = -1; if ((vol->flags & HFS_VOL_USINGCACHE) && b_finish(vol) == -1) result = -1; if (os_close(&vol->priv) == -1) result = -1; vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE); /* free dynamically allocated structures */ FREE(vol->vbm); vol->vbm = 0; vol->vbmsz = 0; FREE(vol->ext.map); FREE(vol->cat.map); vol->ext.map = 0; vol->cat.map = 0; done: return result; } /* * NAME: vol->same() * DESCRIPTION: return 1 iff path is same as open volume */ int v_same(hfsvol *vol, const char *path) { return os_same(&vol->priv, path); } /* * NAME: vol->geometry() * DESCRIPTION: determine volume location and size (possibly in a partition) */ int v_geometry(hfsvol *vol, int pnum) { Partition map; unsigned long bnum = 0; int found; vol->pnum = pnum; if (pnum == 0) { vol->vstart = 0; vol->vlen = b_size(vol); if (vol->vlen == 0) goto fail; } else { while (pnum--) { found = m_findpmentry(vol, "Apple_HFS", &map, &bnum); if (found == -1 || ! found) goto fail; } vol->vstart = map.pmPyPartStart; vol->vlen = map.pmPartBlkCnt; if (map.pmDataCnt) { if ((unsigned long) map.pmLgDataStart + (unsigned long) map.pmDataCnt > vol->vlen) ERROR(EINVAL, "partition data overflows partition"); vol->vstart += (unsigned long) map.pmLgDataStart; vol->vlen = map.pmDataCnt; } if (vol->vlen == 0) ERROR(EINVAL, "volume partition is empty"); } if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS)) ERROR(EINVAL, "volume is smaller than 800K"); return 0; fail: return -1; } /* * NAME: vol->readmdb() * DESCRIPTION: load Master Directory Block into memory */ int v_readmdb(hfsvol *vol) { if (l_getmdb(vol, &vol->mdb, 0) == -1) goto fail; if (vol->mdb.drSigWord != HFS_SIGWORD) { if (vol->mdb.drSigWord == HFS_SIGWORD_MFS) ERROR(EINVAL, "MFS volume format not supported"); else ERROR(EINVAL, "not a Macintosh HFS volume"); } if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0) ERROR(EINVAL, "bad volume allocation block size"); vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS; /* extents pseudo-file structs */ vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN; vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize; vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize; vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; memcpy(&vol->ext.f.cat.u.fil.filExtRec, &vol->mdb.drXTExtRec, sizeof(ExtDataRec)); f_selectfork(&vol->ext.f, fkData); /* catalog pseudo-file structs */ vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN; vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize; vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize; vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; memcpy(&vol->cat.f.cat.u.fil.filExtRec, &vol->mdb.drCTExtRec, sizeof(ExtDataRec)); f_selectfork(&vol->cat.f, fkData); return 0; fail: return -1; } /* * NAME: vol->writemdb() * DESCRIPTION: flush Master Directory Block to medium */ int v_writemdb(hfsvol *vol) { vol->mdb.drLsMod = d_mtime(time(0)); vol->mdb.drXTFlSize = vol->ext.f.cat.u.fil.filPyLen; memcpy(&vol->mdb.drXTExtRec, &vol->ext.f.cat.u.fil.filExtRec, sizeof(ExtDataRec)); vol->mdb.drCTFlSize = vol->cat.f.cat.u.fil.filPyLen; memcpy(&vol->mdb.drCTExtRec, &vol->cat.f.cat.u.fil.filExtRec, sizeof(ExtDataRec)); if (l_putmdb(vol, &vol->mdb, vol->flags & HFS_VOL_UPDATE_ALTMDB) == -1) goto fail; vol->flags &= ~(HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB); return 0; fail: return -1; } /* * NAME: vol->readvbm() * DESCRIPTION: read volume bitmap into memory */ int v_readvbm(hfsvol *vol) { unsigned int vbmst = vol->mdb.drVBMSt; unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12; block *bp; ASSERT(vol->vbm == 0); if (vol->mdb.drAlBlSt - vbmst < vbmsz) ERROR(EIO, "volume bitmap collides with volume data"); vol->vbm = ALLOC(block, vbmsz); if (vol->vbm == 0) ERROR(ENOMEM, 0); vol->vbmsz = vbmsz; for (bp = vol->vbm; vbmsz--; ++bp) { if (b_readlb(vol, vbmst++, bp) == -1) goto fail; } return 0; fail: FREE(vol->vbm); vol->vbm = 0; vol->vbmsz = 0; return -1; } /* * NAME: vol->writevbm() * DESCRIPTION: flush volume bitmap to medium */ int v_writevbm(hfsvol *vol) { unsigned int vbmst = vol->mdb.drVBMSt; unsigned int vbmsz = vol->vbmsz; const block *bp; for (bp = vol->vbm; vbmsz--; ++bp) { if (b_writelb(vol, vbmst++, bp) == -1) goto fail; } vol->flags &= ~HFS_VOL_UPDATE_VBM; return 0; fail: return -1; } /* * NAME: vol->mount() * DESCRIPTION: load volume information into memory */ int v_mount(hfsvol *vol) { /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */ if (v_readmdb(vol) == -1 || v_readvbm(vol) == -1 || bt_readhdr(&vol->ext) == -1 || bt_readhdr(&vol->cat) == -1) goto fail; if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED) && v_scavenge(vol) == -1) goto fail; if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED) vol->flags |= HFS_VOL_READONLY; else if (vol->flags & HFS_VOL_READONLY) vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; else vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED; vol->flags |= HFS_VOL_MOUNTED; return 0; fail: return -1; } /* * NAME: vol->dirty() * DESCRIPTION: ensure the volume is marked "in use" before we make changes */ int v_dirty(hfsvol *vol) { if (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED) { vol->mdb.drAtrb &= ~HFS_ATRB_UMOUNTED; ++vol->mdb.drWrCnt; if (v_writemdb(vol) == -1) goto fail; if ((vol->flags & HFS_VOL_USINGCACHE) && b_flush(vol) == -1) goto fail; } return 0; fail: return -1; } /* * NAME: vol->catsearch() * DESCRIPTION: search catalog tree */ int v_catsearch(hfsvol *vol, unsigned long parid, const char *name, CatDataRec *data, char *cname, node *np) { CatKeyRec key; byte pkey[HFS_CATKEYLEN]; const byte *ptr; node n; 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; byte pkey[HFS_EXTKEYLEN]; const byte *ptr; node n; 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, unsigned 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 == 1 && thread->cdrType != type) ERROR(EIO, "bad thread record"); return found; fail: return -1; } /* * NAME: vol->putcatrec() * DESCRIPTION: store catalog information */ int v_putcatrec(const CatDataRec *data, node *np) { byte pdata[HFS_CATDATALEN], *ptr; unsigned 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(const ExtDataRec *data, node *np) { byte pdata[HFS_EXTDATALEN], *ptr; unsigned 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; register unsigned int pt; block *vbm; int wrap = 0; if (vol->mdb.drFreeBks == 0) ERROR(ENOSPC, "volume full"); request = blocks->xdrNumABlks; found = 0; foundat = 0; start = vol->mdb.drAllocPtr; end = vol->mdb.drNmAlBlks; vbm = vol->vbm; ASSERT(request > 0); /* backtrack the start pointer to recover unused space */ if (! BMTST(vbm, start)) { while (start > 0 && ! BMTST(vbm, start - 1)) --start; } /* find largest unused block which satisfies request */ pt = start; while (1) { 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 (wrap && pt >= start) break; 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"); blocks->xdrStABN = foundat; blocks->xdrNumABlks = found; if (v_dirty(vol) == -1) goto fail; vol->mdb.drAllocPtr = pt; vol->mdb.drFreeBks -= found; for (pt = foundat; pt < foundat + found; ++pt) BMSET(vbm, pt); vol->flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_VBM; if (vol->flags & HFS_OPT_ZERO) { block b; unsigned int i; memset(&b, 0, sizeof(b)); for (pt = foundat; pt < foundat + found; ++pt) { for (i = 0; i < vol->lpa; ++i) b_writeab(vol, pt, i, &b); } } return 0; fail: return -1; } /* * NAME: vol->freeblocks() * DESCRIPTION: deallocate a contiguous range of blocks */ int v_freeblocks(hfsvol *vol, const ExtDescriptor *blocks) { unsigned int start, len, pt; block *vbm; start = blocks->xdrStABN; len = blocks->xdrNumABlks; vbm = vol->vbm; if (v_dirty(vol) == -1) goto fail; vol->mdb.drFreeBks += len; for (pt = start; pt < start + len; ++pt) BMCLR(vbm, pt); vol->flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_VBM; return 0; fail: return -1; } /* * NAME: vol->resolve() * DESCRIPTION: translate a pathname; return catalog information */ int v_resolve(hfsvol **vol, const char *path, CatDataRec *data, long *parid, char *fname, node *np) { unsigned long dirid; char name[HFS_MAX_FLEN + 1], *nptr; int found = 0; if (*path == 0) ERROR(ENOENT, "empty path"); 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 == -1) goto fail; if (found) { if (parid) *parid = data->u.dthd.thdParID; found = v_catsearch(*vol, data->u.dthd.thdParID, data->u.dthd.thdCName, data, fname, np); if (found == -1) goto fail; } goto done; } } else { hfsvol *check; dirid = HFS_CNID_ROOTPAR; /* absolute path */ if (nptr - path > HFS_MAX_VLEN) ERROR(ENAMETOOLONG, 0); 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; } } } while (1) { while (*path == ':') { ++path; found = v_getdthread(*vol, dirid, data, 0); if (found == -1) goto fail; else if (! found) goto done; dirid = data->u.dthd.thdParID; } if (*path == 0) { found = v_getdthread(*vol, dirid, data, 0); if (found == -1) goto fail; if (found) { if (parid) *parid = data->u.dthd.thdParID; found = v_catsearch(*vol, data->u.dthd.thdParID, data->u.dthd.thdCName, data, fname, np); if (found == -1) goto fail; } goto done; } nptr = name; while (nptr < name + sizeof(name) - 1 && *path && *path != ':') *nptr++ = *path++; if (*path && *path != ':') ERROR(ENAMETOOLONG, 0); *nptr = 0; if (*path == ':') ++path; if (parid) *parid = dirid; found = v_catsearch(*vol, dirid, name, data, fname, np); if (found == -1) goto fail; if (! found) { if (*path && parid) *parid = 0; if (*path == 0 && fname) strcpy(fname, name); goto done; } switch (data->cdrType) { case cdrDirRec: if (*path == 0) goto done; dirid = data->u.dir.dirDirID; break; case cdrFilRec: if (*path == 0) goto done; ERROR(ENOTDIR, "invalid pathname"); default: ERROR(EIO, "unexpected catalog record"); } } done: return found; fail: return -1; } /* * NAME: vol->adjvalence() * DESCRIPTION: update a volume's valence counts */ int v_adjvalence(hfsvol *vol, unsigned long parid, int isdir, int adj) { node n; CatDataRec data; int result = 0; if (isdir) vol->mdb.drDirCnt += adj; else vol->mdb.drFilCnt += adj; vol->flags |= HFS_VOL_UPDATE_MDB; if (parid == HFS_CNID_ROOTDIR) { if (isdir) vol->mdb.drNmRtDirs += adj; else vol->mdb.drNmFls += adj; } else if (parid == HFS_CNID_ROOTPAR) goto done; 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"); data.u.dir.dirVal += adj; data.u.dir.dirMdDat = d_mtime(time(0)); result = v_putcatrec(&data, &n); done: return result; fail: return -1; } /* * NAME: vol->mkdir() * DESCRIPTION: create a new HFS directory */ int v_mkdir(hfsvol *vol, unsigned long parid, const char *name) { CatKeyRec key; CatDataRec data; unsigned long id; byte record[HFS_MAX_CATRECLEN]; unsigned int reclen; int i; if (bt_space(&vol->cat, 2) == -1) goto fail; id = vol->mdb.drNxtCNID++; vol->flags |= HFS_VOL_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_mtime(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_packcatrec(&key, &data, record, &reclen); if (bt_insert(&vol->cat, record, reclen) == -1) goto fail; /* 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_packcatrec(&key, &data, record, &reclen); if (bt_insert(&vol->cat, record, reclen) == -1 || v_adjvalence(vol, parid, 1, 1) == -1) goto fail; return 0; fail: return -1; } /* * NAME: markexts() * DESCRIPTION: set bits from an extent record in the volume bitmap */ static void markexts(block *vbm, const ExtDataRec *exts) { int i; unsigned int pt, len; for (i = 0; i < 3; ++i) { for ( pt = (*exts)[i].xdrStABN, len = (*exts)[i].xdrNumABlks; len--; ++pt) BMSET(vbm, pt); } } /* * 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; unsigned long lastcnid = 15; # ifdef DEBUG fprintf(stderr, "VOL: \"%s\" not cleanly unmounted\n", vol->mdb.drVN); # endif if (vol->flags & HFS_VOL_READONLY) goto done; # ifdef DEBUG fprintf(stderr, "VOL: scavenging...\n"); # endif /* reset MDB by marking it dirty again */ vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED; if (v_dirty(vol) == -1) goto fail; /* begin by marking extents in MDB */ markexts(vbm, &vol->mdb.drXTExtRec); markexts(vbm, &vol->mdb.drCTExtRec); vol->flags |= HFS_VOL_UPDATE_VBM; /* scavenge the extents overflow file */ if (vol->ext.hdr.bthFNode > 0) { if (bt_getnode(&n, &vol->ext, vol->ext.hdr.bthFNode) == -1) goto fail; n.rnum = 0; while (1) { ExtDataRec data; const byte *ptr; while (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink > 0) { if (bt_getnode(&n, &vol->ext, n.nd.ndFLink) == -1) goto fail; n.rnum = 0; } if (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink == 0) break; ptr = HFS_NODEREC(n, n.rnum); r_unpackextdata(HFS_RECDATA(ptr), &data); markexts(vbm, &data); ++n.rnum; } } /* scavenge the catalog file */ if (vol->cat.hdr.bthFNode > 0) { if (bt_getnode(&n, &vol->cat, vol->cat.hdr.bthFNode) == -1) goto fail; n.rnum = 0; while (1) { CatDataRec data; const byte *ptr; while (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink > 0) { if (bt_getnode(&n, &vol->cat, n.nd.ndFLink) == -1) goto fail; n.rnum = 0; } if (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink == 0) break; ptr = HFS_NODEREC(n, n.rnum); r_unpackcatdata(HFS_RECDATA(ptr), &data); switch (data.cdrType) { case cdrFilRec: markexts(vbm, &data.u.fil.filExtRec); markexts(vbm, &data.u.fil.filRExtRec); if (data.u.fil.filFlNum > lastcnid) lastcnid = data.u.fil.filFlNum; break; case cdrDirRec: if (data.u.dir.dirDirID > lastcnid) lastcnid = data.u.dir.dirDirID; break; } ++n.rnum; } } /* count free blocks */ for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; ) { if (! BMTST(vbm, pt)) ++blks; } if (vol->mdb.drFreeBks != blks) { # ifdef DEBUG fprintf(stderr, "VOL: updating free blocks from %u to %u\n", vol->mdb.drFreeBks, blks); # endif vol->mdb.drFreeBks = blks; vol->flags |= HFS_VOL_UPDATE_MDB; } /* ensure next CNID is sane */ if ((unsigned long) vol->mdb.drNxtCNID <= lastcnid) { # ifdef DEBUG fprintf(stderr, "VOL: updating next CNID from %lu to %lu\n", vol->mdb.drNxtCNID, lastcnid + 1); # endif vol->mdb.drNxtCNID = lastcnid + 1; vol->flags |= HFS_VOL_UPDATE_MDB; } # ifdef DEBUG fprintf(stderr, "VOL: scavenging complete\n"); # endif done: return 0; fail: return -1; }