2 * libhfs - library for reading and writing Macintosh HFS volumes
3 * Copyright (C) 1996-1998 Robert Leslie
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
20 * $Id: hfs.c,v 1.15 1998/11/02 22:09:00 rob Exp $
34 const char *hfs_error = "no error"; /* static error string */
36 hfsvol *hfs_mounts; /* linked list of mounted volumes */
39 hfsvol *curvol; /* current volume */
44 * DESCRIPTION: validate a volume reference
47 int getvol(hfsvol **vol)
52 ERROR(EINVAL, "no volume is current");
63 /* High-Level Volume Routines ============================================== */
67 * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error)
69 hfsvol *hfs_mount( int os_fd, int pnum)
72 int mode = HFS_MODE_RDONLY;
74 /* see if the volume is already mounted */
75 for (check = hfs_mounts; check; check = check->next)
77 if (check->pnum == pnum && v_same(check, os_fd) == 1)
84 vol = ALLOC(hfsvol, 1);
90 vol->flags |= HFS_VOL_READONLY;
91 if( v_open(vol, os_fd) == -1 )
94 /* mount the volume */
96 if (v_geometry(vol, pnum) == -1 ||
100 /* add to linked list of volumes */
103 vol->next = hfs_mounts;
106 hfs_mounts->prev = vol;
128 * NAME: hfs->umount()
129 * DESCRIPTION: close an HFS volume
131 int hfs_umount(hfsvol *vol)
135 if (getvol(&vol) == -1)
143 /* close all open files and directories */
147 if (hfs_close(vol->files) == -1)
153 if (hfs_closedir(vol->dirs) == -1)
159 if (v_close(vol) == -1)
162 /* remove from linked list of volumes */
165 vol->prev->next = vol->next;
167 vol->next->prev = vol->prev;
169 if (vol == hfs_mounts)
170 hfs_mounts = vol->next;
184 * NAME: hfs->umountall()
185 * DESCRIPTION: unmount all mounted volumes
187 void hfs_umountall(void)
190 hfs_umount(hfs_mounts);
194 * NAME: hfs->getvol()
195 * DESCRIPTION: return a pointer to a mounted volume
197 hfsvol *hfs_getvol(const char *name)
204 for (vol = hfs_mounts; vol; vol = vol->next)
206 if (d_relstring(name, vol->mdb.drVN) == 0)
214 * NAME: hfs->setvol()
215 * DESCRIPTION: change the current volume
217 void hfs_setvol(hfsvol *vol)
224 * DESCRIPTION: return volume statistics
226 int hfs_vstat(hfsvol *vol, hfsvolent *ent)
228 if (getvol(&vol) == -1)
231 strcpy(ent->name, vol->mdb.drVN);
233 ent->flags = (vol->flags & HFS_VOL_READONLY) ? HFS_ISLOCKED : 0;
235 ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz;
236 ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz;
238 ent->alblocksz = vol->mdb.drAlBlkSiz;
239 ent->clumpsz = vol->mdb.drClpSiz;
241 ent->numfiles = vol->mdb.drFilCnt;
242 ent->numdirs = vol->mdb.drDirCnt;
244 ent->crdate = d_ltime(vol->mdb.drCrDate);
245 ent->mddate = d_ltime(vol->mdb.drLsMod);
246 ent->bkdate = d_ltime(vol->mdb.drVolBkUp);
248 ent->blessed = vol->mdb.drFndrInfo[0];
257 /* High-Level Directory Routines =========================================== */
261 * DESCRIPTION: change current HFS directory
263 int hfs_chdir(hfsvol *vol, const char *path)
267 if (getvol(&vol) == -1 ||
268 v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0)
271 if (data.cdrType != cdrDirRec)
272 ERROR(ENOTDIR, NULL);
274 vol->cwd = data.u.dir.dirDirID;
283 * NAME: hfs->getcwd()
284 * DESCRIPTION: return the current working directory ID
286 unsigned long hfs_getcwd(hfsvol *vol)
288 if (getvol(&vol) == -1)
295 * NAME: hfs->setcwd()
296 * DESCRIPTION: set the current working directory ID
298 int hfs_setcwd(hfsvol *vol, unsigned long id)
300 if (getvol(&vol) == -1)
306 /* make sure the directory exists */
308 if (v_getdthread(vol, id, NULL, NULL) <= 0)
321 * NAME: hfs->dirinfo()
322 * DESCRIPTION: given a directory ID, return its (name and) parent ID
324 int hfs_dirinfo(hfsvol *vol, unsigned long *id, char *name)
328 if (getvol(&vol) == -1 ||
329 v_getdthread(vol, *id, &thread, NULL) <= 0)
332 *id = thread.u.dthd.thdParID;
335 strcpy(name, thread.u.dthd.thdCName);
344 * NAME: hfs->opendir()
345 * DESCRIPTION: prepare to read the contents of a directory
347 hfsdir *hfs_opendir(hfsvol *vol, const char *path)
352 byte pkey[HFS_CATKEYLEN];
354 if (getvol(&vol) == -1)
357 dir = ALLOC(hfsdir, 1);
365 /* meta-directory containing root dirs from all mounted volumes */
368 dir->vptr = hfs_mounts;
372 if (v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0)
375 if (data.cdrType != cdrDirRec)
376 ERROR(ENOTDIR, NULL);
378 dir->dirid = data.u.dir.dirDirID;
381 r_makecatkey(&key, dir->dirid, "");
382 r_packcatkey(&key, pkey, NULL);
384 if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
389 dir->next = vol->dirs;
392 vol->dirs->prev = dir;
404 * NAME: hfs->readdir()
405 * DESCRIPTION: return the next entry in the directory
407 int hfs_readdir(hfsdir *dir, hfsdirent *ent)
416 char cname[HFS_MAX_FLEN + 1];
418 for (vol = hfs_mounts; vol; vol = vol->next)
420 if (vol == dir->vptr)
425 ERROR(ENOENT, "no more entries");
427 if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, NULL) <= 0 ||
428 v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName,
429 &data, cname, NULL) <= 0)
432 r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
434 dir->vptr = vol->next;
439 if (dir->n.rnum == -1)
440 ERROR(ENOENT, "no more entries");
446 while (dir->n.rnum >= dir->n.nd.ndNRecs)
448 if (dir->n.nd.ndFLink == 0)
451 ERROR(ENOENT, "no more entries");
454 if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1)
463 ptr = HFS_NODEREC(dir->n, dir->n.rnum);
465 r_unpackcatkey(ptr, &key);
467 if (key.ckrParID != dir->dirid)
470 ERROR(ENOENT, "no more entries");
473 r_unpackcatdata(HFS_RECDATA(ptr), &data);
475 switch (data.cdrType)
479 r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
488 ERROR(EIO, "unexpected directory entry found");
500 * NAME: hfs->closedir()
501 * DESCRIPTION: stop reading a directory
503 int hfs_closedir(hfsdir *dir)
505 hfsvol *vol = dir->vol;
508 dir->prev->next = dir->next;
510 dir->next->prev = dir->prev;
511 if (dir == vol->dirs)
512 vol->dirs = dir->next;
519 /* High-Level File Routines ================================================ */
523 * DESCRIPTION: prepare a file for I/O
525 hfsfile *hfs_open(hfsvol *vol, const char *path)
527 hfsfile *file = NULL;
529 if (getvol(&vol) == -1)
532 file = ALLOC(hfsfile, 1);
536 if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, NULL) <= 0)
539 if (file->cat.cdrType != cdrFilRec)
542 /* package file handle for user */
547 f_selectfork(file, fkData);
550 file->next = vol->files;
553 vol->files->prev = file;
565 * NAME: hfs->setfork()
566 * DESCRIPTION: select file fork for I/O operations
568 int hfs_setfork(hfsfile *file, int fork)
572 f_selectfork(file, fork ? fkRsrc : fkData);
578 * NAME: hfs->getfork()
579 * DESCRIPTION: return the current fork for I/O operations
581 int hfs_getfork(hfsfile *file)
583 return file->fork != fkData;
588 * DESCRIPTION: read from an open file
590 unsigned long hfs_read(hfsfile *file, void *buf, unsigned long len)
592 unsigned long *lglen, count;
595 f_getptrs(file, NULL, &lglen, NULL);
597 if (file->pos + len > *lglen)
598 len = *lglen - file->pos;
603 unsigned long bnum, offs, chunk;
605 bnum = file->pos >> HFS_BLOCKSZ_BITS;
606 offs = file->pos & (HFS_BLOCKSZ - 1);
608 chunk = HFS_BLOCKSZ - offs;
612 if (offs == 0 && chunk == HFS_BLOCKSZ)
614 if (f_getblock(file, bnum, (block *) ptr) == -1)
621 if (f_getblock(file, bnum, &b) == -1)
624 memcpy(ptr, b + offs, chunk);
641 * DESCRIPTION: change file seek pointer
643 unsigned long hfs_seek(hfsfile *file, long offset, int from)
645 unsigned long *lglen, newpos;
647 f_getptrs(file, NULL, &lglen, NULL);
652 newpos = (offset < 0) ? 0 : offset;
656 if (offset < 0 && (unsigned long) -offset > file->pos)
659 newpos = file->pos + offset;
663 if (offset < 0 && (unsigned long) -offset > *lglen)
666 newpos = *lglen + offset;
686 * DESCRIPTION: close a file
688 int hfs_close(hfsfile *file)
690 hfsvol *vol = file->vol;
694 file->prev->next = file->next;
696 file->next->prev = file->prev;
697 if (file == vol->files)
698 vol->files = file->next;
705 /* High-Level Catalog Routines ============================================= */
709 * DESCRIPTION: return catalog information for an arbitrary path
711 int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent)
715 char name[HFS_MAX_FLEN + 1];
717 if (getvol(&vol) == -1 ||
718 v_resolve(&vol, path, &data, &parid, name, NULL) <= 0)
721 r_unpackdirent(parid, name, &data, ent);
731 * DESCRIPTION: return catalog information for an open file
733 int hfs_fstat(hfsfile *file, hfsdirent *ent)
735 r_unpackdirent(file->parid, file->name, &file->cat, ent);
742 * DESCRIPTION: return whether a HFS filesystem is present at the given offset
744 int hfs_probe(int fd, long long offset)
746 return v_probe(fd, offset);