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