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