1 /*
2 * This file has been modified for the cdrkit suite.
3 *
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).
6 *
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.
10 *
11 */
12
13 /* @(#)volume.c 1.4 04/06/17 joerg */
14 /*
15 * hfsutils - tools for reading and writing Macintosh HFS volumes
16 * Copyright (C) 1996, 1997 Robert Leslie
17 *
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.
22 *
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.
27 *
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.
31 */
32
33 #include <mconfig.h>
34 #include <stdxlib.h>
35 #include <strdefs.h>
36 #include <errno.h>
37
38 #include "internal.h"
39 #include "data.h"
40 #include "low.h"
41 #include "btree.h"
42 #include "record.h"
43 #include "volume.h"
44
45 static void markexts(block *vbm, ExtDataRec *exts);
46
47
48 /*
49 * NAME: vol->catsearch()
50 * DESCRIPTION: search catalog tree
51 */
v_catsearch(hfsvol * vol,long parid,char * name,CatDataRec * data,char * cname,node * np)52 int v_catsearch(hfsvol *vol, long parid, char *name, CatDataRec *data,
53 char *cname, node *np)
54 {
55 CatKeyRec key;
56 unsigned char pkey[HFS_CATKEYLEN];
57 node n;
58 unsigned char *ptr;
59 int found;
60
61 if (np == 0)
62 np = &n;
63
64 r_makecatkey(&key, parid, name);
65 r_packcatkey(&key, pkey, 0);
66
67 found = bt_search(&vol->cat, pkey, np);
68 if (found <= 0)
69 return found;
70
71 ptr = HFS_NODEREC(*np, np->rnum);
72
73 if (cname)
74 {
75 r_unpackcatkey(ptr, &key);
76 strcpy(cname, key.ckrCName);
77 }
78
79 if (data)
80 r_unpackcatdata(HFS_RECDATA(ptr), data);
81
82 return 1;
83 }
84
85 /*
86 * NAME: vol->extsearch()
87 * DESCRIPTION: search extents tree
88 */
v_extsearch(hfsfile * file,unsigned int fabn,ExtDataRec * data,node * np)89 int v_extsearch(hfsfile *file, unsigned int fabn, ExtDataRec *data, node *np)
90 {
91 ExtKeyRec key;
92 ExtDataRec extsave;
93 unsigned int fabnsave;
94 unsigned char pkey[HFS_EXTKEYLEN];
95 node n;
96 unsigned char *ptr;
97 int found;
98
99 if (np == 0)
100 np = &n;
101
102 r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
103 r_packextkey(&key, pkey, 0);
104
105 /* in case bt_search() clobbers these */
106
107 memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
108 fabnsave = file->fabn;
109
110 found = bt_search(&file->vol->ext, pkey, np);
111
112 memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
113 file->fabn = fabnsave;
114
115 if (found <= 0)
116 return found;
117
118 if (data)
119 {
120 ptr = HFS_NODEREC(*np, np->rnum);
121 r_unpackextdata(HFS_RECDATA(ptr), data);
122 }
123
124 return 1;
125 }
126
127 /*
128 * NAME: vol->getthread()
129 * DESCRIPTION: retrieve catalog thread information for a file or directory
130 */
v_getthread(hfsvol * vol,long id,CatDataRec * thread,node * np,int type)131 int v_getthread(hfsvol *vol, long id, CatDataRec *thread, node *np, int type)
132 {
133 CatDataRec rec;
134 int found;
135
136 if (thread == 0)
137 thread = &rec;
138
139 found = v_catsearch(vol, id, "", thread, 0, np);
140 if (found <= 0)
141 return found;
142
143 if (thread->cdrType != type)
144 {
145 ERROR(EIO, "bad thread record");
146 return -1;
147 }
148
149 return 1;
150 }
151
152 /*
153 * NAME: vol->putcatrec()
154 * DESCRIPTION: store catalog information
155 */
v_putcatrec(CatDataRec * data,node * np)156 int v_putcatrec(CatDataRec *data, node *np)
157 {
158 unsigned char pdata[HFS_CATDATALEN], *ptr;
159 int len = 0;
160
161 r_packcatdata(data, pdata, &len);
162
163 ptr = HFS_NODEREC(*np, np->rnum);
164 memcpy(HFS_RECDATA(ptr), pdata, len);
165
166 return bt_putnode(np);
167 }
168
169 /*
170 * NAME: vol->putextrec()
171 * DESCRIPTION: store extent information
172 */
v_putextrec(ExtDataRec * data,node * np)173 int v_putextrec(ExtDataRec *data, node *np)
174 {
175 unsigned char pdata[HFS_EXTDATALEN], *ptr;
176 int len = 0;
177
178 r_packextdata(data, pdata, &len);
179
180 ptr = HFS_NODEREC(*np, np->rnum);
181 memcpy(HFS_RECDATA(ptr), pdata, len);
182
183 return bt_putnode(np);
184 }
185
186 /*
187 * NAME: vol->allocblocks()
188 * DESCRIPTION: allocate a contiguous range of blocks
189 */
v_allocblocks(hfsvol * vol,ExtDescriptor * blocks)190 int v_allocblocks(hfsvol *vol, ExtDescriptor *blocks)
191 {
192 unsigned int request, found, foundat, start, end, pt;
193 block *vbm;
194 int wrap = 0;
195
196 if (vol->mdb.drFreeBks == 0)
197 {
198 ERROR(ENOSPC, "volume full");
199 return -1;
200 }
201
202 request = blocks->xdrNumABlks;
203 found = 0;
204 foundat = 0;
205 start = vol->mdb.drAllocPtr;
206 end = vol->mdb.drNmAlBlks;
207 pt = start;
208 vbm = vol->vbm;
209
210 if (request == 0)
211 abort();
212
213 for (;;)
214 {
215 unsigned int mark;
216
217 /* skip blocks in use */
218
219 while (pt < end && BMTST(vbm, pt))
220 ++pt;
221
222 if (wrap && pt >= start)
223 break;
224
225 /* count blocks not in use */
226
227 mark = pt;
228 while (pt < end && pt - mark < request && ! BMTST(vbm, pt))
229 ++pt;
230
231 if (pt - mark > found)
232 {
233 found = pt - mark;
234 foundat = mark;
235 }
236
237 if (pt == end)
238 pt = 0, wrap = 1;
239
240 if (found == request)
241 break;
242 }
243
244 if (found == 0 || found > vol->mdb.drFreeBks)
245 {
246 ERROR(EIO, "bad volume bitmap or free block count");
247 return -1;
248 }
249
250 blocks->xdrStABN = foundat;
251 blocks->xdrNumABlks = found;
252
253 vol->mdb.drAllocPtr = pt;
254 vol->mdb.drFreeBks -= found;
255
256 for (pt = foundat; pt < foundat + found; ++pt)
257 BMSET(vbm, pt);
258
259 vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM;
260
261 return 0;
262 }
263
264 /*
265 * NAME: vol->freeblocks()
266 * DESCRIPTION: deallocate a contiguous range of blocks
267 */
v_freeblocks(hfsvol * vol,ExtDescriptor * blocks)268 void v_freeblocks(hfsvol *vol, ExtDescriptor *blocks)
269 {
270 unsigned int start, len, pt;
271 block *vbm;
272
273 start = blocks->xdrStABN;
274 len = blocks->xdrNumABlks;
275 vbm = vol->vbm;
276
277 vol->mdb.drFreeBks += len;
278
279 for (pt = start; pt < start + len; ++pt)
280 BMCLR(vbm, pt);
281
282 vol->flags |= HFS_UPDATE_MDB | HFS_UPDATE_VBM;
283 }
284
285 /*
286 * NAME: vol->resolve()
287 * DESCRIPTION: translate a pathname; return catalog information
288 */
v_resolve(hfsvol ** vol,char * path,CatDataRec * data,long * parid,char * fname,node * np)289 int v_resolve(hfsvol **vol, char *path, CatDataRec *data, long *parid,
290 char *fname, node *np)
291 {
292 long dirid;
293 char name[HFS_MAX_FLEN + 1], *nptr;
294 int found;
295
296 if (*path == 0)
297 {
298 ERROR(ENOENT, "empty path");
299 return -1;
300 }
301
302 if (parid)
303 *parid = 0;
304
305 nptr = strchr(path, ':');
306
307 if (*path == ':' || nptr == 0)
308 {
309 dirid = (*vol)->cwd; /* relative path */
310
311 if (*path == ':')
312 ++path;
313
314 if (*path == 0)
315 {
316 found = v_getdthread(*vol, dirid, data, 0);
317 if (found <= 0)
318 return found;
319
320 if (parid)
321 *parid = data->u.dthd.thdParID;
322
323 return v_catsearch(*vol, data->u.dthd.thdParID,
324 data->u.dthd.thdCName, data, fname, np);
325 }
326 }
327 else
328 {
329 hfsvol *check;
330
331 dirid = HFS_CNID_ROOTPAR; /* absolute path */
332
333 if (nptr - path > HFS_MAX_VLEN)
334 {
335 ERROR(ENAMETOOLONG, 0);
336 return -1;
337 }
338
339 strncpy(name, path, nptr - path);
340 name[nptr - path] = 0;
341
342 for (check = hfs_mounts; check; check = check->next)
343 {
344 if (d_relstring(check->mdb.drVN, name) == 0)
345 {
346 *vol = check;
347 break;
348 }
349 }
350 }
351
352 for (;;)
353 {
354 while (*path == ':')
355 {
356 ++path;
357
358 found = v_getdthread(*vol, dirid, data, 0);
359 if (found <= 0)
360 return found;
361
362 dirid = data->u.dthd.thdParID;
363 }
364
365 if (*path == 0)
366 {
367 found = v_getdthread(*vol, dirid, data, 0);
368 if (found <= 0)
369 return found;
370
371 if (parid)
372 *parid = data->u.dthd.thdParID;
373
374 return v_catsearch(*vol, data->u.dthd.thdParID,
375 data->u.dthd.thdCName, data, fname, np);
376 }
377
378 nptr = name;
379 while (nptr < name + sizeof(name) - 1 && *path && *path != ':')
380 *nptr++ = *path++;
381
382 if (*path && *path != ':')
383 {
384 ERROR(ENAMETOOLONG, 0);
385 return -1;
386 }
387
388 *nptr = 0;
389 if (*path == ':')
390 ++path;
391
392 if (parid)
393 *parid = dirid;
394
395 found = v_catsearch(*vol, dirid, name, data, fname, np);
396 if (found < 0)
397 return -1;
398
399 if (found == 0)
400 {
401 if (*path && parid)
402 *parid = 0;
403
404 if (*path == 0 && fname)
405 strcpy(fname, name);
406
407 return 0;
408 }
409
410 switch (data->cdrType)
411 {
412 case cdrDirRec:
413 if (*path == 0)
414 return 1;
415
416 dirid = data->u.dir.dirDirID;
417 break;
418
419 case cdrFilRec:
420 if (*path == 0)
421 return 1;
422
423 ERROR(ENOTDIR, "invalid pathname");
424 return -1;
425
426 default:
427 ERROR(EIO, "unexpected catalog record");
428 return -1;
429 }
430 }
431 }
432
433 /*
434 * NAME: vol->destruct()
435 * DESCRIPTION: free memory consumed by a volume descriptor
436 */
v_destruct(hfsvol * vol)437 void v_destruct(hfsvol *vol)
438 {
439 FREE(vol->vbm);
440
441 FREE(vol->ext.map);
442 FREE(vol->cat.map);
443
444 FREE(vol);
445 }
446
447 /*
448 * NAME: vol->getvol()
449 * DESCRIPTION: validate a volume reference
450 */
v_getvol(hfsvol ** vol)451 int v_getvol(hfsvol **vol)
452 {
453 if (*vol == 0)
454 {
455 if (hfs_curvol == 0)
456 {
457 ERROR(EINVAL, "no volume is current");
458 return -1;
459 }
460
461 *vol = hfs_curvol;
462 }
463
464 return 0;
465 }
466
467 /*
468 * NAME: vol->flush()
469 * DESCRIPTION: flush all pending changes (B*-tree, MDB, VBM) to disk
470 */
v_flush(hfsvol * vol,int umounting)471 int v_flush(hfsvol *vol, int umounting)
472 {
473 if (! (vol->flags & HFS_READONLY))
474 {
475 if ((vol->ext.flags & HFS_UPDATE_BTHDR) &&
476 bt_writehdr(&vol->ext) < 0)
477 return -1;
478
479 if ((vol->cat.flags & HFS_UPDATE_BTHDR) &&
480 bt_writehdr(&vol->cat) < 0)
481 return -1;
482
483 if ((vol->flags & HFS_UPDATE_VBM) &&
484 l_writevbm(vol) < 0)
485 return -1;
486
487 if (umounting &&
488 ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED))
489 {
490 vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED;
491 vol->flags |= HFS_UPDATE_MDB;
492 }
493
494 if ((vol->flags & (HFS_UPDATE_MDB | HFS_UPDATE_ALTMDB)) &&
495 l_writemdb(vol) < 0)
496 return -1;
497 }
498
499 return 0;
500 }
501
502 /*
503 * NAME: vol->adjvalence()
504 * DESCRIPTION: update a volume's valence counts
505 */
v_adjvalence(hfsvol * vol,long parid,int isdir,int adj)506 int v_adjvalence(hfsvol *vol, long parid, int isdir, int adj)
507 {
508 node n;
509 CatDataRec data;
510
511 if (isdir)
512 vol->mdb.drDirCnt += adj;
513 else
514 vol->mdb.drFilCnt += adj;
515
516 vol->flags |= HFS_UPDATE_MDB;
517
518 if (parid == HFS_CNID_ROOTDIR)
519 {
520 if (isdir)
521 vol->mdb.drNmRtDirs += adj;
522 else
523 vol->mdb.drNmFls += adj;
524 }
525 else if (parid == HFS_CNID_ROOTPAR)
526 return 0;
527
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)
532 {
533 ERROR(EIO, "can't find parent directory");
534 return -1;
535 }
536
537 data.u.dir.dirVal += adj;
538 data.u.dir.dirMdDat = d_tomtime(time(0));
539
540 return v_putcatrec(&data, &n);
541 }
542
543 /*
544 * NAME: vol->newfolder()
545 * DESCRIPTION: create a new HFS folder
546 */
v_newfolder(hfsvol * vol,long parid,char * name)547 int v_newfolder(hfsvol *vol, long parid, char *name)
548 {
549 CatKeyRec key;
550 CatDataRec data;
551 long id;
552 unsigned char record[HFS_CATRECMAXLEN];
553 int i, reclen;
554
555 if (bt_space(&vol->cat, 2) < 0)
556 return -1;
557
558 id = vol->mdb.drNxtCNID++;
559 vol->flags |= HFS_UPDATE_MDB;
560
561 /* create directory record */
562
563 data.cdrType = cdrDirRec;
564 data.cdrResrv2 = 0;
565
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;
572
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;
577
578 r_makecatkey(&key, parid, name);
579 r_packcatkey(&key, record, &reclen);
580 r_packcatdata(&data, HFS_RECDATA(record), &reclen);
581
582 if (bt_insert(&vol->cat, record, reclen) < 0)
583 return -1;
584
585 /* create thread record */
586
587 data.cdrType = cdrThdRec;
588 data.cdrResrv2 = 0;
589
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);
594
595 r_makecatkey(&key, id, "");
596 r_packcatkey(&key, record, &reclen);
597 r_packcatdata(&data, HFS_RECDATA(record), &reclen);
598
599 if (bt_insert(&vol->cat, record, reclen) < 0 ||
600 v_adjvalence(vol, parid, 1, 1) < 0)
601 return -1;
602
603 return 0;
604 }
605
606 /*
607 * NAME: markexts()
608 * DESCRIPTION: set bits from an extent record in the volume bitmap
609 */
610 static
markexts(block * vbm,ExtDataRec * exts)611 void markexts(block *vbm, ExtDataRec *exts)
612 {
613 int i;
614 unsigned int start, len;
615
616 for (i = 0; i < 3; ++i)
617 {
618 for (start = (*exts)[i].xdrStABN,
619 len = (*exts)[i].xdrNumABlks; len--; ++start)
620 BMSET(vbm, start);
621 }
622 }
623
624 /*
625 * NAME: vol->scavenge()
626 * DESCRIPTION: safeguard blocks in the volume bitmap
627 */
v_scavenge(hfsvol * vol)628 int v_scavenge(hfsvol *vol)
629 {
630 block *vbm = vol->vbm;
631 node n;
632 unsigned int pt, blks;
633
634 if (vbm == 0)
635 return 0;
636
637 markexts(vbm, &vol->mdb.drXTExtRec);
638 markexts(vbm, &vol->mdb.drCTExtRec);
639
640 vol->flags |= HFS_UPDATE_VBM;
641
642 /* scavenge the extents overflow file */
643
644 n.bt = &vol->ext;
645 n.nnum = vol->ext.hdr.bthFNode;
646
647 if (n.nnum > 0)
648 {
649 if (bt_getnode(&n) < 0)
650 return -1;
651
652 n.rnum = 0;
653
654 for (;;)
655 {
656 ExtDataRec data;
657 unsigned char *ptr;
658
659 while (n.rnum >= (int)n.nd.ndNRecs)
660 {
661 n.nnum = n.nd.ndFLink;
662 if (n.nnum == 0)
663 break;
664
665 if (bt_getnode(&n) < 0)
666 return -1;
667
668 n.rnum = 0;
669 }
670
671 if (n.nnum == 0)
672 break;
673
674 ptr = HFS_NODEREC(n, n.rnum);
675 r_unpackextdata(HFS_RECDATA(ptr), &data);
676
677 markexts(vbm, &data);
678
679 ++n.rnum;
680 }
681 }
682
683 /* scavenge the catalog file */
684
685 n.bt = &vol->cat;
686 n.nnum = vol->cat.hdr.bthFNode;
687
688 if (n.nnum > 0)
689 {
690 if (bt_getnode(&n) < 0)
691 return -1;
692
693 n.rnum = 0;
694
695 for (;;)
696 {
697 CatDataRec data;
698 unsigned char *ptr;
699
700 while (n.rnum >= (int)n.nd.ndNRecs)
701 {
702 n.nnum = n.nd.ndFLink;
703 if (n.nnum == 0)
704 break;
705
706 if (bt_getnode(&n) < 0)
707 return -1;
708
709 n.rnum = 0;
710 }
711
712 if (n.nnum == 0)
713 break;
714
715 ptr = HFS_NODEREC(n, n.rnum);
716 r_unpackcatdata(HFS_RECDATA(ptr), &data);
717
718 if (data.cdrType == cdrFilRec)
719 {
720 markexts(vbm, &data.u.fil.filExtRec);
721 markexts(vbm, &data.u.fil.filRExtRec);
722 }
723
724 ++n.rnum;
725 }
726 }
727
728 for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; )
729 {
730 if (! BMTST(vbm, pt))
731 ++blks;
732 }
733
734 if (vol->mdb.drFreeBks != blks)
735 {
736 vol->mdb.drFreeBks = blks;
737 vol->flags |= HFS_UPDATE_MDB;
738 }
739
740 return 0;
741 }
742