Use CSTD and remove local CSTD settings which are no longer needed.
[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;
83684e07
MD
95 int64_t last_dir_ino;
96 u_int8_t last_dir_cap_flags;
bc767f46
SS
97 int lru;
98 struct blockentry cache[NUMCACHE];
99};
100
101static void *
102hread(struct hfs *hfs, hammer_off_t off)
103{
463f2cb1 104 hammer_off_t boff = off & ~HAMMER_BUFMASK64;
bc767f46
SS
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
d64b2e33
MD
144struct hammer_dmadat {
145 struct boot2_dmadat boot2;
bc767f46
SS
146 char buf[HAMMER_BUFSIZE];
147};
148
d64b2e33 149#define fsdmadat ((struct hammer_dmadat *)boot2_dmadat)
bc767f46
SS
150
151struct hfs {
152 hammer_off_t root;
83684e07
MD
153 int64_t last_dir_ino;
154 u_int8_t last_dir_cap_flags;
bc767f46
SS
155 int64_t buf_beg;
156};
157
158static void *
159hread(struct hfs *hfs, hammer_off_t off)
160{
d64b2e33 161 char *buf = fsdmadat->buf;
bc767f46 162
463f2cb1 163 hammer_off_t boff = off & ~HAMMER_BUFMASK64;
bc767f46
SS
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
174static void
175bzero(void *buf, size_t size)
176{
177 for (size_t i = 0; i < size; i++)
178 ((char *)buf)[i] = 0;
179}
180
181static void
182bcopy(void *src, void *dst, size_t size)
183{
184 memcpy(dst, src, size);
185}
186
187static size_t
188strlen(const char *s)
189{
190 size_t l = 0;
191 for (; *s != 0; s++)
192 l++;
193 return (l);
194}
195
196static int
197memcmp(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 */
221static int
222hammer_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 */
267static int
268hammer_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 */
307static int64_t
308hammer_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
318static int64_t
319hammer_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 */
330static u_int32_t
331hammer_to_unix_xid(uuid_t *uuid)
332{
333 return(*(u_int32_t *)&uuid->node[2]);
334}
335
336static int
337hammer_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
362static int
363hammer_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
75f9bffc 388#if DEBUG > 1
bc767f46
SS
389static void
390hprintb(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}
75f9bffc
SW
410#endif /* DEBUG > 1 */
411#endif /* !BOOT2 */
bc767f46
SS
412
413static hammer_btree_leaf_elm_t
414hfind(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);
e6dcfedc
SS
419 printf(" end ");
420 hprintb(end);
bc767f46
SS
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;
75f9bffc 430 hammer_btree_elm_t e = NULL;
5e43e8b4 431 int internal;
bc767f46
SS
432
433loop:
434 node = hread(hfs, nodeoff);
435 if (node == NULL)
436 return (NULL);
5e43e8b4 437 internal = node->type == HAMMER_BTREE_TYPE_INTERNAL;
bc767f46 438
e6dcfedc 439#if DEBUG > 3
bc767f46
SS
440 for (int i = 0; i < node->count; i++) {
441 printf("E: ");
442 hprintb(&node->elms[i].base);
443 printf("\n");
444 }
5e43e8b4 445 if (internal) {
213776bd
SS
446 printf("B: ");
447 hprintb(&node->elms[node->count].base);
448 printf("\n");
449 }
bc767f46
SS
450#endif
451
452 n = hammer_btree_search_node(&search, node);
453
5e43e8b4
SS
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++) {
bc767f46
SS
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)
5e43e8b4 466 e = &node->elms[--n];
bc767f46
SS
467
468#if DEBUG > 2
469 printf(" found: ");
470 hprintb(&e->base);
471 printf("\n");
472#endif
473
5e43e8b4
SS
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;
bc767f46
SS
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
5e43e8b4 492 while (n < node->count && e->base.delete_tid != 0) {
bc767f46
SS
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
5e43e8b4
SS
500 if (n == node->count) {
501backtrack:
bc767f46
SS
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
526fail:
527#if DEBUG > 1
528 printf(" fail.\n");
529#endif
530 return (NULL);
531}
532
83684e07
MD
533/*
534 * Returns the directory entry localization field based on the directory
535 * inode's capabilities.
536 */
537static u_int32_t
538hdirlocalization(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
bc767f46
SS
571#ifndef BOOT2
572static int
573hreaddir(struct hfs *hfs, ino_t ino, int64_t *off, struct dirent *de)
574{
575 struct hammer_base_elm key, end;
576
e6dcfedc
SS
577#if DEBUG > 2
578 printf("%s(%llx, %lld)\n", __FUNCTION__, (long long)ino, *off);
579#endif
580
bc767f46
SS
581 bzero(&key, sizeof(key));
582 key.obj_id = ino;
83684e07 583 key.localization = hdirlocalization(hfs, ino);
bc767f46
SS
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
613static ino_t
614hresolve(struct hfs *hfs, ino_t dirino, const char *name)
615{
616 struct hammer_base_elm key, end;
617 size_t namel = strlen(name);
618
e6dcfedc
SS
619#if DEBUG > 2
620 printf("%s(%llx, %s)\n", __FUNCTION__, (long long)dirino, name);
621#endif
622
bc767f46
SS
623 bzero(&key, sizeof(key));
624 key.obj_id = dirino;
83684e07 625 key.localization = hdirlocalization(hfs, dirino);
bc767f46
SS
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
660static ino_t
661hlookup(struct hfs *hfs, const char *path)
662{
e6dcfedc
SS
663#if DEBUG > 2
664 printf("%s(%s)\n", __FUNCTION__, path);
665#endif
666
bc767f46
SS
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++;
e6dcfedc
SS
675 if (*path == 0)
676 break;
bc767f46
SS
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
696static int
697hstat(struct hfs *hfs, ino_t ino, struct stat* st)
698{
699 struct hammer_base_elm key;
700
e6dcfedc
SS
701#if DEBUG > 2
702 printf("%s(%llx)\n", __FUNCTION__, (long long)ino);
703#endif
704
bc767f46
SS
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
731static ssize_t
732hreadf(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
463f2cb1
MD
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))
bc767f46 784 dlen = HAMMER_BUFSIZE - ((boff + roff) & HAMMER_BUFMASK);
bc767f46
SS
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
800struct hfs hfs;
801
802static int
d64b2e33 803boot2_hammer_init(void)
bc767f46 804{
d64b2e33 805 hammer_volume_ondisk_t volhead;
bc767f46 806
d64b2e33 807 volhead = hread(&hfs, HAMMER_ZONE_ENCODE(1, 0));
bc767f46
SS
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;
bc767f46
SS
814 return (0);
815}
816
d64b2e33
MD
817static boot2_ino_t
818boot2_hammer_lookup(const char *path)
bc767f46 819{
bc767f46
SS
820 ino_t ino = hlookup(&hfs, path);
821
822 if (ino == -1)
823 ino = 0;
9364ac09
SS
824
825 fs_off = 0;
826
bc767f46
SS
827 return (ino);
828}
829
830static ssize_t
d64b2e33 831boot2_hammer_read(boot2_ino_t ino, void *buf, size_t len)
bc767f46 832{
bc767f46
SS
833 ssize_t rlen = hreadf(&hfs, ino, fs_off, len, buf);
834 if (rlen != -1)
835 fs_off += rlen;
836 return (rlen);
837}
d64b2e33
MD
838
839const struct boot2_fsapi boot2_hammer_api = {
840 .fsinit = boot2_hammer_init,
841 .fslookup = boot2_hammer_lookup,
842 .fsread = boot2_hammer_read
843};
844
bc767f46
SS
845#endif
846
847#ifndef BOOT2
848static int
849hinit(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;
83684e07 865 hfs->last_dir_ino = -1;
bc767f46
SS
866
867 hammer_volume_ondisk_t volhead = hread(hfs, HAMMER_ZONE_ENCODE(1, 0));
bc767f46
SS
868
869#ifdef TESTING
6825e71f
MD
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 }
bc767f46
SS
877#endif
878
6825e71f
MD
879 if (volhead == NULL || volhead->vol_signature != HAMMER_FSBUF_VOLUME) {
880 for (int i = 0; i < NUMCACHE; i++) {
bc767f46 881 free(hfs->cache[i].data);
6825e71f
MD
882 hfs->cache[i].data = NULL;
883 }
bc767f46
SS
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
894static void
895hclose(struct hfs *hfs)
896{
897#if DEBUG
898 printf("hclose\n");
899#endif
6825e71f
MD
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 }
bc767f46
SS
906}
907#endif
908
909#ifdef LIBSTAND
910struct hfile {
911 struct hfs hfs;
912 ino_t ino;
913 int64_t fsize;
914};
915
916static int
917hammer_open(const char *path, struct open_file *f)
918{
919 struct hfile *hf = malloc(sizeof(*hf));
bc767f46 920
6825e71f 921 bzero(hf, sizeof(*hf));
bc767f46
SS
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) {
6825e71f 928 f->f_fsdata = NULL;
bc767f46
SS
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
952fail:
953#if DEBUG
954 printf("hammer_open fail\n");
955#endif
6825e71f 956 f->f_fsdata = NULL;
bc767f46
SS
957 hclose(&hf->hfs);
958 free(hf);
959 return (ENOENT);
960}
961
962static int
963hammer_close(struct open_file *f)
964{
965 struct hfile *hf = f->f_fsdata;
966
bc767f46 967 f->f_fsdata = NULL;
f453291a
MD
968 if (hf) {
969 hclose(&hf->hfs);
970 free(hf);
971 }
bc767f46
SS
972 return (0);
973}
974
975static int
976hammer_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
1001static off_t
1002hammer_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
1022static int
1023hammer_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
1030static int
1031hammer_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
1042struct 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
1055int
1056main(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