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
);