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