/[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

Annotation of /src/io/prom/fs/hfs/block.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide 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 dpavlin 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