boot: Use snprintf() when filling command_errbuf[] w/ dynamic content.
[dragonfly.git] / lib / libstand / hammer2.c
blob3f9361dd62ef6e23c104756929dbaa2dd16e83f4
1 /*
2 * Copyright (c) 2011-2013 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@dragonflybsd.org>
6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
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.
36 #if !defined(BOOT2) && !defined(TESTING)
37 #define LIBSTAND 1
38 #endif
40 #ifdef BOOT2
41 #include "boot2.h"
42 #endif
44 #ifdef TESTING
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <sys/uuid.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <stddef.h>
51 #include <stdint.h>
52 #include <unistd.h>
53 #include <fcntl.h>
54 #include <string.h>
55 #include <strings.h>
56 #include <errno.h>
57 #endif
59 #ifdef LIBSTAND
60 #include "stand.h"
61 #endif
63 #include <vfs/hammer2/hammer2_disk.h>
65 uint32_t iscsi_crc32(const void *buf, size_t size);
66 uint32_t iscsi_crc32_ext(const void *buf, size_t size, uint32_t ocrc);
68 static hammer2_media_data_t media;
69 static hammer2_blockref_t saved_base;
71 #define hammer2_icrc32(buf, size) iscsi_crc32(buf, size)
73 struct hammer2_fs {
74 hammer2_blockref_t sroot;
75 hammer2_blockset_t sroot_blockset;
76 #if defined(TESTING)
77 int fd;
78 #elif defined(LIBSTAND)
79 struct open_file *f;
80 #elif defined(BOOT2)
81 /* BOOT2 doesn't use a descriptor */
82 #else
83 #error "hammer2: unknown library API"
84 #endif
87 struct hammer2_inode {
88 struct hammer2_inode_data ino; /* raw inode data */
89 off_t doff; /* disk inode offset */
92 #ifdef BOOT2
94 static void
95 bzero(void *buf, size_t size)
97 for (size_t i = 0; i < size; i++)
98 ((char *)buf)[i] = 0;
101 static void
102 bcopy(void *src, void *dst, size_t size)
104 memcpy(dst, src, size);
107 #if 0
108 static size_t
109 strlen(const char *s)
111 size_t l = 0;
112 for (; *s != 0; s++)
113 l++;
114 return (l);
116 #endif
118 static int
119 memcmp(const void *a, const void *b, size_t len)
121 for (size_t p = 0; p < len; p++) {
122 int r = ((const char *)a)[p] - ((const char *)b)[p];
123 if (r != 0)
124 return (r);
127 return (0);
130 #endif
132 static
133 off_t
134 blockoff(hammer2_blockref_t *bref)
136 return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX);
139 static
140 size_t
141 blocksize(hammer2_blockref_t *bref)
143 return(1 << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX));
146 static
147 hammer2_key_t
148 hammer2_dirhash(const unsigned char *name, size_t len)
150 const unsigned char *aname = name;
151 uint32_t crcx;
152 uint64_t key;
153 size_t i;
154 size_t j;
156 key = 0;
159 * m32
161 crcx = 0;
162 for (i = j = 0; i < len; ++i) {
163 if (aname[i] == '.' ||
164 aname[i] == '-' ||
165 aname[i] == '_' ||
166 aname[i] == '~') {
167 if (i != j)
168 crcx += hammer2_icrc32(aname + j, i - j);
169 j = i + 1;
172 if (i != j)
173 crcx += hammer2_icrc32(aname + j, i - j);
176 * The directory hash utilizes the top 32 bits of the 64-bit key.
177 * Bit 63 must be set to 1.
179 crcx |= 0x80000000U;
180 key |= (uint64_t)crcx << 32;
183 * l16 - crc of entire filename
185 * This crc reduces degenerate hash collision conditions
187 crcx = hammer2_icrc32(aname, len);
188 crcx = crcx ^ (crcx << 16);
189 key |= crcx & 0xFFFF0000U;
192 * Set bit 15. This allows readdir to strip bit 63 so a positive
193 * 64-bit cookie/offset can always be returned, and still guarantee
194 * that the values 0x0000-0x7FFF are available for artificial entries.
195 * ('.' and '..').
197 key |= 0x8000U;
199 return (key);
203 * Low level read
205 static
207 h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off)
209 #if defined(LIBSTAND)
210 size_t rlen;
211 #endif
212 int rc;
214 #if defined(TESTING)
215 rc = pread(hfs->fd, &media, nbytes, off);
216 if (rc == (int)nbytes)
217 rc = 0;
218 else
219 rc = -1;
220 #elif defined(LIBSTAND)
221 rc = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ,
222 off >> DEV_BSHIFT, nbytes,
223 buf, &rlen);
224 if (rc || rlen != nbytes)
225 rc = -1;
226 #elif defined(BOOT2)
227 /* BIOS interface may barf on 64KB reads */
228 rc = 0;
229 while (nbytes > 16384) {
230 rc = dskread(buf, off >> DEV_BSHIFT, 16384 >> DEV_BSHIFT);
231 nbytes -= 16384;
232 buf = (char *)buf + 16384;
233 off += 16384;
235 if (nbytes)
236 rc = dskread(buf, off >> DEV_BSHIFT, nbytes >> DEV_BSHIFT);
237 if (rc)
238 rc = -1;
239 #else
240 #error "hammer2: unknown library API"
241 #endif
242 return rc;
246 * Common code
248 * Initialize for HAMMER2 filesystem access given a hammer2_fs with
249 * its device file descriptor initialized.
253 * Lookup within the block specified by (*base), loading the block from disk
254 * if necessary. Locate the first key within the requested range and
255 * recursively run through indirect blocks as needed. The caller may loop
256 * by setting key_beg to *key_ret.
258 * Returns 0 if nothing could be found and the key range has been exhausted.
259 * returns -1 if a disk error occured. Otherwise returns the size of the
260 * data block and returns the data block in *pptr and bref in *bref_ret.
262 * NOTE! When reading file data, the returned bref's key will be the nearest
263 * data block we looked up. The file read procedure must handle any
264 * zero-fill or skip. However, we will truncate the return value to
265 * the file size.
267 static int
268 h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base,
269 hammer2_key_t key_beg, hammer2_key_t key_end,
270 hammer2_blockref_t *bref_ret, void **pptr)
272 hammer2_blockref_t *bref;
273 hammer2_blockref_t best;
274 hammer2_key_t scan_beg;
275 hammer2_key_t scan_end;
276 int i;
277 int rc;
278 int count;
279 int dev_boff;
280 int dev_bsize;
282 if (base == NULL) {
283 saved_base.data_off = (hammer2_off_t)-1;
284 return(0);
286 if (base->data_off == (hammer2_off_t)-1)
287 return(-1);
290 * Calculate the number of blockrefs to scan
292 switch(base->type) {
293 case HAMMER2_BREF_TYPE_VOLUME:
294 count = HAMMER2_SET_COUNT;
295 break;
296 case HAMMER2_BREF_TYPE_INODE:
297 count = HAMMER2_SET_COUNT;
298 break;
299 case HAMMER2_BREF_TYPE_INDIRECT:
300 count = blocksize(base) / sizeof(hammer2_blockref_t);
301 break;
305 * Find the best candidate (the lowest blockref within the specified
306 * range). The array can be fully set associative so make no ordering
307 * assumptions.
309 again:
310 best.key = HAMMER2_KEY_MAX;
311 best.type = 0;
313 for (i = 0; i < count; ++i) {
315 * [re]load when returning from our recursion
317 if (base->type != HAMMER2_BREF_TYPE_VOLUME &&
318 base->data_off != saved_base.data_off) {
319 if (h2read(hfs, &media,
320 blocksize(base), blockoff(base))) {
321 return(-1);
323 saved_base = *base;
327 * Special case embedded file data
329 if (base->type == HAMMER2_BREF_TYPE_INODE) {
330 if (media.ipdata.meta.op_flags &
331 HAMMER2_OPFLAG_DIRECTDATA) {
332 *pptr = media.ipdata.u.data;
333 bref_ret->type = HAMMER2_BREF_TYPE_DATA;
334 bref_ret->key = 0;
335 return HAMMER2_EMBEDDED_BYTES;
340 * Calculate the bref in our scan.
342 switch(base->type) {
343 case HAMMER2_BREF_TYPE_VOLUME:
344 bref = &hfs->sroot_blockset.blockref[i];
345 break;
346 case HAMMER2_BREF_TYPE_INODE:
347 bref = &media.ipdata.u.blockset.blockref[i];
348 break;
349 case HAMMER2_BREF_TYPE_INDIRECT:
350 bref = &media.npdata[i];
351 break;
353 if (bref->type == 0)
354 continue;
355 if (bref->key > best.key)
356 continue;
357 scan_beg = bref->key;
358 scan_end = scan_beg + ((hammer2_key_t)1 << bref->keybits) - 1;
359 if (scan_end >= key_beg && scan_beg <= key_end) {
360 best = *bref;
365 * Figure out what to do with the results.
367 switch(best.type) {
368 case 0:
370 * Return 0
372 rc = 0;
373 break;
374 case HAMMER2_BREF_TYPE_INDIRECT:
376 * Matched an indirect block. If the block turns out to
377 * contain nothing we continue the iteration, otherwise
378 * we return the data from the recursion.
380 * Be sure to handle the overflow case when recalculating
381 * key_beg.
383 rc = h2lookup(hfs, &best, key_beg, key_end, bref_ret, pptr);
384 if (rc == 0) {
385 key_beg = best.key +
386 ((hammer2_key_t)1 << best.keybits);
387 if (key_beg > best.key && key_beg <= key_end)
388 goto again;
390 break;
391 case HAMMER2_BREF_TYPE_INODE:
392 case HAMMER2_BREF_TYPE_DATA:
394 * Terminal match. Leaf elements might not be data-aligned.
396 dev_bsize = blocksize(&best);
397 if (dev_bsize < HAMMER2_LBUFSIZE)
398 dev_bsize = HAMMER2_LBUFSIZE;
399 dev_boff = blockoff(&best) -
400 (blockoff(&best) & ~HAMMER2_LBUFMASK64);
401 if (h2read(hfs, &media, dev_bsize, blockoff(&best) - dev_boff))
402 return(-1);
403 saved_base.data_off = (hammer2_off_t)-1;
404 *bref_ret = best;
405 *pptr = media.buf + dev_boff;
406 rc = blocksize(&best);
407 break;
409 return(rc);
412 static
413 void
414 h2resolve(struct hammer2_fs *hfs, const char *path,
415 hammer2_blockref_t *bref, hammer2_inode_data_t **inop)
417 hammer2_blockref_t bres;
418 hammer2_inode_data_t *ino;
419 hammer2_key_t key;
420 ssize_t bytes;
421 size_t len;
424 * Start point (superroot)
426 *bref = hfs->sroot;
427 if (inop)
428 *inop = NULL;
431 * Iterate path elements
433 while (*path) {
434 while (*path == '/')
435 ++path;
436 if (*path == 0) /* terminal */
437 break;
440 * Calculate path element and look for it in the directory
442 for (len = 0; path[len]; ++len) {
443 if (path[len] == '/')
444 break;
446 key = hammer2_dirhash(path, len);
447 for (;;) {
448 bytes = h2lookup(hfs, bref,
449 key, key | 0xFFFFU,
450 &bres, (void **)&ino);
451 if (bytes == 0)
452 break;
453 if (len == ino->meta.name_len &&
454 memcmp(path, ino->filename, len) == 0) {
455 if (inop)
456 *inop = ino;
457 break;
459 key = bres.key + 1;
463 * Lookup failure
465 if (bytes == 0) {
466 bref->data_off = (hammer2_off_t)-1;
467 break;
471 * Check path continuance, inode must be a directory or
472 * we fail.
474 path += len;
475 if (*path && ino->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
476 bref->data_off = (hammer2_off_t)-1;
477 break;
479 *bref = bres;
483 static
484 ssize_t
485 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref,
486 off_t off, off_t filesize, void *buf, size_t len)
488 hammer2_blockref_t bres;
489 ssize_t total;
490 ssize_t bytes;
491 ssize_t zfill;
492 char *data;
495 * EOF edge cases
497 if (off >= filesize)
498 return (0);
499 if (off + len > filesize)
500 len = filesize - off;
503 * Loop until done
505 total = 0;
506 while (len) {
508 * Find closest bres >= requested offset.
510 bytes = h2lookup(hfs, bref, off, off + len - 1,
511 &bres, (void **)&data);
513 if (bytes < 0) {
514 if (total == 0)
515 total = -1;
516 break;
520 * Load the data into the buffer. First handle a degenerate
521 * zero-fill case.
523 if (bytes == 0) {
524 bzero(buf, len);
525 total += len;
526 break;
530 * Returned record overlaps to the left of the requested
531 * position. It must overlap in this case or h2lookup()
532 * would have returned something else.
534 if (bres.key < off) {
535 data += off - bres.key;
536 bytes -= off - bres.key;
540 * Returned record overlaps to the right of the requested
541 * position, handle zero-fill. Again h2lookup() only returns
542 * this case if there is an actual overlap.
544 if (bres.key > off) {
545 zfill = (ssize_t)(bres.key - off);
546 bzero(buf, zfill);
547 len -= zfill;
548 off += zfill;
549 total += zfill;
550 buf = (char *)buf + zfill;
554 * Trim returned request before copying.
556 if (bytes > len)
557 bytes = len;
558 bcopy(data, buf, bytes);
559 len -= bytes;
560 off += bytes;
561 total += bytes;
562 buf = (char *)buf + bytes;
564 return (total);
567 static
569 h2init(struct hammer2_fs *hfs)
571 #if 0
572 uint32_t crc0;
573 #endif
574 hammer2_tid_t best_tid = 0;
575 void *data;
576 off_t off;
577 int best;
578 int i;
579 int r;
582 * Find the best volume header.
584 * WARNING BIOS BUGS: It looks like some BIOSes will implode when
585 * given a disk offset beyond the EOM. XXX We need to probe the
586 * size of the media and limit our accesses, until then we have
587 * to give up if the first volume header does not have a hammer2
588 * signature.
590 * XXX Probably still going to be problems w/ HAMMER2 volumes on
591 * media which is too small w/certain BIOSes.
593 best = -1;
594 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
595 off = i * HAMMER2_ZONE_BYTES64;
596 if (i)
597 no_io_error = 1;
598 if (h2read(hfs, &media, sizeof(media.voldata), off))
599 break;
600 if (media.voldata.magic != HAMMER2_VOLUME_ID_HBO)
601 break;
602 if (best < 0 || best_tid < media.voldata.mirror_tid) {
603 best = i;
604 best_tid = media.voldata.mirror_tid;
607 no_io_error = 0;
608 if (best < 0)
609 return(-1);
612 * Reload the best volume header and set up the blockref.
613 * We messed with media, clear the cache before continuing.
615 off = best * HAMMER2_ZONE_BYTES64;
616 if (h2read(hfs, &media, sizeof(media.voldata), off))
617 return(-1);
618 hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME;
619 hfs->sroot.data_off = off;
620 hfs->sroot_blockset = media.voldata.sroot_blockset;
621 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
624 * Lookup sroot/BOOT and clear the cache again.
626 r = h2lookup(hfs, &hfs->sroot,
627 HAMMER2_SROOT_KEY, HAMMER2_SROOT_KEY,
628 &hfs->sroot, &data);
629 if (r <= 0)
630 return(-1);
631 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
632 r = h2lookup(hfs, &hfs->sroot,
633 HAMMER2_BOOT_KEY, HAMMER2_BOOT_KEY,
634 &hfs->sroot, &data);
635 if (r <= 0) {
636 printf("hammer2: 'BOOT' PFS not found\n");
637 return(-1);
639 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
641 return (0);
644 /************************************************************************
645 * BOOT2 SUPPORT *
646 ************************************************************************
649 #ifdef BOOT2
651 static struct hammer2_fs hfs;
653 static int
654 boot2_hammer2_init(void)
656 if (h2init(&hfs))
657 return(-1);
658 return(0);
661 static boot2_ino_t
662 boot2_hammer2_lookup(const char *path)
664 hammer2_blockref_t bref;
666 h2resolve(&hfs, path, &bref, NULL);
667 return ((boot2_ino_t)bref.data_off);
670 static ssize_t
671 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len)
673 hammer2_blockref_t bref;
674 ssize_t total;
676 bzero(&bref, sizeof(bref));
677 bref.type = HAMMER2_BREF_TYPE_INODE;
678 bref.data_off = ino;
680 total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len);
681 if (total > 0)
682 fs_off += total;
683 return total;
686 const struct boot2_fsapi boot2_hammer2_api = {
687 .fsinit = boot2_hammer2_init,
688 .fslookup = boot2_hammer2_lookup,
689 .fsread = boot2_hammer2_read
692 #endif
694 /************************************************************************
695 * BOOT2 SUPPORT *
696 ************************************************************************
699 #ifdef LIBSTAND
701 struct hfile {
702 struct hammer2_fs hfs;
703 hammer2_blockref_t bref;
704 int64_t fsize;
705 uint32_t mode;
706 uint8_t type;
709 static
711 hammer2_get_dtype(uint8_t type)
713 switch(type) {
714 case HAMMER2_OBJTYPE_DIRECTORY:
715 return(DT_DIR);
716 case HAMMER2_OBJTYPE_REGFILE:
717 return(DT_REG);
718 case HAMMER2_OBJTYPE_FIFO:
719 return(DT_FIFO);
720 case HAMMER2_OBJTYPE_CDEV:
721 return(DT_CHR);
722 case HAMMER2_OBJTYPE_BDEV:
723 return(DT_BLK);
724 case HAMMER2_OBJTYPE_SOFTLINK:
725 return(DT_LNK);
726 case HAMMER2_OBJTYPE_HARDLINK:
727 return(DT_UNKNOWN);
728 case HAMMER2_OBJTYPE_SOCKET:
729 return(DT_SOCK);
730 default:
731 return(DT_UNKNOWN);
735 static
736 mode_t
737 hammer2_get_mode(uint8_t type)
739 switch(type) {
740 case HAMMER2_OBJTYPE_DIRECTORY:
741 return(S_IFDIR);
742 case HAMMER2_OBJTYPE_REGFILE:
743 return(S_IFREG);
744 case HAMMER2_OBJTYPE_FIFO:
745 return(S_IFIFO);
746 case HAMMER2_OBJTYPE_CDEV:
747 return(S_IFCHR);
748 case HAMMER2_OBJTYPE_BDEV:
749 return(S_IFBLK);
750 case HAMMER2_OBJTYPE_SOFTLINK:
751 return(S_IFLNK);
752 case HAMMER2_OBJTYPE_HARDLINK:
753 return(0);
754 case HAMMER2_OBJTYPE_SOCKET:
755 return(S_IFSOCK);
756 default:
757 return(0);
761 static int
762 hammer2_open(const char *path, struct open_file *f)
764 struct hfile *hf = malloc(sizeof(*hf));
765 hammer2_inode_data_t *ipdata;
767 bzero(hf, sizeof(*hf));
768 f->f_offset = 0;
769 f->f_fsdata = hf;
770 hf->hfs.f = f;
772 if (h2init(&hf->hfs)) {
773 f->f_fsdata = NULL;
774 free(hf);
775 errno = ENOENT;
776 return(-1);
778 h2resolve(&hf->hfs, path, &hf->bref, &ipdata);
779 if (hf->bref.data_off == (hammer2_off_t)-1 ||
780 (hf->bref.type != HAMMER2_BREF_TYPE_INODE &&
781 hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) {
782 f->f_fsdata = NULL;
783 free(hf);
784 errno = ENOENT;
785 return(-1);
787 if (ipdata) {
788 hf->fsize = ipdata->meta.size;
789 hf->type = ipdata->meta.type;
790 hf->mode = ipdata->meta.mode |
791 hammer2_get_mode(ipdata->meta.type);
792 } else {
793 hf->fsize = 0;
794 hf->type = HAMMER2_OBJTYPE_DIRECTORY;
795 hf->mode = 0755 | S_IFDIR;
797 return(0);
800 static int
801 hammer2_close(struct open_file *f)
803 struct hfile *hf = f->f_fsdata;
805 f->f_fsdata = NULL;
806 if (hf)
807 free(hf);
808 return (0);
811 static int
812 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid)
814 struct hfile *hf = f->f_fsdata;
815 ssize_t total;
816 int rc = 0;
818 total = h2readfile(&hf->hfs, &hf->bref,
819 f->f_offset, hf->fsize, buf, len);
820 if (total < 0) {
821 rc = EIO;
822 total = 0;
823 } else {
824 f->f_offset += total;
825 rc = 0;
827 *resid = len - total;
828 return rc;
831 static off_t
832 hammer2_seek(struct open_file *f, off_t offset, int whence)
834 struct hfile *hf = f->f_fsdata;
836 switch (whence) {
837 case SEEK_SET:
838 f->f_offset = offset;
839 break;
840 case SEEK_CUR:
841 f->f_offset += offset;
842 break;
843 case SEEK_END:
844 f->f_offset = hf->fsize - offset;
845 break;
846 default:
847 return (-1);
849 return (f->f_offset);
852 static int
853 hammer2_stat(struct open_file *f, struct stat *st)
855 struct hfile *hf = f->f_fsdata;
857 st->st_mode = hf->mode;
858 st->st_uid = 0;
859 st->st_gid = 0;
860 st->st_size = hf->fsize;
862 return (0);
865 static int
866 hammer2_readdir(struct open_file *f, struct dirent *den)
868 struct hfile *hf = f->f_fsdata;
869 hammer2_blockref_t bres;
870 hammer2_inode_data_t *ipdata;
871 int bytes;
873 for (;;) {
874 bytes = h2lookup(&hf->hfs, &hf->bref,
875 f->f_offset | HAMMER2_DIRHASH_VISIBLE,
876 HAMMER2_KEY_MAX,
877 &bres, (void **)&ipdata);
878 if (bytes <= 0)
879 break;
880 den->d_namlen = ipdata->meta.name_len;
881 den->d_type = hammer2_get_dtype(ipdata->meta.type);
882 den->d_ino = ipdata->meta.inum;
883 bcopy(ipdata->filename, den->d_name, den->d_namlen);
884 den->d_name[den->d_namlen] = 0;
886 f->f_offset = bres.key + 1;
888 return(0);
890 return ENOENT;
893 struct fs_ops hammer_fsops = {
894 "hammer2",
895 hammer2_open,
896 hammer2_close,
897 hammer2_read,
898 null_write,
899 hammer2_seek,
900 hammer2_stat,
901 hammer2_readdir
904 #endif