newsyslog.8: Comment out another zstd reference (and fix a typo).
[dragonfly.git] / lib / libstand / hammer2.c
blobc11226b66120a48eb0ad98fdcd4c23c89ffa9c59
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 size_t bytes;
145 bytes = (size_t)(bref->data_off & HAMMER2_OFF_MASK_RADIX);
146 if (bytes)
147 bytes = (size_t)1 << bytes;
148 return bytes;
151 static
152 hammer2_key_t
153 hammer2_dirhash(const unsigned char *name, size_t len)
155 const unsigned char *aname = name;
156 uint32_t crcx;
157 uint64_t key;
158 size_t i;
159 size_t j;
161 key = 0;
164 * m32
166 crcx = 0;
167 for (i = j = 0; i < len; ++i) {
168 if (aname[i] == '.' ||
169 aname[i] == '-' ||
170 aname[i] == '_' ||
171 aname[i] == '~') {
172 if (i != j)
173 crcx += hammer2_icrc32(aname + j, i - j);
174 j = i + 1;
177 if (i != j)
178 crcx += hammer2_icrc32(aname + j, i - j);
181 * The directory hash utilizes the top 32 bits of the 64-bit key.
182 * Bit 63 must be set to 1.
184 crcx |= 0x80000000U;
185 key |= (uint64_t)crcx << 32;
188 * l16 - crc of entire filename
190 * This crc reduces degenerate hash collision conditions
192 crcx = hammer2_icrc32(aname, len);
193 crcx = crcx ^ (crcx << 16);
194 key |= crcx & 0xFFFF0000U;
197 * Set bit 15. This allows readdir to strip bit 63 so a positive
198 * 64-bit cookie/offset can always be returned, and still guarantee
199 * that the values 0x0000-0x7FFF are available for artificial entries.
200 * ('.' and '..').
202 key |= 0x8000U;
204 return (key);
208 * Low level read
210 static
212 h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off)
214 #if defined(LIBSTAND)
215 size_t rlen;
216 #endif
217 int rc;
219 #if defined(TESTING)
220 rc = pread(hfs->fd, &media, nbytes, off);
221 if (rc == (int)nbytes)
222 rc = 0;
223 else
224 rc = -1;
225 #elif defined(LIBSTAND)
226 rc = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ,
227 off >> DEV_BSHIFT, nbytes,
228 buf, &rlen);
229 if (rc || rlen != nbytes)
230 rc = -1;
231 #elif defined(BOOT2)
232 /* BIOS interface may barf on 64KB reads */
233 rc = 0;
234 while (nbytes > 16384) {
235 rc = dskread(buf, off >> DEV_BSHIFT, 16384 >> DEV_BSHIFT);
236 nbytes -= 16384;
237 buf = (char *)buf + 16384;
238 off += 16384;
240 if (nbytes)
241 rc = dskread(buf, off >> DEV_BSHIFT, nbytes >> DEV_BSHIFT);
242 if (rc)
243 rc = -1;
244 #else
245 #error "hammer2: unknown library API"
246 #endif
247 return rc;
251 * Common code
253 * Initialize for HAMMER2 filesystem access given a hammer2_fs with
254 * its device file descriptor initialized.
258 * Lookup within the block specified by (*base), loading the block from disk
259 * if necessary. Locate the first key within the requested range and
260 * recursively run through indirect blocks as needed. The caller may loop
261 * by setting key_beg to *key_ret.
263 * Returns 0 if nothing could be found and the key range has been exhausted.
264 * returns -1 if a disk error occured. Otherwise returns the size of the
265 * data block and returns the data block in *pptr and bref in *bref_ret.
267 * NOTE! When reading file data, the returned bref's key will be the nearest
268 * data block we looked up. The file read procedure must handle any
269 * zero-fill or skip. However, we will truncate the return value to
270 * the file size.
272 static int
273 h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base,
274 hammer2_key_t key_beg, hammer2_key_t key_end,
275 hammer2_blockref_t *bref_ret, void **pptr)
277 hammer2_blockref_t *bref;
278 hammer2_blockref_t best;
279 hammer2_key_t scan_beg;
280 hammer2_key_t scan_end;
281 int i;
282 int rc;
283 int count;
284 int dev_boff;
285 int dev_bsize;
287 if (base == NULL) {
288 saved_base.data_off = (hammer2_off_t)-1;
289 return(0);
291 if (base->data_off == (hammer2_off_t)-1) {
292 bref_ret->type = 0;
293 return(-1);
297 * Calculate the number of blockrefs to scan
299 switch(base->type) {
300 case HAMMER2_BREF_TYPE_VOLUME:
301 count = HAMMER2_SET_COUNT;
302 break;
303 case HAMMER2_BREF_TYPE_INODE:
304 count = HAMMER2_SET_COUNT;
305 break;
306 case HAMMER2_BREF_TYPE_INDIRECT:
307 count = blocksize(base) / sizeof(hammer2_blockref_t);
308 break;
309 default:
310 count = 0;
311 break;
315 * Find the best candidate (the lowest blockref within the specified
316 * range). The array can be fully set associative so make no ordering
317 * assumptions.
319 again:
320 best.key = HAMMER2_KEY_MAX;
321 best.type = 0;
323 for (i = 0; i < count; ++i) {
325 * [re]load when returning from our recursion
327 if (base->type != HAMMER2_BREF_TYPE_VOLUME &&
328 base->data_off != saved_base.data_off) {
329 if (blocksize(base) && h2read(hfs, &media,
330 blocksize(base),
331 blockoff(base))) {
332 bref_ret->type = 0;
333 return(-1);
335 saved_base = *base;
339 * Special case embedded file data
341 if (base->type == HAMMER2_BREF_TYPE_INODE) {
342 if (media.ipdata.meta.op_flags &
343 HAMMER2_OPFLAG_DIRECTDATA) {
344 *pptr = media.ipdata.u.data;
345 bref_ret->type = HAMMER2_BREF_TYPE_DATA;
346 bref_ret->key = 0;
347 return HAMMER2_EMBEDDED_BYTES;
352 * Calculate the bref in our scan.
354 switch(base->type) {
355 case HAMMER2_BREF_TYPE_VOLUME:
356 bref = &hfs->sroot_blockset.blockref[i];
357 break;
358 case HAMMER2_BREF_TYPE_INODE:
359 bref = &media.ipdata.u.blockset.blockref[i];
360 break;
361 case HAMMER2_BREF_TYPE_INDIRECT:
362 bref = &media.npdata[i];
363 break;
365 if (bref->type == 0)
366 continue;
367 if (bref->key > best.key)
368 continue;
369 scan_beg = bref->key;
370 scan_end = scan_beg + ((hammer2_key_t)1 << bref->keybits) - 1;
371 if (scan_end >= key_beg && scan_beg <= key_end) {
372 best = *bref;
377 * Figure out what to do with the results.
379 switch(best.type) {
380 case 0:
382 * Return 0
384 bref_ret->type = 0;
385 rc = 0;
386 break;
387 case HAMMER2_BREF_TYPE_INDIRECT:
389 * Matched an indirect block. If the block turns out to
390 * contain nothing we continue the iteration, otherwise
391 * we return the data from the recursion.
393 * Be sure to handle the overflow case when recalculating
394 * key_beg.
396 rc = h2lookup(hfs, &best, key_beg, key_end, bref_ret, pptr);
397 if (rc == 0) {
398 key_beg = best.key +
399 ((hammer2_key_t)1 << best.keybits);
400 if (key_beg > best.key && key_beg <= key_end)
401 goto again;
403 break;
404 case HAMMER2_BREF_TYPE_DIRENT:
405 case HAMMER2_BREF_TYPE_INODE:
406 case HAMMER2_BREF_TYPE_DATA:
408 * Terminal match. Leaf elements might not be data-aligned.
410 dev_bsize = blocksize(&best);
411 if (dev_bsize) {
412 if (dev_bsize < HAMMER2_LBUFSIZE)
413 dev_bsize = HAMMER2_LBUFSIZE;
414 dev_boff = blockoff(&best) -
415 (blockoff(&best) & ~HAMMER2_LBUFMASK64);
416 if (h2read(hfs, &media,
417 dev_bsize,
418 blockoff(&best) - dev_boff)) {
419 return(-1);
422 saved_base.data_off = (hammer2_off_t)-1;
423 *bref_ret = best;
424 *pptr = media.buf + dev_boff;
425 rc = blocksize(&best);
426 break;
428 return(rc);
431 static
432 void
433 h2resolve(struct hammer2_fs *hfs, const char *path,
434 hammer2_blockref_t *bref, hammer2_inode_data_t **inop)
436 hammer2_blockref_t bres;
437 hammer2_inode_data_t *ino;
438 hammer2_key_t key;
439 void *data;
440 ssize_t bytes;
441 size_t len;
444 * Start point (superroot)
446 ino = NULL;
447 *bref = hfs->sroot;
448 if (inop)
449 *inop = NULL;
452 * Iterate path elements
454 while (*path) {
455 while (*path == '/')
456 ++path;
457 if (*path == 0) /* terminal */
458 break;
461 * Calculate path element and look for it in the directory
463 for (len = 0; path[len]; ++len) {
464 if (path[len] == '/')
465 break;
467 key = hammer2_dirhash(path, len);
468 for (;;) {
469 bytes = h2lookup(hfs, bref,
470 key, key | 0xFFFFU,
471 &bres, (void **)&data);
472 if (bytes < 0)
473 break;
474 if (bres.type == 0)
475 break;
476 switch (bres.type) {
477 case HAMMER2_BREF_TYPE_DIRENT:
478 if (bres.embed.dirent.namlen != len)
479 break;
480 if (bres.embed.dirent.namlen <=
481 sizeof(bres.check.buf)) {
482 if (memcmp(path, bres.check.buf, len))
483 break;
484 } else {
485 if (memcmp(path, data, len))
486 break;
490 * Found, resolve inode. This will set
491 * ino similarly to HAMMER2_BREF_TYPE_INODE
492 * and adjust bres, which path continuation
493 * needs.
495 *bref = hfs->sroot;
496 bytes = h2lookup(hfs, bref,
497 bres.embed.dirent.inum,
498 bres.embed.dirent.inum,
499 &bres, (void **)&ino);
500 if (inop)
501 *inop = ino;
502 goto found;
503 break; /* NOT REACHED */
504 case HAMMER2_BREF_TYPE_INODE:
505 ino = data;
506 if (ino->meta.name_len != len)
507 break;
508 if (memcmp(path, ino->filename, len) == 0) {
509 if (inop)
510 *inop = ino;
511 goto found;
513 break;
515 if ((bres.key & 0xFFFF) == 0xFFFF) {
516 bres.type = 0;
517 break;
519 key = bres.key + 1;
521 found:
524 * Lookup failure
526 if (bytes < 0 || bres.type == 0) {
527 bref->data_off = (hammer2_off_t)-1;
528 ino = NULL;
529 break;
533 * Check path continuance, inode must be a directory or
534 * we fail.
536 path += len;
537 if (*path && ino->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
538 bref->data_off = (hammer2_off_t)-1;
539 break;
541 *bref = bres;
545 static
546 ssize_t
547 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref,
548 off_t off, off_t filesize, void *buf, size_t len)
550 hammer2_blockref_t bres;
551 ssize_t total;
552 ssize_t bytes;
553 ssize_t zfill;
554 char *data;
557 * EOF edge cases
559 if (off >= filesize)
560 return (0);
561 if (off + len > filesize)
562 len = filesize - off;
565 * Loop until done
567 total = 0;
568 while (len) {
570 * Find closest bres >= requested offset.
572 bytes = h2lookup(hfs, bref, off, off + len - 1,
573 &bres, (void **)&data);
575 if (bytes < 0) {
576 if (total == 0)
577 total = -1;
578 break;
582 * Load the data into the buffer. First handle a degenerate
583 * zero-fill case.
585 if (bytes == 0) {
586 bzero(buf, len);
587 total += len;
588 break;
592 * Returned record overlaps to the left of the requested
593 * position. It must overlap in this case or h2lookup()
594 * would have returned something else.
596 if (bres.key < off) {
597 data += off - bres.key;
598 bytes -= off - bres.key;
602 * Returned record overlaps to the right of the requested
603 * position, handle zero-fill. Again h2lookup() only returns
604 * this case if there is an actual overlap.
606 if (bres.key > off) {
607 zfill = (ssize_t)(bres.key - off);
608 bzero(buf, zfill);
609 len -= zfill;
610 off += zfill;
611 total += zfill;
612 buf = (char *)buf + zfill;
616 * Trim returned request before copying.
618 if (bytes > len)
619 bytes = len;
620 bcopy(data, buf, bytes);
621 len -= bytes;
622 off += bytes;
623 total += bytes;
624 buf = (char *)buf + bytes;
626 return (total);
629 static
631 h2init(struct hammer2_fs *hfs)
633 #if 0
634 uint32_t crc0;
635 #endif
636 hammer2_tid_t best_tid = 0;
637 void *data;
638 off_t off;
639 int best;
640 int i;
641 int r;
644 * Find the best volume header.
646 * WARNING BIOS BUGS: It looks like some BIOSes will implode when
647 * given a disk offset beyond the EOM. XXX We need to probe the
648 * size of the media and limit our accesses, until then we have
649 * to give up if the first volume header does not have a hammer2
650 * signature.
652 * XXX Probably still going to be problems w/ HAMMER2 volumes on
653 * media which is too small w/certain BIOSes.
655 best = -1;
656 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
657 off = i * HAMMER2_ZONE_BYTES64;
658 if (i)
659 no_io_error = 1;
660 if (h2read(hfs, &media, sizeof(media.voldata), off))
661 break;
662 if (media.voldata.magic != HAMMER2_VOLUME_ID_HBO)
663 break;
664 if (best < 0 || best_tid < media.voldata.mirror_tid) {
665 best = i;
666 best_tid = media.voldata.mirror_tid;
669 no_io_error = 0;
670 if (best < 0)
671 return(-1);
674 * Reload the best volume header and set up the blockref.
675 * We messed with media, clear the cache before continuing.
677 off = best * HAMMER2_ZONE_BYTES64;
678 if (h2read(hfs, &media, sizeof(media.voldata), off))
679 return(-1);
680 hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME;
681 hfs->sroot.data_off = off;
682 hfs->sroot_blockset = media.voldata.sroot_blockset;
683 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
686 * Lookup sroot/BOOT and clear the cache again.
688 r = h2lookup(hfs, &hfs->sroot,
689 HAMMER2_SROOT_KEY, HAMMER2_SROOT_KEY,
690 &hfs->sroot, &data);
691 if (r <= 0)
692 return(-1);
693 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
694 r = h2lookup(hfs, &hfs->sroot,
695 HAMMER2_BOOT_KEY, HAMMER2_BOOT_KEY,
696 &hfs->sroot, &data);
697 if (r <= 0) {
698 printf("hammer2: 'BOOT' PFS not found\n");
699 return(-1);
701 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
703 return (0);
706 /************************************************************************
707 * BOOT2 SUPPORT *
708 ************************************************************************
711 #ifdef BOOT2
713 static struct hammer2_fs hfs;
715 static int
716 boot2_hammer2_init(void)
718 if (h2init(&hfs))
719 return(-1);
720 return(0);
723 static boot2_ino_t
724 boot2_hammer2_lookup(const char *path)
726 hammer2_blockref_t bref;
728 h2resolve(&hfs, path, &bref, NULL);
729 return ((boot2_ino_t)bref.data_off);
732 static ssize_t
733 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len)
735 hammer2_blockref_t bref;
736 ssize_t total;
738 bzero(&bref, sizeof(bref));
739 bref.type = HAMMER2_BREF_TYPE_INODE;
740 bref.data_off = ino;
742 total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len);
743 if (total > 0)
744 fs_off += total;
745 return total;
748 const struct boot2_fsapi boot2_hammer2_api = {
749 .fsinit = boot2_hammer2_init,
750 .fslookup = boot2_hammer2_lookup,
751 .fsread = boot2_hammer2_read
754 #endif
756 /************************************************************************
757 * BOOT2 SUPPORT *
758 ************************************************************************
761 #ifdef LIBSTAND
763 struct hfile {
764 struct hammer2_fs hfs;
765 hammer2_blockref_t bref;
766 int64_t fsize;
767 uint32_t mode;
768 uint8_t type;
771 static
773 hammer2_get_dtype(uint8_t type)
775 switch(type) {
776 case HAMMER2_OBJTYPE_DIRECTORY:
777 return(DT_DIR);
778 case HAMMER2_OBJTYPE_REGFILE:
779 return(DT_REG);
780 case HAMMER2_OBJTYPE_FIFO:
781 return(DT_FIFO);
782 case HAMMER2_OBJTYPE_CDEV:
783 return(DT_CHR);
784 case HAMMER2_OBJTYPE_BDEV:
785 return(DT_BLK);
786 case HAMMER2_OBJTYPE_SOFTLINK:
787 return(DT_LNK);
788 case HAMMER2_OBJTYPE_SOCKET:
789 return(DT_SOCK);
790 default:
791 return(DT_UNKNOWN);
795 static
796 mode_t
797 hammer2_get_mode(uint8_t type)
799 switch(type) {
800 case HAMMER2_OBJTYPE_DIRECTORY:
801 return(S_IFDIR);
802 case HAMMER2_OBJTYPE_REGFILE:
803 return(S_IFREG);
804 case HAMMER2_OBJTYPE_FIFO:
805 return(S_IFIFO);
806 case HAMMER2_OBJTYPE_CDEV:
807 return(S_IFCHR);
808 case HAMMER2_OBJTYPE_BDEV:
809 return(S_IFBLK);
810 case HAMMER2_OBJTYPE_SOFTLINK:
811 return(S_IFLNK);
812 case HAMMER2_OBJTYPE_SOCKET:
813 return(S_IFSOCK);
814 default:
815 return(0);
819 static int
820 hammer2_open(const char *path, struct open_file *f)
822 struct hfile *hf = malloc(sizeof(*hf));
823 hammer2_inode_data_t *ipdata;
825 bzero(hf, sizeof(*hf));
826 f->f_offset = 0;
827 f->f_fsdata = hf;
828 hf->hfs.f = f;
830 if (h2init(&hf->hfs)) {
831 f->f_fsdata = NULL;
832 free(hf);
833 errno = ENOENT;
834 return(-1);
836 h2resolve(&hf->hfs, path, &hf->bref, &ipdata);
837 if (hf->bref.data_off == (hammer2_off_t)-1 ||
838 (hf->bref.type != HAMMER2_BREF_TYPE_INODE &&
839 hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) {
840 f->f_fsdata = NULL;
841 free(hf);
842 errno = ENOENT;
843 return(-1);
845 if (ipdata) {
846 hf->fsize = ipdata->meta.size;
847 hf->type = ipdata->meta.type;
848 hf->mode = ipdata->meta.mode |
849 hammer2_get_mode(ipdata->meta.type);
850 } else {
851 hf->fsize = 0;
852 hf->type = HAMMER2_OBJTYPE_DIRECTORY;
853 hf->mode = 0755 | S_IFDIR;
855 return(0);
858 static int
859 hammer2_close(struct open_file *f)
861 struct hfile *hf = f->f_fsdata;
863 f->f_fsdata = NULL;
864 if (hf)
865 free(hf);
866 return (0);
869 static int
870 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid)
872 struct hfile *hf = f->f_fsdata;
873 ssize_t total;
874 int rc = 0;
876 total = h2readfile(&hf->hfs, &hf->bref,
877 f->f_offset, hf->fsize, buf, len);
878 if (total < 0) {
879 rc = EIO;
880 total = 0;
881 } else {
882 f->f_offset += total;
883 rc = 0;
885 *resid = len - total;
886 return rc;
889 static off_t
890 hammer2_seek(struct open_file *f, off_t offset, int whence)
892 struct hfile *hf = f->f_fsdata;
894 switch (whence) {
895 case SEEK_SET:
896 f->f_offset = offset;
897 break;
898 case SEEK_CUR:
899 f->f_offset += offset;
900 break;
901 case SEEK_END:
902 f->f_offset = hf->fsize - offset;
903 break;
904 default:
905 return (-1);
907 return (f->f_offset);
910 static int
911 hammer2_stat(struct open_file *f, struct stat *st)
913 struct hfile *hf = f->f_fsdata;
915 st->st_mode = hf->mode;
916 st->st_uid = 0;
917 st->st_gid = 0;
918 st->st_size = hf->fsize;
920 return (0);
923 static int
924 hammer2_readdir(struct open_file *f, struct dirent *den)
926 struct hfile *hf = f->f_fsdata;
927 hammer2_blockref_t bres;
928 hammer2_inode_data_t *ipdata;
929 void *data;
930 int bytes;
932 for (;;) {
933 bytes = h2lookup(&hf->hfs, &hf->bref,
934 f->f_offset | HAMMER2_DIRHASH_VISIBLE,
935 HAMMER2_KEY_MAX,
936 &bres, (void **)&data);
937 if (bytes < 0)
938 break;
939 switch (bres.type) {
940 case HAMMER2_BREF_TYPE_INODE:
941 ipdata = data;
942 den->d_namlen = ipdata->meta.name_len;
943 den->d_type = hammer2_get_dtype(ipdata->meta.type);
944 den->d_ino = ipdata->meta.inum;
945 bcopy(ipdata->filename, den->d_name, den->d_namlen);
946 den->d_name[den->d_namlen] = 0;
947 break;
948 case HAMMER2_BREF_TYPE_DIRENT:
949 den->d_namlen = bres.embed.dirent.namlen;
950 den->d_type = hammer2_get_dtype(bres.embed.dirent.type);
951 den->d_ino = bres.embed.dirent.inum;
952 if (den->d_namlen <= sizeof(bres.check.buf)) {
953 bcopy(bres.check.buf,
954 den->d_name,
955 den->d_namlen);
956 } else {
957 bcopy(data, den->d_name, den->d_namlen);
959 den->d_name[den->d_namlen] = 0;
960 break;
961 default:
962 den->d_namlen = 1;
963 den->d_type =
964 hammer2_get_dtype(HAMMER2_OBJTYPE_REGFILE);
965 den->d_name[0] = '?';
966 den->d_name[1] = 0;
967 break;
970 f->f_offset = bres.key + 1;
972 return(0);
974 return ENOENT;
977 struct fs_ops hammer_fsops = {
978 "hammer2",
979 hammer2_open,
980 hammer2_close,
981 hammer2_read,
982 null_write,
983 hammer2_seek,
984 hammer2_stat,
985 hammer2_readdir
988 #endif