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