/[pearpc]/src/io/prom/fs/hfs/volume.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /src/io/prom/fs/hfs/volume.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations)
Wed Sep 5 17:11:21 2007 UTC (16 years, 7 months ago) by dpavlin
File MIME type: text/plain
File size: 23744 byte(s)
import upstream CVS
1 /*
2 * libhfs - library for reading and writing Macintosh HFS volumes
3 * Copyright (C) 1996-1998 Robert Leslie
4 * Modified for use with PearPC (c) 2004 Stefan Weyergraf <stefan@weyergraf.de>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 */
21
22 # ifdef HAVE_CONFIG_H
23 # include "config.h"
24 # endif
25
26 # include <stdlib.h>
27 # include <string.h>
28 # include <time.h>
29 # include <errno.h>
30
31 # include "libhfs.h"
32 # include "volume.h"
33 # include "data.h"
34 # include "block.h"
35 # include "low.h"
36 # include "medium.h"
37 # include "file.h"
38 # include "btree.h"
39 # include "record.h"
40 # include "os.h"
41
42 /*
43 * NAME: vol->init()
44 * DESCRIPTION: initialize volume structure
45 */
46 void v_init(hfsvol *vol, int flags)
47 {
48 btree *ext = &vol->ext;
49 btree *cat = &vol->cat;
50
51 vol->priv = 0;
52 vol->flags = flags & HFS_VOL_OPT_MASK;
53
54 vol->pnum = -1;
55 vol->vstart = 0;
56 vol->vlen = 0;
57 vol->lpa = 0;
58
59 vol->cache = 0;
60
61 vol->vbm = 0;
62 vol->vbmsz = 0;
63
64 f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow");
65
66 ext->map = 0;
67 ext->mapsz = 0;
68 ext->flags = 0;
69
70 ext->keyunpack = (keyunpackfunc) r_unpackextkey;
71 ext->keycompare = (keycomparefunc) r_compareextkeys;
72
73 f_init(&cat->f, vol, HFS_CNID_CAT, "catalog");
74
75 cat->map = 0;
76 cat->mapsz = 0;
77 cat->flags = 0;
78
79 cat->keyunpack = (keyunpackfunc) r_unpackcatkey;
80 cat->keycompare = (keycomparefunc) r_comparecatkeys;
81
82 vol->cwd = HFS_CNID_ROOTDIR;
83
84 vol->refs = 0;
85 vol->files = 0;
86 vol->dirs = 0;
87
88 vol->prev = 0;
89 vol->next = 0;
90 }
91
92 /*
93 * NAME: vol->open()
94 * DESCRIPTION: open volume source and lock against concurrent updates
95 */
96 int v_open(hfsvol *vol, const void *devicehandle, int mode)
97 {
98 if (vol->flags & HFS_VOL_OPEN)
99 ERROR(EINVAL, "volume already open");
100
101 if (hfs_os_open(&vol->priv, devicehandle, mode) == -1)
102 goto fail;
103
104 vol->flags |= HFS_VOL_OPEN;
105
106 /* initialize volume block cache (OK to fail) */
107
108 if (! (vol->flags & HFS_OPT_NOCACHE) &&
109 b_init(vol) != -1)
110 vol->flags |= HFS_VOL_USINGCACHE;
111
112 return 0;
113
114 fail:
115 return -1;
116 }
117
118 /*
119 * NAME: flushvol()
120 * DESCRIPTION: flush all pending changes (B*-tree, MDB, VBM) to volume
121 */
122 static
123 int flushvol(hfsvol *vol, int umount)
124 {
125 if (vol->flags & HFS_VOL_READONLY)
126 goto done;
127
128 if ((vol->ext.flags & HFS_BT_UPDATE_HDR) &&
129 bt_writehdr(&vol->ext) == -1)
130 goto fail;
131
132 if ((vol->cat.flags & HFS_BT_UPDATE_HDR) &&
133 bt_writehdr(&vol->cat) == -1)
134 goto fail;
135
136 if ((vol->flags & HFS_VOL_UPDATE_VBM) &&
137 v_writevbm(vol) == -1)
138 goto fail;
139
140 if (umount && ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED))
141 {
142 vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED;
143 vol->flags |= HFS_VOL_UPDATE_MDB;
144 }
145
146 if ((vol->flags & (HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB)) &&
147 v_writemdb(vol) == -1)
148 goto fail;
149
150 done:
151 return 0;
152
153 fail:
154 return -1;
155 }
156
157 /*
158 * NAME: vol->flush()
159 * DESCRIPTION: commit all pending changes to volume device
160 */
161 int v_flush(hfsvol *vol)
162 {
163 if (flushvol(vol, 0) == -1)
164 goto fail;
165
166 if ((vol->flags & HFS_VOL_USINGCACHE) &&
167 b_flush(vol) == -1)
168 goto fail;
169
170 return 0;
171
172 fail:
173 return -1;
174 }
175
176 /*
177 * NAME: vol->close()
178 * DESCRIPTION: close access path to volume source
179 */
180 int v_close(hfsvol *vol)
181 {
182 int result = 0;
183
184 if (! (vol->flags & HFS_VOL_OPEN))
185 goto done;
186
187 if ((vol->flags & HFS_VOL_MOUNTED) &&
188 flushvol(vol, 1) == -1)
189 result = -1;
190
191 if ((vol->flags & HFS_VOL_USINGCACHE) &&
192 b_finish(vol) == -1)
193 result = -1;
194
195 if (hfs_os_close(&vol->priv) == -1)
196 result = -1;
197
198 vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE);
199
200 /* free dynamically allocated structures */
201
202 FREE(vol->vbm);
203
204 vol->vbm = 0;
205 vol->vbmsz = 0;
206
207 FREE(vol->ext.map);
208 FREE(vol->cat.map);
209
210 vol->ext.map = 0;
211 vol->cat.map = 0;
212
213 done:
214 return result;
215 }
216
217 /*
218 * NAME: vol->same()
219 * DESCRIPTION: return 1 iff path is same as open volume
220 */
221 int v_same(hfsvol *vol, const char *path)
222 {
223 return hfs_os_same(&vol->priv, path);
224 }
225
226 /*
227 * NAME: vol->geometry()
228 * DESCRIPTION: determine volume location and size (possibly in a partition)
229 */
230 int v_geometry(hfsvol *vol, int pnum)
231 {
232 ApplePartition map;
233 unsigned long bnum = 0;
234 int found;
235
236 vol->pnum = pnum;
237
238 if (pnum == 0)
239 {
240 vol->vstart = 0;
241 vol->vlen = b_size(vol);
242
243 if (vol->vlen == 0)
244 goto fail;
245 }
246 else
247 {
248 /* while (pnum--)
249 {
250 found = m_findpmentry(vol, "Apple_HFS", &map, &bnum);
251 if ((found == -1) || !found) found = m_findpmentry(vol, "Apple_Bootstrap", &map, &bnum);
252 if ((found == -1 || !found) && !pnum)
253 goto fail;
254 }*/
255 bnum = pnum;
256 found = m_findpmentry(vol, "Apple_HFS", &map, &bnum);
257 bnum = pnum;
258 if ((found == -1) || !found) found = m_findpmentry(vol, "Apple_Bootstrap", &map, &bnum);
259 if (found == -1 || !found) goto fail;
260
261 vol->vstart = map.pmPyPartStart;
262 vol->vlen = map.pmPartBlkCnt;
263
264 if (map.pmDataCnt)
265 {
266 if ((unsigned long) map.pmLgDataStart +
267 (unsigned long) map.pmDataCnt > vol->vlen)
268 ERROR(EINVAL, "partition data overflows partition");
269
270 vol->vstart += (unsigned long) map.pmLgDataStart;
271 vol->vlen = map.pmDataCnt;
272 }
273
274 if (vol->vlen == 0)
275 ERROR(EINVAL, "volume partition is empty");
276 }
277
278 if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS))
279 ERROR(EINVAL, "volume is smaller than 800K");
280
281 return 0;
282
283 fail:
284 return -1;
285 }
286
287 /*
288 * NAME: vol->readmdb()
289 * DESCRIPTION: load Master Directory Block into memory
290 */
291 int v_readmdb(hfsvol *vol)
292 {
293 if (l_getmdb(vol, &vol->mdb, 0) == -1)
294 goto fail;
295
296 if (vol->mdb.drSigWord != HFS_SIGWORD)
297 {
298 if (vol->mdb.drSigWord == HFS_SIGWORD_MFS)
299 ERROR(EINVAL, "MFS volume format not supported");
300 else
301 ERROR(EINVAL, "not a Macintosh HFS volume");
302 }
303
304 if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0)
305 ERROR(EINVAL, "bad volume allocation block size");
306
307 vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS;
308
309 /* extents pseudo-file structs */
310
311 vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN;
312 vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize;
313 vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize;
314
315 vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
316 vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
317
318 memcpy(&vol->ext.f.cat.u.fil.filExtRec,
319 &vol->mdb.drXTExtRec, sizeof(ExtDataRec));
320
321 f_selectfork(&vol->ext.f, fkData);
322
323 /* catalog pseudo-file structs */
324
325 vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN;
326 vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize;
327 vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize;
328
329 vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate;
330 vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod;
331
332 memcpy(&vol->cat.f.cat.u.fil.filExtRec,
333 &vol->mdb.drCTExtRec, sizeof(ExtDataRec));
334
335 f_selectfork(&vol->cat.f, fkData);
336
337 return 0;
338
339 fail:
340 return -1;
341 }
342
343 /*
344 * NAME: vol->writemdb()
345 * DESCRIPTION: flush Master Directory Block to medium
346 */
347 int v_writemdb(hfsvol *vol)
348 {
349 vol->mdb.drLsMod = d_mtime(time(0));
350
351 vol->mdb.drXTFlSize = vol->ext.f.cat.u.fil.filPyLen;
352 memcpy(&vol->mdb.drXTExtRec,
353 &vol->ext.f.cat.u.fil.filExtRec, sizeof(ExtDataRec));
354
355 vol->mdb.drCTFlSize = vol->cat.f.cat.u.fil.filPyLen;
356 memcpy(&vol->mdb.drCTExtRec,
357 &vol->cat.f.cat.u.fil.filExtRec, sizeof(ExtDataRec));
358
359 if (l_putmdb(vol, &vol->mdb, vol->flags & HFS_VOL_UPDATE_ALTMDB) == -1)
360 goto fail;
361
362 vol->flags &= ~(HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB);
363
364 return 0;
365
366 fail:
367 return -1;
368 }
369
370 /*
371 * NAME: vol->readvbm()
372 * DESCRIPTION: read volume bitmap into memory
373 */
374 int v_readvbm(hfsvol *vol)
375 {
376 unsigned int vbmst = vol->mdb.drVBMSt;
377 unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12;
378 block *bp;
379
380 ASSERT(vol->vbm == 0);
381
382 if (vol->mdb.drAlBlSt - vbmst < vbmsz)
383 ERROR(EIO, "volume bitmap collides with volume data");
384
385 vol->vbm = ALLOC(block, vbmsz);
386 if (vol->vbm == 0)
387 ERROR(ENOMEM, 0);
388
389 vol->vbmsz = vbmsz;
390
391 for (bp = vol->vbm; vbmsz--; ++bp)
392 {
393 if (b_readlb(vol, vbmst++, bp) == -1)
394 goto fail;
395 }
396
397 return 0;
398
399 fail:
400 FREE(vol->vbm);
401
402 vol->vbm = 0;
403 vol->vbmsz = 0;
404
405 return -1;
406 }
407
408 /*
409 * NAME: vol->writevbm()
410 * DESCRIPTION: flush volume bitmap to medium
411 */
412 int v_writevbm(hfsvol *vol)
413 {
414 unsigned int vbmst = vol->mdb.drVBMSt;
415 unsigned int vbmsz = vol->vbmsz;
416 // const
417 block *bp;
418
419 for (bp = vol->vbm; vbmsz--; ++bp)
420 {
421 if (b_writelb(vol, vbmst++, bp) == -1)
422 goto fail;
423 }
424
425 vol->flags &= ~HFS_VOL_UPDATE_VBM;
426
427 return 0;
428
429 fail:
430 return -1;
431 }
432
433 /*
434 * NAME: vol->mount()
435 * DESCRIPTION: load volume information into memory
436 */
437 int v_mount(hfsvol *vol)
438 {
439 /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */
440
441 if (v_readmdb(vol) == -1 ||
442 v_readvbm(vol) == -1 ||
443 bt_readhdr(&vol->ext) == -1 ||
444 bt_readhdr(&vol->cat) == -1)
445 goto fail;
446
447 if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED) &&
448 v_scavenge(vol) == -1)
449 goto fail;
450
451 if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED)
452 vol->flags |= HFS_VOL_READONLY;
453 else if (vol->flags & HFS_VOL_READONLY)
454 vol->mdb.drAtrb |= HFS_ATRB_HLOCKED;
455 else
456 vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED;
457
458 vol->flags |= HFS_VOL_MOUNTED;
459
460 return 0;
461
462 fail:
463 return -1;
464 }
465
466 /*
467 * NAME: vol->dirty()
468 * DESCRIPTION: ensure the volume is marked "in use" before we make changes
469 */
470 int v_dirty(hfsvol *vol)
471 {
472 if (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED)
473 {
474 vol->mdb.drAtrb &= ~HFS_ATRB_UMOUNTED;
475 ++vol->mdb.drWrCnt;
476
477 if (v_writemdb(vol) == -1)
478 goto fail;
479
480 if ((vol->flags & HFS_VOL_USINGCACHE) &&
481 b_flush(vol) == -1)
482 goto fail;
483 }
484
485 return 0;
486
487 fail:
488 return -1;
489 }
490
491 /*
492 * NAME: vol->catsearch()
493 * DESCRIPTION: search catalog tree
494 */
495 int v_catsearch(hfsvol *vol, unsigned long parid, const char *name,
496 CatDataRec *data, char *cname, node *np)
497 {
498 CatKeyRec key;
499 byte pkey[HFS_CATKEYLEN];
500 const byte *ptr;
501 node n;
502 int found;
503
504 if (np == 0)
505 np = &n;
506
507 r_makecatkey(&key, parid, name);
508 r_packcatkey(&key, pkey, 0);
509
510 found = bt_search(&vol->cat, pkey, np);
511 if (found <= 0)
512 return found;
513
514 ptr = HFS_NODEREC(*np, np->rnum);
515
516 if (cname)
517 {
518 r_unpackcatkey(ptr, &key);
519 strcpy(cname, key.ckrCName);
520 }
521
522 if (data)
523 r_unpackcatdata(HFS_RECDATA(ptr), data);
524
525 return 1;
526 }
527
528 /*
529 * NAME: vol->extsearch()
530 * DESCRIPTION: search extents tree
531 */
532 int v_extsearch(hfsfile *file, unsigned int fabn,
533 ExtDataRec *data, node *np)
534 {
535 ExtKeyRec key;
536 ExtDataRec extsave;
537 unsigned int fabnsave;
538 byte pkey[HFS_EXTKEYLEN];
539 const byte *ptr;
540 node n;
541 int found;
542
543 if (np == 0)
544 np = &n;
545
546 r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn);
547 r_packextkey(&key, pkey, 0);
548
549 /* in case bt_search() clobbers these */
550
551 memcpy(&extsave, &file->ext, sizeof(ExtDataRec));
552 fabnsave = file->fabn;
553
554 found = bt_search(&file->vol->ext, pkey, np);
555
556 memcpy(&file->ext, &extsave, sizeof(ExtDataRec));
557 file->fabn = fabnsave;
558
559 if (found <= 0)
560 return found;
561
562 if (data)
563 {
564 ptr = HFS_NODEREC(*np, np->rnum);
565 r_unpackextdata(HFS_RECDATA(ptr), data);
566 }
567
568 return 1;
569 }
570
571 /*
572 * NAME: vol->getthread()
573 * DESCRIPTION: retrieve catalog thread information for a file or directory
574 */
575 int v_getthread(hfsvol *vol, unsigned long id,
576 CatDataRec *thread, node *np, int type)
577 {
578 CatDataRec rec;
579 int found;
580
581 if (thread == 0)
582 thread = &rec;
583
584 found = v_catsearch(vol, id, "", thread, 0, np);
585 if (found == 1 && thread->cdrType != type)
586 ERROR(EIO, "bad thread record");
587
588 return found;
589
590 fail:
591 return -1;
592 }
593
594 /*
595 * NAME: vol->putcatrec()
596 * DESCRIPTION: store catalog information
597 */
598 int v_putcatrec(const CatDataRec *data, node *np)
599 {
600 byte pdata[HFS_CATDATALEN], *ptr;
601 unsigned int len = 0;
602
603 r_packcatdata(data, pdata, &len);
604
605 ptr = HFS_NODEREC(*np, np->rnum);
606 memcpy(HFS_RECDATA(ptr), pdata, len);
607
608 return bt_putnode(np);
609 }
610
611 /*
612 * NAME: vol->putextrec()
613 * DESCRIPTION: store extent information
614 */
615 int v_putextrec(const ExtDataRec *data, node *np)
616 {
617 byte pdata[HFS_EXTDATALEN], *ptr;
618 unsigned int len = 0;
619
620 r_packextdata(data, pdata, &len);
621
622 ptr = HFS_NODEREC(*np, np->rnum);
623 memcpy(HFS_RECDATA(ptr), pdata, len);
624
625 return bt_putnode(np);
626 }
627
628 /*
629 * NAME: vol->allocblocks()
630 * DESCRIPTION: allocate a contiguous range of blocks
631 */
632 int v_allocblocks(hfsvol *vol, ExtDescriptor *blocks)
633 {
634 unsigned int request, found, foundat, start, end;
635 register unsigned int pt;
636 block *vbm;
637 int wrap = 0;
638
639 if (vol->mdb.drFreeBks == 0)
640 ERROR(ENOSPC, "volume full");
641
642 request = blocks->xdrNumABlks;
643 found = 0;
644 foundat = 0;
645 start = vol->mdb.drAllocPtr;
646 end = vol->mdb.drNmAlBlks;
647 vbm = vol->vbm;
648
649 ASSERT(request > 0);
650
651 /* backtrack the start pointer to recover unused space */
652
653 if (! BMTST(vbm, start))
654 {
655 while (start > 0 && ! BMTST(vbm, start - 1))
656 --start;
657 }
658
659 /* find largest unused block which satisfies request */
660
661 pt = start;
662
663 while (1)
664 {
665 unsigned int mark;
666
667 /* skip blocks in use */
668
669 while (pt < end && BMTST(vbm, pt))
670 ++pt;
671
672 if (wrap && pt >= start)
673 break;
674
675 /* count blocks not in use */
676
677 mark = pt;
678 while (pt < end && pt - mark < request && ! BMTST(vbm, pt))
679 ++pt;
680
681 if (pt - mark > found)
682 {
683 found = pt - mark;
684 foundat = mark;
685 }
686
687 if (wrap && pt >= start)
688 break;
689
690 if (pt == end)
691 pt = 0, wrap = 1;
692
693 if (found == request)
694 break;
695 }
696
697 if (found == 0 || found > vol->mdb.drFreeBks)
698 ERROR(EIO, "bad volume bitmap or free block count");
699
700 blocks->xdrStABN = foundat;
701 blocks->xdrNumABlks = found;
702
703 if (v_dirty(vol) == -1)
704 goto fail;
705
706 vol->mdb.drAllocPtr = pt;
707 vol->mdb.drFreeBks -= found;
708
709 for (pt = foundat; pt < foundat + found; ++pt)
710 BMSET(vbm, pt);
711
712 vol->flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_VBM;
713
714 if (vol->flags & HFS_OPT_ZERO)
715 {
716 block b;
717 unsigned int i;
718
719 memset(&b, 0, sizeof(b));
720
721 for (pt = foundat; pt < foundat + found; ++pt)
722 {
723 for (i = 0; i < vol->lpa; ++i)
724 b_writeab(vol, pt, i, &b);
725 }
726 }
727
728 return 0;
729
730 fail:
731 return -1;
732 }
733
734 /*
735 * NAME: vol->freeblocks()
736 * DESCRIPTION: deallocate a contiguous range of blocks
737 */
738 int v_freeblocks(hfsvol *vol, const ExtDescriptor *blocks)
739 {
740 unsigned int start, len, pt;
741 block *vbm;
742
743 start = blocks->xdrStABN;
744 len = blocks->xdrNumABlks;
745 vbm = vol->vbm;
746
747 if (v_dirty(vol) == -1)
748 goto fail;
749
750 vol->mdb.drFreeBks += len;
751
752 for (pt = start; pt < start + len; ++pt)
753 BMCLR(vbm, pt);
754
755 vol->flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_VBM;
756
757 return 0;
758
759 fail:
760 return -1;
761 }
762
763 /*
764 * NAME: vol->resolve()
765 * DESCRIPTION: translate a pathname; return catalog information
766 */
767 int v_resolve(hfsvol **vol, const char *path,
768 CatDataRec *data, unsigned long *parid, char *fname, node *np)
769 {
770 unsigned long dirid;
771 char name[HFS_MAX_FLEN + 1], *nptr;
772 int found = 0;
773
774 if (*path == 0)
775 ERROR(ENOENT, "empty path");
776
777 if (parid)
778 *parid = 0;
779
780 nptr = strchr(path, '/');
781
782 if (*path == '/' || nptr == 0)
783 {
784 dirid = (*vol)->cwd; /* relative path */
785
786 if (*path == '/')
787 ++path;
788
789 if (*path == 0)
790 {
791 found = v_getdthread(*vol, dirid, data, 0);
792 if (found == -1)
793 goto fail;
794
795 if (found)
796 {
797 if (parid)
798 *parid = data->u.dthd.thdParID;
799
800 found = v_catsearch(*vol, data->u.dthd.thdParID,
801 data->u.dthd.thdCName, data, fname, np);
802 if (found == -1)
803 goto fail;
804 }
805
806 goto done;
807 }
808 }
809 else
810 {
811 hfsvol *check;
812
813 dirid = HFS_CNID_ROOTPAR; /* absolute path */
814
815 if (nptr - path > HFS_MAX_VLEN)
816 ERROR(ENAMETOOLONG, 0);
817
818 strncpy(name, path, nptr - path);
819 name[nptr - path] = 0;
820
821 for (check = hfs_mounts; check; check = check->next)
822 {
823 if (d_relstring(check->mdb.drVN, name) == 0)
824 {
825 *vol = check;
826 break;
827 }
828 }
829 }
830
831 while (1)
832 {
833 while (*path == '/')
834 {
835 ++path;
836
837 found = v_getdthread(*vol, dirid, data, 0);
838 if (found == -1)
839 goto fail;
840 else if (! found)
841 goto done;
842
843 dirid = data->u.dthd.thdParID;
844 }
845
846 if (*path == 0)
847 {
848 found = v_getdthread(*vol, dirid, data, 0);
849 if (found == -1)
850 goto fail;
851
852 if (found)
853 {
854 if (parid)
855 *parid = data->u.dthd.thdParID;
856
857 found = v_catsearch(*vol, data->u.dthd.thdParID,
858 data->u.dthd.thdCName, data, fname, np);
859 if (found == -1)
860 goto fail;
861 }
862
863 goto done;
864 }
865
866 nptr = name;
867 while (nptr < name + sizeof(name) - 1 && *path && *path != '/')
868 *nptr++ = *path++;
869
870 if (*path && *path != '/')
871 ERROR(ENAMETOOLONG, 0);
872
873 *nptr = 0;
874 if (*path == '/')
875 ++path;
876
877 if (parid)
878 *parid = dirid;
879
880 found = v_catsearch(*vol, dirid, name, data, fname, np);
881 if (found == -1)
882 goto fail;
883
884 if (! found)
885 {
886 if (*path && parid)
887 *parid = 0;
888
889 if (*path == 0 && fname)
890 strcpy(fname, name);
891
892 goto done;
893 }
894
895 switch (data->cdrType)
896 {
897 case cdrDirRec:
898 if (*path == 0)
899 goto done;
900
901 dirid = data->u.dir.dirDirID;
902 break;
903
904 case cdrFilRec:
905 if (*path == 0)
906 goto done;
907
908 ERROR(ENOTDIR, "invalid pathname");
909
910 default:
911 ERROR(EIO, "unexpected catalog record");
912 }
913 }
914
915 done:
916 return found;
917
918 fail:
919 return -1;
920 }
921
922 /*
923 * NAME: vol->adjvalence()
924 * DESCRIPTION: update a volume's valence counts
925 */
926 int v_adjvalence(hfsvol *vol, unsigned long parid, int isdir, int adj)
927 {
928 node n;
929 CatDataRec data;
930 int result = 0;
931
932 if (isdir)
933 vol->mdb.drDirCnt += adj;
934 else
935 vol->mdb.drFilCnt += adj;
936
937 vol->flags |= HFS_VOL_UPDATE_MDB;
938
939 if (parid == HFS_CNID_ROOTDIR)
940 {
941 if (isdir)
942 vol->mdb.drNmRtDirs += adj;
943 else
944 vol->mdb.drNmFls += adj;
945 }
946 else if (parid == HFS_CNID_ROOTPAR)
947 goto done;
948
949 if (v_getdthread(vol, parid, &data, 0) <= 0 ||
950 v_catsearch(vol, data.u.dthd.thdParID, data.u.dthd.thdCName,
951 &data, 0, &n) <= 0 ||
952 data.cdrType != cdrDirRec)
953 ERROR(EIO, "can't find parent directory");
954
955 data.u.dir.dirVal += adj;
956 data.u.dir.dirMdDat = d_mtime(time(0));
957
958 result = v_putcatrec(&data, &n);
959
960 done:
961 return result;
962
963 fail:
964 return -1;
965 }
966
967 /*
968 * NAME: vol->mkdir()
969 * DESCRIPTION: create a new HFS directory
970 */
971 int v_mkdir(hfsvol *vol, unsigned long parid, const char *name)
972 {
973 CatKeyRec key;
974 CatDataRec data;
975 unsigned long id;
976 byte record[HFS_MAX_CATRECLEN];
977 unsigned int reclen;
978 int i;
979
980 if (bt_space(&vol->cat, 2) == -1)
981 goto fail;
982
983 id = vol->mdb.drNxtCNID++;
984 vol->flags |= HFS_VOL_UPDATE_MDB;
985
986 /* create directory record */
987
988 data.cdrType = cdrDirRec;
989 data.cdrResrv2 = 0;
990
991 data.u.dir.dirFlags = 0;
992 data.u.dir.dirVal = 0;
993 data.u.dir.dirDirID = id;
994 data.u.dir.dirCrDat = d_mtime(time(0));
995 data.u.dir.dirMdDat = data.u.dir.dirCrDat;
996 data.u.dir.dirBkDat = 0;
997
998 memset(&data.u.dir.dirUsrInfo, 0, sizeof(data.u.dir.dirUsrInfo));
999 memset(&data.u.dir.dirFndrInfo, 0, sizeof(data.u.dir.dirFndrInfo));
1000 for (i = 0; i < 4; ++i)
1001 data.u.dir.dirResrv[i] = 0;
1002
1003 r_makecatkey(&key, parid, name);
1004 r_packcatrec(&key, &data, record, &reclen);
1005
1006 if (bt_insert(&vol->cat, record, reclen) == -1)
1007 goto fail;
1008
1009 /* create thread record */
1010
1011 data.cdrType = cdrThdRec;
1012 data.cdrResrv2 = 0;
1013
1014 data.u.dthd.thdResrv[0] = 0;
1015 data.u.dthd.thdResrv[1] = 0;
1016 data.u.dthd.thdParID = parid;
1017 strcpy(data.u.dthd.thdCName, name);
1018
1019 r_makecatkey(&key, id, "");
1020 r_packcatrec(&key, &data, record, &reclen);
1021
1022 if (bt_insert(&vol->cat, record, reclen) == -1 ||
1023 v_adjvalence(vol, parid, 1, 1) == -1)
1024 goto fail;
1025
1026 return 0;
1027
1028 fail:
1029 return -1;
1030 }
1031
1032 /*
1033 * NAME: markexts()
1034 * DESCRIPTION: set bits from an extent record in the volume bitmap
1035 */
1036 static
1037 void markexts(block *vbm, const ExtDataRec *exts)
1038 {
1039 int i;
1040 unsigned int pt, len;
1041
1042 for (i = 0; i < 3; ++i)
1043 {
1044 for ( pt = (*exts)[i].xdrStABN,
1045 len = (*exts)[i].xdrNumABlks; len--; ++pt)
1046 BMSET(vbm, pt);
1047 }
1048 }
1049
1050 /*
1051 * NAME: vol->scavenge()
1052 * DESCRIPTION: safeguard blocks in the volume bitmap
1053 */
1054 int v_scavenge(hfsvol *vol)
1055 {
1056 block *vbm = vol->vbm;
1057 node n;
1058 unsigned int pt, blks;
1059 unsigned long lastcnid = 15;
1060
1061 # ifdef DEBUG
1062 fprintf(stderr, "VOL: \"%s\" not cleanly unmounted\n",
1063 vol->mdb.drVN);
1064 # endif
1065
1066 if (vol->flags & HFS_VOL_READONLY)
1067 goto done;
1068
1069 # ifdef DEBUG
1070 fprintf(stderr, "VOL: scavenging...\n");
1071 # endif
1072
1073 /* reset MDB by marking it dirty again */
1074
1075 vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED;
1076 if (v_dirty(vol) == -1)
1077 goto fail;
1078
1079 /* begin by marking extents in MDB */
1080
1081 markexts(vbm, &vol->mdb.drXTExtRec);
1082 markexts(vbm, &vol->mdb.drCTExtRec);
1083
1084 vol->flags |= HFS_VOL_UPDATE_VBM;
1085
1086 /* scavenge the extents overflow file */
1087
1088 if (vol->ext.hdr.bthFNode > 0)
1089 {
1090 if (bt_getnode(&n, &vol->ext, vol->ext.hdr.bthFNode) == -1)
1091 goto fail;
1092
1093 n.rnum = 0;
1094
1095 while (1)
1096 {
1097 ExtDataRec data;
1098 const byte *ptr;
1099
1100 while (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink > 0)
1101 {
1102 if (bt_getnode(&n, &vol->ext, n.nd.ndFLink) == -1)
1103 goto fail;
1104
1105 n.rnum = 0;
1106 }
1107
1108 if (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink == 0)
1109 break;
1110
1111 ptr = HFS_NODEREC(n, n.rnum);
1112 r_unpackextdata(HFS_RECDATA(ptr), &data);
1113
1114 markexts(vbm, &data);
1115
1116 ++n.rnum;
1117 }
1118 }
1119
1120 /* scavenge the catalog file */
1121
1122 if (vol->cat.hdr.bthFNode > 0)
1123 {
1124 if (bt_getnode(&n, &vol->cat, vol->cat.hdr.bthFNode) == -1)
1125 goto fail;
1126
1127 n.rnum = 0;
1128
1129 while (1)
1130 {
1131 CatDataRec data;
1132 const byte *ptr;
1133
1134 while (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink > 0)
1135 {
1136 if (bt_getnode(&n, &vol->cat, n.nd.ndFLink) == -1)
1137 goto fail;
1138
1139 n.rnum = 0;
1140 }
1141
1142 if (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink == 0)
1143 break;
1144
1145 ptr = HFS_NODEREC(n, n.rnum);
1146 r_unpackcatdata(HFS_RECDATA(ptr), &data);
1147
1148 switch (data.cdrType)
1149 {
1150 case cdrFilRec:
1151 markexts(vbm, &data.u.fil.filExtRec);
1152 markexts(vbm, &data.u.fil.filRExtRec);
1153
1154 if (data.u.fil.filFlNum > lastcnid)
1155 lastcnid = data.u.fil.filFlNum;
1156 break;
1157
1158 case cdrDirRec:
1159 if (data.u.dir.dirDirID > lastcnid)
1160 lastcnid = data.u.dir.dirDirID;
1161 break;
1162 }
1163
1164 ++n.rnum;
1165 }
1166 }
1167
1168 /* count free blocks */
1169
1170 for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; )
1171 {
1172 if (! BMTST(vbm, pt))
1173 ++blks;
1174 }
1175
1176 if (vol->mdb.drFreeBks != blks)
1177 {
1178 # ifdef DEBUG
1179 fprintf(stderr, "VOL: updating free blocks from %u to %u\n",
1180 vol->mdb.drFreeBks, blks);
1181 # endif
1182
1183 vol->mdb.drFreeBks = blks;
1184 vol->flags |= HFS_VOL_UPDATE_MDB;
1185 }
1186
1187 /* ensure next CNID is sane */
1188
1189 if ((unsigned long) vol->mdb.drNxtCNID <= lastcnid)
1190 {
1191 # ifdef DEBUG
1192 fprintf(stderr, "VOL: updating next CNID from %lu to %lu\n",
1193 vol->mdb.drNxtCNID, lastcnid + 1);
1194 # endif
1195
1196 vol->mdb.drNxtCNID = lastcnid + 1;
1197 vol->flags |= HFS_VOL_UPDATE_MDB;
1198 }
1199
1200 # ifdef DEBUG
1201 fprintf(stderr, "VOL: scavenging complete\n");
1202 # endif
1203
1204 done:
1205 return 0;
1206
1207 fail:
1208 return -1;
1209 }

  ViewVC Help
Powered by ViewVC 1.1.26