|
|
1.1 root 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., 51 Franklin Street - Fifth Floor, Boston,
18: * MA 02110-1301, USA.
19: *
20: * $Id: block.c,v 1.11 1998/11/02 22:08:52 rob Exp $
21: */
22:
23: #include "config.h"
24:
25: #include "libhfs.h"
26: #include "volume.h"
27: #include "block.h"
28: #include "os.h"
29:
30: #define INUSE(b) ((b)->flags & HFS_BUCKET_INUSE)
31: #define DIRTY(b) ((b)->flags & HFS_BUCKET_DIRTY)
32:
33: /*
34: * NAME: block->init()
35: * DESCRIPTION: initialize a volume's block cache
36: */
37: int b_init(hfsvol *vol)
38: {
39: bcache *cache;
40: int i;
41:
42: ASSERT(vol->cache == 0);
43:
44: cache = ALLOC(bcache, 1);
45: if (cache == NULL)
46: ERROR(ENOMEM, NULL);
47:
48: vol->cache = cache;
49:
50: cache->vol = vol;
51: cache->tail = &cache->chain[HFS_CACHESZ - 1];
52:
53: cache->hits = 0;
54: cache->misses = 0;
55:
56: for (i = 0; i < HFS_CACHESZ; ++i)
57: {
58: bucket *b = &cache->chain[i];
59:
60: b->flags = 0;
61: b->count = 0;
62:
63: b->bnum = 0;
64: b->data = &cache->pool[i];
65:
66: b->cnext = b + 1;
67: b->cprev = b - 1;
68:
69: b->hnext = NULL;
70: b->hprev = NULL;
71: }
72:
73: cache->chain[0].cprev = cache->tail;
74: cache->tail->cnext = &cache->chain[0];
75:
76: for (i = 0; i < HFS_HASHSZ; ++i)
77: cache->hash[i] = NULL;
78:
79: return 0;
80:
81: fail:
82: return -1;
83: }
84:
85: # ifdef DEBUG
86: /*
87: * NAME: block->showstats()
88: * DESCRIPTION: output cache hit/miss ratio
89: */
90: void b_showstats(const bcache *cache)
91: {
92: fprintf(stderr, "BLOCK: CACHE vol 0x%lx \"%s\" hit/miss ratio = %.3f\n",
93: (unsigned long) cache->vol, cache->vol->mdb.drVN,
94: (float) cache->hits / (float) cache->misses);
95: }
96:
97: /*
98: * NAME: block->dumpcache()
99: * DESCRIPTION: dump the cache tables for a volume
100: */
101: void b_dumpcache(const bcache *cache)
102: {
103: const bucket *b;
104: int i;
105:
106: fprintf(stderr, "BLOCK CACHE DUMP:\n");
107:
108: for (i = 0, b = cache->tail->cnext; i < HFS_CACHESZ; ++i, b = b->cnext)
109: {
110: if (INUSE(b))
111: {
112: fprintf(stderr, "\t %lu", b->bnum);
113: if (DIRTY(b))
114: fprintf(stderr, "*");
115:
116: fprintf(stderr, ":%u", b->count);
117: }
118: }
119:
120: fprintf(stderr, "\n");
121:
122: fprintf(stderr, "BLOCK HASH DUMP:\n");
123:
124: for (i = 0; i < HFS_HASHSZ; ++i)
125: {
126: int seen = 0;
127:
128: for (b = cache->hash[i]; b; b = b->hnext)
129: {
130: if (! seen)
131: fprintf(stderr, " %d:", i);
132:
133: if (INUSE(b))
134: {
135: fprintf(stderr, " %lu", b->bnum);
136: if (DIRTY(b))
137: fprintf(stderr, "*");
138:
139: fprintf(stderr, ":%u", b->count);
140: }
141:
142: seen = 1;
143: }
144:
145: if (seen)
146: fprintf(stderr, "\n");
147: }
148: }
149: # endif
150:
151: /*
152: * NAME: fillchain()
153: * DESCRIPTION: fill a chain of bucket buffers with a single read
154: */
155: static
156: int fillchain(hfsvol *vol, bucket **bptr, unsigned int *count)
157: {
158: bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
159: unsigned long bnum=-2; // XXX
160: unsigned int len, i;
161:
162: for (len = 0; len < HFS_BLOCKBUFSZ &&
163: (unsigned int) (bptr - start) < *count; ++bptr)
164: {
165: if (INUSE(*bptr))
166: continue;
167:
168: if (len > 0 && (*bptr)->bnum != bnum)
169: break;
170:
171: blist[len++] = *bptr;
172: bnum = (*bptr)->bnum + 1;
173: }
174:
175: *count = bptr - start;
176:
177: if (len == 0)
178: goto done;
179: else if (len == 1)
180: {
181: if (b_readpb(vol, vol->vstart + blist[0]->bnum,
182: blist[0]->data, 1) == -1)
183: goto fail;
184: }
185: else
186: {
187: block buffer[HFS_BLOCKBUFSZ];
188:
189: if (b_readpb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1)
190: goto fail;
191:
192: for (i = 0; i < len; ++i)
193: memcpy(blist[i]->data, buffer[i], HFS_BLOCKSZ);
194: }
195:
196: for (i = 0; i < len; ++i)
197: {
198: blist[i]->flags |= HFS_BUCKET_INUSE;
199: blist[i]->flags &= ~HFS_BUCKET_DIRTY;
200: }
201:
202: done:
203: return 0;
204:
205: fail:
206: return -1;
207: }
208:
209:
210: /*
211: * NAME: compare()
212: * DESCRIPTION: comparison function for qsort of cache bucket pointers
213: */
214: static
215: int compare(const bucket **b1, const bucket **b2)
216: {
217: long diff;
218:
219: diff = (*b1)->bnum - (*b2)->bnum;
220:
221: if (diff < 0)
222: return -1;
223: else if (diff > 0)
224: return 1;
225: else
226: return 0;
227: }
228:
229: /*
230: * NAME: dobuckets()
231: * DESCRIPTION: fill or flush an array of cache buckets to a volume
232: */
233: static
234: int dobuckets(hfsvol *vol, bucket **chain, unsigned int len,
235: int (*func)(hfsvol *, bucket **, unsigned int *))
236: {
237: unsigned int count, i;
238: int result = 0;
239:
240: qsort(chain, len, sizeof(*chain),
241: (int (*)(const void *, const void *)) compare);
242:
243: for (i = 0; i < len; i += count)
244: {
245: count = len - i;
246: if (func(vol, chain + i, &count) == -1)
247: result = -1;
248: }
249:
250: return result;
251: }
252:
253: # define fillbuckets(vol, chain, len) dobuckets(vol, chain, len, fillchain)
254:
255: /*
256: * NAME: block->finish()
257: * DESCRIPTION: commit and free a volume's block cache
258: */
259: int b_finish(hfsvol *vol)
260: {
261: int result = 0;
262:
263: if (vol->cache == NULL)
264: goto done;
265:
266: # ifdef DEBUG
267: b_dumpcache(vol->cache);
268: # endif
269:
270: FREE(vol->cache);
271: vol->cache = NULL;
272:
273: done:
274: return result;
275: }
276:
277: /*
278: * NAME: findbucket()
279: * DESCRIPTION: locate a bucket in the cache, and/or its hash slot
280: */
281: static
282: bucket *findbucket(bcache *cache, unsigned long bnum, bucket ***hslot)
283: {
284: bucket *b;
285:
286: *hslot = &cache->hash[bnum & (HFS_HASHSZ - 1)];
287:
288: for (b = **hslot; b; b = b->hnext)
289: {
290: if (INUSE(b) && b->bnum == bnum)
291: break;
292: }
293:
294: return b;
295: }
296:
297: /*
298: * NAME: reuse()
299: * DESCRIPTION: free a bucket for reuse, flushing if necessary
300: */
301: static
302: int reuse(bcache *cache, bucket *b, unsigned long bnum)
303: {
304: bucket *bptr;
305: int i;
306:
307: # ifdef DEBUG
308: if (INUSE(b))
309: fprintf(stderr, "BLOCK: CACHE reusing bucket containing "
310: "vol 0x%lx block %lu:%u\n",
311: (unsigned long) cache->vol, b->bnum, b->count);
312: # endif
313:
314: if (INUSE(b) && DIRTY(b))
315: {
316: /* flush most recently unused buckets */
317:
318: for (bptr = b, i = 0; i < HFS_BLOCKBUFSZ; ++i)
319: {
320: bptr = bptr->cprev;
321: }
322: }
323:
324: b->flags &= ~HFS_BUCKET_INUSE;
325: b->count = 1;
326: b->bnum = bnum;
327:
328: return 0;
329: }
330:
331: /*
332: * NAME: cplace()
333: * DESCRIPTION: move a bucket to an appropriate place near head of the chain
334: */
335: static
336: void cplace(bcache *cache, bucket *b)
337: {
338: bucket *p;
339:
340: for (p = cache->tail->cnext; p->count > 1; p = p->cnext)
341: --p->count;
342:
343: b->cnext->cprev = b->cprev;
344: b->cprev->cnext = b->cnext;
345:
346: if (cache->tail == b)
347: cache->tail = b->cprev;
348:
349: b->cprev = p->cprev;
350: b->cnext = p;
351:
352: p->cprev->cnext = b;
353: p->cprev = b;
354: }
355:
356: /*
357: * NAME: hplace()
358: * DESCRIPTION: move a bucket to the head of its hash slot
359: */
360: static
361: void hplace(bucket **hslot, bucket *b)
362: {
363: if (*hslot != b)
364: {
365: if (b->hprev)
366: *b->hprev = b->hnext;
367: if (b->hnext)
368: b->hnext->hprev = b->hprev;
369:
370: b->hprev = hslot;
371: b->hnext = *hslot;
372:
373: if (*hslot)
374: (*hslot)->hprev = &b->hnext;
375:
376: *hslot = b;
377: }
378: }
379:
380: /*
381: * NAME: getbucket()
382: * DESCRIPTION: fetch a bucket from the cache, or an empty one to be filled
383: */
384: static
385: bucket *getbucket(bcache *cache, unsigned long bnum, int fill)
386: {
387: bucket **hslot, *b, *p, *bptr,
388: *chain[HFS_BLOCKBUFSZ], **slots[HFS_BLOCKBUFSZ];
389:
390: b = findbucket(cache, bnum, &hslot);
391:
392: if (b)
393: {
394: /* cache hit; move towards head of cache chain */
395:
396: ++cache->hits;
397:
398: if (++b->count > b->cprev->count &&
399: b != cache->tail->cnext)
400: {
401: p = b->cprev;
402:
403: p->cprev->cnext = b;
404: b->cnext->cprev = p;
405:
406: p->cnext = b->cnext;
407: b->cprev = p->cprev;
408:
409: p->cprev = b;
410: b->cnext = p;
411:
412: if (cache->tail == b)
413: cache->tail = p;
414: }
415: }
416: else
417: {
418: /* cache miss; reuse least-used cache bucket */
419:
420: ++cache->misses;
421:
422: b = cache->tail;
423:
424: if (reuse(cache, b, bnum) == -1)
425: goto fail;
426:
427: if (fill)
428: {
429: unsigned int len = 0;
430:
431: chain[len] = b;
432: slots[len++] = hslot;
433:
434: for (bptr = b->cprev;
435: len < (HFS_BLOCKBUFSZ >> 1) && ++bnum < cache->vol->vlen;
436: bptr = bptr->cprev)
437: {
438: if (findbucket(cache, bnum, &hslot))
439: break;
440:
441: if (reuse(cache, bptr, bnum) == -1)
442: goto fail;
443:
444: chain[len] = bptr;
445: slots[len++] = hslot;
446: }
447:
448: if (fillbuckets(cache->vol, chain, len) == -1)
449: goto fail;
450:
451: while (--len)
452: {
453: cplace(cache, chain[len]);
454: hplace(slots[len], chain[len]);
455: }
456:
457: hslot = slots[0];
458: }
459:
460: /* move bucket to appropriate place in chain */
461:
462: cplace(cache, b);
463: }
464:
465: /* insert at front of hash chain */
466:
467: hplace(hslot, b);
468:
469: return b;
470:
471: fail:
472: return NULL;
473: }
474:
475: /*
476: * NAME: block->readpb()
477: * DESCRIPTION: read blocks from the physical medium (bypassing cache)
478: */
479: int b_readpb(hfsvol *vol, unsigned long bnum, block *bp, unsigned int blen)
480: {
481: unsigned long nblocks;
482:
483: # ifdef DEBUG
484: fprintf(stderr, "BLOCK: READ vol 0x%lx block %lu",
485: (unsigned long) vol, bnum);
486: if (blen > 1)
487: fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
488: else
489: fprintf(stderr, "\n");
490: # endif
491:
492: nblocks = os_seek(vol->os_fd, bnum, HFS_BLOCKSZ_BITS );
493: if (nblocks == (unsigned long) -1)
494: goto fail;
495:
496: if (nblocks != bnum)
497: ERROR(EIO, "block seek failed for read");
498:
499: nblocks = os_read(vol->os_fd, bp, blen, HFS_BLOCKSZ_BITS);
500: if (nblocks == (unsigned long) -1)
501: goto fail;
502:
503: if (nblocks != blen)
504: ERROR(EIO, "incomplete block read");
505:
506: return 0;
507:
508: fail:
509: return -1;
510: }
511:
512:
513: /*
514: * NAME: block->readlb()
515: * DESCRIPTION: read a logical block from a volume (or from the cache)
516: */
517: int b_readlb(hfsvol *vol, unsigned long bnum, block *bp)
518: {
519: if (vol->vlen > 0 && bnum >= vol->vlen)
520: ERROR(EIO, "read nonexistent logical block");
521:
522: if (vol->cache)
523: {
524: bucket *b;
525:
526: b = getbucket(vol->cache, bnum, 1);
527: if (b == NULL)
528: goto fail;
529:
530: memcpy(bp, b->data, HFS_BLOCKSZ);
531: }
532: else
533: {
534: if (b_readpb(vol, vol->vstart + bnum, bp, 1) == -1)
535: goto fail;
536: }
537:
538: return 0;
539:
540: fail:
541: return -1;
542: }
543:
544: /*
545: * NAME: block->readab()
546: * DESCRIPTION: read a block from an allocation block from a volume
547: */
548: int b_readab(hfsvol *vol, unsigned int anum, unsigned int index, block *bp)
549: {
550: /* verify the allocation block exists and is marked as in-use */
551:
552: if (anum >= vol->mdb.drNmAlBlks)
553: ERROR(EIO, "read nonexistent allocation block");
554: else if (vol->vbm && ! BMTST(vol->vbm, anum))
555: ERROR(EIO, "read unallocated block");
556:
557: return b_readlb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
558:
559: fail:
560: return -1;
561: }
562:
563:
564: /*
565: * NAME: block->size()
566: * DESCRIPTION: return the number of physical blocks on a volume's medium
567: */
568: unsigned long b_size(hfsvol *vol)
569: {
570: unsigned long low, high, mid;
571: block b;
572:
573: high = os_seek(vol->os_fd, -1, HFS_BLOCKSZ_BITS);
574:
575: if (high != (unsigned long) -1 && high > 0)
576: return high;
577:
578: /* manual size detection: first check there is at least 1 block in medium */
579:
580: if (b_readpb(vol, 0, &b, 1) == -1)
581: ERROR(EIO, "size of medium indeterminable or empty");
582:
583: for (low = 0, high = 2880;
584: high > 0 && b_readpb(vol, high - 1, &b, 1) != -1;
585: high <<= 1)
586: low = high - 1;
587:
588: if (high == 0)
589: ERROR(EIO, "size of medium indeterminable or too large");
590:
591: /* common case: 1440K floppy */
592:
593: if (low == 2879 && b_readpb(vol, 2880, &b, 1) == -1)
594: return 2880;
595:
596: /* binary search for other sizes */
597:
598: while (low < high - 1)
599: {
600: mid = (low + high) >> 1;
601:
602: if (b_readpb(vol, mid, &b, 1) == -1)
603: high = mid;
604: else
605: low = mid;
606: }
607:
608: return low + 1;
609:
610: fail:
611: return 0;
612: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.