disklabel64 UFS+HAMMER boot support (was previously just HAMMER boot support)
[dragonfly.git] / lib / libstand / hammerread.c
CommitLineData
bc767f46
SS
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 *
75f9bffc 35 * $DragonFly: src/lib/libstand/hammerread.c,v 1.2 2008/10/29 22:14:25 swildner Exp $
bc767f46
SS
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
d64b2e33
MD
48#ifdef BOOT2
49#include "boot2.h"
50#else
bc767f46 51#include <sys/param.h>
bc767f46
SS
52#include <stddef.h>
53#include <stdint.h>
d64b2e33 54#endif
bc767f46
SS
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
75struct 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
87struct 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
99static void *
100hread(struct hfs *hfs, hammer_off_t off)
101{
463f2cb1 102 hammer_off_t boff = off & ~HAMMER_BUFMASK64;
bc767f46
SS
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
d64b2e33
MD
142struct hammer_dmadat {
143 struct boot2_dmadat boot2;
bc767f46
SS
144 char buf[HAMMER_BUFSIZE];
145};
146
d64b2e33 147#define fsdmadat ((struct hammer_dmadat *)boot2_dmadat)
bc767f46
SS
148
149struct hfs {
150 hammer_off_t root;
151 int64_t buf_beg;
152};
153
154static void *
155hread(struct hfs *hfs, hammer_off_t off)
156{
d64b2e33 157 char *buf = fsdmadat->buf;
bc767f46 158
463f2cb1 159 hammer_off_t boff = off & ~HAMMER_BUFMASK64;
bc767f46
SS
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
170static void
171bzero(void *buf, size_t size)
172{
173 for (size_t i = 0; i < size; i++)
174 ((char *)buf)[i] = 0;
175}
176
177static void
178bcopy(void *src, void *dst, size_t size)
179{
180 memcpy(dst, src, size);
181}
182
183static size_t
184strlen(const char *s)
185{
186 size_t l = 0;
187 for (; *s != 0; s++)
188 l++;
189 return (l);
190}
191
192static int
193memcmp(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 */
217static int
218hammer_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 */
263static int
264hammer_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 */
303static int64_t
304hammer_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
314static int64_t
315hammer_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 */
326static u_int32_t
327hammer_to_unix_xid(uuid_t *uuid)
328{
329 return(*(u_int32_t *)&uuid->node[2]);
330}
331
332static int
333hammer_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
358static int
359hammer_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
75f9bffc 384#if DEBUG > 1
bc767f46
SS
385static void
386hprintb(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}
75f9bffc
SW
406#endif /* DEBUG > 1 */
407#endif /* !BOOT2 */
bc767f46
SS
408
409static hammer_btree_leaf_elm_t
410hfind(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);
e6dcfedc
SS
415 printf(" end ");
416 hprintb(end);
bc767f46
SS
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;
75f9bffc 426 hammer_btree_elm_t e = NULL;
5e43e8b4 427 int internal;
bc767f46
SS
428
429loop:
430 node = hread(hfs, nodeoff);
431 if (node == NULL)
432 return (NULL);
5e43e8b4 433 internal = node->type == HAMMER_BTREE_TYPE_INTERNAL;
bc767f46 434
e6dcfedc 435#if DEBUG > 3
bc767f46
SS
436 for (int i = 0; i < node->count; i++) {
437 printf("E: ");
438 hprintb(&node->elms[i].base);
439 printf("\n");
440 }
5e43e8b4 441 if (internal) {
213776bd
SS
442 printf("B: ");
443 hprintb(&node->elms[node->count].base);
444 printf("\n");
445 }
bc767f46
SS
446#endif
447
448 n = hammer_btree_search_node(&search, node);
449
5e43e8b4
SS
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++) {
bc767f46
SS
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)
5e43e8b4 462 e = &node->elms[--n];
bc767f46
SS
463
464#if DEBUG > 2
465 printf(" found: ");
466 hprintb(&e->base);
467 printf("\n");
468#endif
469
5e43e8b4
SS
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;
bc767f46
SS
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
5e43e8b4 488 while (n < node->count && e->base.delete_tid != 0) {
bc767f46
SS
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
5e43e8b4
SS
496 if (n == node->count) {
497backtrack:
bc767f46
SS
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
522fail:
523#if DEBUG > 1
524 printf(" fail.\n");
525#endif
526 return (NULL);
527}
528
529#ifndef BOOT2
530static int
531hreaddir(struct hfs *hfs, ino_t ino, int64_t *off, struct dirent *de)
532{
533 struct hammer_base_elm key, end;
534
e6dcfedc
SS
535#if DEBUG > 2
536 printf("%s(%llx, %lld)\n", __FUNCTION__, (long long)ino, *off);
537#endif
538
bc767f46
SS
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
571static ino_t
572hresolve(struct hfs *hfs, ino_t dirino, const char *name)
573{
574 struct hammer_base_elm key, end;
575 size_t namel = strlen(name);
576
e6dcfedc
SS
577#if DEBUG > 2
578 printf("%s(%llx, %s)\n", __FUNCTION__, (long long)dirino, name);
579#endif
580
bc767f46
SS
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
618static ino_t
619hlookup(struct hfs *hfs, const char *path)
620{
e6dcfedc
SS
621#if DEBUG > 2
622 printf("%s(%s)\n", __FUNCTION__, path);
623#endif
624
bc767f46
SS
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++;
e6dcfedc
SS
633 if (*path == 0)
634 break;
bc767f46
SS
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
654static int
655hstat(struct hfs *hfs, ino_t ino, struct stat* st)
656{
657 struct hammer_base_elm key;
658
e6dcfedc
SS
659#if DEBUG > 2
660 printf("%s(%llx)\n", __FUNCTION__, (long long)ino);
661#endif
662
bc767f46
SS
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
689static ssize_t
690hreadf(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
463f2cb1
MD
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))
bc767f46 742 dlen = HAMMER_BUFSIZE - ((boff + roff) & HAMMER_BUFMASK);
bc767f46
SS
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
758struct hfs hfs;
759
760static int
d64b2e33 761boot2_hammer_init(void)
bc767f46 762{
d64b2e33 763 hammer_volume_ondisk_t volhead;
bc767f46 764
d64b2e33 765 volhead = hread(&hfs, HAMMER_ZONE_ENCODE(1, 0));
bc767f46
SS
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;
bc767f46
SS
772 return (0);
773}
774
d64b2e33
MD
775static boot2_ino_t
776boot2_hammer_lookup(const char *path)
bc767f46 777{
bc767f46
SS
778 ino_t ino = hlookup(&hfs, path);
779
780 if (ino == -1)
781 ino = 0;
9364ac09
SS
782
783 fs_off = 0;
784
bc767f46
SS
785 return (ino);
786}
787
788static ssize_t
d64b2e33 789boot2_hammer_read(boot2_ino_t ino, void *buf, size_t len)
bc767f46 790{
bc767f46
SS
791 ssize_t rlen = hreadf(&hfs, ino, fs_off, len, buf);
792 if (rlen != -1)
793 fs_off += rlen;
794 return (rlen);
795}
d64b2e33
MD
796
797const struct boot2_fsapi boot2_hammer_api = {
798 .fsinit = boot2_hammer_init,
799 .fslookup = boot2_hammer_lookup,
800 .fsread = boot2_hammer_read
801};
802
bc767f46
SS
803#endif
804
805#ifndef BOOT2
806static int
807hinit(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));
bc767f46
SS
825
826#ifdef TESTING
6825e71f
MD
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 }
bc767f46
SS
834#endif
835
6825e71f
MD
836 if (volhead == NULL || volhead->vol_signature != HAMMER_FSBUF_VOLUME) {
837 for (int i = 0; i < NUMCACHE; i++) {
bc767f46 838 free(hfs->cache[i].data);
6825e71f
MD
839 hfs->cache[i].data = NULL;
840 }
bc767f46
SS
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
851static void
852hclose(struct hfs *hfs)
853{
854#if DEBUG
855 printf("hclose\n");
856#endif
6825e71f
MD
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 }
bc767f46
SS
863}
864#endif
865
866#ifdef LIBSTAND
867struct hfile {
868 struct hfs hfs;
869 ino_t ino;
870 int64_t fsize;
871};
872
873static int
874hammer_open(const char *path, struct open_file *f)
875{
876 struct hfile *hf = malloc(sizeof(*hf));
bc767f46 877
6825e71f 878 bzero(hf, sizeof(*hf));
bc767f46
SS
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) {
6825e71f 885 f->f_fsdata = NULL;
bc767f46
SS
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
909fail:
910#if DEBUG
911 printf("hammer_open fail\n");
912#endif
6825e71f 913 f->f_fsdata = NULL;
bc767f46
SS
914 hclose(&hf->hfs);
915 free(hf);
916 return (ENOENT);
917}
918
919static int
920hammer_close(struct open_file *f)
921{
922 struct hfile *hf = f->f_fsdata;
923
bc767f46 924 f->f_fsdata = NULL;
f453291a
MD
925 if (hf) {
926 hclose(&hf->hfs);
927 free(hf);
928 }
bc767f46
SS
929 return (0);
930}
931
932static int
933hammer_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
958static off_t
959hammer_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
979static int
980hammer_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
987static int
988hammer_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
999struct 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
1012int
1013main(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