|
|
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: volume.c,v 1.12 1998/11/02 22:09:10 rob Exp $
21: */
22:
23: #include "config.h"
24: #include "libhfs.h"
25: #include "volume.h"
26: #include "data.h"
27: #include "block.h"
28: #include "low.h"
29: #include "medium.h"
30: #include "file.h"
31: #include "btree.h"
32: #include "record.h"
33: #include "os.h"
34:
35: #include "libc/byteorder.h"
36:
37: /*
38: * NAME: vol->init()
39: * DESCRIPTION: initialize volume structure
40: */
41: void v_init(hfsvol *vol, int flags)
42: {
43: btree *ext = &vol->ext;
44: btree *cat = &vol->cat;
45:
46: vol->os_fd = 0;
47: vol->flags = flags & HFS_VOL_OPT_MASK;
48:
49: vol->pnum = -1;
50: vol->vstart = 0;
51: vol->vlen = 0;
52: vol->lpa = 0;
53:
54: vol->cache = NULL;
55:
56: vol->vbm = NULL;
57: vol->vbmsz = 0;
58:
59: f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow");
60:
61: ext->map = NULL;
62: ext->mapsz = 0;
63: ext->flags = 0;
64:
65: ext->keyunpack = (keyunpackfunc) r_unpackextkey;
66: ext->keycompare = (keycomparefunc) r_compareextkeys;
67:
68: f_init(&cat->f, vol, HFS_CNID_CAT, "catalog");
69:
70: cat->map = NULL;
71: cat->mapsz = 0;
72: cat->flags = 0;
73:
74: cat->keyunpack = (keyunpackfunc) r_unpackcatkey;
75: cat->keycompare = (keycomparefunc) r_comparecatkeys;
76:
77: vol->cwd = HFS_CNID_ROOTDIR;
78:
79: vol->refs = 0;
80: vol->files = NULL;
81: vol->dirs = NULL;
82:
83: vol->prev = NULL;
84: vol->next = NULL;
85: }
86:
87: /*
88: * NAME: vol->open()
89: * DESCRIPTION: open volume source and lock against concurrent updates
90: */
91: int v_open(hfsvol *vol, int os_fd )
92: {
93: if (vol->flags & HFS_VOL_OPEN)
94: ERROR(EINVAL, "volume already open");
95:
96: vol->flags |= HFS_VOL_OPEN;
97: vol->os_fd = os_fd;
98:
99: /* initialize volume block cache (OK to fail) */
100:
101: if (! (vol->flags & HFS_OPT_NOCACHE) &&
102: b_init(vol) != -1)
103: vol->flags |= HFS_VOL_USINGCACHE;
104:
105: return 0;
106:
107: fail:
108: return -1;
109: }
110:
111: /*
112: * NAME: vol->close()
113: * DESCRIPTION: close access path to volume source
114: */
115: int v_close(hfsvol *vol)
116: {
117: int result = 0;
118:
119: if (! (vol->flags & HFS_VOL_OPEN))
120: goto done;
121:
122: if ((vol->flags & HFS_VOL_USINGCACHE) &&
123: b_finish(vol) == -1)
124: result = -1;
125:
126: vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE);
127:
128: /* free dynamically allocated structures */
129:
130: FREE(vol->vbm);
131:
132: vol->vbm = NULL;
133: vol->vbmsz = 0;
134:
135: FREE(vol->ext.map);
136: FREE(vol->cat.map);
137:
138: vol->ext.map = NULL;
139: vol->cat.map = NULL;
140:
141: done:
142: return result;
143: }
144:
145: /*
146: * NAME: vol->same()
147: * DESCRIPTION: return 1 iff path is same as open volume
148: */
149: int v_same(hfsvol *vol, int os_fd )
150: {
151: return vol->os_fd == os_fd;
152: }
153:
154: /*
155: * NAME: vol->geometry()
156: * DESCRIPTION: determine volume location and size (possibly in a partition)
157: */
158: int v_geometry(hfsvol *vol, int pnum)
159: {
160: Partition map;
161: unsigned long bnum = 0;
162: int found;
163:
164: vol->pnum = pnum;
165:
166: if (pnum == 0)
167: {
168: vol->vstart = 0;
169: vol->vlen = b_size(vol);
170:
171: if (vol->vlen == 0)
172: goto fail;
173: }
174: else
175: {
176: while (pnum--)
177: {
178: found = m_findpmentry(vol, "Apple_HFS", &map, &bnum);
179: if (found == -1 || ! found)
180: goto fail;
181: }
182:
183: vol->vstart = map.pmPyPartStart;
184: vol->vlen = map.pmPartBlkCnt;
185:
186: if (map.pmDataCnt)
187: {
188: if ((unsigned long) map.pmLgDataStart +
189: (unsigned long) map.pmDataCnt > vol->vlen)
190: ERROR(EINVAL, "partition data overflows partition");
191:
192: vol->vstart += (unsigned long) map.pmLgDataStart;
193: vol->vlen = map.pmDataCnt;
194: }
195:
196: if (vol->vlen == 0)
197: ERROR(EINVAL, "volume partition is empty");
198: }
199:
200: if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS))
201: ERROR(EINVAL, "volume is smaller than 800K");
202:
203: return 0;
204:
205: fail:
206: return -1;
207: }
208:
209: /*
210: * NAME: vol->readmdb()
211: * DESCRIPTION: load Master Directory Block into memory
212: */
213: int v_readmdb(hfsvol *vol)
214: {
215: if (l_getmdb(vol, &vol->mdb, 0) == -1)
216: goto fail;
217:
218: if (vol->mdb.drSigWord != HFS_SIGWORD)
219: {
220: if (vol->mdb.drSigWord == HFS_SIGWORD_MFS)
221: ERROR(EINVAL, "MFS volume format not supported");
222: else
223: ERROR(EINVAL, "not a Macintosh HFS volume");
224: }
225:
226: if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
227: ERROR(EINVAL, "bad volume allocation block size");
228:
229: vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS;
230:
231: /* extents pseudo-file structs */
232:
233: vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN;
234: vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize;
235: vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize;
236:
237: vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
238: vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
239:
240: memcpy(&vol->ext.f.cat.u.fil.filExtRec,
241: &vol->mdb.drXTExtRec, sizeof(ExtDataRec));
242:
243: f_selectfork(&vol->ext.f, fkData);
244:
245: /* catalog pseudo-file structs */
246:
247: vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN;
248: vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize;
249: vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize;
250:
251: vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
252: vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
253:
254: memcpy(&vol->cat.f.cat.u.fil.filExtRec,
255: &vol->mdb.drCTExtRec, sizeof(ExtDataRec));
256:
257: f_selectfork(&vol->cat.f, fkData);
258:
259: return 0;
260:
261: fail:
262: return -1;
263: }
264:
265: /*
266: * NAME: vol->readvbm()
267: * DESCRIPTION: read volume bitmap into memory
268: */
269: int v_readvbm(hfsvol *vol)
270: {
271: unsigned int vbmst = vol->mdb.drVBMSt;
272: unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12;
273: block *bp;
274:
275: ASSERT(vol->vbm == 0);
276:
277: if (vol->mdb.drAlBlSt - vbmst < vbmsz)
278: ERROR(EIO, "volume bitmap collides with volume data");
279:
280: vol->vbm = ALLOC(block, vbmsz);
281: if (vol->vbm == NULL)
282: ERROR(ENOMEM, NULL);
283:
284: vol->vbmsz = vbmsz;
285:
286: for (bp = vol->vbm; vbmsz--; ++bp)
287: {
288: if (b_readlb(vol, vbmst++, bp) == -1)
289: goto fail;
290: }
291:
292: return 0;
293:
294: fail:
295: FREE(vol->vbm);
296:
297: vol->vbm = NULL;
298: vol->vbmsz = 0;
299:
300: return -1;
301: }
302:
303: /*
304: * NAME: vol->mount()
305: * DESCRIPTION: load volume information into memory
306: */
307: int v_mount(hfsvol *vol)
308: {
309: /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */
310:
311: if (v_readmdb(vol) == -1 ||
312: v_readvbm(vol) == -1 ||
313: bt_readhdr(&vol->ext) == -1 ||
314: bt_readhdr(&vol->cat) == -1)
315: goto fail;
316:
317: if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED)
318: vol->flags |= HFS_VOL_READONLY;
319: else if (vol->flags & HFS_VOL_READONLY)
320: vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
321: else
322: vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
323:
324: vol->flags |= HFS_VOL_MOUNTED;
325:
326: return 0;
327:
328: fail:
329: return -1;
330: }
331:
332: /*
333: * NAME: vol->catsearch()
334: * DESCRIPTION: search catalog tree
335: */
336: int v_catsearch(hfsvol *vol, unsigned long parid, const char *name,
337: CatDataRec *data, char *cname, node *np)
338: {
339: CatKeyRec key;
340: byte pkey[HFS_CATKEYLEN];
341: const byte *ptr;
342: node n;
343: int found;
344:
345: if (np == NULL)
346: np = &n;
347:
348: r_makecatkey(&key, parid, name);
349: r_packcatkey(&key, pkey, NULL);
350:
351: found = bt_search(&vol->cat, pkey, np);
352: if (found <= 0)
353: return found;
354:
355: ptr = HFS_NODEREC(*np, np->rnum);
356:
357: if (cname)
358: {
359: r_unpackcatkey(ptr, &key);
360: strcpy(cname, key.ckrCName);
361: }
362:
363: if (data)
364: r_unpackcatdata(HFS_RECDATA(ptr), data);
365:
366: return 1;
367: }
368:
369: /*
370: * NAME: vol->extsearch()
371: * DESCRIPTION: search extents tree
372: */
373: int v_extsearch(hfsfile *file, unsigned int fabn,
374: ExtDataRec *data, node *np)
375: {
376: ExtKeyRec key;
377: ExtDataRec extsave;
378: unsigned int fabnsave;
379: byte pkey[HFS_EXTKEYLEN];
380: const byte *ptr;
381: node n;
382: int found;
383:
384: if (np == NULL)
385: np = &n;
386:
387: r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
388: r_packextkey(&key, pkey, NULL);
389:
390: /* in case bt_search() clobbers these */
391:
392: memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
393: fabnsave = file->fabn;
394:
395: found = bt_search(&file->vol->ext, pkey, np);
396:
397: memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
398: file->fabn = fabnsave;
399:
400: if (found <= 0)
401: return found;
402:
403: if (data)
404: {
405: ptr = HFS_NODEREC(*np, np->rnum);
406: r_unpackextdata(HFS_RECDATA(ptr), data);
407: }
408:
409: return 1;
410: }
411:
412: /*
413: * NAME: vol->getthread()
414: * DESCRIPTION: retrieve catalog thread information for a file or directory
415: */
416: int v_getthread(hfsvol *vol, unsigned long id,
417: CatDataRec *thread, node *np, int type)
418: {
419: CatDataRec rec;
420: int found;
421:
422: if (thread == NULL)
423: thread = &rec;
424:
425: found = v_catsearch(vol, id, "", thread, NULL, np);
426: if (found == 1 && thread->cdrType != type)
427: ERROR(EIO, "bad thread record");
428:
429: return found;
430:
431: fail:
432: return -1;
433: }
434:
435:
436: /*
437: * NAME: vol->resolve()
438: * DESCRIPTION: translate a pathname; return catalog information
439: */
440: int v_resolve(hfsvol **vol, const char *path,
441: CatDataRec *data, unsigned long *parid, char *fname, node *np)
442: {
443: unsigned long dirid;
444: char name[HFS_MAX_FLEN + 1], *nptr;
445: int found = 0;
446:
447: if (*path == 0)
448: ERROR(ENOENT, "empty path");
449:
450: if (parid)
451: *parid = 0;
452:
453: nptr = strchr(path, ':');
454:
455: if (*path == ':' || nptr == NULL)
456: {
457: dirid = (*vol)->cwd; /* relative path */
458:
459: if (*path == ':')
460: ++path;
461:
462: if (*path == 0)
463: {
464: found = v_getdthread(*vol, dirid, data, NULL);
465: if (found == -1)
466: goto fail;
467:
468: if (found)
469: {
470: if (parid)
471: *parid = data->u.dthd.thdParID;
472:
473: found = v_catsearch(*vol, data->u.dthd.thdParID,
474: data->u.dthd.thdCName, data, fname, np);
475: if (found == -1)
476: goto fail;
477: }
478:
479: goto done;
480: }
481: }
482: else
483: {
484: hfsvol *check;
485:
486: dirid = HFS_CNID_ROOTPAR; /* absolute path */
487:
488: if (nptr - path > HFS_MAX_VLEN)
489: ERROR(ENAMETOOLONG, NULL);
490:
491: strncpy(name, path, nptr - path);
492: name[nptr - path] = 0;
493:
494: for (check = hfs_mounts; check; check = check->next)
495: {
496: if (d_relstring(check->mdb.drVN, name) == 0)
497: {
498: *vol = check;
499: break;
500: }
501: }
502: }
503:
504: while (1)
505: {
506: while (*path == ':')
507: {
508: ++path;
509:
510: found = v_getdthread(*vol, dirid, data, NULL);
511: if (found == -1)
512: goto fail;
513: else if (! found)
514: goto done;
515:
516: dirid = data->u.dthd.thdParID;
517: }
518:
519: if (*path == 0)
520: {
521: found = v_getdthread(*vol, dirid, data, NULL);
522: if (found == -1)
523: goto fail;
524:
525: if (found)
526: {
527: if (parid)
528: *parid = data->u.dthd.thdParID;
529:
530: found = v_catsearch(*vol, data->u.dthd.thdParID,
531: data->u.dthd.thdCName, data, fname, np);
532: if (found == -1)
533: goto fail;
534: }
535:
536: goto done;
537: }
538:
539: nptr = name;
540: while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
541: *nptr++ = *path++;
542:
543: if (*path && *path != ':')
544: ERROR(ENAMETOOLONG, NULL);
545:
546: *nptr = 0;
547: if (*path == ':')
548: ++path;
549:
550: if (parid)
551: *parid = dirid;
552:
553: found = v_catsearch(*vol, dirid, name, data, fname, np);
554: if (found == -1)
555: goto fail;
556:
557: if (! found)
558: {
559: if (*path && parid)
560: *parid = 0;
561:
562: if (*path == 0 && fname)
563: strcpy(fname, name);
564:
565: goto done;
566: }
567:
568: switch (data->cdrType)
569: {
570: case cdrDirRec:
571: if (*path == 0)
572: goto done;
573:
574: dirid = data->u.dir.dirDirID;
575: break;
576:
577: case cdrFilRec:
578: if (*path == 0)
579: goto done;
580:
581: ERROR(ENOTDIR, "invalid pathname");
582:
583: default:
584: ERROR(EIO, "unexpected catalog record");
585: }
586: }
587:
588: done:
589: return found;
590:
591: fail:
592: return -1;
593: }
594:
595: /* Determine whether the volume is a HFS volume */
596: int
597: v_probe(int fd, long long offset)
598: {
599: MDB *mdb;
600:
601: mdb = (MDB*)malloc(2 * 512);
602: os_seek_offset( fd, 2 * 512 + offset );
603: os_read(fd, mdb, 2, 9);
604:
605: if (__be16_to_cpu(mdb->drSigWord) != HFS_SIGWORD) {
606: free(mdb);
607: return 0;
608: }
609:
610: free(mdb);
611: return -1;
612: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.