/* * NAME: hfs->mkdir() * DESCRIPTION: create a new directory */ int hfs_mkdir(hfsvol *vol, const char *path) { CatDataRec data; unsigned long parid; char name[HFS_MAX_FLEN + 1]; int found; if (getvol(&vol) == -1) goto fail; found = v_resolve(&vol, path, &data, &parid, name, 0); if (found == -1 || parid == 0) goto fail; if (found) ERROR(EEXIST, 0); if (parid == HFS_CNID_ROOTPAR) ERROR(EINVAL, 0); if (vol->flags & HFS_VOL_READONLY) ERROR(EROFS, 0); return v_mkdir(vol, parid, name); fail: return -1; }
/* * NAME: hfs->isopen() * DESCRIPTION: check if a file is open * RETURNS: */ int hfs_isopen(hfsvol *vol, const char *path) { hfsfile *file = 0; hfsfile *f = 0; if (getvol(&vol) == -1) ERROR(ENODEV, 0); file = ALLOC(hfsfile, 1); if (file == 0) ERROR(ENOMEM, 0); if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0) ERROR(ENOENT, 0); if (file->cat.cdrType != cdrFilRec) ERROR(EISDIR, 0); // see if it's already open ULongInt num = file->cat.u.fil.filFlNum; FREE(file); for(f = vol->files; f; f = f->next) { if (f->cat.u.fil.filFlNum == num) return 1; } return 0; fail: FREE(file); return -1; }
/* * NAME: hfs->opendir() * DESCRIPTION: prepare to read the contents of a directory */ hfsdir *hfs_opendir(hfsvol *vol, const char *path) { hfsdir *dir = 0; CatKeyRec key; CatDataRec data; byte pkey[HFS_CATKEYLEN]; if (getvol(&vol) == -1) goto fail; dir = ALLOC(hfsdir, 1); if (dir == 0) ERROR(ENOMEM, 0); dir->vol = vol; if (*path == 0) { #ifdef CP_NO_STATIC /* meta-directory containing root dirs from all mounted volumes */ dir->dirid = 0; dir->vptr = hfs_mounts; #else assert(0); #endif } else { if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0) goto fail; if (data.cdrType != cdrDirRec) ERROR(ENOTDIR, 0); dir->dirid = data.u.dir.dirDirID; dir->vptr = 0; r_makecatkey(&key, dir->dirid, ""); r_packcatkey(&key, pkey, 0); if (bt_search(&vol->cat, pkey, &dir->n) <= 0) goto fail; } dir->prev = 0; dir->next = vol->dirs; if (vol->dirs) vol->dirs->prev = dir; vol->dirs = dir; return dir; fail: FREE(dir); return 0; }
/* * NAME: hfs->rmdir() * DESCRIPTION: delete an empty directory */ int hfs_rmdir(hfsvol *vol, char *path) { CatKeyRec key; CatDataRec data; long parid; char name[HFS_MAX_FLEN + 1]; unsigned char pkey[HFS_CATKEYLEN]; if (v_getvol(&vol) < 0 || v_resolve(&vol, path, &data, &parid, name, 0) <= 0) return -1; if (data.cdrType != cdrDirRec) { ERROR(ENOTDIR, 0); return -1; } if (data.u.dir.dirVal != 0) { ERROR(ENOTEMPTY, 0); return -1; } if (parid == HFS_CNID_ROOTPAR) { ERROR(EINVAL, 0); return -1; } if (vol->flags & HFS_READONLY) { ERROR(EROFS, 0); return -1; } /* delete directory record */ r_makecatkey(&key, parid, name); r_packcatkey(&key, pkey, 0); if (bt_delete(&vol->cat, pkey) < 0) return -1; /* delete thread record */ r_makecatkey(&key, data.u.dir.dirDirID, (char *)""); r_packcatkey(&key, pkey, 0); if (bt_delete(&vol->cat, pkey) < 0 || v_adjvalence(vol, parid, 1, -1) < 0) return -1; return 0; }
/* * NAME: hfs->open() * DESCRIPTION: prepare a file for I/O */ hfsfile *hfs_open(hfsvol *vol, const char *path) { hfsfile *file = 0; hfsfile *f = 0; if (getvol(&vol) == -1) ERROR(ENODEV, 0); file = ALLOC(hfsfile, 1); if (file == 0) ERROR(ENOMEM, 0); if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0) ERROR(ENOENT, 0); if (file->cat.cdrType != cdrFilRec) ERROR(EISDIR, 0); // see if it's already open for(f = vol->files; f; f = f->next) { if (f->cat.u.fil.filFlNum == file->cat.u.fil.filFlNum) { // already open, increment refcount and return FREE(file); file = f; file->refs++; printf("REOPEN: file %s now has %d refs\n", path, file->refs); return file; } } /* package file handle for user */ file->refs = 1; file->vol = vol; file->flags = 0; f_selectfork(file, fkData); file->prev = 0; file->next = vol->files; if (vol->files) vol->files->prev = file; vol->files = file; return file; fail: FREE(file); return 0; }
/* * NAME: hfs->stat() * DESCRIPTION: return catalog information for an arbitrary path */ int hfs_stat(hfsvol *vol, char *path, hfsdirent *ent) { CatDataRec data; long parid; char name[HFS_MAX_FLEN + 1]; if (v_getvol(&vol) < 0 || v_resolve(&vol, path, &data, &parid, name, 0) <= 0) return -1; r_unpackdirent(parid, name, &data, ent); return 0; }
/* * NAME: hfs->rmdir() * DESCRIPTION: delete an empty directory */ int hfs_rmdir(hfsvol *vol, const char *path) { CatKeyRec key; CatDataRec data; unsigned long parid; char name[HFS_MAX_FLEN + 1]; byte pkey[HFS_CATKEYLEN]; if (getvol(&vol) == -1 || v_resolve(&vol, path, &data, &parid, name, 0) <= 0) goto fail; if (data.cdrType != cdrDirRec) ERROR(ENOTDIR, 0); if (data.u.dir.dirVal != 0) ERROR(ENOTEMPTY, 0); if (parid == HFS_CNID_ROOTPAR) ERROR(EINVAL, 0); if (vol->flags & HFS_VOL_READONLY) ERROR(EROFS, 0); /* delete directory record */ r_makecatkey(&key, parid, name); r_packcatkey(&key, pkey, 0); if (bt_delete(&vol->cat, pkey) == -1) goto fail; /* delete thread record */ r_makecatkey(&key, data.u.dir.dirDirID, ""); r_packcatkey(&key, pkey, 0); if (bt_delete(&vol->cat, pkey) == -1 || v_adjvalence(vol, parid, 1, -1) == -1) goto fail; return 0; fail: return -1; }
/* * NAME: hfs->stat() * DESCRIPTION: return catalog information for an arbitrary path */ int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent) { CatDataRec data; unsigned long parid; char name[HFS_MAX_FLEN + 1]; if (getvol(&vol) == -1 || v_resolve(&vol, path, &data, &parid, name, NULL) <= 0) goto fail; r_unpackdirent(parid, name, &data, ent); return 0; fail: return -1; }
/* * NAME: hfs->chdir() * DESCRIPTION: change current HFS directory */ int hfs_chdir(hfsvol *vol, const char *path) { CatDataRec data; if (getvol(&vol) == -1 || v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0) goto fail; if (data.cdrType != cdrDirRec) ERROR(ENOTDIR, NULL); vol->cwd = data.u.dir.dirDirID; return 0; fail: return -1; }
/* * NAME: hfs->chdir() * DESCRIPTION: change current HFS directory */ int hfs_chdir(hfsvol *vol, char *path) { CatDataRec data; if (v_getvol(&vol) < 0 || v_resolve(&vol, path, &data, 0, 0, 0) <= 0) return -1; if (data.cdrType != cdrDirRec) { ERROR(ENOTDIR, 0); return -1; } vol->cwd = data.u.dir.dirDirID; return 0; }
/* * NAME: hfs->setattr() * DESCRIPTION: change a file's attributes */ int hfs_setattr(hfsvol *vol, const char *path, const hfsdirent *ent) { CatDataRec data; node n; if (getvol(&vol) == -1 || v_resolve(&vol, path, &data, 0, 0, &n) <= 0) goto fail; if (vol->flags & HFS_VOL_READONLY) ERROR(EROFS, 0); r_packdirent(&data, ent); return v_putcatrec(&data, &n); fail: return -1; }
/* * NAME: hfs->open() * DESCRIPTION: prepare a file for I/O */ hfsfile *hfs_open(hfsvol *vol, char *path) { hfsfile *file; if (v_getvol(&vol) < 0) return 0; file = ALLOC(hfsfile, 1); if (file == 0) { ERROR(ENOMEM, 0); return 0; } if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0) { FREE(file); return 0; } if (file->cat.cdrType != cdrFilRec) { FREE(file); ERROR(EISDIR, 0); return 0; } file->vol = vol; file->clump = file->cat.u.fil.filClpSize; file->flags = 0; f_selectfork(file, 0); file->prev = 0; file->next = vol->files; if (vol->files) vol->files->prev = file; vol->files = file; return file; }
/* * NAME: hfs->setattr() * DESCRIPTION: change a file's attributes */ int hfs_setattr(hfsvol *vol, char *path, hfsdirent *ent) { CatDataRec data; node n; if (v_getvol(&vol) < 0 || v_resolve(&vol, path, &data, 0, 0, &n) <= 0) return -1; if (vol->flags & HFS_READONLY) { ERROR(EROFS, 0); return -1; } r_packdirent(&data, ent); if (v_putcatrec(&data, &n) < 0) return -1; return 0; }
/* * NAME: hfs->open() * DESCRIPTION: prepare a file for I/O */ hfsfile *hfs_open(hfsvol *vol, const char *path) { hfsfile *file = NULL; if (getvol(&vol) == -1) goto fail; file = ALLOC(hfsfile, 1); if (file == NULL) ERROR(ENOMEM, NULL); if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, NULL) <= 0) goto fail; if (file->cat.cdrType != cdrFilRec) ERROR(EISDIR, NULL); /* package file handle for user */ file->vol = vol; file->flags = 0; f_selectfork(file, fkData); file->prev = NULL; file->next = vol->files; if (vol->files) vol->files->prev = file; vol->files = file; return file; fail: FREE(file); return NULL; }
/* * NAME: hfs->mkdir() * DESCRIPTION: create a new directory */ int hfs_mkdir(hfsvol *vol, char *path) { CatDataRec data; long parid; char name[HFS_MAX_FLEN + 1]; int found; 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; } if (v_newfolder(vol, parid, name) < 0) return -1; return 0; }
/* * NAME: hfs->create() * DESCRIPTION: create and open a new file */ hfsfile *hfs_create(hfsvol *vol, const char *path, const char *type, const char *creator) { hfsfile *file = 0; unsigned long parid; char name[HFS_MAX_FLEN + 1]; CatKeyRec key; byte record[HFS_MAX_CATRECLEN]; unsigned reclen; int found; if (getvol(&vol) == -1) goto fail; file = ALLOC(hfsfile, 1); if (file == 0) ERROR(ENOMEM, 0); found = v_resolve(&vol, path, &file->cat, &parid, name, 0); if (found == -1 || parid == 0) goto fail; if (found) ERROR(EEXIST, 0); if (parid == HFS_CNID_ROOTPAR) ERROR(EINVAL, 0); if (vol->flags & HFS_VOL_READONLY) ERROR(EROFS, 0); /* create file `name' in parent `parid' */ if (bt_space(&vol->cat, 1) == -1) goto fail; f_init(file, vol, vol->mdb.drNxtCNID++, name); vol->flags |= HFS_VOL_UPDATE_MDB; file->parid = parid; /* create catalog record */ file->cat.u.fil.filUsrWds.fdType = d_getsl((const unsigned char *) type); file->cat.u.fil.filUsrWds.fdCreator = d_getsl((const unsigned char *) creator); file->cat.u.fil.filCrDat = d_mtime(time(0)); file->cat.u.fil.filMdDat = file->cat.u.fil.filCrDat; r_makecatkey(&key, file->parid, file->name); r_packcatrec(&key, &file->cat, record, &reclen); if (bt_insert(&vol->cat, record, reclen) == -1 || v_adjvalence(vol, file->parid, 0, 1) == -1) goto fail; /* package file handle for user */ file->next = vol->files; if (vol->files) vol->files->prev = file; vol->files = file; return file; fail: FREE(file); return 0; }
/* * 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; }
/* * NAME: hfs->delete() * DESCRIPTION: remove both forks of a file */ int hfs_delete(hfsvol *vol, const char *path) { hfsfile file; CatKeyRec key; byte pkey[HFS_CATKEYLEN]; int found; if (getvol(&vol) == -1 || v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0) goto fail; if (file.cat.cdrType != cdrFilRec) ERROR(EISDIR, 0); if (file.parid == HFS_CNID_ROOTPAR) ERROR(EINVAL, 0); if (vol->flags & HFS_VOL_READONLY) ERROR(EROFS, 0); /* free allocation blocks */ file.vol = vol; file.flags = 0; file.cat.u.fil.filLgLen = 0; file.cat.u.fil.filRLgLen = 0; f_selectfork(&file, fkData); if (f_trunc(&file) == -1) goto fail; f_selectfork(&file, fkRsrc); if (f_trunc(&file) == -1) goto fail; /* delete file record */ r_makecatkey(&key, file.parid, file.name); r_packcatkey(&key, pkey, 0); if (bt_delete(&vol->cat, pkey) == -1 || v_adjvalence(vol, file.parid, 0, -1) == -1) goto fail; /* delete file thread, if any */ found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0); if (found == -1) goto fail; if (found) { r_makecatkey(&key, file.cat.u.fil.filFlNum, ""); r_packcatkey(&key, pkey, 0); if (bt_delete(&vol->cat, pkey) == -1) goto fail; } return 0; fail: return -1; }
/* * 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; }
/* * NAME: hfs->delete() * DESCRIPTION: remove both forks of a file */ int hfs_delete(hfsvol *vol, char *path) { hfsfile file; CatKeyRec key; unsigned char pkey[HFS_CATKEYLEN]; int found; if (v_getvol(&vol) < 0 || v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0) return -1; if (file.cat.cdrType != cdrFilRec) { ERROR(EISDIR, 0); return -1; } if (file.parid == HFS_CNID_ROOTPAR) { ERROR(EINVAL, 0); return -1; } if (vol->flags & HFS_READONLY) { ERROR(EROFS, 0); return -1; } /* free disk blocks */ file.vol = vol; file.flags = 0; file.cat.u.fil.filLgLen = 0; file.cat.u.fil.filRLgLen = 0; f_selectfork(&file, 0); if (f_trunc(&file) < 0) return -1; f_selectfork(&file, 1); if (f_trunc(&file) < 0) return -1; /* delete file record */ r_makecatkey(&key, file.parid, file.name); r_packcatkey(&key, pkey, 0); if (bt_delete(&vol->cat, pkey) < 0 || v_adjvalence(vol, file.parid, 0, -1) < 0) return -1; /* delete file thread, if any */ found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0); if (found < 0) return -1; if (found) { r_makecatkey(&key, file.cat.u.fil.filFlNum, (char *)""); r_packcatkey(&key, pkey, 0); if (bt_delete(&vol->cat, pkey) < 0) return -1; } return 0; }
/* * NAME: hfs->opendir() * DESCRIPTION: prepare to read the contents of a directory */ hfsdir *hfs_opendir(hfsvol *vol, char *path) { hfsdir *dir; CatKeyRec key; CatDataRec data; unsigned char pkey[HFS_CATKEYLEN]; if (v_getvol(&vol) < 0) return 0; dir = ALLOC(hfsdir, 1); if (dir == 0) { ERROR(ENOMEM, 0); return 0; } dir->vol = vol; if (*path == 0) { /* meta-directory containing root dirs from all mounted volumes */ dir->dirid = 0; dir->vptr = hfs_mounts; } else { if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0) { FREE(dir); return 0; } if (data.cdrType != cdrDirRec) { FREE(dir); ERROR(ENOTDIR, 0); return 0; } dir->dirid = data.u.dir.dirDirID; dir->vptr = 0; r_makecatkey(&key, dir->dirid, (char *)""); r_packcatkey(&key, pkey, 0); if (bt_search(&vol->cat, pkey, &dir->n) <= 0) { FREE(dir); return 0; } } dir->prev = 0; dir->next = vol->dirs; if (vol->dirs) vol->dirs->prev = dir; vol->dirs = dir; return dir; }