sys/vfs/hammer2: Use HAMMER2_ROOT_VOLUME instead of 0
[dragonfly.git] / sys / vfs / hammer2 / hammer2_ondisk.c
blobb3b3907dc534ee4b33544f9d3310cc2f4f55b7fd
1 /*
2 * Copyright (c) 2020 Tomohiro Kusumi <tkusumi@netbsd.org>
3 * Copyright (c) 2020 The DragonFly Project
4 * All rights reserved.
6 * This code is derived from software contributed to The DragonFly Project
7 * by Matthew Dillon <dillon@dragonflybsd.org>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
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.
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.
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>
48 #include "hammer2.h"
50 #define hprintf(X, ...) kprintf("hammer2_ondisk: " X, ## __VA_ARGS__)
52 static int
53 hammer2_lookup_device(const char *path, int rootmount, struct vnode **devvp)
55 struct vnode *vp = NULL;
56 struct nlookupdata nd;
57 int error = 0;
59 KKASSERT(path);
60 KKASSERT(*path != '\0');
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);
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);
85 if (error && vp) {
86 vrele(vp);
87 vp = NULL;
90 *devvp = vp;
91 return error;
94 int
95 hammer2_open_devvp(const hammer2_devvp_list_t *devvpl, int ronly)
97 hammer2_devvp_t *e;
98 struct vnode *devvp;
99 const char *path;
100 int count, error;
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;
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);
122 vn_unlock(devvp);
123 if (error)
124 return error;
125 KKASSERT(e->open);
128 return 0;
132 hammer2_close_devvp(const hammer2_devvp_list_t *devvpl, int ronly)
134 hammer2_devvp_t *e;
135 struct vnode *devvp;
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;
149 return 0;
153 hammer2_init_devvp(const char *blkdevs, int rootmount,
154 hammer2_devvp_list_t *devvpl)
156 hammer2_devvp_t *e;
157 struct vnode *devvp;
158 const char *p;
159 char *path;
160 int i, error = 0;
162 KKASSERT(TAILQ_EMPTY(devvpl));
163 KKASSERT(blkdevs); /* could be empty string */
164 p = blkdevs;
166 path = objcache_get(namei_oc, M_WAITOK);
167 while (1) {
168 strcpy(path, "");
169 if (*p != '/') {
170 strcpy(path, "/dev/"); /* relative path */
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++;
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;
196 /* lookup path from above */
197 KKASSERT(strncmp(path, "/dev/", 5) == 0);
198 devvp = NULL;
199 error = hammer2_lookup_device(path, rootmount, &devvp);
200 if (error) {
201 KKASSERT(!devvp);
202 hprintf("failed to lookup %s %d\n", path, error);
203 break;
205 KKASSERT(devvp);
206 e = kmalloc(sizeof(*e), M_HAMMER2, M_WAITOK | M_ZERO);
207 e->devvp = devvp;
208 e->path = kstrdup(path, M_HAMMER2);
209 TAILQ_INSERT_TAIL(devvpl, e, entry);
211 objcache_put(namei_oc, path);
213 return error;
216 void
217 hammer2_cleanup_devvp(hammer2_devvp_list_t *devvpl)
219 hammer2_devvp_t *e;
221 while (!TAILQ_EMPTY(devvpl)) {
222 e = TAILQ_FIRST(devvpl);
223 TAILQ_REMOVE(devvpl, e, entry);
224 /* devvp */
225 KKASSERT(e->devvp);
226 if (e->devvp->v_rdev)
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);
238 static int
239 hammer2_verify_volumes_common(const hammer2_volume_t *volumes)
241 const hammer2_volume_t *vol;
242 struct partinfo part;
243 const char *path;
244 int i;
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;
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;
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;
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;
278 return 0;
281 static int
282 hammer2_verify_volumes_1(const hammer2_volume_t *volumes,
283 const hammer2_volume_data_t *rootvoldata)
285 const hammer2_volume_t *vol;
286 hammer2_off_t off;
287 const char *path;
288 int i, nvolumes = 0;
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++;
296 if (nvolumes != 1) {
297 hprintf("only 1 volume supported\n");
298 return EINVAL;
301 /* check volume header */
302 if (rootvoldata->volu_id) {
303 hprintf("volume id %d must be 0\n", rootvoldata->volu_id);
304 return EINVAL;
306 if (rootvoldata->nvolumes) {
307 hprintf("volume count %d must be 0\n", rootvoldata->nvolumes);
308 return EINVAL;
310 if (rootvoldata->total_size) {
311 hprintf("total size 0x%016jx must be 0\n",
312 (intmax_t)rootvoldata->total_size);
313 return EINVAL;
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;
324 /* check volume */
325 vol = &volumes[HAMMER2_ROOT_VOLUME];
326 path = vol->dev->path;
327 if (vol->id) {
328 hprintf("%s has non zero id %d\n", path, vol->id);
329 return EINVAL;
331 if (vol->offset) {
332 hprintf("%s has non zero offset 0x%016jx\n", path,
333 (intmax_t)vol->offset);
334 return EINVAL;
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;
342 return 0;
345 static int
346 hammer2_verify_volumes_2(const hammer2_volume_t *volumes,
347 const hammer2_volume_data_t *rootvoldata)
349 const hammer2_volume_t *vol;
350 hammer2_off_t off, total_size = 0;
351 const char *path;
352 int i, nvolumes = 0;
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;
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;
369 if (rootvoldata->nvolumes != nvolumes) {
370 hprintf("volume header requires %d devices, %d specified\n",
371 rootvoldata->nvolumes, nvolumes);
372 return EINVAL;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
451 return 0;
454 static int
455 hammer2_verify_volumes(const hammer2_volume_t *volumes,
456 const hammer2_volume_data_t *rootvoldata)
458 int error;
460 error = hammer2_verify_volumes_common(volumes);
461 if (error)
462 return error;
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);
471 * Returns zone# of returned volume header or < 0 on failure.
473 static int
474 hammer2_read_volume_header(struct vnode *devvp, const char *path,
475 hammer2_volume_data_t *voldata)
477 hammer2_volume_data_t *vd;
478 struct buf *bp = NULL;
479 hammer2_crc32_t crc0, crc1;
480 int zone = -1;
481 int i;
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.
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;
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;
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;
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;
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;
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;
549 if (zone == -1 || voldata->mirror_tid < vd->mirror_tid) {
550 *voldata = *vd;
551 zone = i;
553 brelse(bp);
554 bp = NULL;
557 if (zone == -1) {
558 hprintf("%s has no valid volume headers\n", path);
559 return -EINVAL;
561 return zone;
564 static void
565 hammer2_print_uuid_mismatch(uuid_t *uuid1, uuid_t *uuid2, const char *id)
567 char buf1[64], buf2[64];
569 snprintf_uuid(buf1, sizeof(buf1), uuid1);
570 snprintf_uuid(buf2, sizeof(buf2), uuid2);
572 hprintf("%s uuid mismatch %s vs %s\n", id, buf1, buf2);
576 hammer2_init_volumes(struct mount *mp, const hammer2_devvp_list_t *devvpl,
577 hammer2_volume_t *volumes,
578 hammer2_volume_data_t *rootvoldata,
579 int *rootvolzone,
580 struct vnode **rootvoldevvp)
582 hammer2_devvp_t *e;
583 hammer2_volume_data_t *voldata;
584 hammer2_volume_t *vol;
585 struct vnode *devvp;
586 const char *path;
587 uuid_t fsid, fstype;
588 int i, zone, error = 0, version = -1, nvolumes = 0;
590 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) {
591 vol = &volumes[i];
592 vol->dev = NULL;
593 vol->id = -1;
594 vol->offset = (hammer2_off_t)-1;
595 vol->size = (hammer2_off_t)-1;
598 voldata = kmalloc(sizeof(*voldata), M_HAMMER2, M_WAITOK | M_ZERO);
599 bzero(&fsid, sizeof(fsid));
600 bzero(&fstype, sizeof(fstype));
601 bzero(rootvoldata, sizeof(*rootvoldata));
603 TAILQ_FOREACH(e, devvpl, entry) {
604 devvp = e->devvp;
605 path = e->path;
606 KKASSERT(devvp);
608 /* returns negative error or positive zone# */
609 error = hammer2_read_volume_header(devvp, path, voldata);
610 if (error < 0) {
611 hprintf("failed to read %s's volume header\n", path);
612 error = -error;
613 goto done;
615 zone = error;
616 error = 0; /* reset error */
618 if (voldata->volu_id >= HAMMER2_MAX_VOLUMES) {
619 hprintf("%s has bad volume id %d\n", path,
620 voldata->volu_id);
621 error = EINVAL;
622 goto done;
624 vol = &volumes[voldata->volu_id];
625 if (vol->id != -1) {
626 hprintf("volume id %d already initialized\n",
627 voldata->volu_id);
628 error = EINVAL;
629 goto done;
631 /* all headers must have the same version, nvolumes and uuid */
632 if (version == -1) {
633 version = voldata->version;
634 nvolumes = voldata->nvolumes;
635 fsid = voldata->fsid;
636 fstype = voldata->fstype;
637 } else {
638 if (version != (int)voldata->version) {
639 hprintf("volume version mismatch %d vs %d\n",
640 version, (int)voldata->version);
641 error = ENXIO;
642 goto done;
644 if (nvolumes != voldata->nvolumes) {
645 hprintf("volume count mismatch %d vs %d\n",
646 nvolumes, voldata->nvolumes);
647 error = ENXIO;
648 goto done;
650 if (bcmp(&fsid, &voldata->fsid, sizeof(fsid))) {
651 hammer2_print_uuid_mismatch(&fsid,
652 &voldata->fsid, "fsid");
653 error = ENXIO;
654 goto done;
656 if (bcmp(&fstype, &voldata->fstype, sizeof(fstype))) {
657 hammer2_print_uuid_mismatch(&fstype,
658 &voldata->fstype, "fstype");
659 error = ENXIO;
660 goto done;
663 if (version < HAMMER2_VOL_VERSION_MIN ||
664 version > HAMMER2_VOL_VERSION_WIP) {
665 hprintf("bad volume version %d\n", version);
666 error = EINVAL;
667 goto done;
669 /* all per-volume tests passed */
670 vol->dev = e;
671 vol->id = voldata->volu_id;
672 vol->offset = voldata->volu_loff[vol->id];
673 vol->size = voldata->volu_size;
674 if (vol->id == HAMMER2_ROOT_VOLUME) {
675 bcopy(voldata, rootvoldata, sizeof(*rootvoldata));
676 *rootvolzone = zone;
677 KKASSERT(*rootvoldevvp == NULL);
678 *rootvoldevvp = e->devvp;
680 devvp->v_rdev->si_mountpoint = mp;
681 hprintf("\"%s\" zone=%d id=%d offset=0x%016jx size=0x%016jx\n",
682 path, zone, vol->id, (intmax_t)vol->offset,
683 (intmax_t)vol->size);
685 done:
686 if (!error) {
687 if (!rootvoldata->version) {
688 hprintf("root volume not found\n");
689 error = EINVAL;
691 if (!error)
692 error = hammer2_verify_volumes(volumes, rootvoldata);
694 kfree(voldata, M_HAMMER2);
696 return error;
699 hammer2_volume_t*
700 hammer2_get_volume(hammer2_dev_t *hmp, hammer2_off_t offset)
702 hammer2_volume_t *vol, *ret = NULL;
703 int i;
705 offset &= ~HAMMER2_OFF_MASK_RADIX;
707 /* locking is unneeded until volume-add support */
708 //hammer2_voldata_lock(hmp);
709 /* do binary search if users really use this many supported volumes */
710 for (i = 0; i < hmp->nvolumes; ++i) {
711 vol = &hmp->volumes[i];
712 if ((offset >= vol->offset) &&
713 (offset < vol->offset + vol->size)) {
714 ret = vol;
715 break;
718 //hammer2_voldata_unlock(hmp);
720 if (!ret)
721 panic("no volume for offset 0x%016jx", (intmax_t)offset);
723 KKASSERT(ret);
724 KKASSERT(ret->dev);
725 KKASSERT(ret->dev->devvp);
726 KKASSERT(ret->dev->path);
728 return ret;