/* * NAME: node->free() * DESCRIPTION: deallocate and remove a b*-tree node */ int n_free(node *np) { btree *bt = np->bt; node sib; if (bt->hdr.bthFNode == np->nnum) bt->hdr.bthFNode = np->nd.ndFLink; if (bt->hdr.bthLNode == np->nnum) bt->hdr.bthLNode = np->nd.ndBLink; if (np->nd.ndFLink > 0) { if (bt_getnode(&sib, bt, np->nd.ndFLink) == -1) goto fail; sib.nd.ndBLink = np->nd.ndBLink; if (bt_putnode(&sib) == -1) goto fail; } if (np->nd.ndBLink > 0) { if (bt_getnode(&sib, bt, np->nd.ndBLink) == -1) goto fail; sib.nd.ndFLink = np->nd.ndFLink; if (bt_putnode(&sib) == -1) goto fail; } BMCLR(bt->map, np->nnum); ++bt->hdr.bthFree; bt->flags |= HFS_BT_UPDATE_HDR; 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->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->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: 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: 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; }
/* * NAME: node->split() * DESCRIPTION: divide a node into two and insert a record */ int n_split(node *left, unsigned char *record, int *reclen) { node right; int nrecs, i, mid; unsigned char *rec; right = *left; right.nd.ndBLink = left->nnum; if (n_new(&right) < 0) return -1; left->nd.ndFLink = right.nnum; nrecs = left->nd.ndNRecs; /* * Ensure node split leaves enough room for new record. * The size calculations used are based on the NODESPACE() macro, but * I don't know what the extra 2's and 1's are needed for. * John Witford <*****@*****.**> */ n_search(&right, record); mid = nrecs/2; for(;;) { if (right.rnum < mid) { if ( mid > 0 && (int)left->roff[mid] + *reclen + 2 > HFS_BLOCKSZ - 2 * (mid + 1)) { --mid; if (mid > 0) continue; } } else { if ( mid < nrecs && (int)right.roff[nrecs] - (int)right.roff[mid] + (int)left->roff[0] + *reclen + 2 > HFS_BLOCKSZ - 2 * (mid + 1)) { ++mid; if (mid < nrecs) continue; } } break; } for (i = 0; i < nrecs; ++i) { if (i < mid) rec = HFS_NODEREC(right, i); else rec = HFS_NODEREC(*left, i); HFS_RECKEYLEN(rec) = 0; } /* original code ... for (i = 0; i < nrecs; ++i) { if (i < nrecs / 2) rec = HFS_NODEREC(right, i); else rec = HFS_NODEREC(*left, i); HFS_RECKEYLEN(rec) = 0; } */ n_compact(left); n_compact(&right); n_search(&right, record); if (right.rnum >= 0) n_insertx(&right, record, *reclen); else { n_search(left, record); n_insertx(left, record, *reclen); } /* store the new/modified nodes */ if (bt_putnode(left) < 0 || bt_putnode(&right) < 0) return -1; /* create an index record for the new node in the parent */ n_index(right.bt, HFS_NODEREC(right, 0), right.nnum, record, reclen); /* update link pointers */ if (left->bt->hdr.bthLNode == left->nnum) { left->bt->hdr.bthLNode = right.nnum; left->bt->flags |= HFS_UPDATE_BTHDR; } if (right.nd.ndFLink) { node n; n.bt = right.bt; n.nnum = right.nd.ndFLink; if (bt_getnode(&n) < 0) return -1; n.nd.ndBLink = right.nnum; if (bt_putnode(&n) < 0) return -1; } return 0; }
/* * NAME: node->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; }