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