Add missing break.
[dragonfly.git] / lib / libstand / hammerread.c
blob68d09c1d2959d8ef92600bda7c2bb6cc8b1e831f
1 /*
2 * Copyright (c) 2008 The DragonFly Project. All rights reserved.
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>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
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.
35 * $DragonFly: src/lib/libstand/hammerread.c,v 1.2 2008/10/29 22:14:25 swildner Exp $
39 * This file is being used by boot2 and libstand (loader).
40 * Compile with -DTESTING to obtain a binary.
44 #if !defined(BOOT2) && !defined(TESTING)
45 #define LIBSTAND 1
46 #endif
48 #include <sys/param.h>
50 #include <stddef.h>
51 #include <stdint.h>
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
65 #ifdef LIBSTAND
66 #include "stand.h"
67 #endif
69 #include <vfs/hammer/hammer_disk.h>
71 #ifndef BOOT2
72 struct blockentry {
73 hammer_off_t off;
74 int use;
75 char *data;
78 #ifdef TESTING
79 #define NUMCACHE 16
80 #else
81 #define NUMCACHE 6
82 #endif
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];
96 static void *
97 hread(struct hfs *hfs, hammer_off_t off)
99 hammer_off_t boff = off & ~HAMMER_BUFMASK;
101 boff &= HAMMER_OFF_LONG_MASK;
103 if (HAMMER_ZONE_DECODE(off) != HAMMER_ZONE_RAW_VOLUME_INDEX)
104 boff += hfs->buf_beg;
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;
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
133 be->use = ++hfs->lru;
134 return &be->data[off & HAMMER_BUFMASK];
137 #else /* BOOT2 */
139 struct dmadat {
140 char secbuf[DEV_BSIZE];
141 char buf[HAMMER_BUFSIZE];
144 static struct dmadat *dmadat;
146 struct hfs {
147 hammer_off_t root;
148 int64_t buf_beg;
151 static void *
152 hread(struct hfs *hfs, hammer_off_t off)
154 char *buf = dmadat->buf;
156 hammer_off_t boff = off & ~HAMMER_BUFMASK;
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]);
167 static void
168 bzero(void *buf, size_t size)
170 for (size_t i = 0; i < size; i++)
171 ((char *)buf)[i] = 0;
174 static void
175 bcopy(void *src, void *dst, size_t size)
177 memcpy(dst, src, size);
180 static size_t
181 strlen(const char *s)
183 size_t l = 0;
184 for (; *s != 0; s++)
185 l++;
186 return (l);
189 static int
190 memcmp(const void *a, const void *b, size_t len)
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);
198 return (0);
201 #endif
204 * (from hammer_btree.c)
206 * Compare two B-Tree elements, return -N, 0, or +N (e.g. similar to strcmp).
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.
212 * See also hammer_rec_rb_compare() and hammer_rec_cmp() in hammer_object.c.
214 static int
215 hammer_btree_cmp(hammer_base_elm_t key1, hammer_base_elm_t key2)
217 if (key1->localization < key2->localization)
218 return(-5);
219 if (key1->localization > key2->localization)
220 return(5);
222 if (key1->obj_id < key2->obj_id)
223 return(-4);
224 if (key1->obj_id > key2->obj_id)
225 return(4);
227 if (key1->rec_type < key2->rec_type)
228 return(-3);
229 if (key1->rec_type > key2->rec_type)
230 return(3);
232 if (key1->key < key2->key)
233 return(-2);
234 if (key1->key > key2->key)
235 return(2);
238 * A create_tid of zero indicates a record which is undeletable
239 * and must be considered to have a value of positive infinity.
241 if (key1->create_tid == 0) {
242 if (key2->create_tid == 0)
243 return(0);
244 return(1);
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);
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.
260 static int
261 hammer_btree_search_node(hammer_base_elm_t elm, hammer_node_ondisk_t node)
263 int b;
264 int s;
265 int i;
266 int r;
269 * Don't bother if the node does not have very many elements
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;
282 return(b);
285 #if 0
287 * (from hammer_subs.c)
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.
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.
297 * This function can never return 0. We use the MSB-0 space to synthesize
298 * artificial directory entries such as "." and "..".
300 static int64_t
301 hammer_directory_namekey(const void *name, int len)
303 int64_t key;
305 key = (int64_t)(crc32(name, len) & 0x7FFFFFFF) << 32;
306 if (key == 0)
307 key |= 0x100000000LL;
308 return(key);
310 #else
311 static int64_t
312 hammer_directory_namekey(const void *name __unused, int len __unused)
314 return (0);
316 #endif
319 #ifndef BOOT2
321 * Misc
323 static u_int32_t
324 hammer_to_unix_xid(uuid_t *uuid)
326 return(*(u_int32_t *)&uuid->node[2]);
329 static int
330 hammer_get_dtype(u_int8_t obj_type)
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);
352 /* not reached */
355 static int
356 hammer_get_mode(u_int8_t obj_type)
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);
378 /* not reached */
381 #if DEBUG > 1
382 static void
383 hprintb(hammer_base_elm_t e)
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
403 #endif /* DEBUG > 1 */
404 #endif /* !BOOT2 */
406 static hammer_btree_leaf_elm_t
407 hfind(struct hfs *hfs, hammer_base_elm_t key, hammer_base_elm_t end)
409 #if DEBUG > 1
410 printf("searching for ");
411 hprintb(key);
412 printf("\n");
413 #endif
415 int n;
416 int r;
417 struct hammer_base_elm search = *key;
418 struct hammer_base_elm backtrack;
419 hammer_off_t nodeoff = hfs->root;
420 hammer_node_ondisk_t node;
421 hammer_btree_elm_t e = NULL;
423 loop:
424 node = hread(hfs, nodeoff);
425 if (node == NULL)
426 return (NULL);
428 #if 0
429 for (int i = 0; i < node->count; i++) {
430 printf("E: ");
431 hprintb(&node->elms[i].base);
432 printf("\n");
434 #endif
436 n = hammer_btree_search_node(&search, node);
438 for (; n < node->count; n++) {
439 e = &node->elms[n];
440 r = hammer_btree_cmp(&search, &e->base);
442 if (r < 0)
443 break;
446 // unless we stopped right on the left side, we need to back off a bit
447 if (n > 0)
448 e = &node->elms[n - 1];
450 #if DEBUG > 2
451 printf(" found: ");
452 hprintb(&e->base);
453 printf("\n");
454 #endif
456 if (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
457 nodeoff = e->internal.subtree_offset;
458 backtrack = (e+1)->base;
459 goto loop;
462 r = hammer_btree_cmp(key, &e->base);
463 // If we're more off than the createtid, take the next elem
464 if (r > 1) {
465 e++;
466 n++;
469 // Skip deleted elements
470 while (n < node->count && e->base.delete_tid != 0) {
471 e++;
472 n++;
475 // In the unfortunate event when there is no next
476 // element in this node, we repeat the search with
477 // a key beyond the right boundary
478 if (n == node->count) {
479 search = backtrack;
480 nodeoff = hfs->root;
482 #if DEBUG > 2
483 printf("hit right boundary (%d), resetting search to ",
484 node->count);
485 hprintb(&search);
486 printf("\n");
487 #endif
488 goto loop;
491 #if DEBUG > 1
492 printf(" result: ");
493 hprintb(&e->base);
494 printf("\n");
495 #endif
497 if (end != NULL)
498 if (hammer_btree_cmp(end, &e->base) < -1)
499 goto fail;
501 return (&e->leaf);
503 fail:
504 #if DEBUG > 1
505 printf(" fail.\n");
506 #endif
507 return (NULL);
510 #ifndef BOOT2
511 static int
512 hreaddir(struct hfs *hfs, ino_t ino, int64_t *off, struct dirent *de)
514 struct hammer_base_elm key, end;
516 bzero(&key, sizeof(key));
517 key.obj_id = ino;
518 key.localization = HAMMER_LOCALIZE_MISC;
519 key.rec_type = HAMMER_RECTYPE_DIRENTRY;
520 key.key = *off;
522 end = key;
523 end.key = HAMMER_MAX_KEY;
525 hammer_btree_leaf_elm_t e;
527 e = hfind(hfs, &key, &end);
528 if (e == NULL) {
529 errno = ENOENT;
530 return (-1);
533 *off = e->base.key + 1; // remember next pos
535 de->d_namlen = e->data_len - HAMMER_ENTRY_NAME_OFF;
536 de->d_type = hammer_get_dtype(e->base.obj_type);
537 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
538 if (ed == NULL)
539 return (-1);
540 de->d_ino = ed->entry.obj_id;
541 bcopy(ed->entry.name, de->d_name, de->d_namlen);
542 de->d_name[de->d_namlen] = 0;
544 return (0);
546 #endif
548 static ino_t
549 hresolve(struct hfs *hfs, ino_t dirino, const char *name)
551 struct hammer_base_elm key, end;
552 size_t namel = strlen(name);
554 bzero(&key, sizeof(key));
555 key.obj_id = dirino;
556 key.localization = HAMMER_LOCALIZE_MISC;
557 key.key = hammer_directory_namekey(name, namel);
558 key.rec_type = HAMMER_RECTYPE_DIRENTRY;
559 end = key;
560 end.key = HAMMER_MAX_KEY;
562 hammer_btree_leaf_elm_t e;
563 while ((e = hfind(hfs, &key, &end)) != NULL) {
564 key.key = e->base.key + 1;
566 size_t elen = e->data_len - HAMMER_ENTRY_NAME_OFF;
567 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
568 if (ed == NULL)
569 return (-1);
570 #ifdef BOOT2
571 if (ls) {
572 for (int i = 0; i < elen; i++)
573 putchar(ed->entry.name[i]);
574 putchar(' ');
575 ls = 2;
576 continue;
578 #endif
579 if (elen == namel && memcmp(ed->entry.name, name, MIN(elen, namel)) == 0)
580 return (ed->entry.obj_id);
583 #if BOOT2
584 if (ls == 2)
585 printf("\n");
586 #endif
588 return -1;
591 static ino_t
592 hlookup(struct hfs *hfs, const char *path)
594 #ifdef BOOT2
595 ls = 0;
596 #endif
597 ino_t ino = 1;
598 do {
599 char name[MAXPATHLEN + 1];
600 while (*path == '/')
601 path++;
602 for (char *n = name; *path != 0 && *path != '/'; path++, n++) {
603 n[0] = *path;
604 n[1] = 0;
607 #ifdef BOOT2
608 // A single ? means "list"
609 if (name[0] == '?' && name[1] == 0)
610 ls = 1;
611 #endif
613 ino = hresolve(hfs, ino, name);
614 } while (ino != (ino_t)-1 && *path != 0);
616 return (ino);
620 #ifndef BOOT2
621 static int
622 hstat(struct hfs *hfs, ino_t ino, struct stat* st)
624 struct hammer_base_elm key;
626 bzero(&key, sizeof(key));
627 key.obj_id = ino;
628 key.localization = HAMMER_LOCALIZE_INODE;
629 key.rec_type = HAMMER_RECTYPE_INODE;
631 hammer_btree_leaf_elm_t e = hfind(hfs, &key, &key);
632 if (e == NULL) {
633 #ifndef BOOT2
634 errno = ENOENT;
635 #endif
636 return -1;
639 hammer_data_ondisk_t ed = hread(hfs, e->data_offset);
640 if (ed == NULL)
641 return (-1);
643 st->st_mode = ed->inode.mode | hammer_get_mode(ed->inode.obj_type);
644 st->st_uid = hammer_to_unix_xid(&ed->inode.uid);
645 st->st_gid = hammer_to_unix_xid(&ed->inode.gid);
646 st->st_size = ed->inode.size;
648 return (0);
650 #endif
652 static ssize_t
653 hreadf(struct hfs *hfs, ino_t ino, int64_t off, int64_t len, char *buf)
655 int64_t startoff = off;
656 struct hammer_base_elm key, end;
658 bzero(&key, sizeof(key));
659 key.obj_id = ino;
660 key.localization = HAMMER_LOCALIZE_MISC;
661 key.rec_type = HAMMER_RECTYPE_DATA;
662 end = key;
663 end.key = HAMMER_MAX_KEY;
665 while (len > 0) {
666 key.key = off + 1;
667 hammer_btree_leaf_elm_t e = hfind(hfs, &key, &end);
668 int64_t dlen;
670 if (e == NULL || off > e->base.key) {
671 bzero(buf, len);
672 off += len;
673 len = 0;
674 break;
677 int64_t doff = e->base.key - e->data_len;
678 if (off < doff) {
679 // sparse file, beginning
680 dlen = doff - off;
681 dlen = MIN(dlen, len);
682 bzero(buf, dlen);
683 } else {
684 int64_t boff = off - doff;
685 hammer_off_t roff = e->data_offset;
687 dlen = e->data_len;
688 dlen -= boff;
689 dlen = MIN(dlen, len);
691 while (boff >= HAMMER_BUFSIZE) {
692 boff -= HAMMER_BUFSIZE;
693 roff += HAMMER_BUFSIZE;
696 // cut to HAMMER_BUFSIZE
697 if ((roff & ~HAMMER_BUFMASK) != ((roff + dlen - 1) & ~HAMMER_BUFMASK))
698 dlen = HAMMER_BUFSIZE - ((boff + roff) & HAMMER_BUFMASK);
700 char *data = hread(hfs, roff);
701 if (data == NULL)
702 return (-1);
703 bcopy(data + boff, buf, dlen);
706 buf += dlen;
707 off += dlen;
708 len -= dlen;
711 return (off - startoff);
714 #ifdef BOOT2
715 struct hfs hfs;
717 static int
718 hammerinit(void)
720 if (dsk_meta)
721 return (0);
723 hammer_volume_ondisk_t volhead = hread(&hfs, HAMMER_ZONE_ENCODE(1, 0));
724 if (volhead == NULL)
725 return (-1);
726 if (volhead->vol_signature != HAMMER_FSBUF_VOLUME)
727 return (-1);
728 hfs.root = volhead->vol0_btree_root;
729 hfs.buf_beg = volhead->vol_buf_beg;
730 dsk_meta++;
731 return (0);
734 static ino_t
735 lookup(const char *path)
737 hammerinit();
739 ino_t ino = hlookup(&hfs, path);
741 if (ino == -1)
742 ino = 0;
743 return (ino);
746 static ssize_t
747 fsread(ino_t ino, void *buf, size_t len)
749 hammerinit();
751 ssize_t rlen = hreadf(&hfs, ino, fs_off, len, buf);
752 if (rlen != -1)
753 fs_off += rlen;
754 return (rlen);
756 #endif
758 #ifndef BOOT2
759 static int
760 hinit(struct hfs *hfs)
762 #if DEBUG
763 printf("hinit\n");
764 #endif
765 for (int i = 0; i < NUMCACHE; i++) {
766 hfs->cache[i].data = malloc(HAMMER_BUFSIZE);
767 hfs->cache[i].off = -1; // invalid
768 hfs->cache[i].use = 0;
770 #if DEBUG
771 if (hfs->cache[i].data == NULL)
772 printf("malloc failed\n");
773 #endif
775 hfs->lru = 0;
777 hammer_volume_ondisk_t volhead = hread(hfs, HAMMER_ZONE_ENCODE(1, 0));
778 if (volhead == NULL)
779 return (-1);
781 #ifdef TESTING
782 printf("signature: %svalid\n",
783 volhead->vol_signature != HAMMER_FSBUF_VOLUME ?
784 "in" :
785 "");
786 printf("name: %s\n", volhead->vol_name);
787 #endif
789 if (volhead->vol_signature != HAMMER_FSBUF_VOLUME) {
790 for (int i = 0; i < NUMCACHE; i++)
791 free(hfs->cache[i].data);
792 errno = ENODEV;
793 return (-1);
796 hfs->root = volhead->vol0_btree_root;
797 hfs->buf_beg = volhead->vol_buf_beg;
799 return (0);
802 static void
803 hclose(struct hfs *hfs)
805 #if DEBUG
806 printf("hclose\n");
807 #endif
808 for (int i = 0; i < NUMCACHE; i++)
809 free(hfs->cache[i].data);
811 #endif
813 #ifdef LIBSTAND
814 struct hfile {
815 struct hfs hfs;
816 ino_t ino;
817 int64_t fsize;
820 static int
821 hammer_open(const char *path, struct open_file *f)
823 struct hfile *hf = malloc(sizeof(*hf));
824 bzero(hf, sizeof(*hf));
826 f->f_fsdata = hf;
827 hf->hfs.f = f;
828 f->f_offset = 0;
830 int rv = hinit(&hf->hfs);
831 if (rv) {
832 free(hf);
833 return (rv);
836 #if DEBUG
837 printf("hammer_open %s %p %ld\n", path, f);
838 #endif
840 hf->ino = hlookup(&hf->hfs, path);
841 if (hf->ino == -1)
842 goto fail;
844 struct stat st;
845 if (hstat(&hf->hfs, hf->ino, &st) == -1)
846 goto fail;
847 hf->fsize = st.st_size;
849 #if DEBUG
850 printf(" %ld\n", (long)hf->fsize);
851 #endif
853 return (0);
855 fail:
856 #if DEBUG
857 printf("hammer_open fail\n");
858 #endif
859 hclose(&hf->hfs);
860 free(hf);
861 return (ENOENT);
864 static int
865 hammer_close(struct open_file *f)
867 struct hfile *hf = f->f_fsdata;
869 hclose(&hf->hfs);
870 f->f_fsdata = NULL;
871 free(hf);
872 return (0);
875 static int
876 hammer_read(struct open_file *f, void *buf, size_t len, size_t *resid)
878 struct hfile *hf = f->f_fsdata;
880 #if DEBUG
881 printf("hammer_read %p %ld %ld\n", f, f->f_offset, len);
882 #endif
884 if (f->f_offset >= hf->fsize)
885 return (EINVAL);
887 size_t maxlen = len;
888 if (f->f_offset + len > hf->fsize)
889 maxlen = hf->fsize - f->f_offset;
891 ssize_t rlen = hreadf(&hf->hfs, hf->ino, f->f_offset, maxlen, buf);
892 if (rlen == -1)
893 return (EINVAL);
895 f->f_offset += rlen;
897 *resid = len - rlen;
898 return (0);
901 static off_t
902 hammer_seek(struct open_file *f, off_t offset, int whence)
904 struct hfile *hf = f->f_fsdata;
906 switch (whence) {
907 case SEEK_SET:
908 f->f_offset = offset;
909 break;
910 case SEEK_CUR:
911 f->f_offset += offset;
912 break;
913 case SEEK_END:
914 f->f_offset = hf->fsize - offset;
915 break;
916 default:
917 return (-1);
919 return (f->f_offset);
922 static int
923 hammer_stat(struct open_file *f, struct stat *st)
925 struct hfile *hf = f->f_fsdata;
927 return (hstat(&hf->hfs, hf->ino, st));
930 static int
931 hammer_readdir(struct open_file *f, struct dirent *d)
933 struct hfile *hf = f->f_fsdata;
935 int64_t off = f->f_offset;
936 int rv = hreaddir(&hf->hfs, hf->ino, &off, d);
937 f->f_offset = off;
938 return (rv);
941 // libstand
942 struct fs_ops hammer_fsops = {
943 "hammer",
944 hammer_open,
945 hammer_close,
946 hammer_read,
947 null_write,
948 hammer_seek,
949 hammer_stat,
950 hammer_readdir
952 #endif // LIBSTAND
954 #ifdef TESTING
956 main(int argc, char **argv)
958 if (argc < 2) {
959 fprintf(stderr, "usage: hammerread <dev>\n");
960 return (1);
963 struct hfs hfs;
964 hfs.fd = open(argv[1], O_RDONLY);
965 if (hfs.fd == -1)
966 err(1, "unable to open %s", argv[1]);
968 if (hinit(&hfs) == -1)
969 err(1, "invalid hammerfs");
971 for (int i = 2; i < argc; i++) {
972 ino_t ino = hlookup(&hfs, argv[i]);
973 if (ino == (ino_t)-1) {
974 warn("hlookup %s", argv[i]);
975 continue;
978 struct stat st;
979 if (hstat(&hfs, ino, &st)) {
980 warn("hstat %s", argv[i]);
981 continue;
984 printf("%s %d/%d %o %lld\n",
985 argv[i],
986 st.st_uid, st.st_gid,
987 st.st_mode, st.st_size);
989 if (S_ISDIR(st.st_mode)) {
990 int64_t off = 0;
991 struct dirent de;
992 while (hreaddir(&hfs, ino, &off, &de) == 0) {
993 printf("%s %d %llx\n",
994 de.d_name, de.d_type, de.d_ino);
996 } else if (S_ISREG(st.st_mode)) {
997 char *buf = malloc(100000);
998 int64_t off = 0;
999 while (off < st.st_size) {
1000 int64_t len = MIN(100000, st.st_size - off);
1001 int64_t rl = hreadf(&hfs, ino, off, len, buf);
1002 fwrite(buf, rl, 1, stdout);
1003 off += rl;
1005 free(buf);
1009 return 0;
1011 #endif