|
|
1.1 root 1: /*
2: * libhfs - library for reading and writing Macintosh HFS volumes
3: * Copyright (C) 1996-1998 Robert Leslie
4: *
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.
9: *
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.
14: *
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,
18: * MA 02110-1301, USA.
19: *
20: * $Id: hfs.c,v 1.15 1998/11/02 22:09:00 rob Exp $
21: */
22:
23: #include "config.h"
24: #include "libhfs.h"
25: #include "data.h"
26: #include "block.h"
27: #include "medium.h"
28: #include "file.h"
29: #include "btree.h"
30: #include "node.h"
31: #include "record.h"
32: #include "volume.h"
33:
34: const char *hfs_error = "no error"; /* static error string */
35:
36: hfsvol *hfs_mounts; /* linked list of mounted volumes */
37:
38: static
39: hfsvol *curvol; /* current volume */
40:
41:
42: /*
43: * NAME: getvol()
44: * DESCRIPTION: validate a volume reference
45: */
46: static
47: int getvol(hfsvol **vol)
48: {
49: if (*vol == NULL)
50: {
51: if (curvol == NULL)
52: ERROR(EINVAL, "no volume is current");
53:
54: *vol = curvol;
55: }
56:
57: return 0;
58:
59: fail:
60: return -1;
61: }
62:
63: /* High-Level Volume Routines ============================================== */
64:
65: /*
66: * NAME: hfs->mount()
67: * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error)
68: */
69: hfsvol *hfs_mount( int os_fd, int pnum)
70: {
71: hfsvol *vol, *check;
72: int mode = HFS_MODE_RDONLY;
73:
74: /* see if the volume is already mounted */
75: for (check = hfs_mounts; check; check = check->next)
76: {
77: if (check->pnum == pnum && v_same(check, os_fd) == 1)
78: {
79: vol = check;
80: goto done;
81: }
82: }
83:
84: vol = ALLOC(hfsvol, 1);
85: if (vol == NULL)
86: ERROR(ENOMEM, NULL);
87:
88: v_init(vol, mode);
89:
90: vol->flags |= HFS_VOL_READONLY;
91: if( v_open(vol, os_fd) == -1 )
92: goto fail;
93:
94: /* mount the volume */
95:
96: if (v_geometry(vol, pnum) == -1 ||
97: v_mount(vol) == -1)
98: goto fail;
99:
100: /* add to linked list of volumes */
101:
102: vol->prev = NULL;
103: vol->next = hfs_mounts;
104:
105: if (hfs_mounts)
106: hfs_mounts->prev = vol;
107:
108: hfs_mounts = vol;
109:
110: done:
111: ++vol->refs;
112: curvol = vol;
113:
114: return vol;
115:
116: fail:
117: if (vol)
118: {
119: v_close(vol);
120: FREE(vol);
121: }
122:
123: return NULL;
124: }
125:
126:
127: /*
128: * NAME: hfs->umount()
129: * DESCRIPTION: close an HFS volume
130: */
131: int hfs_umount(hfsvol *vol)
132: {
133: int result = 0;
134:
135: if (getvol(&vol) == -1)
136: goto fail;
137:
138: if (--vol->refs)
139: {
140: goto done;
141: }
142:
143: /* close all open files and directories */
144:
145: while (vol->files)
146: {
147: if (hfs_close(vol->files) == -1)
148: result = -1;
149: }
150:
151: while (vol->dirs)
152: {
153: if (hfs_closedir(vol->dirs) == -1)
154: result = -1;
155: }
156:
157: /* close medium */
158:
159: if (v_close(vol) == -1)
160: result = -1;
161:
162: /* remove from linked list of volumes */
163:
164: if (vol->prev)
165: vol->prev->next = vol->next;
166: if (vol->next)
167: vol->next->prev = vol->prev;
168:
169: if (vol == hfs_mounts)
170: hfs_mounts = vol->next;
171: if (vol == curvol)
172: curvol = NULL;
173:
174: FREE(vol);
175:
176: done:
177: return result;
178:
179: fail:
180: return -1;
181: }
182:
183: /*
184: * NAME: hfs->umountall()
185: * DESCRIPTION: unmount all mounted volumes
186: */
187: void hfs_umountall(void)
188: {
189: while (hfs_mounts)
190: hfs_umount(hfs_mounts);
191: }
192:
193: /*
194: * NAME: hfs->getvol()
195: * DESCRIPTION: return a pointer to a mounted volume
196: */
197: hfsvol *hfs_getvol(const char *name)
198: {
199: hfsvol *vol;
200:
201: if (name == NULL)
202: return curvol;
203:
204: for (vol = hfs_mounts; vol; vol = vol->next)
205: {
206: if (d_relstring(name, vol->mdb.drVN) == 0)
207: return vol;
208: }
209:
210: return NULL;
211: }
212:
213: /*
214: * NAME: hfs->setvol()
215: * DESCRIPTION: change the current volume
216: */
217: void hfs_setvol(hfsvol *vol)
218: {
219: curvol = vol;
220: }
221:
222: /*
223: * NAME: hfs->vstat()
224: * DESCRIPTION: return volume statistics
225: */
226: int hfs_vstat(hfsvol *vol, hfsvolent *ent)
227: {
228: if (getvol(&vol) == -1)
229: goto fail;
230:
231: strcpy(ent->name, vol->mdb.drVN);
232:
233: ent->flags = (vol->flags & HFS_VOL_READONLY) ? HFS_ISLOCKED : 0;
234:
235: ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz;
236: ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz;
237:
238: ent->alblocksz = vol->mdb.drAlBlkSiz;
239: ent->clumpsz = vol->mdb.drClpSiz;
240:
241: ent->numfiles = vol->mdb.drFilCnt;
242: ent->numdirs = vol->mdb.drDirCnt;
243:
244: ent->crdate = d_ltime(vol->mdb.drCrDate);
245: ent->mddate = d_ltime(vol->mdb.drLsMod);
246: ent->bkdate = d_ltime(vol->mdb.drVolBkUp);
247:
248: ent->blessed = vol->mdb.drFndrInfo[0];
249:
250: return 0;
251:
252: fail:
253: return -1;
254: }
255:
256:
257: /* High-Level Directory Routines =========================================== */
258:
259: /*
260: * NAME: hfs->chdir()
261: * DESCRIPTION: change current HFS directory
262: */
263: int hfs_chdir(hfsvol *vol, const char *path)
264: {
265: CatDataRec data;
266:
267: if (getvol(&vol) == -1 ||
268: v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0)
269: goto fail;
270:
271: if (data.cdrType != cdrDirRec)
272: ERROR(ENOTDIR, NULL);
273:
274: vol->cwd = data.u.dir.dirDirID;
275:
276: return 0;
277:
278: fail:
279: return -1;
280: }
281:
282: /*
283: * NAME: hfs->getcwd()
284: * DESCRIPTION: return the current working directory ID
285: */
286: unsigned long hfs_getcwd(hfsvol *vol)
287: {
288: if (getvol(&vol) == -1)
289: return 0;
290:
291: return vol->cwd;
292: }
293:
294: /*
295: * NAME: hfs->setcwd()
296: * DESCRIPTION: set the current working directory ID
297: */
298: int hfs_setcwd(hfsvol *vol, unsigned long id)
299: {
300: if (getvol(&vol) == -1)
301: goto fail;
302:
303: if (id == vol->cwd)
304: goto done;
305:
306: /* make sure the directory exists */
307:
308: if (v_getdthread(vol, id, NULL, NULL) <= 0)
309: goto fail;
310:
311: vol->cwd = id;
312:
313: done:
314: return 0;
315:
316: fail:
317: return -1;
318: }
319:
320: /*
321: * NAME: hfs->dirinfo()
322: * DESCRIPTION: given a directory ID, return its (name and) parent ID
323: */
324: int hfs_dirinfo(hfsvol *vol, unsigned long *id, char *name)
325: {
326: CatDataRec thread;
327:
328: if (getvol(&vol) == -1 ||
329: v_getdthread(vol, *id, &thread, NULL) <= 0)
330: goto fail;
331:
332: *id = thread.u.dthd.thdParID;
333:
334: if (name)
335: strcpy(name, thread.u.dthd.thdCName);
336:
337: return 0;
338:
339: fail:
340: return -1;
341: }
342:
343: /*
344: * NAME: hfs->opendir()
345: * DESCRIPTION: prepare to read the contents of a directory
346: */
347: hfsdir *hfs_opendir(hfsvol *vol, const char *path)
348: {
349: hfsdir *dir = NULL;
350: CatKeyRec key;
351: CatDataRec data;
352: byte pkey[HFS_CATKEYLEN];
353:
354: if (getvol(&vol) == -1)
355: goto fail;
356:
357: dir = ALLOC(hfsdir, 1);
358: if (dir == NULL)
359: ERROR(ENOMEM, NULL);
360:
361: dir->vol = vol;
362:
363: if (*path == 0)
364: {
365: /* meta-directory containing root dirs from all mounted volumes */
366:
367: dir->dirid = 0;
368: dir->vptr = hfs_mounts;
369: }
370: else
371: {
372: if (v_resolve(&vol, path, &data, NULL, NULL, NULL) <= 0)
373: goto fail;
374:
375: if (data.cdrType != cdrDirRec)
376: ERROR(ENOTDIR, NULL);
377:
378: dir->dirid = data.u.dir.dirDirID;
379: dir->vptr = NULL;
380:
381: r_makecatkey(&key, dir->dirid, "");
382: r_packcatkey(&key, pkey, NULL);
383:
384: if (bt_search(&vol->cat, pkey, &dir->n) <= 0)
385: goto fail;
386: }
387:
388: dir->prev = NULL;
389: dir->next = vol->dirs;
390:
391: if (vol->dirs)
392: vol->dirs->prev = dir;
393:
394: vol->dirs = dir;
395:
396: return dir;
397:
398: fail:
399: FREE(dir);
400: return NULL;
401: }
402:
403: /*
404: * NAME: hfs->readdir()
405: * DESCRIPTION: return the next entry in the directory
406: */
407: int hfs_readdir(hfsdir *dir, hfsdirent *ent)
408: {
409: CatKeyRec key;
410: CatDataRec data;
411: const byte *ptr;
412:
413: if (dir->dirid == 0)
414: {
415: hfsvol *vol;
416: char cname[HFS_MAX_FLEN + 1];
417:
418: for (vol = hfs_mounts; vol; vol = vol->next)
419: {
420: if (vol == dir->vptr)
421: break;
422: }
423:
424: if (vol == NULL)
425: ERROR(ENOENT, "no more entries");
426:
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)
430: goto fail;
431:
432: r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent);
433:
434: dir->vptr = vol->next;
435:
436: goto done;
437: }
438:
439: if (dir->n.rnum == -1)
440: ERROR(ENOENT, "no more entries");
441:
442: while (1)
443: {
444: ++dir->n.rnum;
445:
446: while (dir->n.rnum >= dir->n.nd.ndNRecs)
447: {
448: if (dir->n.nd.ndFLink == 0)
449: {
450: dir->n.rnum = -1;
451: ERROR(ENOENT, "no more entries");
452: }
453:
454: if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1)
455: {
456: dir->n.rnum = -1;
457: goto fail;
458: }
459:
460: dir->n.rnum = 0;
461: }
462:
463: ptr = HFS_NODEREC(dir->n, dir->n.rnum);
464:
465: r_unpackcatkey(ptr, &key);
466:
467: if (key.ckrParID != dir->dirid)
468: {
469: dir->n.rnum = -1;
470: ERROR(ENOENT, "no more entries");
471: }
472:
473: r_unpackcatdata(HFS_RECDATA(ptr), &data);
474:
475: switch (data.cdrType)
476: {
477: case cdrDirRec:
478: case cdrFilRec:
479: r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent);
480: goto done;
481:
482: case cdrThdRec:
483: case cdrFThdRec:
484: break;
485:
486: default:
487: dir->n.rnum = -1;
488: ERROR(EIO, "unexpected directory entry found");
489: }
490: }
491:
492: done:
493: return 0;
494:
495: fail:
496: return -1;
497: }
498:
499: /*
500: * NAME: hfs->closedir()
501: * DESCRIPTION: stop reading a directory
502: */
503: int hfs_closedir(hfsdir *dir)
504: {
505: hfsvol *vol = dir->vol;
506:
507: if (dir->prev)
508: dir->prev->next = dir->next;
509: if (dir->next)
510: dir->next->prev = dir->prev;
511: if (dir == vol->dirs)
512: vol->dirs = dir->next;
513:
514: FREE(dir);
515:
516: return 0;
517: }
518:
519: /* High-Level File Routines ================================================ */
520:
521: /*
522: * NAME: hfs->open()
523: * DESCRIPTION: prepare a file for I/O
524: */
525: hfsfile *hfs_open(hfsvol *vol, const char *path)
526: {
527: hfsfile *file = NULL;
528:
529: if (getvol(&vol) == -1)
530: goto fail;
531:
532: file = ALLOC(hfsfile, 1);
533: if (file == NULL)
534: ERROR(ENOMEM, NULL);
535:
536: if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, NULL) <= 0)
537: goto fail;
538:
539: if (file->cat.cdrType != cdrFilRec)
540: ERROR(EISDIR, NULL);
541:
542: /* package file handle for user */
543:
544: file->vol = vol;
545: file->flags = 0;
546:
547: f_selectfork(file, fkData);
548:
549: file->prev = NULL;
550: file->next = vol->files;
551:
552: if (vol->files)
553: vol->files->prev = file;
554:
555: vol->files = file;
556:
557: return file;
558:
559: fail:
560: FREE(file);
561: return NULL;
562: }
563:
564: /*
565: * NAME: hfs->setfork()
566: * DESCRIPTION: select file fork for I/O operations
567: */
568: int hfs_setfork(hfsfile *file, int fork)
569: {
570: int result = 0;
571:
572: f_selectfork(file, fork ? fkRsrc : fkData);
573:
574: return result;
575: }
576:
577: /*
578: * NAME: hfs->getfork()
579: * DESCRIPTION: return the current fork for I/O operations
580: */
581: int hfs_getfork(hfsfile *file)
582: {
583: return file->fork != fkData;
584: }
585:
586: /*
587: * NAME: hfs->read()
588: * DESCRIPTION: read from an open file
589: */
590: unsigned long hfs_read(hfsfile *file, void *buf, unsigned long len)
591: {
592: unsigned long *lglen, count;
593: byte *ptr = buf;
594:
595: f_getptrs(file, NULL, &lglen, NULL);
596:
597: if (file->pos + len > *lglen)
598: len = *lglen - file->pos;
599:
600: count = len;
601: while (count)
602: {
603: unsigned long bnum, offs, chunk;
604:
605: bnum = file->pos >> HFS_BLOCKSZ_BITS;
606: offs = file->pos & (HFS_BLOCKSZ - 1);
607:
608: chunk = HFS_BLOCKSZ - offs;
609: if (chunk > count)
610: chunk = count;
611:
612: if (offs == 0 && chunk == HFS_BLOCKSZ)
613: {
614: if (f_getblock(file, bnum, (block *) ptr) == -1)
615: goto fail;
616: }
617: else
618: {
619: block b;
620:
621: if (f_getblock(file, bnum, &b) == -1)
622: goto fail;
623:
624: memcpy(ptr, b + offs, chunk);
625: }
626:
627: ptr += chunk;
628:
629: file->pos += chunk;
630: count -= chunk;
631: }
632:
633: return len;
634:
635: fail:
636: return -1;
637: }
638:
639: /*
640: * NAME: hfs->seek()
641: * DESCRIPTION: change file seek pointer
642: */
643: unsigned long hfs_seek(hfsfile *file, long offset, int from)
644: {
645: unsigned long *lglen, newpos;
646:
647: f_getptrs(file, NULL, &lglen, NULL);
648:
649: switch (from)
650: {
651: case HFS_SEEK_SET:
652: newpos = (offset < 0) ? 0 : offset;
653: break;
654:
655: case HFS_SEEK_CUR:
656: if (offset < 0 && (unsigned long) -offset > file->pos)
657: newpos = 0;
658: else
659: newpos = file->pos + offset;
660: break;
661:
662: case HFS_SEEK_END:
663: if (offset < 0 && (unsigned long) -offset > *lglen)
664: newpos = 0;
665: else
666: newpos = *lglen + offset;
667: break;
668:
669: default:
670: ERROR(EINVAL, NULL);
671: }
672:
673: if (newpos > *lglen)
674: newpos = *lglen;
675:
676: file->pos = newpos;
677:
678: return newpos;
679:
680: fail:
681: return -1;
682: }
683:
684: /*
685: * NAME: hfs->close()
686: * DESCRIPTION: close a file
687: */
688: int hfs_close(hfsfile *file)
689: {
690: hfsvol *vol = file->vol;
691: int result = 0;
692:
693: if (file->prev)
694: file->prev->next = file->next;
695: if (file->next)
696: file->next->prev = file->prev;
697: if (file == vol->files)
698: vol->files = file->next;
699:
700: FREE(file);
701:
702: return result;
703: }
704:
705: /* High-Level Catalog Routines ============================================= */
706:
707: /*
708: * NAME: hfs->stat()
709: * DESCRIPTION: return catalog information for an arbitrary path
710: */
711: int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent)
712: {
713: CatDataRec data;
714: unsigned long parid;
715: char name[HFS_MAX_FLEN + 1];
716:
717: if (getvol(&vol) == -1 ||
718: v_resolve(&vol, path, &data, &parid, name, NULL) <= 0)
719: goto fail;
720:
721: r_unpackdirent(parid, name, &data, ent);
722:
723: return 0;
724:
725: fail:
726: return -1;
727: }
728:
729: /*
730: * NAME: hfs->fstat()
731: * DESCRIPTION: return catalog information for an open file
732: */
733: int hfs_fstat(hfsfile *file, hfsdirent *ent)
734: {
735: r_unpackdirent(file->parid, file->name, &file->cat, ent);
736:
737: return 0;
738: }
739:
740: /*
741: * NAME: hfs->probe()
742: * DESCRIPTION: return whether a HFS filesystem is present at the given offset
743: */
744: int hfs_probe(int fd, long long offset)
745: {
746: return v_probe(fd, offset);
747: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.