2 * This file has been modified for the cdrkit suite.
4 * The behaviour and appearence of the program code below can differ to a major
5 * extent from the version distributed by the original author(s).
7 * For details, see Changelog file distributed with the cdrkit package. If you
8 * received this file from another source then ask the distributing person for
9 * a log of modifications.
13 /* @(#)volume.c 1.4 04/06/17 joerg */
15 * hfsutils - tools for reading and writing Macintosh HFS volumes
16 * Copyright (C) 1996, 1997 Robert Leslie
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
45 static void markexts(block *vbm, ExtDataRec *exts);
49 * NAME: vol->catsearch()
50 * DESCRIPTION: search catalog tree
52 int v_catsearch(hfsvol *vol, long parid, char *name, CatDataRec *data,
53 char *cname, node *np)
56 unsigned char pkey[HFS_CATKEYLEN];
64 r_makecatkey(&key, parid, name);
65 r_packcatkey(&key, pkey, 0);
67 found = bt_search(&vol->cat, pkey, np);
71 ptr = HFS_NODEREC(*np, np->rnum);
75 r_unpackcatkey(ptr, &key);
76 strcpy(cname, key.ckrCName);
80 r_unpackcatdata(HFS_RECDATA(ptr), data);
86 * NAME: vol->extsearch()
87 * DESCRIPTION: search extents tree
89 int v_extsearch(hfsfile *file, unsigned int fabn, ExtDataRec *data, node *np)
93 unsigned int fabnsave;
94 unsigned char pkey[HFS_EXTKEYLEN];
102 r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
103 r_packextkey(&key, pkey, 0);
105 /* in case bt_search() clobbers these */
107 memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
108 fabnsave = file->fabn;
110 found = bt_search(&file->vol->ext, pkey, np);
112 memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
113 file->fabn = fabnsave;
120 ptr = HFS_NODEREC(*np, np->rnum);
121 r_unpackextdata(HFS_RECDATA(ptr), data);
128 * NAME: vol->getthread()
129 * DESCRIPTION: retrieve catalog thread information for a file or directory
131 int v_getthread(hfsvol *vol, long id, CatDataRec *thread, node *np, int type)
139 found = v_catsearch(vol, id, "", thread, 0, np);
143 if (thread->cdrType != type)
145 ERROR(EIO, "bad thread record");
153 * NAME: vol->putcatrec()
154 * DESCRIPTION: store catalog information
156 int v_putcatrec(CatDataRec *data, node *np)
158 unsigned char pdata[HFS_CATDATALEN], *ptr;
161 r_packcatdata(data, pdata, &len);
163 ptr = HFS_NODEREC(*np, np->rnum);
164 memcpy(HFS_RECDATA(ptr), pdata, len);
166 return bt_putnode(np);
170 * NAME: vol->putextrec()
171 * DESCRIPTION: store extent information
173 int v_putextrec(ExtDataRec *data, node *np)
175 unsigned char pdata[HFS_EXTDATALEN], *ptr;
178 r_packextdata(data, pdata, &len);
180 ptr = HFS_NODEREC(*np, np->rnum);
181 memcpy(HFS_RECDATA(ptr), pdata, len);
183 return bt_putnode(np);
187 * NAME: vol->allocblocks()
188 * DESCRIPTION: allocate a contiguous range of blocks
190 int v_allocblocks(hfsvol *vol, ExtDescriptor *blocks)
192 unsigned int request, found, foundat, start, end, pt;
196 if (vol->mdb.drFreeBks == 0)
198 ERROR(ENOSPC, "volume full");
202 request = blocks->xdrNumABlks;
205 start = vol->mdb.drAllocPtr;
206 end = vol->mdb.drNmAlBlks;
217 /* skip blocks in use */
219 while (pt < end && BMTST(vbm, pt))
222 if (wrap && pt >= start)
225 /* count blocks not in use */
228 while (pt < end && pt - mark < request && ! BMTST(vbm, pt))
231 if (pt - mark > found)
240 if (found == request)
244 if (found == 0 || found > vol->mdb.drFreeBks)
246 ERROR(EIO, "bad volume bitmap or free block count");
250 blocks->xdrStABN = foundat;
251 blocks->xdrNumABlks = found;
253 vol->mdb.drAllocPtr = pt;
254 vol->mdb.drFreeBks -= found;
256 for (pt = foundat; pt < foundat + found; ++pt)
259 vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM;
265 * NAME: vol->freeblocks()
266 * DESCRIPTION: deallocate a contiguous range of blocks
268 void v_freeblocks(hfsvol *vol, ExtDescriptor *blocks)
270 unsigned int start, len, pt;
273 start = blocks->xdrStABN;
274 len = blocks->xdrNumABlks;
277 vol->mdb.drFreeBks += len;
279 for (pt = start; pt < start + len; ++pt)
282 vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM;
286 * NAME: vol->resolve()
287 * DESCRIPTION: translate a pathname; return catalog information
289 int v_resolve(hfsvol **vol, char *path, CatDataRec *data, long *parid,
290 char *fname, node *np)
293 char name[HFS_MAX_FLEN + 1], *nptr;
298 ERROR(ENOENT, "empty path");
305 nptr = strchr(path, ':');
307 if (*path == ':' || nptr == 0)
309 dirid = (*vol)->cwd; /* relative path */
316 found = v_getdthread(*vol, dirid, data, 0);
321 *parid = data->u.dthd.thdParID;
323 return v_catsearch(*vol, data->u.dthd.thdParID,
324 data->u.dthd.thdCName, data, fname, np);
331 dirid = HFS_CNID_ROOTPAR; /* absolute path */
333 if (nptr - path > HFS_MAX_VLEN)
335 ERROR(ENAMETOOLONG, 0);
339 strncpy(name, path, nptr - path);
340 name[nptr - path] = 0;
342 for (check = hfs_mounts; check; check = check->next)
344 if (d_relstring(check->mdb.drVN, name) == 0)
358 found = v_getdthread(*vol, dirid, data, 0);
362 dirid = data->u.dthd.thdParID;
367 found = v_getdthread(*vol, dirid, data, 0);
372 *parid = data->u.dthd.thdParID;
374 return v_catsearch(*vol, data->u.dthd.thdParID,
375 data->u.dthd.thdCName, data, fname, np);
379 while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
382 if (*path && *path != ':')
384 ERROR(ENAMETOOLONG, 0);
395 found = v_catsearch(*vol, dirid, name, data, fname, np);
404 if (*path == 0 && fname)
410 switch (data->cdrType)
416 dirid = data->u.dir.dirDirID;
423 ERROR(ENOTDIR, "invalid pathname");
427 ERROR(EIO, "unexpected catalog record");
434 * NAME: vol->destruct()
435 * DESCRIPTION: free memory consumed by a volume descriptor
437 void v_destruct(hfsvol *vol)
448 * NAME: vol->getvol()
449 * DESCRIPTION: validate a volume reference
451 int v_getvol(hfsvol **vol)
457 ERROR(EINVAL, "no volume is current");
469 * DESCRIPTION: flush all pending changes (B*-tree, MDB, VBM) to disk
471 int v_flush(hfsvol *vol, int umounting)
473 if (! (vol->flags & HFS_READONLY))
475 if ((vol->ext.flags & HFS_UPDATE_BTHDR) &&
476 bt_writehdr(&vol->ext) < 0)
479 if ((vol->cat.flags & HFS_UPDATE_BTHDR) &&
480 bt_writehdr(&vol->cat) < 0)
483 if ((vol->flags & HFS_UPDATE_VBM) &&
488 ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED))
490 vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED;
491 vol->flags |= HFS_UPDATE_MDB;
494 if ((vol->flags & (HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB)) &&
503 * NAME: vol->adjvalence()
504 * DESCRIPTION: update a volume's valence counts
506 int v_adjvalence(hfsvol *vol, long parid, int isdir, int adj)
512 vol->mdb.drDirCnt += adj;
514 vol->mdb.drFilCnt += adj;
516 vol->flags |= HFS_UPDATE_MDB;
518 if (parid == HFS_CNID_ROOTDIR)
521 vol->mdb.drNmRtDirs += adj;
523 vol->mdb.drNmFls += adj;
525 else if (parid == HFS_CNID_ROOTPAR)
528 if (v_getdthread(vol, parid, &data, 0) <= 0 ||
529 v_catsearch(vol, data.u.dthd.thdParID, data.u.dthd.thdCName,
530 &data, 0, &n) <= 0 ||
531 data.cdrType != cdrDirRec)
533 ERROR(EIO, "can't find parent directory");
537 data.u.dir.dirVal += adj;
538 data.u.dir.dirMdDat = d_tomtime(time(0));
540 return v_putcatrec(&data, &n);
544 * NAME: vol->newfolder()
545 * DESCRIPTION: create a new HFS folder
547 int v_newfolder(hfsvol *vol, long parid, char *name)
552 unsigned char record[HFS_CATRECMAXLEN];
555 if (bt_space(&vol->cat, 2) < 0)
558 id = vol->mdb.drNxtCNID++;
559 vol->flags |= HFS_UPDATE_MDB;
561 /* create directory record */
563 data.cdrType = cdrDirRec;
566 data.u.dir.dirFlags = 0;
567 data.u.dir.dirVal = 0;
568 data.u.dir.dirDirID = id;
569 data.u.dir.dirCrDat = d_tomtime(time(0));
570 data.u.dir.dirMdDat = data.u.dir.dirCrDat;
571 data.u.dir.dirBkDat = 0;
573 memset(&data.u.dir.dirUsrInfo, 0, sizeof(data.u.dir.dirUsrInfo));
574 memset(&data.u.dir.dirFndrInfo, 0, sizeof(data.u.dir.dirFndrInfo));
575 for (i = 0; i < 4; ++i)
576 data.u.dir.dirResrv[i] = 0;
578 r_makecatkey(&key, parid, name);
579 r_packcatkey(&key, record, &reclen);
580 r_packcatdata(&data, HFS_RECDATA(record), &reclen);
582 if (bt_insert(&vol->cat, record, reclen) < 0)
585 /* create thread record */
587 data.cdrType = cdrThdRec;
590 data.u.dthd.thdResrv[0] = 0;
591 data.u.dthd.thdResrv[1] = 0;
592 data.u.dthd.thdParID = parid;
593 strcpy(data.u.dthd.thdCName, name);
595 r_makecatkey(&key, id, "");
596 r_packcatkey(&key, record, &reclen);
597 r_packcatdata(&data, HFS_RECDATA(record), &reclen);
599 if (bt_insert(&vol->cat, record, reclen) < 0 ||
600 v_adjvalence(vol, parid, 1, 1) < 0)
608 * DESCRIPTION: set bits from an extent record in the volume bitmap
611 void markexts(block *vbm, ExtDataRec *exts)
614 unsigned int start, len;
616 for (i = 0; i < 3; ++i)
618 for (start = (*exts)[i].xdrStABN,
619 len = (*exts)[i].xdrNumABlks; len--; ++start)
625 * NAME: vol->scavenge()
626 * DESCRIPTION: safeguard blocks in the volume bitmap
628 int v_scavenge(hfsvol *vol)
630 block *vbm = vol->vbm;
632 unsigned int pt, blks;
637 markexts(vbm, &vol->mdb.drXTExtRec);
638 markexts(vbm, &vol->mdb.drCTExtRec);
640 vol->flags |= HFS_UPDATE_VBM;
642 /* scavenge the extents overflow file */
645 n.nnum = vol->ext.hdr.bthFNode;
649 if (bt_getnode(&n) < 0)
659 while (n.rnum >= (int)n.nd.ndNRecs)
661 n.nnum = n.nd.ndFLink;
665 if (bt_getnode(&n) < 0)
674 ptr = HFS_NODEREC(n, n.rnum);
675 r_unpackextdata(HFS_RECDATA(ptr), &data);
677 markexts(vbm, &data);
683 /* scavenge the catalog file */
686 n.nnum = vol->cat.hdr.bthFNode;
690 if (bt_getnode(&n) < 0)
700 while (n.rnum >= (int)n.nd.ndNRecs)
702 n.nnum = n.nd.ndFLink;
706 if (bt_getnode(&n) < 0)
715 ptr = HFS_NODEREC(n, n.rnum);
716 r_unpackcatdata(HFS_RECDATA(ptr), &data);
718 if (data.cdrType == cdrFilRec)
720 markexts(vbm, &data.u.fil.filExtRec);
721 markexts(vbm, &data.u.fil.filRExtRec);
728 for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; )
730 if (! BMTST(vbm, pt))
734 if (vol->mdb.drFreeBks != blks)
736 vol->mdb.drFreeBks = blks;
737 vol->flags |= HFS_UPDATE_MDB;