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