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 LIBSTAND
45 #include "stand.h"
46 #endif
47
48 #ifdef TESTING
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/uuid.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <stddef.h>
55 #include <stdint.h>
56 #include <unistd.h>
57 #include <fcntl.h>
58 #include <string.h>
59 #include <strings.h>
60 #include <errno.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
bzero(void * buf,size_t size)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
bcopy(void * src,void * dst,size_t size)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
memcmp(const void * a,const void * b,size_t len)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
blockoff(hammer2_blockref_t * bref)135 blockoff(hammer2_blockref_t *bref)
136 {
137 return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX);
138 }
139
140 static
141 size_t
blocksize(hammer2_blockref_t * bref)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
hammer2_dirhash(const unsigned char * name,size_t len)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
h2read(struct hammer2_fs * hfs,void * buf,size_t nbytes,off_t off)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
h2lookup(struct hammer2_fs * hfs,hammer2_blockref_t * base,hammer2_key_t key_beg,hammer2_key_t key_end,hammer2_blockref_t * bref_ret,void ** pptr)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
h2resolve(struct hammer2_fs * hfs,const char * path,hammer2_blockref_t * bref,hammer2_inode_data_t ** inop)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,
472 key | HAMMER2_DIRHASH_LOMASK,
473 &bres, (void **)&data);
474 if (bytes < 0)
475 break;
476 if (bres.type == 0)
477 break;
478 switch (bres.type) {
479 case HAMMER2_BREF_TYPE_DIRENT:
480 if (bres.embed.dirent.namlen != len)
481 break;
482 if (bres.embed.dirent.namlen <=
483 sizeof(bres.check.buf)) {
484 if (memcmp(path, bres.check.buf, len))
485 break;
486 } else {
487 if (memcmp(path, data, len))
488 break;
489 }
490
491 /*
492 * Found, resolve inode. This will set
493 * ino similarly to HAMMER2_BREF_TYPE_INODE
494 * and adjust bres, which path continuation
495 * needs.
496 */
497 *bref = hfs->sroot;
498 bytes = h2lookup(hfs, bref,
499 bres.embed.dirent.inum,
500 bres.embed.dirent.inum,
501 &bres, (void **)&ino);
502 if (inop)
503 *inop = ino;
504 goto found;
505 break; /* NOT REACHED */
506 case HAMMER2_BREF_TYPE_INODE:
507 ino = data;
508 if (ino->meta.name_len != len)
509 break;
510 if (memcmp(path, ino->filename, len) == 0) {
511 if (inop)
512 *inop = ino;
513 goto found;
514 }
515 break;
516 }
517 if ((bres.key & 0xFFFF) == 0xFFFF) {
518 bres.type = 0;
519 break;
520 }
521 key = bres.key + 1;
522 }
523 found:
524
525 /*
526 * Lookup failure
527 */
528 if (bytes < 0 || bres.type == 0) {
529 bref->data_off = (hammer2_off_t)-1;
530 ino = NULL;
531 break;
532 }
533
534 /*
535 * Check path continuance, inode must be a directory or
536 * we fail.
537 */
538 path += len;
539 if (*path && ino->meta.type != HAMMER2_OBJTYPE_DIRECTORY) {
540 bref->data_off = (hammer2_off_t)-1;
541 break;
542 }
543 *bref = bres;
544 }
545 }
546
547 static
548 ssize_t
h2readfile(struct hammer2_fs * hfs,hammer2_blockref_t * bref,off_t off,off_t filesize,void * buf,size_t len)549 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref,
550 off_t off, off_t filesize, void *buf, size_t len)
551 {
552 hammer2_blockref_t bres;
553 ssize_t total;
554 ssize_t bytes;
555 ssize_t zfill;
556 char *data;
557
558 /*
559 * EOF edge cases
560 */
561 if (off >= filesize)
562 return (0);
563 if (off + len > filesize)
564 len = filesize - off;
565
566 /*
567 * Loop until done
568 */
569 total = 0;
570 while (len) {
571 /*
572 * Find closest bres >= requested offset.
573 */
574 bytes = h2lookup(hfs, bref, off, off + len - 1,
575 &bres, (void **)&data);
576
577 if (bytes < 0) {
578 if (total == 0)
579 total = -1;
580 break;
581 }
582
583 /*
584 * Load the data into the buffer. First handle a degenerate
585 * zero-fill case.
586 */
587 if (bytes == 0) {
588 bzero(buf, len);
589 total += len;
590 break;
591 }
592
593 /*
594 * Returned record overlaps to the left of the requested
595 * position. It must overlap in this case or h2lookup()
596 * would have returned something else.
597 */
598 if (bres.key < off) {
599 data += off - bres.key;
600 bytes -= off - bres.key;
601 }
602
603 /*
604 * Returned record overlaps to the right of the requested
605 * position, handle zero-fill. Again h2lookup() only returns
606 * this case if there is an actual overlap.
607 */
608 if (bres.key > off) {
609 zfill = (ssize_t)(bres.key - off);
610 bzero(buf, zfill);
611 len -= zfill;
612 off += zfill;
613 total += zfill;
614 buf = (char *)buf + zfill;
615 }
616
617 /*
618 * Trim returned request before copying.
619 */
620 if (bytes > len)
621 bytes = len;
622 bcopy(data, buf, bytes);
623 len -= bytes;
624 off += bytes;
625 total += bytes;
626 buf = (char *)buf + bytes;
627 }
628 return (total);
629 }
630
631 static
632 int
h2init(struct hammer2_fs * hfs)633 h2init(struct hammer2_fs *hfs)
634 {
635 #if 0
636 uint32_t crc0;
637 #endif
638 hammer2_tid_t best_tid = 0;
639 void *data;
640 off_t off;
641 int best;
642 int i;
643 int r;
644
645 /*
646 * Find the best volume header.
647 *
648 * WARNING BIOS BUGS: It looks like some BIOSes will implode when
649 * given a disk offset beyond the EOM. XXX We need to probe the
650 * size of the media and limit our accesses, until then we have
651 * to give up if the first volume header does not have a hammer2
652 * signature.
653 *
654 * XXX Probably still going to be problems w/ HAMMER2 volumes on
655 * media which is too small w/certain BIOSes.
656 */
657 best = -1;
658 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
659 off = i * HAMMER2_ZONE_BYTES64;
660 if (i)
661 no_io_error = 1;
662 if (h2read(hfs, &media, sizeof(media.voldata), off))
663 break;
664 if (media.voldata.magic != HAMMER2_VOLUME_ID_HBO)
665 break;
666 if (best < 0 || best_tid < media.voldata.mirror_tid) {
667 best = i;
668 best_tid = media.voldata.mirror_tid;
669 }
670 }
671 no_io_error = 0;
672 if (best < 0)
673 return(-1);
674
675 /*
676 * Reload the best volume header and set up the blockref.
677 * We messed with media, clear the cache before continuing.
678 */
679 off = best * HAMMER2_ZONE_BYTES64;
680 if (h2read(hfs, &media, sizeof(media.voldata), off))
681 return(-1);
682 hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME;
683 hfs->sroot.data_off = off;
684 hfs->sroot_blockset = media.voldata.sroot_blockset;
685 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
686
687 /*
688 * Lookup sroot/BOOT and clear the cache again.
689 */
690 r = h2lookup(hfs, &hfs->sroot,
691 HAMMER2_SROOT_KEY, HAMMER2_SROOT_KEY,
692 &hfs->sroot, &data);
693 if (r <= 0)
694 return(-1);
695 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
696 r = h2lookup(hfs, &hfs->sroot,
697 HAMMER2_BOOT_KEY,
698 HAMMER2_BOOT_KEY | HAMMER2_DIRHASH_LOMASK,
699 &hfs->sroot, &data);
700 if (r <= 0) {
701 printf("hammer2: 'BOOT' PFS not found\n");
702 return(-1);
703 }
704 h2lookup(hfs, NULL, 0, 0, NULL, NULL);
705
706 return (0);
707 }
708
709 /************************************************************************
710 * BOOT2 SUPPORT *
711 ************************************************************************
712 *
713 */
714 #ifdef BOOT2
715
716 static struct hammer2_fs hfs;
717
718 static int
boot2_hammer2_init(void)719 boot2_hammer2_init(void)
720 {
721 if (h2init(&hfs))
722 return(-1);
723 return(0);
724 }
725
726 static boot2_ino_t
boot2_hammer2_lookup(const char * path)727 boot2_hammer2_lookup(const char *path)
728 {
729 hammer2_blockref_t bref;
730
731 h2resolve(&hfs, path, &bref, NULL);
732 return ((boot2_ino_t)bref.data_off);
733 }
734
735 static ssize_t
boot2_hammer2_read(boot2_ino_t ino,void * buf,size_t len)736 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len)
737 {
738 hammer2_blockref_t bref;
739 ssize_t total;
740
741 bzero(&bref, sizeof(bref));
742 bref.type = HAMMER2_BREF_TYPE_INODE;
743 bref.data_off = ino;
744
745 total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len);
746 if (total > 0)
747 fs_off += total;
748 return total;
749 }
750
751 const struct boot2_fsapi boot2_hammer2_api = {
752 .fsinit = boot2_hammer2_init,
753 .fslookup = boot2_hammer2_lookup,
754 .fsread = boot2_hammer2_read
755 };
756
757 #endif
758
759 /************************************************************************
760 * LIBSTAND SUPPORT *
761 ************************************************************************
762 *
763 */
764 #ifdef LIBSTAND
765
766 struct hfile {
767 struct hammer2_fs hfs;
768 hammer2_blockref_t bref;
769 int64_t fsize;
770 uint32_t mode;
771 uint8_t type;
772 };
773
774 static
775 int
hammer2_get_dtype(uint8_t type)776 hammer2_get_dtype(uint8_t type)
777 {
778 switch(type) {
779 case HAMMER2_OBJTYPE_DIRECTORY:
780 return(DT_DIR);
781 case HAMMER2_OBJTYPE_REGFILE:
782 return(DT_REG);
783 case HAMMER2_OBJTYPE_FIFO:
784 return(DT_FIFO);
785 case HAMMER2_OBJTYPE_CDEV:
786 return(DT_CHR);
787 case HAMMER2_OBJTYPE_BDEV:
788 return(DT_BLK);
789 case HAMMER2_OBJTYPE_SOFTLINK:
790 return(DT_LNK);
791 case HAMMER2_OBJTYPE_SOCKET:
792 return(DT_SOCK);
793 default:
794 return(DT_UNKNOWN);
795 }
796 }
797
798 static
799 mode_t
hammer2_get_mode(uint8_t type)800 hammer2_get_mode(uint8_t type)
801 {
802 switch(type) {
803 case HAMMER2_OBJTYPE_DIRECTORY:
804 return(S_IFDIR);
805 case HAMMER2_OBJTYPE_REGFILE:
806 return(S_IFREG);
807 case HAMMER2_OBJTYPE_FIFO:
808 return(S_IFIFO);
809 case HAMMER2_OBJTYPE_CDEV:
810 return(S_IFCHR);
811 case HAMMER2_OBJTYPE_BDEV:
812 return(S_IFBLK);
813 case HAMMER2_OBJTYPE_SOFTLINK:
814 return(S_IFLNK);
815 case HAMMER2_OBJTYPE_SOCKET:
816 return(S_IFSOCK);
817 default:
818 return(0);
819 }
820 }
821
822 static int
hammer2_open(const char * path,struct open_file * f)823 hammer2_open(const char *path, struct open_file *f)
824 {
825 struct hfile *hf = malloc(sizeof(*hf));
826 hammer2_inode_data_t *ipdata;
827
828 bzero(hf, sizeof(*hf));
829 f->f_offset = 0;
830 f->f_fsdata = hf;
831 hf->hfs.f = f;
832
833 if (h2init(&hf->hfs)) {
834 f->f_fsdata = NULL;
835 free(hf);
836 errno = ENOENT;
837 return(-1);
838 }
839 h2resolve(&hf->hfs, path, &hf->bref, &ipdata);
840 if (hf->bref.data_off == (hammer2_off_t)-1 ||
841 (hf->bref.type != HAMMER2_BREF_TYPE_INODE &&
842 hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) {
843 f->f_fsdata = NULL;
844 free(hf);
845 errno = ENOENT;
846 return(-1);
847 }
848 if (ipdata) {
849 hf->fsize = ipdata->meta.size;
850 hf->type = ipdata->meta.type;
851 hf->mode = ipdata->meta.mode |
852 hammer2_get_mode(ipdata->meta.type);
853 } else {
854 hf->fsize = 0;
855 hf->type = HAMMER2_OBJTYPE_DIRECTORY;
856 hf->mode = 0755 | S_IFDIR;
857 }
858 return(0);
859 }
860
861 static int
hammer2_close(struct open_file * f)862 hammer2_close(struct open_file *f)
863 {
864 struct hfile *hf = f->f_fsdata;
865
866 f->f_fsdata = NULL;
867 if (hf)
868 free(hf);
869 return (0);
870 }
871
872 static int
hammer2_read(struct open_file * f,void * buf,size_t len,size_t * resid)873 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid)
874 {
875 struct hfile *hf = f->f_fsdata;
876 ssize_t total;
877 int rc = 0;
878
879 total = h2readfile(&hf->hfs, &hf->bref,
880 f->f_offset, hf->fsize, buf, len);
881 if (total < 0) {
882 rc = EIO;
883 total = 0;
884 } else {
885 f->f_offset += total;
886 rc = 0;
887 }
888 *resid = len - total;
889 return rc;
890 }
891
892 static off_t
hammer2_seek(struct open_file * f,off_t offset,int whence)893 hammer2_seek(struct open_file *f, off_t offset, int whence)
894 {
895 struct hfile *hf = f->f_fsdata;
896
897 switch (whence) {
898 case SEEK_SET:
899 f->f_offset = offset;
900 break;
901 case SEEK_CUR:
902 f->f_offset += offset;
903 break;
904 case SEEK_END:
905 f->f_offset = hf->fsize - offset;
906 break;
907 default:
908 return (-1);
909 }
910 return (f->f_offset);
911 }
912
913 static int
hammer2_stat(struct open_file * f,struct stat * st)914 hammer2_stat(struct open_file *f, struct stat *st)
915 {
916 struct hfile *hf = f->f_fsdata;
917
918 st->st_mode = hf->mode;
919 st->st_uid = 0;
920 st->st_gid = 0;
921 st->st_size = hf->fsize;
922
923 return (0);
924 }
925
926 static int
hammer2_readdir(struct open_file * f,struct dirent * den)927 hammer2_readdir(struct open_file *f, struct dirent *den)
928 {
929 struct hfile *hf = f->f_fsdata;
930 hammer2_blockref_t bres;
931 hammer2_inode_data_t *ipdata;
932 void *data;
933 int bytes;
934
935 for (;;) {
936 bytes = h2lookup(&hf->hfs, &hf->bref,
937 f->f_offset | HAMMER2_DIRHASH_VISIBLE,
938 HAMMER2_KEY_MAX,
939 &bres, (void **)&data);
940 if (bytes < 0)
941 break;
942 switch (bres.type) {
943 case HAMMER2_BREF_TYPE_INODE:
944 ipdata = data;
945 den->d_namlen = ipdata->meta.name_len;
946 den->d_type = hammer2_get_dtype(ipdata->meta.type);
947 den->d_ino = ipdata->meta.inum;
948 bcopy(ipdata->filename, den->d_name, den->d_namlen);
949 den->d_name[den->d_namlen] = 0;
950 break;
951 case HAMMER2_BREF_TYPE_DIRENT:
952 den->d_namlen = bres.embed.dirent.namlen;
953 den->d_type = hammer2_get_dtype(bres.embed.dirent.type);
954 den->d_ino = bres.embed.dirent.inum;
955 if (den->d_namlen <= sizeof(bres.check.buf)) {
956 bcopy(bres.check.buf,
957 den->d_name,
958 den->d_namlen);
959 } else {
960 bcopy(data, den->d_name, den->d_namlen);
961 }
962 den->d_name[den->d_namlen] = 0;
963 break;
964 default:
965 den->d_namlen = 1;
966 den->d_type =
967 hammer2_get_dtype(HAMMER2_OBJTYPE_REGFILE);
968 den->d_name[0] = '?';
969 den->d_name[1] = 0;
970 break;
971 }
972
973 f->f_offset = bres.key + 1;
974
975 return(0);
976 }
977 return ENOENT;
978 }
979
980 struct fs_ops hammer2_fsops = {
981 "hammer2",
982 hammer2_open,
983 hammer2_close,
984 hammer2_read,
985 null_write,
986 hammer2_seek,
987 hammer2_stat,
988 hammer2_readdir
989 };
990
991 #endif
992