/[pearpc]/src/io/prom/fs/hfs/block.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/block.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: 15556 byte(s)
import upstream CVS
1 /*
2 * libhfs - library for reading and writing Macintosh HFS volumes
3 * Copyright (C) 1996-1998 Robert Leslie
4 *
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.
9 *
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.
14 *
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 */
20
21 # ifdef HAVE_CONFIG_H
22 # include "config.h"
23 # endif
24
25 # include <stdlib.h>
26 # include <string.h>
27 # include <errno.h>
28
29 # include "libhfs.h"
30 # include "volume.h"
31 # include "block.h"
32 # include "os.h"
33
34 # define INUSE(b) ((b)->flags & HFS_BUCKET_INUSE)
35 # define DIRTY(b) ((b)->flags & HFS_BUCKET_DIRTY)
36
37 /*
38 * NAME: block->init()
39 * DESCRIPTION: initialize a volume's block cache
40 */
41 int b_init(hfsvol *vol)
42 {
43 bcache *cache;
44 int i;
45
46 ASSERT(vol->cache == 0);
47
48 cache = ALLOC(bcache, 1);
49 if (cache == 0)
50 ERROR(ENOMEM, 0);
51
52 vol->cache = cache;
53
54 cache->vol = vol;
55 cache->tail = &cache->chain[HFS_CACHESZ - 1];
56
57 cache->hits = 0;
58 cache->misses = 0;
59
60 for (i = 0; i < HFS_CACHESZ; ++i)
61 {
62 bucket *b = &cache->chain[i];
63
64 b->flags = 0;
65 b->count = 0;
66
67 b->bnum = 0;
68 b->data = &cache->pool[i];
69
70 b->cnext = b + 1;
71 b->cprev = b - 1;
72
73 b->hnext = 0;
74 b->hprev = 0;
75 }
76
77 cache->chain[0].cprev = cache->tail;
78 cache->tail->cnext = &cache->chain[0];
79
80 for (i = 0; i < HFS_HASHSZ; ++i)
81 cache->hash[i] = 0;
82
83 return 0;
84
85 fail:
86 return -1;
87 }
88
89 # ifdef DEBUG
90 /*
91 * NAME: block->showstats()
92 * DESCRIPTION: output cache hit/miss ratio
93 */
94 void b_showstats(const bcache *cache)
95 {
96 // fprintf(stderr, "BLOCK: CACHE vol 0x%lx \"%s\" hit/miss ratio = %.3f\n",
97 (unsigned long) cache->vol, cache->vol->mdb.drVN,
98 (float) cache->hits / (float) cache->misses);
99 }
100
101 /*
102 * NAME: block->dumpcache()
103 * DESCRIPTION: dump the cache tables for a volume
104 */
105 /*void b_dumpcache(const bcache *cache)
106 {
107 const bucket *b;
108 int i;
109
110 fprintf(stderr, "BLOCK CACHE DUMP:\n");
111
112 for (i = 0, b = cache->tail->cnext; i < HFS_CACHESZ; ++i, b = b->cnext)
113 {
114 if (INUSE(b))
115 {
116 fprintf(stderr, "\t %lu", b->bnum);
117 if (DIRTY(b))
118 fprintf(stderr, "*");
119
120 fprintf(stderr, ":%u", b->count);
121 }
122 }
123
124 fprintf(stderr, "\n");
125
126 fprintf(stderr, "BLOCK HASH DUMP:\n");
127
128 for (i = 0; i < HFS_HASHSZ; ++i)
129 {
130 int seen = 0;
131
132 for (b = cache->hash[i]; b; b = b->hnext)
133 {
134 if (! seen)
135 fprintf(stderr, " %d:", i);
136
137 if (INUSE(b))
138 {
139 fprintf(stderr, " %lu", b->bnum);
140 if (DIRTY(b))
141 fprintf(stderr, "*");
142
143 fprintf(stderr, ":%u", b->count);
144 }
145
146 seen = 1;
147 }
148
149 if (seen)
150 fprintf(stderr, "\n");
151 }
152 }*/
153 # endif
154
155 /*
156 * NAME: fillchain()
157 * DESCRIPTION: fill a chain of bucket buffers with a single read
158 */
159 static
160 int fillchain(hfsvol *vol, bucket **bptr, unsigned int *count)
161 {
162 bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
163 unsigned long bnum;
164 unsigned int len, i;
165
166 for (len = 0; len < HFS_BLOCKBUFSZ &&
167 (unsigned int) (bptr - start) < *count; ++bptr)
168 {
169 if (INUSE(*bptr))
170 continue;
171
172 if (len > 0 && (*bptr)->bnum != bnum)
173 break;
174
175 blist[len++] = *bptr;
176 bnum = (*bptr)->bnum + 1;
177 }
178
179 *count = bptr - start;
180
181 if (len == 0)
182 goto done;
183 else if (len == 1)
184 {
185 if (b_readpb(vol, vol->vstart + blist[0]->bnum,
186 blist[0]->data, 1) == -1)
187 goto fail;
188 }
189 else
190 {
191 block buffer[HFS_BLOCKBUFSZ];
192
193 if (b_readpb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1)
194 goto fail;
195
196 for (i = 0; i < len; ++i)
197 memcpy(blist[i]->data, buffer[i], HFS_BLOCKSZ);
198 }
199
200 for (i = 0; i < len; ++i)
201 {
202 blist[i]->flags |= HFS_BUCKET_INUSE;
203 blist[i]->flags &= ~HFS_BUCKET_DIRTY;
204 }
205
206 done:
207 return 0;
208
209 fail:
210 return -1;
211 }
212
213 /*
214 * NAME: flushchain()
215 * DESCRIPTION: store a chain of bucket buffers with a single write
216 */
217 static
218 int flushchain(hfsvol *vol, bucket **bptr, unsigned int *count)
219 {
220 bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
221 unsigned long bnum;
222 unsigned int len, i;
223
224 for (len = 0; len < HFS_BLOCKBUFSZ &&
225 (unsigned int) (bptr - start) < *count; ++bptr)
226 {
227 if (! INUSE(*bptr) || ! DIRTY(*bptr))
228 continue;
229
230 if (len > 0 && (*bptr)->bnum != bnum)
231 break;
232
233 blist[len++] = *bptr;
234 bnum = (*bptr)->bnum + 1;
235 }
236
237 *count = bptr - start;
238
239 if (len == 0)
240 goto done;
241 else if (len == 1)
242 {
243 if (b_writepb(vol, vol->vstart + blist[0]->bnum,
244 (const block*)blist[0]->data, 1) == -1)
245 goto fail;
246 }
247 else
248 {
249 block buffer[HFS_BLOCKBUFSZ];
250
251 for (i = 0; i < len; ++i)
252 memcpy(buffer[i], blist[i]->data, HFS_BLOCKSZ);
253
254 if (b_writepb(vol, vol->vstart + blist[0]->bnum, (const block*)buffer, len) == -1)
255 goto fail;
256 }
257
258 for (i = 0; i < len; ++i)
259 blist[i]->flags &= ~HFS_BUCKET_DIRTY;
260
261 done:
262 return 0;
263
264 fail:
265 return -1;
266 }
267
268 /*
269 * NAME: compare()
270 * DESCRIPTION: comparison function for qsort of cache bucket pointers
271 */
272 static
273 int compare(const bucket **b1, const bucket **b2)
274 {
275 long diff;
276
277 diff = (*b1)->bnum - (*b2)->bnum;
278
279 if (diff < 0)
280 return -1;
281 else if (diff > 0)
282 return 1;
283 else
284 return 0;
285 }
286
287 /*
288 * NAME: dobuckets()
289 * DESCRIPTION: fill or flush an array of cache buckets to a volume
290 */
291 static
292 int dobuckets(hfsvol *vol, bucket **chain, unsigned int len,
293 int (*func)(hfsvol *, bucket **, unsigned int *))
294 {
295 unsigned int count, i;
296 int result = 0;
297
298 qsort(chain, len, sizeof(*chain),
299 (int (*)(const void *, const void *)) compare);
300
301 for (i = 0; i < len; i += count)
302 {
303 count = len - i;
304 if (func(vol, chain + i, &count) == -1)
305 result = -1;
306 }
307
308 return result;
309 }
310
311 # define fillbuckets(vol, chain, len) dobuckets(vol, chain, len, fillchain)
312 # define flushbuckets(vol, chain, len) dobuckets(vol, chain, len, flushchain)
313
314 /*
315 * NAME: block->flush()
316 * DESCRIPTION: commit dirty cache blocks to a volume
317 */
318 int b_flush(hfsvol *vol)
319 {
320 bcache *cache = vol->cache;
321 bucket *chain[HFS_CACHESZ];
322 int i;
323
324 if (cache == 0 || (vol->flags & HFS_VOL_READONLY))
325 goto done;
326
327 for (i = 0; i < HFS_CACHESZ; ++i)
328 chain[i] = &cache->chain[i];
329
330 if (flushbuckets(vol, chain, HFS_CACHESZ) == -1)
331 goto fail;
332
333 done:
334 # ifdef DEBUG
335 if (cache)
336 b_showstats(cache);
337 # endif
338
339 return 0;
340
341 fail:
342 return -1;
343 }
344
345 /*
346 * NAME: block->finish()
347 * DESCRIPTION: commit and free a volume's block cache
348 */
349 int b_finish(hfsvol *vol)
350 {
351 int result = 0;
352
353 if (vol->cache == 0)
354 goto done;
355
356 # ifdef DEBUG
357 b_dumpcache(vol->cache);
358 # endif
359
360 result = b_flush(vol);
361
362 FREE(vol->cache);
363 vol->cache = 0;
364
365 done:
366 return result;
367 }
368
369 /*
370 * NAME: findbucket()
371 * DESCRIPTION: locate a bucket in the cache, and/or its hash slot
372 */
373 static
374 bucket *findbucket(bcache *cache, unsigned long bnum, bucket ***hslot)
375 {
376 bucket *b;
377
378 *hslot = &cache->hash[bnum & (HFS_HASHSZ - 1)];
379
380 for (b = **hslot; b; b = b->hnext)
381 {
382 if (INUSE(b) && b->bnum == bnum)
383 break;
384 }
385
386 return b;
387 }
388
389 /*
390 * NAME: reuse()
391 * DESCRIPTION: free a bucket for reuse, flushing if necessary
392 */
393 static
394 int reuse(bcache *cache, bucket *b, unsigned long bnum)
395 {
396 bucket *chain[HFS_BLOCKBUFSZ], *bptr;
397 int i;
398
399 # ifdef DEBUG
400 if (INUSE(b))
401 printf(stderr, "BLOCK: CACHE reusing bucket containing "
402 "vol 0x%lx block %lu:%u\n",
403 (unsigned long) cache->vol, b->bnum, b->count);
404 # endif
405
406 if (INUSE(b) && DIRTY(b))
407 {
408 /* flush most recently unused buckets */
409
410 for (bptr = b, i = 0; i < HFS_BLOCKBUFSZ; ++i)
411 {
412 chain[i] = bptr;
413 bptr = bptr->cprev;
414 }
415
416 if (flushbuckets(cache->vol, chain, HFS_BLOCKBUFSZ) == -1)
417 goto fail;
418 }
419
420 b->flags &= ~HFS_BUCKET_INUSE;
421 b->count = 1;
422 b->bnum = bnum;
423
424 return 0;
425
426 fail:
427 return -1;
428 }
429
430 /*
431 * NAME: cplace()
432 * DESCRIPTION: move a bucket to an appropriate place near head of the chain
433 */
434 static
435 void cplace(bcache *cache, bucket *b)
436 {
437 bucket *p;
438
439 for (p = cache->tail->cnext; p->count > 1; p = p->cnext)
440 --p->count;
441
442 b->cnext->cprev = b->cprev;
443 b->cprev->cnext = b->cnext;
444
445 if (cache->tail == b)
446 cache->tail = b->cprev;
447
448 b->cprev = p->cprev;
449 b->cnext = p;
450
451 p->cprev->cnext = b;
452 p->cprev = b;
453 }
454
455 /*
456 * NAME: hplace()
457 * DESCRIPTION: move a bucket to the head of its hash slot
458 */
459 static
460 void hplace(bucket **hslot, bucket *b)
461 {
462 if (*hslot != b)
463 {
464 if (b->hprev)
465 *b->hprev = b->hnext;
466 if (b->hnext)
467 b->hnext->hprev = b->hprev;
468
469 b->hprev = hslot;
470 b->hnext = *hslot;
471
472 if (*hslot)
473 (*hslot)->hprev = &b->hnext;
474
475 *hslot = b;
476 }
477 }
478
479 /*
480 * NAME: getbucket()
481 * DESCRIPTION: fetch a bucket from the cache, or an empty one to be filled
482 */
483 static
484 bucket *getbucket(bcache *cache, unsigned long bnum, int fill)
485 {
486 bucket **hslot, *b, *p, *bptr,
487 *chain[HFS_BLOCKBUFSZ], **slots[HFS_BLOCKBUFSZ];
488
489 b = findbucket(cache, bnum, &hslot);
490
491 if (b)
492 {
493 /* cache hit; move towards head of cache chain */
494
495 ++cache->hits;
496
497 if (++b->count > b->cprev->count &&
498 b != cache->tail->cnext)
499 {
500 p = b->cprev;
501
502 p->cprev->cnext = b;
503 b->cnext->cprev = p;
504
505 p->cnext = b->cnext;
506 b->cprev = p->cprev;
507
508 p->cprev = b;
509 b->cnext = p;
510
511 if (cache->tail == b)
512 cache->tail = p;
513 }
514 }
515 else
516 {
517 /* cache miss; reuse least-used cache bucket */
518
519 ++cache->misses;
520
521 b = cache->tail;
522
523 if (reuse(cache, b, bnum) == -1)
524 goto fail;
525
526 if (fill)
527 {
528 unsigned int len = 0;
529
530 chain[len] = b;
531 slots[len++] = hslot;
532
533 for (bptr = b->cprev;
534 len < (HFS_BLOCKBUFSZ >> 1) && ++bnum < cache->vol->vlen;
535 bptr = bptr->cprev)
536 {
537 if (findbucket(cache, bnum, &hslot))
538 break;
539
540 if (reuse(cache, bptr, bnum) == -1)
541 goto fail;
542
543 chain[len] = bptr;
544 slots[len++] = hslot;
545 }
546
547 if (fillbuckets(cache->vol, chain, len) == -1)
548 goto fail;
549
550 while (--len)
551 {
552 cplace(cache, chain[len]);
553 hplace(slots[len], chain[len]);
554 }
555
556 hslot = slots[0];
557 }
558
559 /* move bucket to appropriate place in chain */
560
561 cplace(cache, b);
562 }
563
564 /* insert at front of hash chain */
565
566 hplace(hslot, b);
567
568 return b;
569
570 fail:
571 return 0;
572 }
573
574 /*
575 * NAME: block->readpb()
576 * DESCRIPTION: read blocks from the physical medium (bypassing cache)
577 */
578 int b_readpb(hfsvol *vol, unsigned long bnum, block *bp, unsigned int blen)
579 {
580 unsigned long nblocks;
581
582 # ifdef DEBUG
583 fprintf(stderr, "BLOCK: READ vol 0x%lx block %lu",
584 (unsigned long) vol, bnum);
585 if (blen > 1)
586 fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
587 else
588 fprintf(stderr, "\n");
589 # endif
590
591 nblocks = hfs_os_seek(&vol->priv, bnum);
592 if (nblocks == (unsigned long) -1)
593 goto fail;
594
595 if (nblocks != bnum)
596 ERROR(EIO, "block seek failed for read");
597
598 nblocks = hfs_os_read(&vol->priv, bp, blen);
599 if (nblocks == (unsigned long) -1)
600 goto fail;
601
602 if (nblocks != blen)
603 ERROR(EIO, "incomplete block read");
604
605 return 0;
606
607 fail:
608 return -1;
609 }
610
611 /*
612 * NAME: block->writepb()
613 * DESCRIPTION: write blocks to the physical medium (bypassing cache)
614 */
615 int b_writepb(hfsvol *vol, unsigned long bnum, const block *bp,
616 unsigned int blen)
617 {
618 unsigned long nblocks;
619
620 # ifdef DEBUG
621 fprintf(stderr, "BLOCK: WRITE vol 0x%lx block %lu",
622 (unsigned long) vol, bnum);
623 if (blen > 1)
624 fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
625 else
626 fprintf(stderr, "\n");
627 # endif
628
629 nblocks = hfs_os_seek(&vol->priv, bnum);
630 if (nblocks == (unsigned long) -1)
631 goto fail;
632
633 if (nblocks != bnum)
634 ERROR(EIO, "block seek failed for write");
635
636 nblocks = hfs_os_write(&vol->priv, bp, blen);
637 if (nblocks == (unsigned long) -1)
638 goto fail;
639
640 if (nblocks != blen)
641 ERROR(EIO, "incomplete block write");
642
643 return 0;
644
645 fail:
646 return -1;
647 }
648
649 /*
650 * NAME: block->readlb()
651 * DESCRIPTION: read a logical block from a volume (or from the cache)
652 */
653 int b_readlb(hfsvol *vol, unsigned long bnum, block *bp)
654 {
655 if (vol->vlen > 0 && bnum >= vol->vlen)
656 ERROR(EIO, "read nonexistent logical block");
657
658 if (vol->cache)
659 {
660 bucket *b;
661
662 b = getbucket(vol->cache, bnum, 1);
663 if (b == 0)
664 goto fail;
665
666 memcpy(bp, b->data, HFS_BLOCKSZ);
667 }
668 else
669 {
670 if (b_readpb(vol, vol->vstart + bnum, bp, 1) == -1)
671 goto fail;
672 }
673
674 return 0;
675
676 fail:
677 return -1;
678 }
679
680 /*
681 * NAME: block->writelb()
682 * DESCRIPTION: write a logical block to a volume (or to the cache)
683 */
684 int b_writelb(hfsvol *vol, unsigned long bnum, const block *bp)
685 {
686 if (vol->vlen > 0 && bnum >= vol->vlen)
687 ERROR(EIO, "write nonexistent logical block");
688
689 if (vol->cache)
690 {
691 bucket *b;
692
693 b = getbucket(vol->cache, bnum, 0);
694 if (b == 0)
695 goto fail;
696
697 if (! INUSE(b) ||
698 memcmp(b->data, bp, HFS_BLOCKSZ) != 0)
699 {
700 memcpy(b->data, bp, HFS_BLOCKSZ);
701 b->flags |= HFS_BUCKET_INUSE | HFS_BUCKET_DIRTY;
702 }
703 }
704 else
705 {
706 if (b_writepb(vol, vol->vstart + bnum, bp, 1) == -1)
707 goto fail;
708 }
709
710 return 0;
711
712 fail:
713 return -1;
714 }
715
716 /*
717 * NAME: block->readab()
718 * DESCRIPTION: read a block from an allocation block from a volume
719 */
720 int b_readab(hfsvol *vol, unsigned int anum, unsigned int index, block *bp)
721 {
722 /* verify the allocation block exists and is marked as in-use */
723
724 if (anum >= vol->mdb.drNmAlBlks)
725 ERROR(EIO, "read nonexistent allocation block");
726 else if (vol->vbm && ! BMTST(vol->vbm, anum))
727 ERROR(EIO, "read unallocated block");
728
729 return b_readlb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
730
731 fail:
732 return -1;
733 }
734
735 /*
736 * NAME: block->writeab()
737 * DESCRIPTION: write a block to an allocation block to a volume
738 */
739 int b_writeab(hfsvol *vol,
740 unsigned int anum, unsigned int index, const block *bp)
741 {
742 /* verify the allocation block exists and is marked as in-use */
743
744 if (anum >= vol->mdb.drNmAlBlks)
745 ERROR(EIO, "write nonexistent allocation block");
746 else if (vol->vbm && ! BMTST(vol->vbm, anum))
747 ERROR(EIO, "write unallocated block");
748
749 if (v_dirty(vol) == -1)
750 goto fail;
751
752 return b_writelb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
753
754 fail:
755 return -1;
756 }
757
758 /*
759 * NAME: block->size()
760 * DESCRIPTION: return the number of physical blocks on a volume's medium
761 */
762 unsigned long b_size(hfsvol *vol)
763 {
764 unsigned long low, high, mid;
765 block b;
766
767 high = hfs_os_seek(&vol->priv, -1);
768
769 if (high != (unsigned long) -1 && high > 0)
770 return high;
771
772 /* manual size detection: first check there is at least 1 block in medium */
773
774 if (b_readpb(vol, 0, &b, 1) == -1)
775 ERROR(EIO, "size of medium indeterminable or empty");
776
777 for (low = 0, high = 2880;
778 high > 0 && b_readpb(vol, high - 1, &b, 1) != -1;
779 high <<= 1)
780 low = high - 1;
781
782 if (high == 0)
783 ERROR(EIO, "size of medium indeterminable or too large");
784
785 /* common case: 1440K floppy */
786
787 if (low == 2879 && b_readpb(vol, 2880, &b, 1) == -1)
788 return 2880;
789
790 /* binary search for other sizes */
791
792 while (low < high - 1)
793 {
794 mid = (low + high) >> 1;
795
796 if (b_readpb(vol, mid, &b, 1) == -1)
797 high = mid;
798 else
799 low = mid;
800 }
801
802 return low + 1;
803
804 fail:
805 return 0;
806 }

  ViewVC Help
Powered by ViewVC 1.1.26