Fix hammer read and add debug info.
[dragonfly.git] / lib / libstand / hammerread.c
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
72 struct 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
84 struct 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
96 static void *
97 hread(struct hfs *hfs, hammer_off_t off)
98 {
99         hammer_off_t boff = off & ~HAMMER_BUFMASK;
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
139 struct dmadat {
140         char            secbuf[DEV_BSIZE];
141         char            buf[HAMMER_BUFSIZE];
142 };
143
144 static struct dmadat *dmadat;
145
146 struct hfs {
147         hammer_off_t    root;
148         int64_t         buf_beg;
149 };
150
151 static void *
152 hread(struct hfs *hfs, hammer_off_t off)
153 {
154         char *buf = dmadat->buf;
155
156         hammer_off_t boff = off & ~HAMMER_BUFMASK;
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
167 static void
168 bzero(void *buf, size_t size)
169 {
170         for (size_t i = 0; i < size; i++)
171                 ((char *)buf)[i] = 0;
172 }
173
174 static void
175 bcopy(void *src, void *dst, size_t size)
176 {
177         memcpy(dst, src, size);
178 }
179
180 static size_t
181 strlen(const char *s)
182 {
183         size_t l = 0;
184         for (; *s != 0; s++)
185                 l++;
186         return (l);
187 }
188
189 static int
190 memcmp(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  */
214 static int
215 hammer_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  */
260 static int
261 hammer_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  */
300 static int64_t
301 hammer_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
311 static int64_t
312 hammer_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  */
323 static u_int32_t
324 hammer_to_unix_xid(uuid_t *uuid)
325 {
326         return(*(u_int32_t *)&uuid->node[2]);
327 }
328
329 static int
330 hammer_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
355 static int
356 hammer_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
382 static void
383 hprintb(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
406 static hammer_btree_leaf_elm_t
407 hfind(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
425 loop:
426         node = hread(hfs, nodeoff);
427         if (node == NULL)
428                 return (NULL);
429
430 #if DEBUG > 3
431         for (int i = 0; i < node->count; i++) {
432                 printf("E: ");
433                 hprintb(&node->elms[i].base);
434                 printf("\n");
435         }
436 #endif
437
438         n = hammer_btree_search_node(&search, node);
439
440         for (; n < node->count; n++) {
441                 e = &node->elms[n];
442                 r = hammer_btree_cmp(&search, &e->base);
443
444                 if (r < 0)
445                         break;
446         }
447
448         // unless we stopped right on the left side, we need to back off a bit
449         if (n > 0)
450                 e = &node->elms[n - 1];
451
452 #if DEBUG > 2
453         printf("  found: ");
454         hprintb(&e->base);
455         printf("\n");
456 #endif
457
458         if (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
459                 nodeoff = e->internal.subtree_offset;
460                 backtrack = (e+1)->base;
461                 goto loop;
462         }
463
464         r = hammer_btree_cmp(key, &e->base);
465         // If we're more off than the createtid, take the next elem
466         if (r > 1) {
467                 e++;
468                 n++;
469         }
470
471         // Skip deleted elements
472         while (n <= node->count && e->base.delete_tid != 0) {
473                 e++;
474                 n++;
475         }
476
477         // In the unfortunate event when there is no next
478         // element in this node, we repeat the search with
479         // a key beyond the right boundary
480         if (n > node->count) {
481                 search = backtrack;
482                 nodeoff = hfs->root;
483
484 #if DEBUG > 2
485                 printf("hit right boundary (%d), resetting search to ",
486                        node->count);
487                 hprintb(&search);
488                 printf("\n");
489 #endif
490                 goto loop;
491         }
492
493 #if DEBUG > 1
494         printf("  result: ");
495         hprintb(&e->base);
496         printf("\n");
497 #endif
498
499         if (end != NULL)
500                 if (hammer_btree_cmp(end, &e->base) < -1)
501                         goto fail;
502
503         return (&e->leaf);
504
505 fail:
506 #if DEBUG > 1
507         printf("  fail.\n");
508 #endif
509         return (NULL);
510 }
511
512 #ifndef BOOT2
513 static int
514 hreaddir(struct hfs *hfs, ino_t ino, int64_t *off, struct dirent *de)
515 {
516         struct hammer_base_elm key, end;
517
518 #if DEBUG > 2
519         printf("%s(%llx, %lld)\n", __FUNCTION__, (long long)ino, *off);
520 #endif
521
522         bzero(&key, sizeof(key));
523         key.obj_id = ino;
524         key.localization = HAMMER_LOCALIZE_MISC;
525         key.rec_type = HAMMER_RECTYPE_DIRENTRY;
526         key.key = *off;
527
528         end = key;
529         end.key = HAMMER_MAX_KEY;
530
531         hammer_btree_leaf_elm_t e;
532
533         e = hfind(hfs, &key, &end);
534         if (e == NULL) {
535                 errno = ENOENT;
536                 return (-1);
537         }
538
539         *off = e->base.key + 1;         // remember next pos
540
541         de->d_namlen = e->data_len - HAMMER_ENTRY_NAME_OFF;
542         de->d_type = hammer_get_dtype(e->base.obj_type);
543         hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
544         if (ed == NULL)
545                 return (-1);
546         de->d_ino = ed->entry.obj_id;
547         bcopy(ed->entry.name, de->d_name, de->d_namlen);
548         de->d_name[de->d_namlen] = 0;
549
550         return (0);
551 }
552 #endif
553
554 static ino_t
555 hresolve(struct hfs *hfs, ino_t dirino, const char *name)
556 {
557         struct hammer_base_elm key, end;
558         size_t namel = strlen(name);
559
560 #if DEBUG > 2
561         printf("%s(%llx, %s)\n", __FUNCTION__, (long long)dirino, name);
562 #endif
563
564         bzero(&key, sizeof(key));
565         key.obj_id = dirino;
566         key.localization = HAMMER_LOCALIZE_MISC;
567         key.key = hammer_directory_namekey(name, namel);
568         key.rec_type = HAMMER_RECTYPE_DIRENTRY;
569         end = key;
570         end.key = HAMMER_MAX_KEY;
571
572         hammer_btree_leaf_elm_t e;
573         while ((e = hfind(hfs, &key, &end)) != NULL) {
574                 key.key = e->base.key + 1;
575
576                 size_t elen = e->data_len - HAMMER_ENTRY_NAME_OFF;
577                 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
578                 if (ed == NULL)
579                         return (-1);
580 #ifdef BOOT2
581                 if (ls) {
582                         for (int i = 0; i < elen; i++)
583                                 putchar(ed->entry.name[i]);
584                         putchar(' ');
585                         ls = 2;
586                         continue;
587                 }
588 #endif
589                 if (elen == namel && memcmp(ed->entry.name, name, MIN(elen, namel)) == 0)
590                         return (ed->entry.obj_id);
591         }
592
593 #if BOOT2
594         if (ls == 2)
595                 printf("\n");
596 #endif
597
598         return -1;
599 }
600
601 static ino_t
602 hlookup(struct hfs *hfs, const char *path)
603 {
604 #if DEBUG > 2
605         printf("%s(%s)\n", __FUNCTION__, path);
606 #endif
607
608 #ifdef BOOT2
609         ls = 0;
610 #endif
611         ino_t ino = 1;
612         do {
613                 char name[MAXPATHLEN + 1];
614                 while (*path == '/')
615                         path++;
616                 if (*path == 0)
617                         break;
618                 for (char *n = name; *path != 0 && *path != '/'; path++, n++) {
619                         n[0] = *path;
620                         n[1] = 0;
621                 }
622
623 #ifdef BOOT2
624                 // A single ? means "list"
625                 if (name[0] == '?' && name[1] == 0)
626                         ls = 1;
627 #endif
628
629                 ino = hresolve(hfs, ino, name);
630         } while (ino != (ino_t)-1 && *path != 0);
631
632         return (ino);
633 }
634
635
636 #ifndef BOOT2
637 static int
638 hstat(struct hfs *hfs, ino_t ino, struct stat* st)
639 {
640         struct hammer_base_elm key;
641
642 #if DEBUG > 2
643         printf("%s(%llx)\n", __FUNCTION__, (long long)ino);
644 #endif
645
646         bzero(&key, sizeof(key));
647         key.obj_id = ino;
648         key.localization = HAMMER_LOCALIZE_INODE;
649         key.rec_type = HAMMER_RECTYPE_INODE;
650
651         hammer_btree_leaf_elm_t e = hfind(hfs, &key, &key);
652         if (e == NULL) {
653 #ifndef BOOT2
654                 errno = ENOENT;
655 #endif
656                 return -1;
657         }
658
659         hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
660         if (ed == NULL)
661                 return (-1);
662
663         st->st_mode = ed->inode.mode | hammer_get_mode(ed->inode.obj_type);
664         st->st_uid = hammer_to_unix_xid(&ed->inode.uid);
665         st->st_gid = hammer_to_unix_xid(&ed->inode.gid);
666         st->st_size = ed->inode.size;
667
668         return (0);
669 }
670 #endif
671
672 static ssize_t
673 hreadf(struct hfs *hfs, ino_t ino, int64_t off, int64_t len, char *buf)
674 {
675         int64_t startoff = off;
676         struct hammer_base_elm key, end;
677
678         bzero(&key, sizeof(key));
679         key.obj_id = ino;
680         key.localization = HAMMER_LOCALIZE_MISC;
681         key.rec_type = HAMMER_RECTYPE_DATA;
682         end = key;
683         end.key = HAMMER_MAX_KEY;
684
685         while (len > 0) {
686                 key.key = off + 1;
687                 hammer_btree_leaf_elm_t e = hfind(hfs, &key, &end);
688                 int64_t dlen;
689
690                 if (e == NULL || off > e->base.key) {
691                         bzero(buf, len);
692                         off += len;
693                         len = 0;
694                         break;
695                 }
696
697                 int64_t doff = e->base.key - e->data_len;
698                 if (off < doff) {
699                         // sparse file, beginning
700                         dlen = doff - off;
701                         dlen = MIN(dlen, len);
702                         bzero(buf, dlen);
703                 } else {
704                         int64_t boff = off - doff;
705                         hammer_off_t roff = e->data_offset;
706
707                         dlen = e->data_len;
708                         dlen -= boff;
709                         dlen = MIN(dlen, len);
710
711                         while (boff >= HAMMER_BUFSIZE) {
712                                 boff -= HAMMER_BUFSIZE;
713                                 roff += HAMMER_BUFSIZE;
714                         }
715
716                         // cut to HAMMER_BUFSIZE
717                         if ((roff & ~HAMMER_BUFMASK) != ((roff + dlen - 1) & ~HAMMER_BUFMASK))
718                                 dlen = HAMMER_BUFSIZE - ((boff + roff) & HAMMER_BUFMASK);
719
720                         char *data = hread(hfs, roff);
721                         if (data == NULL)
722                                 return (-1);
723                         bcopy(data + boff, buf, dlen);
724                 }
725
726                 buf += dlen;
727                 off += dlen;
728                 len -= dlen;
729         }
730
731         return (off - startoff);
732 }
733
734 #ifdef BOOT2
735 struct hfs hfs;
736
737 static int
738 hammerinit(void)
739 {
740         if (dsk_meta)
741                 return (0);
742
743         hammer_volume_ondisk_t volhead = hread(&hfs, HAMMER_ZONE_ENCODE(1, 0));
744         if (volhead == NULL)
745                 return (-1);
746         if (volhead->vol_signature != HAMMER_FSBUF_VOLUME)
747                 return (-1);
748         hfs.root = volhead->vol0_btree_root;
749         hfs.buf_beg = volhead->vol_buf_beg;
750         dsk_meta++;
751         return (0);
752 }
753
754 static ino_t
755 lookup(const char *path)
756 {
757         hammerinit();
758
759         ino_t ino = hlookup(&hfs, path);
760
761         if (ino == -1)
762                 ino = 0;
763         return (ino);
764 }
765
766 static ssize_t
767 fsread(ino_t ino, void *buf, size_t len)
768 {
769         hammerinit();
770
771         ssize_t rlen = hreadf(&hfs, ino, fs_off, len, buf);
772         if (rlen != -1)
773                 fs_off += rlen;
774         return (rlen);
775 }
776 #endif
777
778 #ifndef BOOT2
779 static int
780 hinit(struct hfs *hfs)
781 {
782 #if DEBUG
783         printf("hinit\n");
784 #endif
785         for (int i = 0; i < NUMCACHE; i++) {
786                 hfs->cache[i].data = malloc(HAMMER_BUFSIZE);
787                 hfs->cache[i].off = -1; // invalid
788                 hfs->cache[i].use = 0;
789
790 #if DEBUG
791                 if (hfs->cache[i].data == NULL)
792                         printf("malloc failed\n");
793 #endif
794         }
795         hfs->lru = 0;
796
797         hammer_volume_ondisk_t volhead = hread(hfs, HAMMER_ZONE_ENCODE(1, 0));
798         if (volhead == NULL)
799                 return (-1);
800
801 #ifdef TESTING
802         printf("signature: %svalid\n",
803                volhead->vol_signature != HAMMER_FSBUF_VOLUME ?
804                         "in" :
805                         "");
806         printf("name: %s\n", volhead->vol_name);
807 #endif
808
809         if (volhead->vol_signature != HAMMER_FSBUF_VOLUME) {
810                 for (int i = 0; i < NUMCACHE; i++)
811                         free(hfs->cache[i].data);
812                 errno = ENODEV;
813                 return (-1);
814         }
815
816         hfs->root = volhead->vol0_btree_root;
817         hfs->buf_beg = volhead->vol_buf_beg;
818
819         return (0);
820 }
821
822 static void
823 hclose(struct hfs *hfs)
824 {
825 #if DEBUG
826         printf("hclose\n");
827 #endif
828         for (int i = 0; i < NUMCACHE; i++)
829                 free(hfs->cache[i].data);
830 }
831 #endif
832
833 #ifdef LIBSTAND
834 struct hfile {
835         struct hfs      hfs;
836         ino_t           ino;
837         int64_t         fsize;
838 };
839
840 static int
841 hammer_open(const char *path, struct open_file *f)
842 {
843         struct hfile *hf = malloc(sizeof(*hf));
844         bzero(hf, sizeof(*hf));
845
846         f->f_fsdata = hf;
847         hf->hfs.f = f;
848         f->f_offset = 0;
849
850         int rv = hinit(&hf->hfs);
851         if (rv) {
852                 free(hf);
853                 return (rv);
854         }
855
856 #if DEBUG
857         printf("hammer_open %s %p %ld\n", path, f);
858 #endif
859
860         hf->ino = hlookup(&hf->hfs, path);
861         if (hf->ino == -1)
862                 goto fail;
863
864         struct stat st;
865         if (hstat(&hf->hfs, hf->ino, &st) == -1)
866                 goto fail;
867         hf->fsize = st.st_size;
868
869 #if DEBUG
870         printf("        %ld\n", (long)hf->fsize);
871 #endif
872
873         return (0);
874
875 fail:
876 #if DEBUG
877         printf("hammer_open fail\n");
878 #endif
879         hclose(&hf->hfs);
880         free(hf);
881         return (ENOENT);
882 }
883
884 static int
885 hammer_close(struct open_file *f)
886 {
887         struct hfile *hf = f->f_fsdata;
888
889         hclose(&hf->hfs);
890         f->f_fsdata = NULL;
891         free(hf);
892         return (0);
893 }
894
895 static int
896 hammer_read(struct open_file *f, void *buf, size_t len, size_t *resid)
897 {
898         struct hfile *hf = f->f_fsdata;
899
900 #if DEBUG
901         printf("hammer_read %p %ld %ld\n", f, f->f_offset, len);
902 #endif
903
904         if (f->f_offset >= hf->fsize)
905                 return (EINVAL);
906
907         size_t maxlen = len;
908         if (f->f_offset + len > hf->fsize)
909                 maxlen = hf->fsize - f->f_offset;
910
911         ssize_t rlen = hreadf(&hf->hfs, hf->ino, f->f_offset, maxlen, buf);
912         if (rlen == -1)
913                 return (EINVAL);
914
915         f->f_offset += rlen;
916
917         *resid = len - rlen;
918         return (0);
919 }
920
921 static off_t
922 hammer_seek(struct open_file *f, off_t offset, int whence)
923 {
924         struct hfile *hf = f->f_fsdata;
925
926         switch (whence) {
927         case SEEK_SET:
928                 f->f_offset = offset;
929                 break;
930         case SEEK_CUR:
931                 f->f_offset += offset;
932                 break;
933         case SEEK_END:
934                 f->f_offset = hf->fsize - offset;
935                 break;
936         default:
937                 return (-1);
938         }
939         return (f->f_offset);
940 }
941
942 static int
943 hammer_stat(struct open_file *f, struct stat *st)
944 {
945         struct hfile *hf = f->f_fsdata;
946
947         return (hstat(&hf->hfs, hf->ino, st));
948 }
949
950 static int
951 hammer_readdir(struct open_file *f, struct dirent *d)
952 {
953         struct hfile *hf = f->f_fsdata;
954
955         int64_t off = f->f_offset;
956         int rv = hreaddir(&hf->hfs, hf->ino, &off, d);
957         f->f_offset = off;
958         return (rv);
959 }
960
961 // libstand
962 struct fs_ops hammer_fsops = {
963         "hammer",
964         hammer_open,
965         hammer_close,
966         hammer_read,
967         null_write,
968         hammer_seek,
969         hammer_stat,
970         hammer_readdir
971 };
972 #endif  // LIBSTAND
973
974 #ifdef TESTING
975 int
976 main(int argc, char **argv)
977 {
978         if (argc < 2) {
979                 fprintf(stderr, "usage: hammerread <dev>\n");
980                 return (1);
981         }
982
983         struct hfs hfs;
984         hfs.fd = open(argv[1], O_RDONLY);
985         if (hfs.fd == -1)
986                 err(1, "unable to open %s", argv[1]);
987
988         if (hinit(&hfs) == -1)
989                 err(1, "invalid hammerfs");
990
991         for (int i = 2; i < argc; i++) {
992                 ino_t ino = hlookup(&hfs, argv[i]);
993                 if (ino == (ino_t)-1) {
994                         warn("hlookup %s", argv[i]);
995                         continue;
996                 }
997
998                 struct stat st;
999                 if (hstat(&hfs, ino, &st)) {
1000                         warn("hstat %s", argv[i]);
1001                         continue;
1002                 }
1003
1004                 printf("%s %d/%d %o %lld\n",
1005                        argv[i],
1006                        st.st_uid, st.st_gid,
1007                        st.st_mode, st.st_size);
1008
1009                 if (S_ISDIR(st.st_mode)) {
1010                         int64_t off = 0;
1011                         struct dirent de;
1012                         while (hreaddir(&hfs, ino, &off, &de) == 0) {
1013                                 printf("%s %d %llx\n",
1014                                        de.d_name, de.d_type, de.d_ino);
1015                         }
1016                 } else if (S_ISREG(st.st_mode)) {
1017                         char *buf = malloc(100000);
1018                         int64_t off = 0;
1019                         while (off < st.st_size) {
1020                                 int64_t len = MIN(100000, st.st_size - off);
1021                                 int64_t rl = hreadf(&hfs, ino, off, len, buf);
1022                                 fwrite(buf, rl, 1, stdout);
1023                                 off += rl;
1024                         }
1025                         free(buf);
1026                 }
1027         }
1028
1029         return 0;
1030 }
1031 #endif