xref: /dflybsd-src/usr.sbin/makefs/hammer2/hammer2_ondisk.c (revision 8f2ce533369498e3276d110254f9f5e755401db2)
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 vnode **devvp)
59  {
60  	struct 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 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 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 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[0];
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 vnode *devvp, const char *path,
453  			   hammer2_volume_data_t *voldata)
454  {
455  	hammer2_volume_data_t *vd;
456  	struct 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 vnode **rootvoldevvp)
562  {
563  	hammer2_devvp_t *e;
564  	hammer2_volume_data_t *voldata;
565  	hammer2_vfsvolume_t *vol;
566  	struct 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("%s already initialized\n", path);
608  			error = EINVAL;
609  			goto done;
610  		}
611  		/* all headers must have the same version, nvolumes and uuid */
612  		if (version == -1) {
613  			version = voldata->version;
614  			nvolumes = voldata->nvolumes;
615  			fsid = voldata->fsid;
616  			fstype = voldata->fstype;
617  		} else {
618  			if (version != (int)voldata->version) {
619  				hprintf("volume version mismatch %d vs %d\n",
620  					version, (int)voldata->version);
621  				error = ENXIO;
622  				goto done;
623  			}
624  			if (nvolumes != voldata->nvolumes) {
625  				hprintf("volume count mismatch %d vs %d\n",
626  					nvolumes, voldata->nvolumes);
627  				error = ENXIO;
628  				goto done;
629  			}
630  			if (bcmp(&fsid, &voldata->fsid, sizeof(fsid))) {
631  				hammer2_print_uuid_mismatch(&fsid,
632  							    &voldata->fsid, "fsid");
633  				error = ENXIO;
634  				goto done;
635  			}
636  			if (bcmp(&fstype, &voldata->fstype, sizeof(fstype))) {
637  				hammer2_print_uuid_mismatch(&fstype,
638  							    &voldata->fstype, "fstype");
639  				error = ENXIO;
640  				goto done;
641  			}
642  		}
643  		if (version < HAMMER2_VOL_VERSION_MIN ||
644  		    version > HAMMER2_VOL_VERSION_WIP) {
645  			hprintf("bad volume version %d\n", version);
646  			error = EINVAL;
647  			goto done;
648  		}
649  		/* all per-volume tests passed */
650  		vol->dev = e;
651  		vol->id = voldata->volu_id;
652  		vol->offset = voldata->volu_loff[vol->id];
653  		vol->size = voldata->volu_size;
654  		if (vol->id == HAMMER2_ROOT_VOLUME) {
655  			bcopy(voldata, rootvoldata, sizeof(*rootvoldata));
656  			*rootvolzone = zone;
657  			KKASSERT(*rootvoldevvp == NULL);
658  			*rootvoldevvp = e->devvp;
659  		}
660  		//devvp->v_rdev->si_mountpoint = mp;
661  		hprintf("\"%s\" zone=%d id=%d offset=0x%016jx size=0x%016jx\n",
662  			path, zone, vol->id, (intmax_t)vol->offset,
663  			(intmax_t)vol->size);
664  	}
665  done:
666  	if (!error)
667  		error = hammer2_verify_vfsvolumes(volumes, rootvoldata);
668  	kfree(voldata, M_HAMMER2);
669  
670  	return error;
671  }
672  
673  hammer2_vfsvolume_t*
674  hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset)
675  {
676  	hammer2_vfsvolume_t *vol, *ret = NULL;
677  	int i;
678  
679  	offset &= ~HAMMER2_OFF_MASK_RADIX;
680  
681  	/* locking is unneeded until volume-add support */
682  	//hammer2_voldata_lock(hmp);
683  	/* do binary search if users really use this many supported volumes */
684  	for (i = 0; i < hmp->nvolumes; ++i) {
685  		vol = &hmp->volumes[i];
686  		if ((offset >= vol->offset) &&
687  		    (offset < vol->offset + vol->size)) {
688  			ret = vol;
689  			break;
690  		}
691  	}
692  	//hammer2_voldata_unlock(hmp);
693  
694  	if (!ret)
695  		panic("no volume for offset 0x%016jx", (intmax_t)offset);
696  
697  	KKASSERT(ret);
698  	KKASSERT(ret->dev);
699  	KKASSERT(ret->dev->devvp);
700  	//KKASSERT(ret->dev->path);
701  
702  	return ret;
703  }
704