Merge branch 'vendor/OPENSSL'
[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 static hammer2_media_data_t media;
69 static hammer2_blockref_t saved_base;
70
71 #define hammer2_icrc32(buf, size)       iscsi_crc32(buf, size)
72
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
85 };
86
87 struct hammer2_inode {
88         struct hammer2_inode_data       ino;    /* raw inode data */
89         off_t                           doff;   /* disk inode offset */
90 };
91
92 #ifdef BOOT2
93
94 static void
95 bzero(void *buf, size_t size)
96 {
97         for (size_t i = 0; i < size; i++)
98                 ((char *)buf)[i] = 0;
99 }
100
101 static void
102 bcopy(void *src, void *dst, size_t size)
103 {
104         memcpy(dst, src, size);
105 }
106
107 #if 0
108 static size_t
109 strlen(const char *s)
110 {
111         size_t l = 0;
112         for (; *s != 0; s++)
113                 l++;
114         return (l);
115 }
116 #endif
117
118 static int
119 memcmp(const void *a, const void *b, size_t len)
120 {
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);
125         }
126
127         return (0);
128 }
129
130 #endif
131
132 static
133 off_t
134 blockoff(hammer2_blockref_t *bref)
135 {
136         return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX);
137 }
138
139 static
140 size_t
141 blocksize(hammer2_blockref_t *bref)
142 {
143         return(1 << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX));
144 }
145
146 static
147 hammer2_key_t
148 hammer2_dirhash(const unsigned char *name, size_t len)
149 {
150         const unsigned char *aname = name;
151         uint32_t crcx;
152         uint64_t key;
153         size_t i;
154         size_t j;
155
156         key = 0;
157
158         /*
159          * m32
160          */
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;
170                 }
171         }
172         if (i != j)
173                 crcx += hammer2_icrc32(aname + j, i - j);
174
175         /*
176          * The directory hash utilizes the top 32 bits of the 64-bit key.
177          * Bit 63 must be set to 1.
178          */
179         crcx |= 0x80000000U;
180         key |= (uint64_t)crcx << 32;
181
182         /*
183          * l16 - crc of entire filename
184          *
185          * This crc reduces degenerate hash collision conditions
186          */
187         crcx = hammer2_icrc32(aname, len);
188         crcx = crcx ^ (crcx << 16);
189         key |= crcx & 0xFFFF0000U;
190
191         /*
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 '..').
196          */
197         key |= 0x8000U;
198
199         return (key);
200 }
201
202 /*
203  * Low level read
204  */
205 static
206 int
207 h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off)
208 {
209 #if defined(LIBSTAND)
210         size_t rlen;
211 #endif
212         int rc;
213
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;
234         }
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;
243 }
244
245 /*
246  * Common code
247  *
248  * Initialize for HAMMER2 filesystem access given a hammer2_fs with
249  * its device file descriptor initialized.
250  */
251
252 /*
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.
257  *
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.
261  *
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.
266  */
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)
271 {
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;
281
282         if (base == NULL) {
283                 saved_base.data_off = (hammer2_off_t)-1;
284                 return(0);
285         }
286         if (base->data_off == (hammer2_off_t)-1)
287                 return(-1);
288
289         /*
290          * Calculate the number of blockrefs to scan
291          */
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;
302         }
303
304         /*
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.
308          */
309 again:
310         best.key = HAMMER2_KEY_MAX;
311         best.type = 0;
312
313         for (i = 0; i < count; ++i) {
314                 /*
315                  * [re]load when returning from our recursion
316                  */
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);
322                         }
323                         saved_base = *base;
324                 }
325
326                 /*
327                  * Special case embedded file data
328                  */
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;
336                         }
337                 }
338
339                 /*
340                  * Calculate the bref in our scan.
341                  */
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;
352                 }
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;
361                 }
362         }
363
364         /*
365          * Figure out what to do with the results.
366          */
367         switch(best.type) {
368         case 0:
369                 /*
370                  * Return 0
371                  */
372                 rc = 0;
373                 break;
374         case HAMMER2_BREF_TYPE_INDIRECT:
375                 /*
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.
379                  *
380                  * Be sure to handle the overflow case when recalculating
381                  * key_beg.
382                  */
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;
389                 }
390                 break;
391         case HAMMER2_BREF_TYPE_INODE:
392         case HAMMER2_BREF_TYPE_DATA:
393                 /*
394                  * Terminal match.  Leaf elements might not be data-aligned.
395                  */
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;
408         }
409         return(rc);
410 }
411
412 static
413 void
414 h2resolve(struct hammer2_fs *hfs, const char *path,
415           hammer2_blockref_t *bref, hammer2_inode_data_t **inop)
416 {
417         hammer2_blockref_t bres;
418         hammer2_inode_data_t *ino;
419         hammer2_key_t key;
420         ssize_t bytes;
421         size_t len;
422
423         /*
424          * Start point (superroot)
425          */
426         *bref = hfs->sroot;
427         if (inop)
428                 *inop = NULL;
429
430         /*
431          * Iterate path elements
432          */
433         while (*path) {
434                 while (*path == '/')
435                         ++path;
436                 if (*path == 0) /* terminal */
437                         break;
438
439                 /*
440                  * Calculate path element and look for it in the directory
441                  */
442                 for (len = 0; path[len]; ++len) {
443                         if (path[len] == '/')
444                                 break;
445                 }
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;
458                         }
459                         key = bres.key + 1;
460                 }
461
462                 /*
463                  * Lookup failure
464                  */
465                 if (bytes == 0) {
466                         bref->data_off = (hammer2_off_t)-1;
467                         break;
468                 }
469
470                 /*
471                  * Check path continuance, inode must be a directory or
472                  * we fail.
473                  */
474                 path += len;
475                 if (*path && ino->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
476                         bref->data_off = (hammer2_off_t)-1;
477                         break;
478                 }
479                 *bref = bres;
480         }
481 }
482
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)
487 {
488         hammer2_blockref_t bres;
489         ssize_t total;
490         ssize_t bytes;
491         ssize_t zfill;
492         char *data;
493
494         /*
495          * EOF edge cases
496          */
497         if (off >= filesize)
498                 return (0);
499         if (off + len > filesize)
500                 len = filesize - off;
501
502         /*
503          * Loop until done 
504          */
505         total = 0;
506         while (len) {
507                 /*
508                  * Find closest bres >= requested offset.
509                  */
510                 bytes = h2lookup(hfs, bref, off, off + len - 1,
511                                  &bres, (void **)&data);
512
513                 if (bytes < 0) {
514                         if (total == 0)
515                                 total = -1;
516                         break;
517                 }
518
519                 /*
520                  * Load the data into the buffer.  First handle a degenerate
521                  * zero-fill case.
522                  */
523                 if (bytes == 0) {
524                         bzero(buf, len);
525                         total += len;
526                         break;
527                 }
528
529                 /*
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.
533                  */
534                 if (bres.key < off) {
535                         data += off - bres.key;
536                         bytes -= off - bres.key;
537                 }
538
539                 /*
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.
543                  */
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;
551                 }
552
553                 /*
554                  * Trim returned request before copying.
555                  */
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;
563         }
564         return (total);
565 }
566
567 static
568 int
569 h2init(struct hammer2_fs *hfs)
570 {
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;
580
581         /*
582          * Find the best volume header.
583          *
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.
589          *
590          * XXX Probably still going to be problems w/ HAMMER2 volumes on
591          *     media which is too small w/certain BIOSes.
592          */
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;
605                 }
606         }
607         no_io_error = 0;
608         if (best < 0)
609                 return(-1);
610
611         /*
612          * Reload the best volume header and set up the blockref.
613          * We messed with media, clear the cache before continuing.
614          */
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);
622
623         /*
624          * Lookup sroot/BOOT and clear the cache again.
625          */
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);
638         }
639         h2lookup(hfs, NULL, 0, 0, NULL, NULL);
640
641         return (0);
642 }
643
644 /************************************************************************
645  *                              BOOT2 SUPPORT                           *
646  ************************************************************************
647  *
648  */
649 #ifdef BOOT2
650
651 static struct hammer2_fs hfs;
652
653 static int
654 boot2_hammer2_init(void)
655 {
656         if (h2init(&hfs))
657                 return(-1);
658         return(0);
659 }
660
661 static boot2_ino_t
662 boot2_hammer2_lookup(const char *path)
663 {
664         hammer2_blockref_t bref;
665
666         h2resolve(&hfs, path, &bref, NULL);
667         return ((boot2_ino_t)bref.data_off);
668 }
669
670 static ssize_t
671 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len)
672 {
673         hammer2_blockref_t bref;
674         ssize_t total;
675
676         bzero(&bref, sizeof(bref));
677         bref.type = HAMMER2_BREF_TYPE_INODE;
678         bref.data_off = ino;
679
680         total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len);
681         if (total > 0)
682                 fs_off += total;
683         return total;
684 }
685
686 const struct boot2_fsapi boot2_hammer2_api = {
687         .fsinit = boot2_hammer2_init,
688         .fslookup = boot2_hammer2_lookup,
689         .fsread = boot2_hammer2_read
690 };
691
692 #endif
693
694 /************************************************************************
695  *                              BOOT2 SUPPORT                           *
696  ************************************************************************
697  *
698  */
699 #ifdef LIBSTAND
700
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;
707 };
708
709 static
710 int
711 hammer2_get_dtype(uint8_t type)
712 {
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);
732         }
733 }
734
735 static
736 mode_t
737 hammer2_get_mode(uint8_t type)
738 {
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);
758         }
759 }
760
761 static int
762 hammer2_open(const char *path, struct open_file *f)
763 {
764         struct hfile *hf = malloc(sizeof(*hf));
765         hammer2_inode_data_t *ipdata;
766
767         bzero(hf, sizeof(*hf));
768         f->f_offset = 0;
769         f->f_fsdata = hf;
770         hf->hfs.f = f;
771
772         if (h2init(&hf->hfs)) {
773                 f->f_fsdata = NULL;
774                 free(hf);
775                 errno = ENOENT;
776                 return(-1);
777         }
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);
786         }
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;
796         }
797         return(0);
798 }
799
800 static int
801 hammer2_close(struct open_file *f)
802 {
803         struct hfile *hf = f->f_fsdata;
804
805         f->f_fsdata = NULL;
806         if (hf)
807                 free(hf);
808         return (0);
809 }
810
811 static int
812 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid)
813 {
814         struct hfile *hf = f->f_fsdata;
815         ssize_t total;
816         int rc = 0;
817
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;
826         }
827         *resid = len - total;
828         return rc;
829 }
830
831 static off_t
832 hammer2_seek(struct open_file *f, off_t offset, int whence)
833 {
834         struct hfile *hf = f->f_fsdata;
835
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);
848         }
849         return (f->f_offset);
850 }
851
852 static int
853 hammer2_stat(struct open_file *f, struct stat *st)
854 {
855         struct hfile *hf = f->f_fsdata;
856
857         st->st_mode = hf->mode;
858         st->st_uid = 0;
859         st->st_gid = 0;
860         st->st_size = hf->fsize;
861
862         return (0);
863 }
864
865 static int
866 hammer2_readdir(struct open_file *f, struct dirent *den)
867 {
868         struct hfile *hf = f->f_fsdata;
869         hammer2_blockref_t bres;
870         hammer2_inode_data_t *ipdata;
871         int bytes;
872
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;
885
886                 f->f_offset = bres.key + 1;
887
888                 return(0);
889         }
890         return ENOENT;
891 }
892
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
902 };
903
904 #endif