Boot loader fixes - fix recursive malloc()/free() errors, NULL freed fields
[dragonfly.git] / lib / libstand / hammerread.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Simon Schubert <corecode@fs.ei.tum.de>
6 * and Matthew Dillon <dillon@backplane.com>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 * 3. Neither the name of The DragonFly Project nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific, prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * $DragonFly: src/lib/libstand/hammerread.c,v 1.2 2008/10/29 22:14:25 swildner Exp $
36 */
37
38/*
39 * This file is being used by boot2 and libstand (loader).
40 * Compile with -DTESTING to obtain a binary.
41 */
42
43
44#if !defined(BOOT2) && !defined(TESTING)
45#define LIBSTAND 1
46#endif
47
48#include <sys/param.h>
49
50#include <stddef.h>
51#include <stdint.h>
52
53#ifdef TESTING
54#include <sys/fcntl.h>
55#include <sys/stat.h>
56#include <unistd.h>
57#include <err.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61#include <errno.h>
62#include <dirent.h>
63#endif
64
65#ifdef LIBSTAND
66#include "stand.h"
67#endif
68
69#include <vfs/hammer/hammer_disk.h>
70
71#ifndef BOOT2
72struct blockentry {
73 hammer_off_t off;
74 int use;
75 char *data;
76};
77
78#ifdef TESTING
79#define NUMCACHE 16
80#else
81#define NUMCACHE 6
82#endif
83
84struct hfs {
85#ifdef TESTING
86 int fd;
87#else // libstand
88 struct open_file *f;
89#endif
90 hammer_off_t root;
91 int64_t buf_beg;
92 int lru;
93 struct blockentry cache[NUMCACHE];
94};
95
96static void *
97hread(struct hfs *hfs, hammer_off_t off)
98{
99 hammer_off_t boff = off & ~HAMMER_BUFMASK64;
100
101 boff &= HAMMER_OFF_LONG_MASK;
102
103 if (HAMMER_ZONE_DECODE(off) != HAMMER_ZONE_RAW_VOLUME_INDEX)
104 boff += hfs->buf_beg;
105
106 struct blockentry *be = NULL;
107 for (int i = 0; i < NUMCACHE; i++) {
108 if (be == NULL || be->use > hfs->cache[i].use)
109 be = &hfs->cache[i];
110 if (hfs->cache[i].off == boff) {
111 be = &hfs->cache[i];
112 break;
113 }
114 }
115 if (be->off != boff) {
116 // Didn't find any match
117 be->off = boff;
118#ifdef TESTING
119 ssize_t res = pread(hfs->fd, be->data, HAMMER_BUFSIZE,
120 boff & HAMMER_OFF_SHORT_MASK);
121 if (res != HAMMER_BUFSIZE)
122 err(1, "short read on off %llx", boff);
123#else // libstand
124 size_t rlen;
125 int rv = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ,
126 boff >> DEV_BSHIFT, HAMMER_BUFSIZE,
127 be->data, &rlen);
128 if (rv || rlen != HAMMER_BUFSIZE)
129 return (NULL);
130#endif
131 }
132
133 be->use = ++hfs->lru;
134 return &be->data[off & HAMMER_BUFMASK];
135}
136
137#else /* BOOT2 */
138
139struct dmadat {
140 char secbuf[DEV_BSIZE];
141 char buf[HAMMER_BUFSIZE];
142};
143
144static struct dmadat *dmadat;
145
146struct hfs {
147 hammer_off_t root;
148 int64_t buf_beg;
149};
150
151static void *
152hread(struct hfs *hfs, hammer_off_t off)
153{
154 char *buf = dmadat->buf;
155
156 hammer_off_t boff = off & ~HAMMER_BUFMASK64;
157 boff &= HAMMER_OFF_LONG_MASK;
158 if (HAMMER_ZONE_DECODE(off) != HAMMER_ZONE_RAW_VOLUME_INDEX)
159 boff += hfs->buf_beg;
160 boff &= HAMMER_OFF_SHORT_MASK;
161 boff >>= DEV_BSHIFT;
162 if (dskread(buf, boff, HAMMER_BUFSIZE >> DEV_BSHIFT))
163 return (NULL);
164 return (&buf[off & HAMMER_BUFMASK]);
165}
166
167static void
168bzero(void *buf, size_t size)
169{
170 for (size_t i = 0; i < size; i++)
171 ((char *)buf)[i] = 0;
172}
173
174static void
175bcopy(void *src, void *dst, size_t size)
176{
177 memcpy(dst, src, size);
178}
179
180static size_t
181strlen(const char *s)
182{
183 size_t l = 0;
184 for (; *s != 0; s++)
185 l++;
186 return (l);
187}
188
189static int
190memcmp(const void *a, const void *b, size_t len)
191{
192 for (size_t p = 0; p < len; p++) {
193 int r = ((const char *)a)[p] - ((const char *)b)[p];
194 if (r != 0)
195 return (r);
196 }
197
198 return (0);
199}
200
201#endif
202
203/*
204 * (from hammer_btree.c)
205 *
206 * Compare two B-Tree elements, return -N, 0, or +N (e.g. similar to strcmp).
207 *
208 * Note that for this particular function a return value of -1, 0, or +1
209 * can denote a match if create_tid is otherwise discounted. A create_tid
210 * of zero is considered to be 'infinity' in comparisons.
211 *
212 * See also hammer_rec_rb_compare() and hammer_rec_cmp() in hammer_object.c.
213 */
214static int
215hammer_btree_cmp(hammer_base_elm_t key1, hammer_base_elm_t key2)
216{
217 if (key1->localization < key2->localization)
218 return(-5);
219 if (key1->localization > key2->localization)
220 return(5);
221
222 if (key1->obj_id < key2->obj_id)
223 return(-4);
224 if (key1->obj_id > key2->obj_id)
225 return(4);
226
227 if (key1->rec_type < key2->rec_type)
228 return(-3);
229 if (key1->rec_type > key2->rec_type)
230 return(3);
231
232 if (key1->key < key2->key)
233 return(-2);
234 if (key1->key > key2->key)
235 return(2);
236
237 /*
238 * A create_tid of zero indicates a record which is undeletable
239 * and must be considered to have a value of positive infinity.
240 */
241 if (key1->create_tid == 0) {
242 if (key2->create_tid == 0)
243 return(0);
244 return(1);
245 }
246 if (key2->create_tid == 0)
247 return(-1);
248 if (key1->create_tid < key2->create_tid)
249 return(-1);
250 if (key1->create_tid > key2->create_tid)
251 return(1);
252 return(0);
253}
254
255/*
256 * Heuristical search for the first element whos comparison is <= 1. May
257 * return an index whos compare result is > 1 but may only return an index
258 * whos compare result is <= 1 if it is the first element with that result.
259 */
260static int
261hammer_btree_search_node(hammer_base_elm_t elm, hammer_node_ondisk_t node)
262{
263 int b;
264 int s;
265 int i;
266 int r;
267
268 /*
269 * Don't bother if the node does not have very many elements
270 */
271 b = 0;
272 s = node->count;
273 while (s - b > 4) {
274 i = b + (s - b) / 2;
275 r = hammer_btree_cmp(elm, &node->elms[i].leaf.base);
276 if (r <= 1) {
277 s = i;
278 } else {
279 b = i;
280 }
281 }
282 return(b);
283}
284
285#if 0
286/*
287 * (from hammer_subs.c)
288 *
289 * Return a namekey hash. The 64 bit namekey hash consists of a 32 bit
290 * crc in the MSB and 0 in the LSB. The caller will use the low bits to
291 * generate a unique key and will scan all entries with the same upper
292 * 32 bits when issuing a lookup.
293 *
294 * We strip bit 63 in order to provide a positive key, this way a seek
295 * offset of 0 will represent the base of the directory.
296 *
297 * This function can never return 0. We use the MSB-0 space to synthesize
298 * artificial directory entries such as "." and "..".
299 */
300static int64_t
301hammer_directory_namekey(const void *name, int len)
302{
303 int64_t key;
304
305 key = (int64_t)(crc32(name, len) & 0x7FFFFFFF) << 32;
306 if (key == 0)
307 key |= 0x100000000LL;
308 return(key);
309}
310#else
311static int64_t
312hammer_directory_namekey(const void *name __unused, int len __unused)
313{
314 return (0);
315}
316#endif
317
318
319#ifndef BOOT2
320/*
321 * Misc
322 */
323static u_int32_t
324hammer_to_unix_xid(uuid_t *uuid)
325{
326 return(*(u_int32_t *)&uuid->node[2]);
327}
328
329static int
330hammer_get_dtype(u_int8_t obj_type)
331{
332 switch(obj_type) {
333 case HAMMER_OBJTYPE_DIRECTORY:
334 return(DT_DIR);
335 case HAMMER_OBJTYPE_REGFILE:
336 return(DT_REG);
337 case HAMMER_OBJTYPE_DBFILE:
338 return(DT_DBF);
339 case HAMMER_OBJTYPE_FIFO:
340 return(DT_FIFO);
341 case HAMMER_OBJTYPE_SOCKET:
342 return(DT_SOCK);
343 case HAMMER_OBJTYPE_CDEV:
344 return(DT_CHR);
345 case HAMMER_OBJTYPE_BDEV:
346 return(DT_BLK);
347 case HAMMER_OBJTYPE_SOFTLINK:
348 return(DT_LNK);
349 default:
350 return(DT_UNKNOWN);
351 }
352 /* not reached */
353}
354
355static int
356hammer_get_mode(u_int8_t obj_type)
357{
358 switch(obj_type) {
359 case HAMMER_OBJTYPE_DIRECTORY:
360 return(S_IFDIR);
361 case HAMMER_OBJTYPE_REGFILE:
362 return(S_IFREG);
363 case HAMMER_OBJTYPE_DBFILE:
364 return(S_IFDB);
365 case HAMMER_OBJTYPE_FIFO:
366 return(S_IFIFO);
367 case HAMMER_OBJTYPE_SOCKET:
368 return(S_IFSOCK);
369 case HAMMER_OBJTYPE_CDEV:
370 return(S_IFCHR);
371 case HAMMER_OBJTYPE_BDEV:
372 return(S_IFBLK);
373 case HAMMER_OBJTYPE_SOFTLINK:
374 return(S_IFLNK);
375 default:
376 return(0);
377 }
378 /* not reached */
379}
380
381#if DEBUG > 1
382static void
383hprintb(hammer_base_elm_t e)
384{
385 printf("%d/", e->localization);
386 if (e->obj_id >> 32 != 0)
387 printf("%lx%08lx",
388 (long)(e->obj_id >> 32),
389 (long)(e->obj_id & 0xffffffff));
390 else
391 printf("%lx", (long)e->obj_id);
392 printf("/%d/", e->rec_type);
393 if (e->key >> 32 != 0)
394 printf("%lx%08lx",
395 (long)(e->key >> 32),
396 (long)(e->key & 0xffffffff));
397 else
398 printf("%lx", (long)e->key);
399#ifdef TESTING
400 printf("/%llx/%llx", e->create_tid, e->delete_tid);
401#endif
402}
403#endif /* DEBUG > 1 */
404#endif /* !BOOT2 */
405
406static hammer_btree_leaf_elm_t
407hfind(struct hfs *hfs, hammer_base_elm_t key, hammer_base_elm_t end)
408{
409#if DEBUG > 1
410 printf("searching for ");
411 hprintb(key);
412 printf(" end ");
413 hprintb(end);
414 printf("\n");
415#endif
416
417 int n;
418 int r;
419 struct hammer_base_elm search = *key;
420 struct hammer_base_elm backtrack;
421 hammer_off_t nodeoff = hfs->root;
422 hammer_node_ondisk_t node;
423 hammer_btree_elm_t e = NULL;
424 int internal;
425
426loop:
427 node = hread(hfs, nodeoff);
428 if (node == NULL)
429 return (NULL);
430 internal = node->type == HAMMER_BTREE_TYPE_INTERNAL;
431
432#if DEBUG > 3
433 for (int i = 0; i < node->count; i++) {
434 printf("E: ");
435 hprintb(&node->elms[i].base);
436 printf("\n");
437 }
438 if (internal) {
439 printf("B: ");
440 hprintb(&node->elms[node->count].base);
441 printf("\n");
442 }
443#endif
444
445 n = hammer_btree_search_node(&search, node);
446
447 // In internal nodes, we cover the right boundary as well.
448 // If we hit it, we'll backtrack.
449 for (; n < node->count + internal; n++) {
450 e = &node->elms[n];
451 r = hammer_btree_cmp(&search, &e->base);
452
453 if (r < 0)
454 break;
455 }
456
457 // unless we stopped right on the left side, we need to back off a bit
458 if (n > 0)
459 e = &node->elms[--n];
460
461#if DEBUG > 2
462 printf(" found: ");
463 hprintb(&e->base);
464 printf("\n");
465#endif
466
467 if (internal) {
468 // If we hit the right boundary, backtrack to
469 // the next higher level.
470 if (n == node->count)
471 goto backtrack;
472 nodeoff = e->internal.subtree_offset;
473 backtrack = (e+1)->base;
474 goto loop;
475 }
476
477 r = hammer_btree_cmp(key, &e->base);
478 // If we're more off than the createtid, take the next elem
479 if (r > 1) {
480 e++;
481 n++;
482 }
483
484 // Skip deleted elements
485 while (n < node->count && e->base.delete_tid != 0) {
486 e++;
487 n++;
488 }
489
490 // In the unfortunate event when there is no next
491 // element in this node, we repeat the search with
492 // a key beyond the right boundary
493 if (n == node->count) {
494backtrack:
495 search = backtrack;
496 nodeoff = hfs->root;
497
498#if DEBUG > 2
499 printf("hit right boundary (%d), resetting search to ",
500 node->count);
501 hprintb(&search);
502 printf("\n");
503#endif
504 goto loop;
505 }
506
507#if DEBUG > 1
508 printf(" result: ");
509 hprintb(&e->base);
510 printf("\n");
511#endif
512
513 if (end != NULL)
514 if (hammer_btree_cmp(end, &e->base) < -1)
515 goto fail;
516
517 return (&e->leaf);
518
519fail:
520#if DEBUG > 1
521 printf(" fail.\n");
522#endif
523 return (NULL);
524}
525
526#ifndef BOOT2
527static int
528hreaddir(struct hfs *hfs, ino_t ino, int64_t *off, struct dirent *de)
529{
530 struct hammer_base_elm key, end;
531
532#if DEBUG > 2
533 printf("%s(%llx, %lld)\n", __FUNCTION__, (long long)ino, *off);
534#endif
535
536 bzero(&key, sizeof(key));
537 key.obj_id = ino;
538 key.localization = HAMMER_LOCALIZE_MISC;
539 key.rec_type = HAMMER_RECTYPE_DIRENTRY;
540 key.key = *off;
541
542 end = key;
543 end.key = HAMMER_MAX_KEY;
544
545 hammer_btree_leaf_elm_t e;
546
547 e = hfind(hfs, &key, &end);
548 if (e == NULL) {
549 errno = ENOENT;
550 return (-1);
551 }
552
553 *off = e->base.key + 1; // remember next pos
554
555 de->d_namlen = e->data_len - HAMMER_ENTRY_NAME_OFF;
556 de->d_type = hammer_get_dtype(e->base.obj_type);
557 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
558 if (ed == NULL)
559 return (-1);
560 de->d_ino = ed->entry.obj_id;
561 bcopy(ed->entry.name, de->d_name, de->d_namlen);
562 de->d_name[de->d_namlen] = 0;
563
564 return (0);
565}
566#endif
567
568static ino_t
569hresolve(struct hfs *hfs, ino_t dirino, const char *name)
570{
571 struct hammer_base_elm key, end;
572 size_t namel = strlen(name);
573
574#if DEBUG > 2
575 printf("%s(%llx, %s)\n", __FUNCTION__, (long long)dirino, name);
576#endif
577
578 bzero(&key, sizeof(key));
579 key.obj_id = dirino;
580 key.localization = HAMMER_LOCALIZE_MISC;
581 key.key = hammer_directory_namekey(name, namel);
582 key.rec_type = HAMMER_RECTYPE_DIRENTRY;
583 end = key;
584 end.key = HAMMER_MAX_KEY;
585
586 hammer_btree_leaf_elm_t e;
587 while ((e = hfind(hfs, &key, &end)) != NULL) {
588 key.key = e->base.key + 1;
589
590 size_t elen = e->data_len - HAMMER_ENTRY_NAME_OFF;
591 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
592 if (ed == NULL)
593 return (-1);
594#ifdef BOOT2
595 if (ls) {
596 for (int i = 0; i < elen; i++)
597 putchar(ed->entry.name[i]);
598 putchar(' ');
599 ls = 2;
600 continue;
601 }
602#endif
603 if (elen == namel && memcmp(ed->entry.name, name, MIN(elen, namel)) == 0)
604 return (ed->entry.obj_id);
605 }
606
607#if BOOT2
608 if (ls == 2)
609 printf("\n");
610#endif
611
612 return -1;
613}
614
615static ino_t
616hlookup(struct hfs *hfs, const char *path)
617{
618#if DEBUG > 2
619 printf("%s(%s)\n", __FUNCTION__, path);
620#endif
621
622#ifdef BOOT2
623 ls = 0;
624#endif
625 ino_t ino = 1;
626 do {
627 char name[MAXPATHLEN + 1];
628 while (*path == '/')
629 path++;
630 if (*path == 0)
631 break;
632 for (char *n = name; *path != 0 && *path != '/'; path++, n++) {
633 n[0] = *path;
634 n[1] = 0;
635 }
636
637#ifdef BOOT2
638 // A single ? means "list"
639 if (name[0] == '?' && name[1] == 0)
640 ls = 1;
641#endif
642
643 ino = hresolve(hfs, ino, name);
644 } while (ino != (ino_t)-1 && *path != 0);
645
646 return (ino);
647}
648
649
650#ifndef BOOT2
651static int
652hstat(struct hfs *hfs, ino_t ino, struct stat* st)
653{
654 struct hammer_base_elm key;
655
656#if DEBUG > 2
657 printf("%s(%llx)\n", __FUNCTION__, (long long)ino);
658#endif
659
660 bzero(&key, sizeof(key));
661 key.obj_id = ino;
662 key.localization = HAMMER_LOCALIZE_INODE;
663 key.rec_type = HAMMER_RECTYPE_INODE;
664
665 hammer_btree_leaf_elm_t e = hfind(hfs, &key, &key);
666 if (e == NULL) {
667#ifndef BOOT2
668 errno = ENOENT;
669#endif
670 return -1;
671 }
672
673 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
674 if (ed == NULL)
675 return (-1);
676
677 st->st_mode = ed->inode.mode | hammer_get_mode(ed->inode.obj_type);
678 st->st_uid = hammer_to_unix_xid(&ed->inode.uid);
679 st->st_gid = hammer_to_unix_xid(&ed->inode.gid);
680 st->st_size = ed->inode.size;
681
682 return (0);
683}
684#endif
685
686static ssize_t
687hreadf(struct hfs *hfs, ino_t ino, int64_t off, int64_t len, char *buf)
688{
689 int64_t startoff = off;
690 struct hammer_base_elm key, end;
691
692 bzero(&key, sizeof(key));
693 key.obj_id = ino;
694 key.localization = HAMMER_LOCALIZE_MISC;
695 key.rec_type = HAMMER_RECTYPE_DATA;
696 end = key;
697 end.key = HAMMER_MAX_KEY;
698
699 while (len > 0) {
700 key.key = off + 1;
701 hammer_btree_leaf_elm_t e = hfind(hfs, &key, &end);
702 int64_t dlen;
703
704 if (e == NULL || off > e->base.key) {
705 bzero(buf, len);
706 off += len;
707 len = 0;
708 break;
709 }
710
711 int64_t doff = e->base.key - e->data_len;
712 if (off < doff) {
713 // sparse file, beginning
714 dlen = doff - off;
715 dlen = MIN(dlen, len);
716 bzero(buf, dlen);
717 } else {
718 int64_t boff = off - doff;
719 hammer_off_t roff = e->data_offset;
720
721 dlen = e->data_len;
722 dlen -= boff;
723 dlen = MIN(dlen, len);
724
725 while (boff >= HAMMER_BUFSIZE) {
726 boff -= HAMMER_BUFSIZE;
727 roff += HAMMER_BUFSIZE;
728 }
729
730 /*
731 * boff - relative offset in disk buffer (not aligned)
732 * roff - base offset of disk buffer (not aligned)
733 * dlen - amount of data we think we can copy
734 *
735 * hread only reads 16K aligned buffers, check for
736 * a length overflow and truncate dlen appropriately.
737 */
738 if ((roff & ~HAMMER_BUFMASK64) != ((roff + boff + dlen - 1) & ~HAMMER_BUFMASK64))
739 dlen = HAMMER_BUFSIZE - ((boff + roff) & HAMMER_BUFMASK);
740 char *data = hread(hfs, roff);
741 if (data == NULL)
742 return (-1);
743 bcopy(data + boff, buf, dlen);
744 }
745
746 buf += dlen;
747 off += dlen;
748 len -= dlen;
749 }
750
751 return (off - startoff);
752}
753
754#ifdef BOOT2
755struct hfs hfs;
756
757static int
758hammerinit(void)
759{
760 if (dsk_meta)
761 return (0);
762
763 hammer_volume_ondisk_t volhead = hread(&hfs, HAMMER_ZONE_ENCODE(1, 0));
764 if (volhead == NULL)
765 return (-1);
766 if (volhead->vol_signature != HAMMER_FSBUF_VOLUME)
767 return (-1);
768 hfs.root = volhead->vol0_btree_root;
769 hfs.buf_beg = volhead->vol_buf_beg;
770 dsk_meta++;
771 return (0);
772}
773
774static ino_t
775lookup(const char *path)
776{
777 hammerinit();
778
779 ino_t ino = hlookup(&hfs, path);
780
781 if (ino == -1)
782 ino = 0;
783
784 fs_off = 0;
785
786 return (ino);
787}
788
789static ssize_t
790fsread(ino_t ino, void *buf, size_t len)
791{
792 hammerinit();
793
794 ssize_t rlen = hreadf(&hfs, ino, fs_off, len, buf);
795 if (rlen != -1)
796 fs_off += rlen;
797 return (rlen);
798}
799#endif
800
801#ifndef BOOT2
802static int
803hinit(struct hfs *hfs)
804{
805#if DEBUG
806 printf("hinit\n");
807#endif
808 for (int i = 0; i < NUMCACHE; i++) {
809 hfs->cache[i].data = malloc(HAMMER_BUFSIZE);
810 hfs->cache[i].off = -1; // invalid
811 hfs->cache[i].use = 0;
812
813#if DEBUG
814 if (hfs->cache[i].data == NULL)
815 printf("malloc failed\n");
816#endif
817 }
818 hfs->lru = 0;
819
820 hammer_volume_ondisk_t volhead = hread(hfs, HAMMER_ZONE_ENCODE(1, 0));
821
822#ifdef TESTING
823 if (volhead) {
824 printf("signature: %svalid\n",
825 volhead->vol_signature != HAMMER_FSBUF_VOLUME ?
826 "in" :
827 "");
828 printf("name: %s\n", volhead->vol_name);
829 }
830#endif
831
832 if (volhead == NULL || volhead->vol_signature != HAMMER_FSBUF_VOLUME) {
833 for (int i = 0; i < NUMCACHE; i++) {
834 free(hfs->cache[i].data);
835 hfs->cache[i].data = NULL;
836 }
837 errno = ENODEV;
838 return (-1);
839 }
840
841 hfs->root = volhead->vol0_btree_root;
842 hfs->buf_beg = volhead->vol_buf_beg;
843
844 return (0);
845}
846
847static void
848hclose(struct hfs *hfs)
849{
850#if DEBUG
851 printf("hclose\n");
852#endif
853 for (int i = 0; i < NUMCACHE; i++) {
854 if (hfs->cache[i].data) {
855 free(hfs->cache[i].data);
856 hfs->cache[i].data = NULL;
857 }
858 }
859}
860#endif
861
862#ifdef LIBSTAND
863struct hfile {
864 struct hfs hfs;
865 ino_t ino;
866 int64_t fsize;
867};
868
869static int
870hammer_open(const char *path, struct open_file *f)
871{
872 struct hfile *hf = malloc(sizeof(*hf));
873
874 bzero(hf, sizeof(*hf));
875 f->f_fsdata = hf;
876 hf->hfs.f = f;
877 f->f_offset = 0;
878
879 int rv = hinit(&hf->hfs);
880 if (rv) {
881 f->f_fsdata = NULL;
882 free(hf);
883 return (rv);
884 }
885
886#if DEBUG
887 printf("hammer_open %s %p %ld\n", path, f);
888#endif
889
890 hf->ino = hlookup(&hf->hfs, path);
891 if (hf->ino == -1)
892 goto fail;
893
894 struct stat st;
895 if (hstat(&hf->hfs, hf->ino, &st) == -1)
896 goto fail;
897 hf->fsize = st.st_size;
898
899#if DEBUG
900 printf(" %ld\n", (long)hf->fsize);
901#endif
902
903 return (0);
904
905fail:
906#if DEBUG
907 printf("hammer_open fail\n");
908#endif
909 f->f_fsdata = NULL;
910 hclose(&hf->hfs);
911 free(hf);
912 return (ENOENT);
913}
914
915static int
916hammer_close(struct open_file *f)
917{
918 struct hfile *hf = f->f_fsdata;
919
920 f->f_fsdata = NULL;
921 if (hf) {
922 hclose(&hf->hfs);
923 free(hf);
924 }
925 return (0);
926}
927
928static int
929hammer_read(struct open_file *f, void *buf, size_t len, size_t *resid)
930{
931 struct hfile *hf = f->f_fsdata;
932
933#if DEBUG
934 printf("hammer_read %p %ld %ld\n", f, f->f_offset, len);
935#endif
936
937 if (f->f_offset >= hf->fsize)
938 return (EINVAL);
939
940 size_t maxlen = len;
941 if (f->f_offset + len > hf->fsize)
942 maxlen = hf->fsize - f->f_offset;
943
944 ssize_t rlen = hreadf(&hf->hfs, hf->ino, f->f_offset, maxlen, buf);
945 if (rlen == -1)
946 return (EINVAL);
947
948 f->f_offset += rlen;
949
950 *resid = len - rlen;
951 return (0);
952}
953
954static off_t
955hammer_seek(struct open_file *f, off_t offset, int whence)
956{
957 struct hfile *hf = f->f_fsdata;
958
959 switch (whence) {
960 case SEEK_SET:
961 f->f_offset = offset;
962 break;
963 case SEEK_CUR:
964 f->f_offset += offset;
965 break;
966 case SEEK_END:
967 f->f_offset = hf->fsize - offset;
968 break;
969 default:
970 return (-1);
971 }
972 return (f->f_offset);
973}
974
975static int
976hammer_stat(struct open_file *f, struct stat *st)
977{
978 struct hfile *hf = f->f_fsdata;
979
980 return (hstat(&hf->hfs, hf->ino, st));
981}
982
983static int
984hammer_readdir(struct open_file *f, struct dirent *d)
985{
986 struct hfile *hf = f->f_fsdata;
987
988 int64_t off = f->f_offset;
989 int rv = hreaddir(&hf->hfs, hf->ino, &off, d);
990 f->f_offset = off;
991 return (rv);
992}
993
994// libstand
995struct fs_ops hammer_fsops = {
996 "hammer",
997 hammer_open,
998 hammer_close,
999 hammer_read,
1000 null_write,
1001 hammer_seek,
1002 hammer_stat,
1003 hammer_readdir
1004};
1005#endif // LIBSTAND
1006
1007#ifdef TESTING
1008int
1009main(int argc, char **argv)
1010{
1011 if (argc < 2) {
1012 fprintf(stderr, "usage: hammerread <dev>\n");
1013 return (1);
1014 }
1015
1016 struct hfs hfs;
1017 hfs.fd = open(argv[1], O_RDONLY);
1018 if (hfs.fd == -1)
1019 err(1, "unable to open %s", argv[1]);
1020
1021 if (hinit(&hfs) == -1)
1022 err(1, "invalid hammerfs");
1023
1024 for (int i = 2; i < argc; i++) {
1025 ino_t ino = hlookup(&hfs, argv[i]);
1026 if (ino == (ino_t)-1) {
1027 warn("hlookup %s", argv[i]);
1028 continue;
1029 }
1030
1031 struct stat st;
1032 if (hstat(&hfs, ino, &st)) {
1033 warn("hstat %s", argv[i]);
1034 continue;
1035 }
1036
1037 printf("%s %d/%d %o %lld\n",
1038 argv[i],
1039 st.st_uid, st.st_gid,
1040 st.st_mode, st.st_size);
1041
1042 if (S_ISDIR(st.st_mode)) {
1043 int64_t off = 0;
1044 struct dirent de;
1045 while (hreaddir(&hfs, ino, &off, &de) == 0) {
1046 printf("%s %d %llx\n",
1047 de.d_name, de.d_type, de.d_ino);
1048 }
1049 } else if (S_ISREG(st.st_mode)) {
1050 char *buf = malloc(100000);
1051 int64_t off = 0;
1052 while (off < st.st_size) {
1053 int64_t len = MIN(100000, st.st_size - off);
1054 int64_t rl = hreadf(&hfs, ino, off, len, buf);
1055 fwrite(buf, rl, 1, stdout);
1056 off += rl;
1057 }
1058 free(buf);
1059 }
1060 }
1061
1062 return 0;
1063}
1064#endif