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