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: volume.c,v 1.12 1998/11/02 22:09:10 rob Exp $
35 #include "libc/byteorder.h"
39 * DESCRIPTION: initialize volume structure
41 void v_init(hfsvol *vol, int flags)
43 btree *ext = &vol->ext;
44 btree *cat = &vol->cat;
47 vol->flags = flags & HFS_VOL_OPT_MASK;
59 f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow");
65 ext->keyunpack = (keyunpackfunc) r_unpackextkey;
66 ext->keycompare = (keycomparefunc) r_compareextkeys;
68 f_init(&cat->f, vol, HFS_CNID_CAT, "catalog");
74 cat->keyunpack = (keyunpackfunc) r_unpackcatkey;
75 cat->keycompare = (keycomparefunc) r_comparecatkeys;
77 vol->cwd = HFS_CNID_ROOTDIR;
89 * DESCRIPTION: open volume source and lock against concurrent updates
91 int v_open(hfsvol *vol, int os_fd )
93 if (vol->flags & HFS_VOL_OPEN)
94 ERROR(EINVAL, "volume already open");
96 vol->flags |= HFS_VOL_OPEN;
99 /* initialize volume block cache (OK to fail) */
101 if (! (vol->flags & HFS_OPT_NOCACHE) &&
103 vol->flags |= HFS_VOL_USINGCACHE;
113 * DESCRIPTION: close access path to volume source
115 int v_close(hfsvol *vol)
119 if (! (vol->flags & HFS_VOL_OPEN))
122 if ((vol->flags & HFS_VOL_USINGCACHE) &&
126 vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE);
128 /* free dynamically allocated structures */
147 * DESCRIPTION: return 1 iff path is same as open volume
149 int v_same(hfsvol *vol, int os_fd )
151 return vol->os_fd == os_fd;
155 * NAME: vol->geometry()
156 * DESCRIPTION: determine volume location and size (possibly in a partition)
158 int v_geometry(hfsvol *vol, int pnum)
161 unsigned long bnum = 0;
169 vol->vlen = b_size(vol);
178 found = m_findpmentry(vol, "Apple_HFS", &map, &bnum);
179 if (found == -1 || ! found)
183 vol->vstart = map.pmPyPartStart;
184 vol->vlen = map.pmPartBlkCnt;
188 if ((unsigned long) map.pmLgDataStart +
189 (unsigned long) map.pmDataCnt > vol->vlen)
190 ERROR(EINVAL, "partition data overflows partition");
192 vol->vstart += (unsigned long) map.pmLgDataStart;
193 vol->vlen = map.pmDataCnt;
197 ERROR(EINVAL, "volume partition is empty");
200 if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS))
201 ERROR(EINVAL, "volume is smaller than 800K");
210 * NAME: vol->readmdb()
211 * DESCRIPTION: load Master Directory Block into memory
213 int v_readmdb(hfsvol *vol)
215 if (l_getmdb(vol, &vol->mdb, 0) == -1)
218 if (vol->mdb.drSigWord != HFS_SIGWORD)
220 if (vol->mdb.drSigWord == HFS_SIGWORD_MFS)
221 ERROR(EINVAL, "MFS volume format not supported");
223 ERROR(EINVAL, "not a Macintosh HFS volume");
226 if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
227 ERROR(EINVAL, "bad volume allocation block size");
229 vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS;
231 /* extents pseudo-file structs */
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;
237 vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
238 vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
240 memcpy(&vol->ext.f.cat.u.fil.filExtRec,
241 &vol->mdb.drXTExtRec, sizeof(ExtDataRec));
243 f_selectfork(&vol->ext.f, fkData);
245 /* catalog pseudo-file structs */
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;
251 vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
252 vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
254 memcpy(&vol->cat.f.cat.u.fil.filExtRec,
255 &vol->mdb.drCTExtRec, sizeof(ExtDataRec));
257 f_selectfork(&vol->cat.f, fkData);
266 * NAME: vol->readvbm()
267 * DESCRIPTION: read volume bitmap into memory
269 int v_readvbm(hfsvol *vol)
271 unsigned int vbmst = vol->mdb.drVBMSt;
272 unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12;
275 ASSERT(vol->vbm == 0);
277 if (vol->mdb.drAlBlSt - vbmst < vbmsz)
278 ERROR(EIO, "volume bitmap collides with volume data");
280 vol->vbm = ALLOC(block, vbmsz);
281 if (vol->vbm == NULL)
286 for (bp = vol->vbm; vbmsz--; ++bp)
288 if (b_readlb(vol, vbmst++, bp) == -1)
305 * DESCRIPTION: load volume information into memory
307 int v_mount(hfsvol *vol)
309 /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */
311 if (v_readmdb(vol) == -1 ||
312 v_readvbm(vol) == -1 ||
313 bt_readhdr(&vol->ext) == -1 ||
314 bt_readhdr(&vol->cat) == -1)
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;
322 vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
324 vol->flags |= HFS_VOL_MOUNTED;
333 * NAME: vol->catsearch()
334 * DESCRIPTION: search catalog tree
336 int v_catsearch(hfsvol *vol, unsigned long parid, const char *name,
337 CatDataRec *data, char *cname, node *np)
340 byte pkey[HFS_CATKEYLEN];
348 r_makecatkey(&key, parid, name);
349 r_packcatkey(&key, pkey, NULL);
351 found = bt_search(&vol->cat, pkey, np);
355 ptr = HFS_NODEREC(*np, np->rnum);
359 r_unpackcatkey(ptr, &key);
360 strcpy(cname, key.ckrCName);
364 r_unpackcatdata(HFS_RECDATA(ptr), data);
370 * NAME: vol->extsearch()
371 * DESCRIPTION: search extents tree
373 int v_extsearch(hfsfile *file, unsigned int fabn,
374 ExtDataRec *data, node *np)
378 unsigned int fabnsave;
379 byte pkey[HFS_EXTKEYLEN];
387 r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
388 r_packextkey(&key, pkey, NULL);
390 /* in case bt_search() clobbers these */
392 memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
393 fabnsave = file->fabn;
395 found = bt_search(&file->vol->ext, pkey, np);
397 memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
398 file->fabn = fabnsave;
405 ptr = HFS_NODEREC(*np, np->rnum);
406 r_unpackextdata(HFS_RECDATA(ptr), data);
413 * NAME: vol->getthread()
414 * DESCRIPTION: retrieve catalog thread information for a file or directory
416 int v_getthread(hfsvol *vol, unsigned long id,
417 CatDataRec *thread, node *np, int type)
425 found = v_catsearch(vol, id, "", thread, NULL, np);
426 if (found == 1 && thread->cdrType != type)
427 ERROR(EIO, "bad thread record");
437 * NAME: vol->resolve()
438 * DESCRIPTION: translate a pathname; return catalog information
440 int v_resolve(hfsvol **vol, const char *path,
441 CatDataRec *data, unsigned long *parid, char *fname, node *np)
444 char name[HFS_MAX_FLEN + 1], *nptr;
448 ERROR(ENOENT, "empty path");
453 nptr = strchr(path, ':');
455 if (*path == ':' || nptr == NULL)
457 dirid = (*vol)->cwd; /* relative path */
464 found = v_getdthread(*vol, dirid, data, NULL);
471 *parid = data->u.dthd.thdParID;
473 found = v_catsearch(*vol, data->u.dthd.thdParID,
474 data->u.dthd.thdCName, data, fname, np);
486 dirid = HFS_CNID_ROOTPAR; /* absolute path */
488 if (nptr - path > HFS_MAX_VLEN)
489 ERROR(ENAMETOOLONG, NULL);
491 strncpy(name, path, nptr - path);
492 name[nptr - path] = 0;
494 for (check = hfs_mounts; check; check = check->next)
496 if (d_relstring(check->mdb.drVN, name) == 0)
510 found = v_getdthread(*vol, dirid, data, NULL);
516 dirid = data->u.dthd.thdParID;
521 found = v_getdthread(*vol, dirid, data, NULL);
528 *parid = data->u.dthd.thdParID;
530 found = v_catsearch(*vol, data->u.dthd.thdParID,
531 data->u.dthd.thdCName, data, fname, np);
540 while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
543 if (*path && *path != ':')
544 ERROR(ENAMETOOLONG, NULL);
553 found = v_catsearch(*vol, dirid, name, data, fname, np);
562 if (*path == 0 && fname)
568 switch (data->cdrType)
574 dirid = data->u.dir.dirDirID;
581 ERROR(ENOTDIR, "invalid pathname");
584 ERROR(EIO, "unexpected catalog record");
595 /* Determine whether the volume is a HFS volume */
597 v_probe(int fd, long long offset)
601 mdb = (MDB*)malloc(2 * 512);
602 os_seek_offset( fd, 2 * 512 + offset );
603 os_read(fd, mdb, 2, 9);
605 if (__be16_to_cpu(mdb->drSigWord) != HFS_SIGWORD) {