1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright (c) 2022 Tomohiro Kusumi <tkusumi@netbsd.org>
5  * Copyright (c) 2011-2022 The DragonFly Project.  All rights reserved.
6  *
7  * This code is derived from software contributed to The DragonFly Project
8  * by Matthew Dillon <dillon@dragonflybsd.org>
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  *
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  * 3. Neither the name of The DragonFly Project nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific, prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
28  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
32  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
34  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  */
37 /*
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/queue.h>
42 #include <sys/nlookup.h>
43 #include <sys/vnode.h>
44 #include <sys/mount.h>
45 #include <sys/buf.h>
46 #include <sys/uuid.h>
47 #include <sys/objcache.h>
48 #include <sys/lock.h>
49 */
50 
51 #include "hammer2.h"
52 #include "makefs.h"
53 
54 #define hprintf(X, ...)	kprintf("hammer2_ondisk: " X, ## __VA_ARGS__)
55 
56 #if 0
57 static int
58 hammer2_lookup_device(const char *path, int rootmount, struct m_vnode **devvp)
59 {
60 	struct m_vnode *vp = NULL;
61 	struct nlookupdata nd;
62 	int error = 0;
63 
64 	KKASSERT(path);
65 	KKASSERT(*path != '\0');
66 
67 	if (rootmount) {
68 		error = bdevvp(kgetdiskbyname(path), &vp);
69 		if (error)
70 			hprintf("cannot find %s %d\n", path, error);
71 	} else {
72 		error = nlookup_init(&nd, path, UIO_SYSSPACE, NLC_FOLLOW);
73 		if (error == 0)
74 			error = nlookup(&nd);
75 		if (error == 0)
76 			error = cache_vref(&nd.nl_nch, nd.nl_cred, &vp);
77 		if (error)
78 			hprintf("failed to nlookup %s %d\n", path, error);
79 		nlookup_done(&nd);
80 	}
81 
82 	if (error == 0) {
83 		KKASSERT(vp);
84 		if (!vn_isdisk(vp, &error)) {
85 			KKASSERT(error);
86 			hprintf("%s not a block device %d\n", path, error);
87 		}
88 	}
89 
90 	if (error && vp) {
91 		vrele(vp);
92 		vp = NULL;
93 	}
94 
95 	*devvp = vp;
96 	return error;
97 }
98 #endif
99 
100 int
101 hammer2_open_devvp(const hammer2_devvp_list_t *devvpl, int ronly)
102 {
103 #if 0
104 	hammer2_devvp_t *e;
105 	struct m_vnode *devvp;
106 	const char *path;
107 	int count, error;
108 
109 	TAILQ_FOREACH(e, devvpl, entry) {
110 		devvp = e->devvp;
111 		path = e->path;
112 		KKASSERT(devvp);
113 		count = vcount(devvp);
114 		if (count > 0) {
115 			hprintf("%s already has %d references\n", path, count);
116 			return EBUSY;
117 		}
118 		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
119 		error = vinvalbuf(devvp, V_SAVE, 0, 0);
120 		if (error == 0) {
121 			KKASSERT(!e->open);
122 			error = VOP_OPEN(devvp, (ronly ? FREAD : FREAD|FWRITE),
123 					 FSCRED, NULL);
124 			if (error == 0)
125 				e->open = 1;
126 			else
127 				hprintf("failed to open %s %d\n", path, error);
128 		}
129 		vn_unlock(devvp);
130 		if (error)
131 			return error;
132 		KKASSERT(e->open);
133 	}
134 #endif
135 
136 	return 0;
137 }
138 
139 int
140 hammer2_close_devvp(const hammer2_devvp_list_t *devvpl, int ronly)
141 {
142 #if 0
143 	hammer2_devvp_t *e;
144 	struct m_vnode *devvp;
145 
146 	TAILQ_FOREACH(e, devvpl, entry) {
147 		devvp = e->devvp;
148 		KKASSERT(devvp);
149 		if (e->open) {
150 			vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
151 			vinvalbuf(devvp, (ronly ? 0 : V_SAVE), 0, 0);
152 			VOP_CLOSE(devvp, (ronly ? FREAD : FREAD|FWRITE), NULL);
153 			vn_unlock(devvp);
154 			e->open = 0;
155 		}
156 	}
157 #endif
158 
159 	return 0;
160 }
161 
162 int
163 hammer2_init_devvp(struct m_vnode *devvp, hammer2_devvp_list_t *devvpl)
164 {
165 	hammer2_devvp_t *e;
166 	int error = 0;
167 
168 	while (1) {
169 		KKASSERT(devvp);
170 		e = kmalloc(sizeof(*e), M_HAMMER2, M_WAITOK | M_ZERO);
171 		e->devvp = devvp;
172 		TAILQ_INSERT_TAIL(devvpl, e, entry);
173 		break;
174 	}
175 
176 	return error;
177 }
178 
179 void
180 hammer2_cleanup_devvp(hammer2_devvp_list_t *devvpl)
181 {
182 	hammer2_devvp_t *e;
183 
184 	while (!TAILQ_EMPTY(devvpl)) {
185 		e = TAILQ_FIRST(devvpl);
186 		TAILQ_REMOVE(devvpl, e, entry);
187 		/* devvp */
188 		KKASSERT(e->devvp);
189 		/*
190 		if (e->devvp->v_rdev)
191 			e->devvp->v_rdev->si_mountpoint = NULL;
192 		*/
193 		vrele(e->devvp);
194 		e->devvp = NULL;
195 		/* path */
196 		/*
197 		KKASSERT(e->path);
198 		kfree(e->path, M_HAMMER2);
199 		*/
200 		e->path = NULL;
201 		kfree(e, M_HAMMER2);
202 	}
203 }
204 
205 static int
206 hammer2_verify_volumes_common(const hammer2_vfsvolume_t *volumes)
207 {
208 	const hammer2_vfsvolume_t *vol;
209 	//struct partinfo part;
210 	struct stat st;
211 	const char *path;
212 	int i, ret;
213 
214 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
215 		vol = &volumes[i];
216 		if (vol->id == -1)
217 			continue;
218 		path = vol->dev->path;
219 		/* check volume fields are initialized */
220 		if (!vol->dev->devvp) {
221 			hprintf("%s has NULL devvp\n", path);
222 			return EINVAL;
223 		}
224 		if (vol->offset == (hammer2_off_t)-1) {
225 			hprintf("%s has bad offset 0x%016jx\n", path,
226 				(intmax_t)vol->offset);
227 			return EINVAL;
228 		}
229 		if (vol->size == (hammer2_off_t)-1) {
230 			hprintf("%s has bad size 0x%016jx\n", path,
231 				(intmax_t)vol->size);
232 			return EINVAL;
233 		}
234 		/* check volume size vs block device size */
235 		/*
236 		if (VOP_IOCTL(vol->dev->devvp, DIOCGPART, (void*)&part, 0,
237 			      curthread->td_ucred , NULL) == 0) {
238 		*/
239 		assert(vol->dev->devvp->fs);
240 		ret = fstat(vol->dev->devvp->fs->fd, &st);
241 		if (ret == -1) {
242 			int error = errno;
243 			hprintf("failed to fstat %d\n",
244 				vol->dev->devvp->fs->fd);
245 			return error;
246 		} else {
247 			if (vol->size > st.st_size) {
248 				hprintf("%s's size 0x%016jx exceeds device size "
249 					"0x%016jx\n", path, (intmax_t)vol->size,
250 					st.st_size);
251 				return EINVAL;
252 			}
253 		}
254 	}
255 
256 	return 0;
257 }
258 
259 static int
260 hammer2_verify_volumes_1(const hammer2_vfsvolume_t *volumes,
261 		         const hammer2_volume_data_t *rootvoldata)
262 {
263 	const hammer2_vfsvolume_t *vol;
264 	hammer2_off_t off;
265 	const char *path;
266 	int i, nvolumes = 0;
267 
268 	/* check initialized volume count */
269 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
270 		vol = &volumes[i];
271 		if (vol->id != -1)
272 			nvolumes++;
273 	}
274 	if (nvolumes != 1) {
275 		hprintf("only 1 volume supported\n");
276 		return EINVAL;
277 	}
278 
279 	/* check volume header */
280 	if (rootvoldata->volu_id) {
281 		hprintf("volume id %d must be 0\n", rootvoldata->volu_id);
282 		return EINVAL;
283 	}
284 	if (rootvoldata->nvolumes) {
285 		hprintf("volume count %d must be 0\n", rootvoldata->nvolumes);
286 		return EINVAL;
287 	}
288 	if (rootvoldata->total_size) {
289 		hprintf("total size 0x%016jx must be 0\n",
290 			(intmax_t)rootvoldata->total_size);
291 		return EINVAL;
292 	}
293 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
294 		off = rootvoldata->volu_loff[i];
295 		if (off) {
296 			hprintf("volume offset[%d] 0x%016jx must be 0\n", i,
297 				(intmax_t)off);
298 			return EINVAL;
299 		}
300 	}
301 
302 	/* check volume */
303 	vol = &volumes[HAMMER2_ROOT_VOLUME];
304 	path = vol->dev->path;
305 	if (vol->id) {
306 		hprintf("%s has non zero id %d\n", path, vol->id);
307 		return EINVAL;
308 	}
309 	if (vol->offset) {
310 		hprintf("%s has non zero offset 0x%016jx\n", path,
311 			(intmax_t)vol->offset);
312 		return EINVAL;
313 	}
314 	if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) {
315 		hprintf("%s's size is not 0x%016jx aligned\n", path,
316 			(intmax_t)HAMMER2_VOLUME_ALIGN);
317 		return EINVAL;
318 	}
319 
320 	return 0;
321 }
322 
323 static int
324 hammer2_verify_volumes_2(const hammer2_vfsvolume_t *volumes,
325 		         const hammer2_volume_data_t *rootvoldata)
326 {
327 	const hammer2_vfsvolume_t *vol;
328 	hammer2_off_t off, total_size = 0;
329 	const char *path;
330 	int i, nvolumes = 0;
331 
332 	/* check initialized volume count */
333 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
334 		vol = &volumes[i];
335 		if (vol->id != -1) {
336 			nvolumes++;
337 			total_size += vol->size;
338 		}
339 	}
340 
341 	/* check volume header */
342 	if (rootvoldata->volu_id != HAMMER2_ROOT_VOLUME) {
343 		hprintf("volume id %d must be %d\n", rootvoldata->volu_id,
344 			HAMMER2_ROOT_VOLUME);
345 		return EINVAL;
346 	}
347 	if (rootvoldata->nvolumes != nvolumes) {
348 		hprintf("volume header requires %d devices, %d specified\n",
349 			rootvoldata->nvolumes, nvolumes);
350 		return EINVAL;
351 	}
352 	if (rootvoldata->total_size != total_size) {
353 		hprintf("total size 0x%016jx does not equal sum of volumes 0x%016jx\n",
354 			rootvoldata->total_size, total_size);
355 		return EINVAL;
356 	}
357 	for (i = 0; i < nvolumes; ++i) {
358 		off = rootvoldata->volu_loff[i];
359 		if (off == (hammer2_off_t)-1) {
360 			hprintf("volume offset[%d] 0x%016jx must not be -1\n",
361 				i, (intmax_t)off);
362 			return EINVAL;
363 		}
364 	}
365 	for (i = nvolumes; i < HAMMER2_MAX_VOLUMES; ++i) {
366 		off = rootvoldata->volu_loff[i];
367 		if (off != (hammer2_off_t)-1) {
368 			hprintf("volume offset[%d] 0x%016jx must be -1\n",
369 				i, (intmax_t)off);
370 			return EINVAL;
371 		}
372 	}
373 
374 	/* check volumes */
375 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
376 		vol = &volumes[i];
377 		if (vol->id == -1)
378 			continue;
379 		path = vol->dev->path;
380 		/* check offset */
381 		if (vol->offset & HAMMER2_FREEMAP_LEVEL1_MASK) {
382 			hprintf("%s's offset 0x%016jx not 0x%016jx aligned\n",
383 				path, (intmax_t)vol->offset,
384 				HAMMER2_FREEMAP_LEVEL1_SIZE);
385 			return EINVAL;
386 		}
387 		/* check vs previous volume */
388 		if (i) {
389 			if (vol->id <= (vol-1)->id) {
390 				hprintf("%s has inconsistent id %d\n", path,
391 					vol->id);
392 				return EINVAL;
393 			}
394 			if (vol->offset != (vol-1)->offset + (vol-1)->size) {
395 				hprintf("%s has inconsistent offset 0x%016jx\n",
396 					path, (intmax_t)vol->offset);
397 				return EINVAL;
398 			}
399 		} else { /* first */
400 			if (vol->offset) {
401 				hprintf("%s has non zero offset 0x%016jx\n",
402 					path, (intmax_t)vol->offset);
403 				return EINVAL;
404 			}
405 		}
406 		/* check size for non-last and last volumes */
407 		if (i != rootvoldata->nvolumes - 1) {
408 			if (vol->size < HAMMER2_FREEMAP_LEVEL1_SIZE) {
409 				hprintf("%s's size must be >= 0x%016jx\n", path,
410 					(intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
411 				return EINVAL;
412 			}
413 			if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK) {
414 				hprintf("%s's size is not 0x%016jx aligned\n",
415 					path,
416 					(intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE);
417 				return EINVAL;
418 			}
419 		} else { /* last */
420 			if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) {
421 				hprintf("%s's size is not 0x%016jx aligned\n",
422 					path,
423 					(intmax_t)HAMMER2_VOLUME_ALIGN);
424 				return EINVAL;
425 			}
426 		}
427 	}
428 
429 	return 0;
430 }
431 
432 static int
433 hammer2_verify_vfsvolumes(const hammer2_vfsvolume_t *volumes,
434 		       const hammer2_volume_data_t *rootvoldata)
435 {
436 	int error;
437 
438 	error = hammer2_verify_volumes_common(volumes);
439 	if (error)
440 		return error;
441 
442 	if (rootvoldata->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES)
443 		return hammer2_verify_volumes_2(volumes, rootvoldata);
444 	else
445 		return hammer2_verify_volumes_1(volumes, rootvoldata);
446 }
447 
448 /*
449  * Returns zone# of returned volume header or < 0 on failure.
450  */
451 static int
452 hammer2_read_volume_header(struct m_vnode *devvp, const char *path,
453 			   hammer2_volume_data_t *voldata)
454 {
455 	hammer2_volume_data_t *vd;
456 	struct m_buf *bp = NULL;
457 	hammer2_crc32_t crc0, crc1;
458 	int zone = -1;
459 	int i;
460 
461 	/*
462 	 * There are up to 4 copies of the volume header (syncs iterate
463 	 * between them so there is no single master).  We don't trust the
464 	 * volu_size field so we don't know precisely how large the filesystem
465 	 * is, so depend on the OS to return an error if we go beyond the
466 	 * block device's EOF.
467 	 */
468 	for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) {
469 		if (breadx(devvp, i * HAMMER2_ZONE_BYTES64, HAMMER2_VOLUME_BYTES,
470 			  &bp)) {
471 			brelse(bp);
472 			bp = NULL;
473 			continue;
474 		}
475 
476 		vd = (struct hammer2_volume_data *)bp->b_data;
477 		/* verify volume header magic */
478 		if ((vd->magic != HAMMER2_VOLUME_ID_HBO) &&
479 		    (vd->magic != HAMMER2_VOLUME_ID_ABO)) {
480 			hprintf("%s #%d: bad magic\n", path, i);
481 			brelse(bp);
482 			bp = NULL;
483 			continue;
484 		}
485 
486 		if (vd->magic == HAMMER2_VOLUME_ID_ABO) {
487 			/* XXX: Reversed-endianness filesystem */
488 			hprintf("%s #%d: reverse-endian filesystem detected\n",
489 				path, i);
490 			brelse(bp);
491 			bp = NULL;
492 			continue;
493 		}
494 
495 		/* verify volume header CRC's */
496 		crc0 = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT0];
497 		crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC0_OFF,
498 				      HAMMER2_VOLUME_ICRC0_SIZE);
499 		if (crc0 != crc1) {
500 			hprintf("%s #%d: volume header crc mismatch sect0 %08x/%08x\n",
501 				path, i, crc0, crc1);
502 			brelse(bp);
503 			bp = NULL;
504 			continue;
505 		}
506 		crc0 = vd->icrc_sects[HAMMER2_VOL_ICRC_SECT1];
507 		crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRC1_OFF,
508 				      HAMMER2_VOLUME_ICRC1_SIZE);
509 		if (crc0 != crc1) {
510 			hprintf("%s #%d: volume header crc mismatch sect1 %08x/%08x\n",
511 				path, i, crc0, crc1);
512 			brelse(bp);
513 			bp = NULL;
514 			continue;
515 		}
516 		crc0 = vd->icrc_volheader;
517 		crc1 = hammer2_icrc32(bp->b_data + HAMMER2_VOLUME_ICRCVH_OFF,
518 				      HAMMER2_VOLUME_ICRCVH_SIZE);
519 		if (crc0 != crc1) {
520 			hprintf("%s #%d: volume header crc mismatch vh %08x/%08x\n",
521 				path, i, crc0, crc1);
522 			brelse(bp);
523 			bp = NULL;
524 			continue;
525 		}
526 
527 		if (zone == -1 || voldata->mirror_tid < vd->mirror_tid) {
528 			*voldata = *vd;
529 			zone = i;
530 		}
531 		brelse(bp);
532 		bp = NULL;
533 	}
534 
535 	if (zone == -1) {
536 		hprintf("%s has no valid volume headers\n", path);
537 		return -EINVAL;
538 	}
539 	return zone;
540 }
541 
542 static void
543 hammer2_print_uuid_mismatch(uuid_t *uuid1, uuid_t *uuid2, const char *id)
544 {
545 	char *buf1 = NULL, *buf2 = NULL;
546 
547 	hammer2_uuid_to_str(uuid1, &buf1);
548 	hammer2_uuid_to_str(uuid2, &buf2);
549 
550 	hprintf("%s uuid mismatch %s vs %s\n", id, buf1, buf2);
551 
552 	free(buf1);
553 	free(buf2);
554 }
555 
556 int
557 hammer2_init_vfsvolumes(struct mount *mp, const hammer2_devvp_list_t *devvpl,
558 		     hammer2_vfsvolume_t *volumes,
559 		     hammer2_volume_data_t *rootvoldata,
560 		     int *rootvolzone,
561 		     struct m_vnode **rootvoldevvp)
562 {
563 	hammer2_devvp_t *e;
564 	hammer2_volume_data_t *voldata;
565 	hammer2_vfsvolume_t *vol;
566 	struct m_vnode *devvp;
567 	const char *path;
568 	uuid_t fsid, fstype;
569 	int i, zone, error = 0, version = -1, nvolumes = 0;
570 
571 	for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
572 		vol = &volumes[i];
573 		vol->dev = NULL;
574 		vol->id = -1;
575 		vol->offset = (hammer2_off_t)-1;
576 		vol->size = (hammer2_off_t)-1;
577 	}
578 
579 	voldata = kmalloc(sizeof(*voldata), M_HAMMER2, M_WAITOK | M_ZERO);
580 	bzero(&fsid, sizeof(fsid));
581 	bzero(&fstype, sizeof(fstype));
582 	bzero(rootvoldata, sizeof(*rootvoldata));
583 
584 	TAILQ_FOREACH(e, devvpl, entry) {
585 		devvp = e->devvp;
586 		path = e->path;
587 		KKASSERT(devvp);
588 
589 		/* returns negative error or positive zone# */
590 		error = hammer2_read_volume_header(devvp, path, voldata);
591 		if (error < 0) {
592 			hprintf("failed to read %s's volume header\n", path);
593 			error = -error;
594 			goto done;
595 		}
596 		zone = error;
597 		error = 0; /* reset error */
598 
599 		if (voldata->volu_id >= HAMMER2_MAX_VOLUMES) {
600 			hprintf("%s has bad volume id %d\n", path,
601 				voldata->volu_id);
602 			error = EINVAL;
603 			goto done;
604 		}
605 		vol = &volumes[voldata->volu_id];
606 		if (vol->id != -1) {
607 			hprintf("volume id %d already initialized\n",
608 				voldata->volu_id);
609 			error = EINVAL;
610 			goto done;
611 		}
612 		/* all headers must have the same version, nvolumes and uuid */
613 		if (version == -1) {
614 			version = voldata->version;
615 			nvolumes = voldata->nvolumes;
616 			fsid = voldata->fsid;
617 			fstype = voldata->fstype;
618 		} else {
619 			if (version != (int)voldata->version) {
620 				hprintf("volume version mismatch %d vs %d\n",
621 					version, (int)voldata->version);
622 				error = ENXIO;
623 				goto done;
624 			}
625 			if (nvolumes != voldata->nvolumes) {
626 				hprintf("volume count mismatch %d vs %d\n",
627 					nvolumes, voldata->nvolumes);
628 				error = ENXIO;
629 				goto done;
630 			}
631 			if (bcmp(&fsid, &voldata->fsid, sizeof(fsid))) {
632 				hammer2_print_uuid_mismatch(&fsid,
633 							    &voldata->fsid, "fsid");
634 				error = ENXIO;
635 				goto done;
636 			}
637 			if (bcmp(&fstype, &voldata->fstype, sizeof(fstype))) {
638 				hammer2_print_uuid_mismatch(&fstype,
639 							    &voldata->fstype, "fstype");
640 				error = ENXIO;
641 				goto done;
642 			}
643 		}
644 		if (version < HAMMER2_VOL_VERSION_MIN ||
645 		    version > HAMMER2_VOL_VERSION_WIP) {
646 			hprintf("bad volume version %d\n", version);
647 			error = EINVAL;
648 			goto done;
649 		}
650 		/* all per-volume tests passed */
651 		vol->dev = e;
652 		vol->id = voldata->volu_id;
653 		vol->offset = voldata->volu_loff[vol->id];
654 		vol->size = voldata->volu_size;
655 		if (vol->id == HAMMER2_ROOT_VOLUME) {
656 			bcopy(voldata, rootvoldata, sizeof(*rootvoldata));
657 			*rootvolzone = zone;
658 			KKASSERT(*rootvoldevvp == NULL);
659 			*rootvoldevvp = e->devvp;
660 		}
661 		//devvp->v_rdev->si_mountpoint = mp;
662 		hprintf("\"%s\" zone=%d id=%d offset=0x%016jx size=0x%016jx\n",
663 			path, zone, vol->id, (intmax_t)vol->offset,
664 			(intmax_t)vol->size);
665 	}
666 done:
667 	if (!error) {
668 		if (!rootvoldata->version) {
669 			hprintf("root volume not found\n");
670 			error = EINVAL;
671 		}
672 		if (!error)
673 			error = hammer2_verify_vfsvolumes(volumes, rootvoldata);
674 	}
675 	kfree(voldata, M_HAMMER2);
676 
677 	return error;
678 }
679 
680 hammer2_vfsvolume_t*
681 hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset)
682 {
683 	hammer2_vfsvolume_t *vol, *ret = NULL;
684 	int i;
685 
686 	offset &= ~HAMMER2_OFF_MASK_RADIX;
687 
688 	/* locking is unneeded until volume-add support */
689 	//hammer2_voldata_lock(hmp);
690 	/* do binary search if users really use this many supported volumes */
691 	for (i = 0; i < hmp->nvolumes; ++i) {
692 		vol = &hmp->volumes[i];
693 		if ((offset >= vol->offset) &&
694 		    (offset < vol->offset + vol->size)) {
695 			ret = vol;
696 			break;
697 		}
698 	}
699 	//hammer2_voldata_unlock(hmp);
700 
701 	if (!ret)
702 		panic("no volume for offset 0x%016jx", (intmax_t)offset);
703 
704 	KKASSERT(ret);
705 	KKASSERT(ret->dev);
706 	KKASSERT(ret->dev->devvp);
707 	//KKASSERT(ret->dev->path);
708 
709 	return ret;
710 }
711