/* * NAME: file->flush() * DESCRIPTION: flush all pending changes to an open file */ int f_flush(hfsfile *file) { hfsvol *vol = file->vol; if (vol->flags & HFS_VOL_READONLY) goto done; if (file->flags & HFS_FILE_UPDATE_CATREC) { node n; file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN; file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN; if (v_catsearch(vol, file->parid, file->name, 0, 0, &n) <= 0 || v_putcatrec(&file->cat, &n) == -1) goto fail; file->flags &= ~HFS_FILE_UPDATE_CATREC; } done: return 0; fail: 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 == 1 && thread->cdrType != type) ERROR(EIO, "bad thread record"); return found; fail: return -1; }
/* * 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; 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: file->flush() * DESCRIPTION: flush all pending changes to an open file */ int f_flush(hfsfile *file) { hfsvol *vol = file->vol; if (! (vol->flags & HFS_READONLY)) { if (file->flags & HFS_UPDATE_CATREC) { node n; file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN; file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN; file->cat.u.fil.filClpSize = file->clump; if (v_catsearch(file->vol, file->parid, file->name, 0, 0, &n) <= 0 || v_putcatrec(&file->cat, &n) < 0) return -1; file->flags &= ~HFS_UPDATE_CATREC; } } return 0; }
/* * 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) { 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: 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: hfs->rename() * DESCRIPTION: change the name of and/or move a file or directory */ int hfs_rename(hfsvol *vol, const char *srcpath, const char *dstpath) { hfsvol *srcvol; CatDataRec src, dst; unsigned long srcid, dstid; CatKeyRec key; char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1]; byte record[HFS_MAX_CATRECLEN]; unsigned int reclen; int found, isdir, moving; node n; if (getvol(&vol) == -1 || v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0) goto fail; isdir = (src.cdrType == cdrDirRec); srcvol = vol; found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0); if (found == -1) goto fail; if (vol != srcvol) ERROR(EINVAL, "can't move across volumes"); if (dstid == 0) ERROR(ENOENT, "bad destination path"); 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 == -1) goto fail; } moving = (srcid != dstid); if (found) { const char *ptr; ptr = strrchr(dstpath, ':'); if (ptr == 0) ptr = dstpath; else ++ptr; if (*ptr) strcpy(dstname, ptr); if (! moving && strcmp(srcname, dstname) == 0) goto done; /* source and destination are identical */ if (moving || d_relstring(srcname, dstname)) ERROR(EEXIST, "can't use destination name"); } /* can't move anything into the root directory's parent */ if (moving && dstid == HFS_CNID_ROOTPAR) ERROR(EINVAL, "can't move above root directory"); if (moving && isdir) { unsigned long id; /* can't move root directory anywhere */ if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR) ERROR(EINVAL, "can't move root directory"); /* 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"); if (v_getdthread(vol, id, &dst, 0) <= 0) goto fail; } } if (vol->flags & HFS_VOL_READONLY) ERROR(EROFS, 0); /* change volume name */ if (dstid == HFS_CNID_ROOTPAR) { if (! validvname(dstname)) goto fail; strcpy(vol->mdb.drVN, dstname); vol->flags |= HFS_VOL_UPDATE_MDB; } /* remove source record */ r_makecatkey(&key, srcid, srcname); r_packcatkey(&key, record, 0); if (bt_delete(&vol->cat, record) == -1) goto fail; /* insert destination record */ r_makecatkey(&key, dstid, dstname); r_packcatrec(&key, &src, record, &reclen); if (bt_insert(&vol->cat, record, reclen) == -1) goto fail; /* update thread record */ if (isdir) { if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0) goto fail; dst.u.dthd.thdParID = dstid; strcpy(dst.u.dthd.thdCName, dstname); if (v_putcatrec(&dst, &n) == -1) goto fail; } else { found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n); if (found == -1) goto fail; if (found) { dst.u.fthd.fthdParID = dstid; strcpy(dst.u.fthd.fthdCName, dstname); if (v_putcatrec(&dst, &n) == -1) goto fail; } } /* update directory valences */ if (moving) { if (v_adjvalence(vol, srcid, isdir, -1) == -1 || v_adjvalence(vol, dstid, isdir, 1) == -1) goto fail; } done: return 0; fail: return -1; }