/* * NAME: join() * DESCRIPTION: combine two nodes into a single node */ static int join(node *left, node *right, byte *record, int *flag) { int i, offset; /* copy records and offsets */ memcpy(HFS_NODEREC(*left, left->nd.ndNRecs), HFS_NODEREC(*right, 0), NODEUSED(*right)); offset = left->roff[left->nd.ndNRecs] - right->roff[0]; for (i = 1; i <= right->nd.ndNRecs; ++i) left->roff[++left->nd.ndNRecs] = offset + right->roff[i]; if (bt_putnode(left) == -1) goto fail; /* eliminate node and update link pointers */ if (n_free(right) == -1) goto fail; HFS_SETKEYLEN(record, 0); *flag = 1; return 0; fail: return -1; }
/* * 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->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: vol->catsearch() * DESCRIPTION: search catalog tree */ int v_catsearch(hfsvol *vol, 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: node->search() * DESCRIPTION: locate a record in a node, or the record it should follow */ int n_search(node *np, const byte *pkey) { const btree *bt = np->bt; byte key1[HFS_MAX_KEYLEN], key2[HFS_MAX_KEYLEN]; int i, comp = -1; bt->keyunpack(pkey, key2); for (i = np->nd.ndNRecs; i--; ) { const byte *rec; rec = HFS_NODEREC(*np, i); if (HFS_RECKEYLEN(rec) == 0) continue; /* deleted record */ bt->keyunpack(rec, key1); comp = bt->keycompare(key1, key2); if (comp <= 0) break; } np->rnum = i; return comp == 0; }
/* * 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(const ExtDataRec *data, node *np) { byte 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: node->insertx() * DESCRIPTION: insert a record into a node (which must already have room) */ void n_insertx(node *np, const byte *record, unsigned int reclen) { int rnum, i; byte *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->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); }
/* * NAME: node->delete() * DESCRIPTION: remove a record from a node */ int n_delete(node *np, byte *record, int *flag) { byte *rec; rec = HFS_NODEREC(*np, np->rnum); HFS_SETKEYLEN(rec, 0); compact(np); if (np->nd.ndNRecs == 0) { if (n_free(np) == -1) goto fail; HFS_SETKEYLEN(record, 0); *flag = 1; return 0; } /* see if we can join with our left sibling */ if (np->nd.ndBLink > 0) { node left; if (bt_getnode(&left, np->bt, np->nd.ndBLink) == -1) goto fail; if (np->nd.ndNRecs + left.nd.ndNRecs <= HFS_MAX_NRECS && NODEUSED(*np) + 2 * np->nd.ndNRecs <= NODEFREE(left)) return join(&left, np, record, flag); } if (np->rnum == 0) { /* special case: first record changed; update parent record key */ n_index(np, record, 0); *flag = 1; } return bt_putnode(np); fail: return -1; }
/* * NAME: node->index() * DESCRIPTION: create an index record from a key and node pointer */ void n_index(const node *np, byte *record, unsigned int *reclen) { const byte *key = HFS_NODEREC(*np, 0); if (np->bt == &np->bt->f.vol->cat) { /* force the key length to be 0x25 */ HFS_SETKEYLEN(record, 0x25); memset(record + 1, 0, 0x25); memcpy(record + 1, key + 1, HFS_RECKEYLEN(key)); } else memcpy(record, key, HFS_RECKEYSKIP(key)); d_putul(HFS_RECDATA(record), np->nnum); if (reclen) *reclen = HFS_RECKEYSKIP(record) + 4; }
/* * 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: compact() * DESCRIPTION: clean up a node, removing deleted records */ static void compact(node *np) { byte *ptr; int offset, nrecs, i; offset = 0x00e; ptr = np->data + offset; nrecs = 0; for (i = 0; i < np->nd.ndNRecs; ++i) { const byte *rec; int reclen; rec = HFS_NODEREC(*np, i); reclen = HFS_RECLEN(*np, 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: split() * DESCRIPTION: divide a node into two and insert a record */ static int split(node *left, byte *record, unsigned int *reclen) { btree *bt = left->bt; node n, *right = &n, *side = 0; int mark, i; /* create a second node by cloning the first */ *right = *left; if (n_new(right) == -1) goto fail; left->nd.ndFLink = right->nnum; right->nd.ndBLink = left->nnum; /* divide all records evenly between the two nodes */ mark = (NODEUSED(*left) + 2 * left->nd.ndNRecs + *reclen + 2) >> 1; if (left->rnum == -1) { side = left; mark -= *reclen + 2; } for (i = 0; i < left->nd.ndNRecs; ++i) { node *np; byte *rec; np = (mark > 0) ? right : left; rec = HFS_NODEREC(*np, i); mark -= HFS_RECLEN(*np, i) + 2; HFS_SETKEYLEN(rec, 0); if (left->rnum == i) { side = (mark > 0) ? left : right; mark -= *reclen + 2; } } compact(left); compact(right); /* insert the new record and store the modified nodes */ ASSERT(side); n_search(side, record); n_insertx(side, record, *reclen); if (bt_putnode(left) == -1 || bt_putnode(right) == -1) goto fail; /* create an index record in the parent for the new node */ n_index(right, record, reclen); /* update link pointers */ if (bt->hdr.bthLNode == left->nnum) { bt->hdr.bthLNode = right->nnum; bt->flags |= HFS_BT_UPDATE_HDR; } if (right->nd.ndFLink > 0) { node sib; if (bt_getnode(&sib, right->bt, right->nd.ndFLink) == -1) goto fail; sib.nd.ndBLink = right->nnum; if (bt_putnode(&sib) == -1) goto fail; } return 0; fail: return -1; }
/* * NAME: vol->scavenge() * DESCRIPTION: safeguard blocks in the volume bitmap */ int v_scavenge(hfsvol *vol) { const block *vbm = vol->vbm; node n; unsigned int pt, blks; if (vbm == 0) goto done; markexts(vbm, &vol->mdb.drXTExtRec); markexts(vbm, &vol->mdb.drCTExtRec); vol->flags |= HFS_VOL_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) == -1) goto fail; n.rnum = 0; while (1) { ExtDataRec data; const byte *ptr; while (n.rnum >= n.nd.ndNRecs) { n.nnum = n.nd.ndFLink; if (n.nnum == 0) break; if (bt_getnode(&n) == -1) goto fail; 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) == -1) goto fail; n.rnum = 0; while (1) { CatDataRec data; const byte *ptr; while (n.rnum >= n.nd.ndNRecs) { n.nnum = n.nd.ndFLink; if (n.nnum == 0) break; if (bt_getnode(&n) == -1) goto fail; 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_VOL_UPDATE_MDB; } done: return 0; fail: return -1; }
/* * 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; 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; 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: hfs->readdir() * DESCRIPTION: return the next entry in the directory */ int hfs_readdir(hfsdir *dir, hfsdirent *ent) { CatKeyRec key; CatDataRec data; const byte *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 == NULL) ERROR(ENOENT, "no more entries"); if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, NULL) <= 0 || v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName, &data, cname, NULL) <= 0) goto fail; r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent); dir->vptr = vol->next; goto done; } if (dir->n.rnum == -1) ERROR(ENOENT, "no more entries"); while (1) { ++dir->n.rnum; while (dir->n.rnum >= dir->n.nd.ndNRecs) { if (dir->n.nd.ndFLink == 0) { dir->n.rnum = -1; ERROR(ENOENT, "no more entries"); } if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1) { dir->n.rnum = -1; goto fail; } 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"); } r_unpackcatdata(HFS_RECDATA(ptr), &data); switch (data.cdrType) { case cdrDirRec: case cdrFilRec: r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent); goto done; case cdrThdRec: case cdrFThdRec: break; default: dir->n.rnum = -1; ERROR(EIO, "unexpected directory entry found"); } } done: return 0; fail: return -1; }
int hfs_callback_format(oscallback func, void* cookie, int mode, const char* vname) #endif { hfsvol vol; btree *ext = &vol.ext; btree *cat = &vol.cat; unsigned int i, *badalloc = 0; v_init(&vol, mode); if (! validvname(vname)) goto fail; #ifdef CP_NOT_USED if (v_open(&vol, path, HFS_MODE_RDWR) == -1 || v_geometry(&vol, pnum) == -1) goto fail; #else if (v_callback_open(&vol, func, cookie) != 0 || v_geometry(&vol, 0) != 0) goto fail; #endif /* initialize volume geometry */ vol.lpa = 1 + ((vol.vlen - 6) >> 16); if (vol.flags & HFS_OPT_2048) vol.lpa = (vol.lpa + 3) & ~3; vol.vbmsz = (vol.vlen / vol.lpa + 0x0fff) >> 12; vol.mdb.drSigWord = HFS_SIGWORD; vol.mdb.drCrDate = d_mtime(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.drAlBlkSiz = vol.lpa << HFS_BLOCKSZ_BITS; vol.mdb.drClpSiz = vol.mdb.drAlBlkSiz << 2; vol.mdb.drAlBlSt = vol.mdb.drVBMSt + vol.vbmsz; if (vol.flags & HFS_OPT_2048) vol.mdb.drAlBlSt = ((vol.vstart & 3) + vol.mdb.drAlBlSt + 3) & ~3; vol.mdb.drNmAlBlks = (vol.vlen - 2 - vol.mdb.drAlBlSt) / vol.lpa; 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; vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz; vol.mdb.drNmRtDirs = 0; vol.mdb.drFilCnt = 0; vol.mdb.drDirCnt = -1; /* incremented when root directory is created */ for (i = 0; i < 8; ++i) vol.mdb.drFndrInfo[i] = 0; vol.mdb.drEmbedSigWord = 0x0000; vol.mdb.drEmbedExtent.xdrStABN = 0; vol.mdb.drEmbedExtent.xdrNumABlks = 0; /* vol.mdb.drXTFlSize */ /* vol.mdb.drCTFlSize */ /* vol.mdb.drXTExtRec[0..2] */ /* vol.mdb.drCTExtRec[0..2] */ vol.flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB; /* initialize volume bitmap */ vol.vbm = ALLOC(block, vol.vbmsz); if (vol.vbm == 0) ERROR(ENOMEM, 0); memset(vol.vbm, 0, vol.vbmsz << HFS_BLOCKSZ_BITS); vol.flags |= HFS_VOL_UPDATE_VBM; /* perform initial bad block sparing */ #ifdef CP_NOT_USED if (nbadblocks > 0) { if (nbadblocks * 4 > vol.vlen) ERROR(EINVAL, "volume contains too many bad blocks"); badalloc = ALLOC(unsigned int, nbadblocks); if (badalloc == 0) ERROR(ENOMEM, 0); if (vol.mdb.drNmAlBlks == 1594) vol.mdb.drFreeBks = --vol.mdb.drNmAlBlks; for (i = 0; i < nbadblocks; ++i) { unsigned long bnum; unsigned int anum; bnum = badblocks[i]; if (bnum < vol.mdb.drAlBlSt || bnum == vol.vlen - 2) ERROR(EINVAL, "can't spare critical bad block"); else if (bnum >= vol.vlen) ERROR(EINVAL, "bad block not in volume"); anum = (bnum - vol.mdb.drAlBlSt) / vol.lpa; if (anum < vol.mdb.drNmAlBlks) BMSET(vol.vbm, anum); badalloc[i] = anum; } vol.mdb.drAtrb |= HFS_ATRB_BBSPARED; } #endif /* create extents overflow file */ 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; ext->map = ALLOC(byte, HFS_MAP1SZ); if (ext->map == 0) ERROR(ENOMEM, 0); memset(ext->map, 0, HFS_MAP1SZ); BMSET(ext->map, 0); ext->mapsz = HFS_MAP1SZ; ext->flags = HFS_BT_UPDATE_HDR; /* create catalog file */ 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; cat->map = ALLOC(byte, HFS_MAP1SZ); if (cat->map == 0) ERROR(ENOMEM, 0); memset(cat->map, 0, HFS_MAP1SZ); BMSET(cat->map, 0); cat->mapsz = HFS_MAP1SZ; cat->flags = HFS_BT_UPDATE_HDR; /* allocate space for header nodes (and initial extents) */ if (bt_space(ext, 1) == -1 || bt_space(cat, 1) == -1) goto fail; --ext->hdr.bthFree; --cat->hdr.bthFree; /* create extent records for bad blocks */ #ifdef CP_NOT_USED if (nbadblocks > 0) { hfsfile bbfile; ExtDescriptor extent; ExtDataRec *extrec; ExtKeyRec key; byte record[HFS_MAX_EXTRECLEN]; unsigned int reclen; f_init(&bbfile, &vol, HFS_CNID_BADALLOC, "bad blocks"); qsort(badalloc, nbadblocks, sizeof(*badalloc), (int (*)(const void *, const void *)) compare); for (i = 0; i < nbadblocks; ++i) { if (i == 0 || badalloc[i] != extent.xdrStABN) { extent.xdrStABN = badalloc[i]; extent.xdrNumABlks = 1; if (extent.xdrStABN < vol.mdb.drNmAlBlks && f_addextent(&bbfile, &extent) == -1) goto fail; } } /* flush local extents into extents overflow file */ f_getptrs(&bbfile, &extrec, 0, 0); r_makeextkey(&key, bbfile.fork, bbfile.cat.u.fil.filFlNum, 0); r_packextrec(&key, extrec, record, &reclen); if (bt_insert(&vol.ext, record, reclen) == -1) goto fail; } #endif vol.flags |= HFS_VOL_MOUNTED; /* create root directory */ if (v_mkdir(&vol, HFS_CNID_ROOTPAR, vname) == -1) goto fail; vol.mdb.drNxtCNID = 16; /* first CNID not reserved by Apple */ /* write boot blocks */ if (m_zerobb(&vol) == -1) goto fail; /* zero other unused space, if requested */ if (vol.flags & HFS_OPT_ZERO) { block b; unsigned long bnum; memset(&b, 0, sizeof(b)); /* between MDB and VBM (never) */ for (bnum = 3; bnum < vol.mdb.drVBMSt; ++bnum) b_writelb(&vol, bnum, &b); /* between VBM and first allocation block (sometimes if HFS_OPT_2048) */ for (bnum = vol.mdb.drVBMSt + vol.vbmsz; bnum < vol.mdb.drAlBlSt; ++bnum) b_writelb(&vol, bnum, &b); /* between last allocation block and alternate MDB (sometimes) */ for (bnum = vol.mdb.drAlBlSt + vol.mdb.drNmAlBlks * vol.lpa; bnum < vol.vlen - 2; ++bnum) b_writelb(&vol, bnum, &b); /* final block (always) */ b_writelb(&vol, vol.vlen - 1, &b); } /* flush remaining state and close volume */ if (v_close(&vol) == -1) goto fail; FREE(badalloc); return 0; fail: v_close(&vol); FREE(badalloc); return -1; }
/* * 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: file->trunc() * DESCRIPTION: release allocation blocks unneeded by a file */ int f_trunc(hfsfile *file) { hfsvol *vol = file->vol; ExtDataRec *extrec; ULongInt *lglen, *pylen, alblksz, newpylen; unsigned int dlen, start, end; node n; int i; if (vol->flags & HFS_VOL_READONLY) goto done; f_getptrs(file, &extrec, &lglen, &pylen); alblksz = vol->mdb.drAlBlkSiz; newpylen = (*lglen / alblksz + (*lglen % alblksz != 0)) * alblksz; if (newpylen > *pylen) ERROR(EIO, "file size exceeds physical length"); else if (newpylen == *pylen) goto done; 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"); } if (start >= end) break; if (v_extsearch(file, start, &file->ext, &n) <= 0) goto fail; 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; if (v_freeblocks(vol, &blocks) == -1) goto fail; } *pylen = newpylen; file->flags |= HFS_FILE_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"); else if (num > dlen) ERROR(EIO, "file extents exceed physical size"); dlen -= num; if (v_freeblocks(vol, &file->ext[i]) == -1) goto fail; 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) goto fail; if (file->ext[0].xdrNumABlks) { if (v_putextrec(&file->ext, &n) == -1) goto fail; } else { if (bt_delete(&vol->ext, HFS_NODEREC(n, n.rnum)) == -1) goto fail; n.nnum = 0; } } else memcpy(extrec, &file->ext, sizeof(ExtDataRec)); if (dlen) { if (v_extsearch(file, start, &file->ext, &n) <= 0) goto fail; file->fabn = start; i = -1; } } while (dlen); done: return 0; fail: return -1; }
/* * NAME: hfs->format() * DESCRIPTION: write a new filesystem */ int hfs_format(char *path, int pnum, char *vname) { hfsvol vol; btree *ext = &vol.ext; btree *cat = &vol.cat; unsigned int vbmsz; int i, result = 0; block vbm[16]; char *map; if (mystrchr(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; #if 0 vol.fd = open(path, O_RDWR); if (vol.fd < 0) { ERROR(errno, "error opening device for writing"); return -1; } #endif if (l_lockvol(&vol) < 0) { #if 0 close(vol.fd); #endif return -1; } if (pnum > 0) { if (l_readpm(&vol) < 0) { #if 0 close(vol.fd); #endif return -1; } } else /* determine size of entire device */ { 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; } if (vol.vlen < 800 * 1024 / HFS_BLOCKSZ) { #if 0 close(vol.fd); #endif ERROR(EINVAL, "volume size must be >= 800K"); return -1; } /* initialize volume geometry */ vol.lpa = 1 + vol.vlen / 65536; 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; 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; vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz; 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)); /* 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; } if ( #if 0 close(vol.fd) < 0 && #endif result == 0) { ERROR(errno, "error closing device"); result = -1; } FREE(vol.ext.map); FREE(vol.cat.map); return result; }