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