xref: /dragonfly/stand/lib/hammer2.c (revision 32efd857)
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
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,
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
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
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
719 boot2_hammer2_init(void)
720 {
721 	if (h2init(&hfs))
722 		return(-1);
723 	return(0);
724 }
725 
726 static boot2_ino_t
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
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
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
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
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
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
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
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
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
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