/* * 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: 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: 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: node->index() * DESCRIPTION: create an index record from a key and node pointer */ void n_index(btree *bt, unsigned char *key, unsigned long nnum, unsigned char *record, int *reclen) { if (bt == &bt->f.vol->cat) { /* force the key length to be 0x25 */ HFS_RECKEYLEN(record) = 0x25; memset(record + 1, 0, 0x25); memcpy(record + 1, key + 1, HFS_RECKEYLEN(key)); } else memcpy(record, key, HFS_RECKEYSKIP(key)); d_putl(HFS_RECDATA(record), nnum); if (reclen) *reclen = HFS_RECKEYSKIP(record) + 4; }
/* * 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: 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: 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; #ifdef UHFS data.u.dir.dirCrDat = 0; #else data.u.dir.dirCrDat = d_tomtime(time(0)); #endif 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: record->packextrec() * DESCRIPTION: create a packed extents record */ void r_packextrec(const ExtKeyRec *key, const ExtDataRec *data, byte *precord, int *len) { r_packextkey(key, precord, len); r_packextdata(data, HFS_RECDATA(precord), len); }
/* * NAME: file->alloc() * DESCRIPTION: reserve disk blocks for a file */ int f_alloc(hfsfile *file) { hfsvol *vol = file->vol; ExtDescriptor blocks; ExtDataRec *extrec; unsigned long *pylen, clumpsz; unsigned int start, end; node n; int i; clumpsz = file->clump; if (clumpsz == 0) clumpsz = vol->mdb.drClpSiz; blocks.xdrNumABlks = clumpsz / vol->mdb.drAlBlkSiz; if (v_allocblocks(vol, &blocks) < 0) return -1; /* update the file's extents */ f_getptrs(file, 0, &pylen, &extrec); start = file->fabn; end = *pylen / vol->mdb.drAlBlkSiz; 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 (start > end) { v_freeblocks(vol, &blocks); ERROR(EIO, "file extents exceed file physical length"); return -1; } else if (num == 0) { v_freeblocks(vol, &blocks); ERROR(EIO, "empty file extent"); return -1; } } if (start == end) break; if (v_extsearch(file, start, &file->ext, &n) <= 0) { v_freeblocks(vol, &blocks); return -1; } file->fabn = start; } if (i >= 0 && file->ext[i].xdrStABN + file->ext[i].xdrNumABlks == blocks.xdrStABN) file->ext[i].xdrNumABlks += blocks.xdrNumABlks; else { /* create a new extent descriptor */ if (++i < 3) file->ext[i] = blocks; else { ExtKeyRec key; unsigned char record[HFS_EXTRECMAXLEN]; int reclen; /* record is full; create a new one */ file->ext[0] = blocks; for (i = 1; i < 3; ++i) { file->ext[i].xdrStABN = 0; file->ext[i].xdrNumABlks = 0; } file->fabn = start; r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, end); r_packextkey(&key, record, &reclen); r_packextdata(&file->ext, HFS_RECDATA(record), &reclen); if (bt_insert(&vol->ext, record, reclen) < 0) { v_freeblocks(vol, &blocks); return -1; } i = -1; } } if (i >= 0) { /* store the modified extent record */ if (file->fabn) { if ((n.nnum == 0 && v_extsearch(file, file->fabn, 0, &n) <= 0) || v_putextrec(&file->ext, &n) < 0) { v_freeblocks(vol, &blocks); return -1; } } else memcpy(extrec, file->ext, sizeof(ExtDataRec)); } *pylen += blocks.xdrNumABlks * vol->mdb.drAlBlkSiz; file->flags |= HFS_UPDATE_CATREC; return blocks.xdrNumABlks; }
/* * 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; }
/* * 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; }