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