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