Unbreak buildworld after recent hammer2 changes.
[dragonfly.git] / lib / libstand / hammer2.c
1 /*
2  * Copyright (c) 2011-2013 The DragonFly Project.  All rights reserved.
3  *
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>
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  */
35
36 #if !defined(BOOT2) && !defined(TESTING)
37 #define LIBSTAND        1
38 #endif
39
40 #ifdef BOOT2
41 #include "boot2.h"
42 #endif
43
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
58
59 #ifdef LIBSTAND
60 #include "stand.h"
61 #endif
62
63 #include <vfs/hammer2/hammer2_disk.h>
64
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);
67
68 #define hammer2_icrc32(buf, size)       iscsi_crc32(buf, size)
69
70 struct hammer2_fs {
71         struct hammer2_volume_data      vol;
72         hammer2_blockref_t              sroot;
73 #if defined(TESTING)
74         int                             fd;
75 #elif defined(LIBSTAND)
76         struct open_file                *f;
77 #elif defined(BOOT2)
78         /* BOOT2 doesn't use a descriptor */
79 #else
80 #error "hammer2: unknown library API"
81 #endif
82 };
83
84 struct hammer2_inode {
85         struct hammer2_inode_data       ino;    /* raw inode data */
86         off_t                           doff;   /* disk inode offset */
87 };
88
89 #ifdef BOOT2
90
91 static void
92 bzero(void *buf, size_t size)
93 {
94         for (size_t i = 0; i < size; i++)
95                 ((char *)buf)[i] = 0;
96 }
97
98 static void
99 bcopy(void *src, void *dst, size_t size)
100 {
101         memcpy(dst, src, size);
102 }
103
104 #if 0
105 static size_t
106 strlen(const char *s)
107 {
108         size_t l = 0;
109         for (; *s != 0; s++)
110                 l++;
111         return (l);
112 }
113 #endif
114
115 static int
116 memcmp(const void *a, const void *b, size_t len)
117 {
118         for (size_t p = 0; p < len; p++) {
119                 int r = ((const char *)a)[p] - ((const char *)b)[p];
120                 if (r != 0)
121                         return (r);
122         }
123
124         return (0);
125 }
126
127 #endif
128
129 static
130 off_t
131 blockoff(hammer2_blockref_t *bref)
132 {
133         return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX);
134 }
135
136 static
137 size_t
138 blocksize(hammer2_blockref_t *bref)
139 {
140         return(1 << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX));
141 }
142
143 static
144 hammer2_key_t
145 hammer2_dirhash(const unsigned char *name, size_t len)
146 {
147         const unsigned char *aname = name;
148         uint32_t crcx;
149         uint64_t key;
150         size_t i;
151         size_t j;
152
153         key = 0;
154
155         /*
156          * m32
157          */
158         crcx = 0;
159         for (i = j = 0; i < len; ++i) {
160                 if (aname[i] == '.' ||
161                     aname[i] == '-' ||
162                     aname[i] == '_' ||
163                     aname[i] == '~') {
164                         if (i != j)
165                                 crcx += hammer2_icrc32(aname + j, i - j);
166                         j = i + 1;
167                 }
168         }
169         if (i != j)
170                 crcx += hammer2_icrc32(aname + j, i - j);
171
172         /*
173          * The directory hash utilizes the top 32 bits of the 64-bit key.
174          * Bit 63 must be set to 1.
175          */
176         crcx |= 0x80000000U;
177         key |= (uint64_t)crcx << 32;
178
179         /*
180          * l16 - crc of entire filename
181          *
182          * This crc reduces degenerate hash collision conditions
183          */
184         crcx = hammer2_icrc32(aname, len);
185         crcx = crcx ^ (crcx << 16);
186         key |= crcx & 0xFFFF0000U;
187
188         /*
189          * Set bit 15.  This allows readdir to strip bit 63 so a positive
190          * 64-bit cookie/offset can always be returned, and still guarantee
191          * that the values 0x0000-0x7FFF are available for artificial entries.
192          * ('.' and '..').
193          */
194         key |= 0x8000U;
195
196         return (key);
197 }
198
199 /*
200  * Low level read
201  */
202 static
203 int
204 h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off)
205 {
206 #if defined(LIBSTAND)
207         size_t rlen;
208 #endif
209         int rc;
210
211 #if defined(TESTING)
212         rc = pread(hfs->fd, &hfs->vol, nbytes, off);
213         if (rc == (int)nbytes)
214                 rc = 0;
215         else
216                 rc = -1;
217 #elif defined(LIBSTAND)
218         rc = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ,
219                                         off >> DEV_BSHIFT, nbytes,
220                                         buf, &rlen);
221         if (rc || rlen != nbytes)
222                 rc = -1;
223 #elif defined(BOOT2)
224         rc = dskread(buf, off >> DEV_BSHIFT, nbytes >> DEV_BSHIFT);
225         if (rc)
226                 rc = -1;
227 #else
228 #error "hammer2: unknown library API"
229 #endif
230         return rc;
231 }
232
233 /*
234  * Common code
235  *
236  * Initialize for HAMMER2 filesystem access given a hammer2_fs with
237  * its device file descriptor initialized.
238  */
239
240 /*
241  * Lookup within the block specified by (*base), loading the block from disk
242  * if necessary.  Locate the first key within the requested range and
243  * recursively run through indirect blocks as needed.  The caller may loop
244  * by setting key_beg to *key_ret.
245  *
246  * Returns 0 if nothing could be found and the key range has been exhausted.
247  * returns -1 if a disk error occured.  Otherwise returns the size of the
248  * data block and returns the data block in *pptr and bref in *bref_ret.
249  *
250  * NOTE! When reading file data, the returned bref's key will be the nearest
251  *       data block we looked up.  The file read procedure must handle any
252  *       zero-fill or skip.  However, we will truncate the return value to
253  *       the file size.
254  */
255 static int
256 h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base,
257          hammer2_key_t key_beg, hammer2_key_t key_end,
258          hammer2_blockref_t *bref_ret, void **pptr)
259 {
260         hammer2_blockref_t *bref;
261         hammer2_blockref_t best;
262         hammer2_key_t scan_beg;
263         hammer2_key_t scan_end;
264         int i;
265         int rc;
266         int count;
267         int dev_boff;
268         int dev_bsize;
269         static hammer2_media_data_t media;
270         static hammer2_blockref_t saved_base;
271
272         if (base == NULL) {
273                 saved_base.data_off = (hammer2_off_t)-1;
274                 return(0);
275         }
276         if (base->data_off == (hammer2_off_t)-1)
277                 return(-1);
278
279         /*
280          * Calculate the number of blockrefs to scan
281          */
282         switch(base->type) {
283         case HAMMER2_BREF_TYPE_VOLUME:
284                 count = HAMMER2_SET_COUNT;
285                 break;
286         case HAMMER2_BREF_TYPE_INODE:
287                 count = HAMMER2_SET_COUNT;
288                 break;
289         case HAMMER2_BREF_TYPE_INDIRECT:
290                 count = blocksize(base) / sizeof(hammer2_blockref_t);
291                 break;
292         }
293
294         /*
295          * Find the best candidate (the lowest blockref within the specified
296          * range).  The array can be fully set associative so make no ordering
297          * assumptions.
298          */
299 again:
300         best.key = HAMMER2_KEY_MAX;
301         best.type = 0;
302
303         for (i = 0; i < count; ++i) {
304                 /*
305                  * [re]load when returning from our recursion
306                  */
307                 if (base->type != HAMMER2_BREF_TYPE_VOLUME &&
308                     base->data_off != saved_base.data_off) {
309                         if (h2read(hfs, &media,
310                                    blocksize(base), blockoff(base))) {
311                                 return(-1);
312                         }
313                         saved_base = *base;
314                 }
315
316                 /*
317                  * Special case embedded file data
318                  */
319                 if (base->type == HAMMER2_BREF_TYPE_INODE) {
320                         if (media.ipdata.op_flags & HAMMER2_OPFLAG_DIRECTDATA) {
321                                 *pptr = media.ipdata.u.data;
322                                 bref_ret->type = HAMMER2_BREF_TYPE_DATA;
323                                 bref_ret->key = 0;
324                                 return HAMMER2_EMBEDDED_BYTES;
325                         }
326                 }
327
328                 /*
329                  * Calculate the bref in our scan.
330                  */
331                 switch(base->type) {
332                 case HAMMER2_BREF_TYPE_VOLUME:
333                         bref = &hfs->vol.sroot_blockset.blockref[i];
334                         break;
335                 case HAMMER2_BREF_TYPE_INODE:
336                         bref = &media.ipdata.u.blockset.blockref[i];
337                         break;
338                 case HAMMER2_BREF_TYPE_INDIRECT:
339                         bref = &media.npdata[i];
340                         break;
341                 }
342                 if (bref->type == 0)
343                         continue;
344                 if (bref->key > best.key)
345                         continue;
346                 scan_beg = bref->key;
347                 scan_end = scan_beg + ((hammer2_key_t)1 << bref->keybits) - 1;
348                 if (scan_end >= key_beg && scan_beg <= key_end) {
349                         best = *bref;
350                 }
351         }
352
353         /*
354          * Figure out what to do with the results.
355          */
356         switch(best.type) {
357         case 0:
358                 /*
359                  * Return 0
360                  */
361                 rc = 0;
362                 break;
363         case HAMMER2_BREF_TYPE_INDIRECT:
364                 /*
365                  * Matched an indirect block.  If the block turns out to
366                  * contain nothing we continue the iteration, otherwise
367                  * we return the data from the recursion.
368                  *
369                  * Be sure to handle the overflow case when recalculating
370                  * key_beg.
371                  */
372                 rc = h2lookup(hfs, &best, key_beg, key_end, bref_ret, pptr);
373                 if (rc == 0) {
374                         key_beg = best.key +
375                                   ((hammer2_key_t)1 << best.keybits);
376                         if (key_beg > best.key && key_beg <= key_end)
377                                 goto again;
378                 }
379                 break;
380         case HAMMER2_BREF_TYPE_INODE:
381         case HAMMER2_BREF_TYPE_DATA:
382                 /*
383                  * Terminal match.  Leaf elements might not be data-aligned.
384                  */
385                 dev_bsize = blocksize(&best);
386                 if (dev_bsize < HAMMER2_LBUFSIZE)
387                         dev_bsize = HAMMER2_LBUFSIZE;
388                 dev_boff = blockoff(&best) -
389                            (blockoff(&best) & ~HAMMER2_LBUFMASK64);
390                 if (h2read(hfs, &media, dev_bsize, blockoff(&best) - dev_boff))
391                         return(-1);
392                 saved_base.data_off = (hammer2_off_t)-1;
393                 *bref_ret = best;
394                 *pptr = media.buf + dev_boff;
395                 rc = blocksize(&best);
396                 break;
397         }
398         return(rc);
399 }
400
401 static
402 void
403 h2resolve(struct hammer2_fs *hfs, const char *path,
404           hammer2_blockref_t *bref, hammer2_inode_data_t **inop)
405 {
406         hammer2_blockref_t bres;
407         hammer2_inode_data_t *ino;
408         hammer2_key_t key;
409         ssize_t bytes;
410         size_t len;
411
412         /*
413          * Start point (superroot)
414          */
415         *bref = hfs->sroot;
416         if (inop)
417                 *inop = NULL;
418
419         /*
420          * Iterate path elements
421          */
422         while (*path) {
423                 while (*path == '/')
424                         ++path;
425                 if (*path == 0) /* terminal */
426                         break;
427
428                 /*
429                  * Calculate path element and look for it in the directory
430                  */
431                 for (len = 0; path[len]; ++len) {
432                         if (path[len] == '/')
433                                 break;
434                 }
435                 key = hammer2_dirhash(path, len);
436                 for (;;) {
437                         bytes = h2lookup(hfs, bref,
438                                          key, key | 0xFFFFU,
439                                          &bres, (void **)&ino);
440                         if (bytes == 0)
441                                 break;
442                         if (len == ino->name_len &&
443                             memcmp(path, ino->filename, len) == 0) {
444                                 if (inop)
445                                         *inop = ino;
446                                 break;
447                         }
448                         key = bres.key + 1;
449                 }
450
451                 /*
452                  * Lookup failure
453                  */
454                 if (bytes == 0) {
455                         bref->data_off = (hammer2_off_t)-1;
456                         break;
457                 }
458
459                 /*
460                  * Check path continuance, inode must be a directory or
461                  * we fail.
462                  */
463                 path += len;
464                 if (*path && ino->type != HAMMER2_OBJTYPE_DIRECTORY) {
465                         bref->data_off = (hammer2_off_t)-1;
466                         break;
467                 }
468                 *bref = bres;
469         }
470 }
471
472 static
473 ssize_t
474 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref,
475            off_t off, off_t filesize, void *buf, size_t len)
476 {
477         hammer2_blockref_t bres;
478         ssize_t total;
479         ssize_t bytes;
480         ssize_t zfill;
481         char *data;
482
483         /*
484          * EOF edge cases
485          */
486         if (off >= filesize)
487                 return (0);
488         if (off + len > filesize)
489                 len = filesize - off;
490
491         /*
492          * Loop until done 
493          */
494         total = 0;
495         while (len) {
496                 /*
497                  * Find closest bres >= requested offset.
498                  */
499                 bytes = h2lookup(hfs, bref, off, off + len - 1,
500                                  &bres, (void **)&data);
501
502                 if (bytes < 0) {
503                         if (total == 0)
504                                 total = -1;
505                         break;
506                 }
507
508                 /*
509                  * Load the data into the buffer.  First handle a degenerate
510                  * zero-fill case.
511                  */
512                 if (bytes == 0) {
513                         bzero(buf, len);
514                         total += len;
515                         break;
516                 }
517
518                 /*
519                  * Returned record overlaps to the left of the requested
520                  * position.  It must overlap in this case or h2lookup()
521                  * would have returned something else.
522                  */
523                 if (bres.key < off) {
524                         data += off - bres.key;
525                         bytes -= off - bres.key;
526                 }
527
528                 /*
529                  * Returned record overlaps to the right of the requested
530                  * position, handle zero-fill.  Again h2lookup() only returns
531                  * this case if there is an actual overlap.
532                  */
533                 if (bres.key > off) {
534                         zfill = (ssize_t)(bres.key - off);
535                         bzero(buf, zfill);
536                         len -= zfill;
537                         off += zfill;
538                         total += zfill;
539                         buf = (char *)buf + zfill;
540                 }
541
542                 /*
543                  * Trim returned request before copying.
544                  */
545                 if (bytes > len)
546                         bytes = len;
547                 bcopy(data, buf, bytes);
548                 len -= bytes;
549                 off += bytes;
550                 total += bytes;
551                 buf = (char *)buf + bytes;
552         }
553         return (total);
554 }
555
556 static
557 int
558 h2init(struct hammer2_fs *hfs)
559 {
560 #if 0
561         uint32_t crc0;
562 #endif
563         hammer2_tid_t best_tid = 0;
564         void *data;
565         off_t off;
566         int best;
567         int i;
568
569         /*
570          * Find the best volume header
571          */
572         best = -1;
573         for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
574                 off = i * HAMMER2_ZONE_BYTES64;
575                 if (i)
576                         no_io_error = 1;
577                 if (h2read(hfs, &hfs->vol, sizeof(hfs->vol), off))
578                         continue;
579                 if (hfs->vol.magic != HAMMER2_VOLUME_ID_HBO)
580                         continue;
581                 if (best < 0 || best_tid < hfs->vol.mirror_tid) {
582                         best = i;
583                         best_tid = hfs->vol.mirror_tid;
584                 }
585         }
586         no_io_error = 0;
587         if (best < 0)
588                 return(-1);
589
590         /*
591          * Reload the best volume header and set up the blockref.
592          */
593         off = best * HAMMER2_ZONE_BYTES64;
594         if (h2read(hfs, &hfs->vol, sizeof(hfs->vol), off))
595                 return(-1);
596         hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME;
597         hfs->sroot.data_off = off;
598         h2lookup(hfs, NULL, 0, 0, NULL, NULL);
599         h2lookup(hfs, &hfs->sroot, 0, 0, &hfs->sroot, &data);
600
601         /*
602          * Clear cache
603          */
604         h2lookup(hfs, NULL, 0, 0, NULL, NULL);
605
606         return (0);
607 }
608
609 /************************************************************************
610  *                              BOOT2 SUPPORT                           *
611  ************************************************************************
612  *
613  */
614 #ifdef BOOT2
615
616 static struct hammer2_fs hfs;
617
618 static int
619 boot2_hammer2_init(void)
620 {
621         if (h2init(&hfs))
622                 return(-1);
623         return(0);
624 }
625
626 static boot2_ino_t
627 boot2_hammer2_lookup(const char *path)
628 {
629         hammer2_blockref_t bref;
630
631         h2resolve(&hfs, path, &bref, NULL);
632         return ((boot2_ino_t)bref.data_off);
633 }
634
635 static ssize_t
636 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len)
637 {
638         hammer2_blockref_t bref;
639         ssize_t total;
640
641         bzero(&bref, sizeof(bref));
642         bref.type = HAMMER2_BREF_TYPE_INODE;
643         bref.data_off = ino;
644
645         total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len);
646         if (total > 0)
647                 fs_off += total;
648         return total;
649 }
650
651 const struct boot2_fsapi boot2_hammer2_api = {
652         .fsinit = boot2_hammer2_init,
653         .fslookup = boot2_hammer2_lookup,
654         .fsread = boot2_hammer2_read
655 };
656
657 #endif
658
659 /************************************************************************
660  *                              BOOT2 SUPPORT                           *
661  ************************************************************************
662  *
663  */
664 #ifdef LIBSTAND
665
666 struct hfile {
667         struct hammer2_fs hfs;
668         hammer2_blockref_t bref;
669         int64_t         fsize;
670         uint32_t        mode;
671         uint8_t         type;
672 };
673
674 static
675 int
676 hammer2_get_dtype(uint8_t type)
677 {
678         switch(type) {
679         case HAMMER2_OBJTYPE_DIRECTORY:
680                 return(DT_DIR);
681         case HAMMER2_OBJTYPE_REGFILE:
682                 return(DT_REG);
683         case HAMMER2_OBJTYPE_FIFO:
684                 return(DT_FIFO);
685         case HAMMER2_OBJTYPE_CDEV:
686                 return(DT_CHR);
687         case HAMMER2_OBJTYPE_BDEV:
688                 return(DT_BLK);
689         case HAMMER2_OBJTYPE_SOFTLINK:
690                 return(DT_LNK);
691         case HAMMER2_OBJTYPE_HARDLINK:
692                 return(DT_UNKNOWN);
693         case HAMMER2_OBJTYPE_SOCKET:
694                 return(DT_SOCK);
695         default:
696                 return(DT_UNKNOWN);
697         }
698 }
699
700 static
701 mode_t
702 hammer2_get_mode(uint8_t type)
703 {
704         switch(type) {
705         case HAMMER2_OBJTYPE_DIRECTORY:
706                 return(S_IFDIR);
707         case HAMMER2_OBJTYPE_REGFILE:
708                 return(S_IFREG);
709         case HAMMER2_OBJTYPE_FIFO:
710                 return(S_IFIFO);
711         case HAMMER2_OBJTYPE_CDEV:
712                 return(S_IFCHR);
713         case HAMMER2_OBJTYPE_BDEV:
714                 return(S_IFBLK);
715         case HAMMER2_OBJTYPE_SOFTLINK:
716                 return(S_IFLNK);
717         case HAMMER2_OBJTYPE_HARDLINK:
718                 return(0);
719         case HAMMER2_OBJTYPE_SOCKET:
720                 return(S_IFSOCK);
721         default:
722                 return(0);
723         }
724 }
725
726 static int
727 hammer2_open(const char *path, struct open_file *f)
728 {
729         struct hfile *hf = malloc(sizeof(*hf));
730         hammer2_inode_data_t *ipdata;
731
732         bzero(hf, sizeof(*hf));
733         f->f_offset = 0;
734         f->f_fsdata = hf;
735         hf->hfs.f = f;
736
737         if (h2init(&hf->hfs)) {
738                 f->f_fsdata = NULL;
739                 free(hf);
740                 errno = ENOENT;
741                 return(-1);
742         }
743         h2resolve(&hf->hfs, path, &hf->bref, &ipdata);
744         if (hf->bref.data_off == (hammer2_off_t)-1 ||
745             (hf->bref.type != HAMMER2_BREF_TYPE_INODE &&
746             hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) {
747                 f->f_fsdata = NULL;
748                 free(hf);
749                 errno = ENOENT;
750                 return(-1);
751         }
752         if (ipdata) {
753                 hf->fsize = ipdata->size;
754                 hf->type = ipdata->type;
755                 hf->mode = ipdata->mode | hammer2_get_mode(ipdata->type);
756         } else {
757                 hf->fsize = 0;
758                 hf->type = HAMMER2_OBJTYPE_DIRECTORY;
759                 hf->mode = 0755 | S_IFDIR;
760         }
761         return(0);
762 }
763
764 static int
765 hammer2_close(struct open_file *f)
766 {
767         struct hfile *hf = f->f_fsdata;
768
769         f->f_fsdata = NULL;
770         if (hf)
771                 free(hf);
772         return (0);
773 }
774
775 static int
776 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid)
777 {
778         struct hfile *hf = f->f_fsdata;
779         ssize_t total;
780         int rc = 0;
781
782         total = h2readfile(&hf->hfs, &hf->bref,
783                            f->f_offset, hf->fsize, buf, len);
784         if (total < 0) {
785                 rc = EIO;
786                 total = 0;
787         } else {
788                 f->f_offset += total;
789                 rc = 0;
790         }
791         *resid = len - total;
792         return rc;
793 }
794
795 static off_t
796 hammer2_seek(struct open_file *f, off_t offset, int whence)
797 {
798         struct hfile *hf = f->f_fsdata;
799
800         switch (whence) {
801         case SEEK_SET:
802                 f->f_offset = offset;
803                 break;
804         case SEEK_CUR:
805                 f->f_offset += offset;
806                 break;
807         case SEEK_END:
808                 f->f_offset = hf->fsize - offset;
809                 break;
810         default:
811                 return (-1);
812         }
813         return (f->f_offset);
814 }
815
816 static int
817 hammer2_stat(struct open_file *f, struct stat *st)
818 {
819         struct hfile *hf = f->f_fsdata;
820
821         st->st_mode = hf->mode;
822         st->st_uid = 0;
823         st->st_gid = 0;
824         st->st_size = hf->fsize;
825
826         return (0);
827 }
828
829 static int
830 hammer2_readdir(struct open_file *f, struct dirent *den)
831 {
832         struct hfile *hf = f->f_fsdata;
833         hammer2_blockref_t bres;
834         hammer2_inode_data_t *ipdata;
835         int bytes;
836
837         for (;;) {
838                 bytes = h2lookup(&hf->hfs, &hf->bref,
839                                  f->f_offset | HAMMER2_DIRHASH_VISIBLE, 
840                                  HAMMER2_KEY_MAX,
841                                  &bres, (void **)&ipdata);
842                 if (bytes <= 0)
843                         break;
844                 den->d_namlen = ipdata->name_len;
845                 den->d_type = hammer2_get_dtype(ipdata->type);
846                 den->d_ino = ipdata->inum;
847                 bcopy(ipdata->filename, den->d_name, den->d_namlen);
848                 den->d_name[den->d_namlen] = 0;
849
850                 f->f_offset = bres.key + 1;
851
852                 return(0);
853         }
854         return ENOENT;
855 }
856
857 struct fs_ops hammer_fsops = {
858         "hammer2",
859         hammer2_open,
860         hammer2_close,
861         hammer2_read,
862         null_write,
863         hammer2_seek,
864         hammer2_stat,
865         hammer2_readdir
866 };
867
868 #endif