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