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 37 #include <sys/types.h> 38 #include <sys/stat.h> 39 #include <unistd.h> 40 #include <fcntl.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <fstab.h> 45 #include <assert.h> 46 #include <err.h> 47 48 #include <vfs/hammer2/hammer2_disk.h> 49 50 #include "hammer2_subs.h" 51 52 static hammer2_ondisk_t fso; 53 static int hammer2_volumes_initialized; 54 55 static void 56 hammer2_init_volume(hammer2_volume_t *vol) 57 { 58 vol->fd = -1; 59 vol->id = -1; 60 vol->offset = (hammer2_off_t)-1; 61 vol->size = (hammer2_off_t)-1; 62 } 63 64 void 65 hammer2_init_ondisk(hammer2_ondisk_t *fsp) 66 { 67 int i; 68 69 bzero(fsp, sizeof(*fsp)); 70 fsp->version = -1; 71 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) 72 hammer2_init_volume(&fsp->volumes[i]); 73 } 74 75 void 76 hammer2_install_volume(hammer2_volume_t *vol, int fd, int id, const char *path, 77 hammer2_off_t offset, hammer2_off_t size) 78 { 79 bzero(vol, sizeof(*vol)); 80 vol->fd = fd; 81 vol->id = id; 82 vol->path = strdup(path); 83 vol->offset = offset; 84 vol->size = size; 85 } 86 87 void 88 hammer2_uninstall_volume(hammer2_volume_t *vol) 89 { 90 fsync(vol->fd); 91 close(vol->fd); 92 free(vol->path); 93 hammer2_init_volume(vol); 94 } 95 96 static int 97 hammer2_read_volume_header(int fd, const char *path, 98 hammer2_volume_data_t *voldata) 99 { 100 hammer2_volume_data_t vd; 101 hammer2_tid_t mirror_tid = -1; 102 hammer2_off_t size = check_volume(fd); 103 hammer2_crc32_t crc0, crc1; 104 const char *p; 105 int i, zone = -1; 106 ssize_t ret; 107 108 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 109 if (i * HAMMER2_ZONE_BYTES64 >= size) 110 break; 111 if (lseek(fd, i * HAMMER2_ZONE_BYTES64, SEEK_SET) == -1) 112 break; 113 ret = read(fd, &vd, HAMMER2_PBUFSIZE); 114 if (ret == -1) 115 err(1, "read"); 116 else if (ret != HAMMER2_PBUFSIZE) 117 errx(1, "%s #%d: failed to read", path, i); 118 119 p = (const char*)&vd; 120 /* verify volume header magic */ 121 if ((vd.magic != HAMMER2_VOLUME_ID_HBO) && 122 (vd.magic != HAMMER2_VOLUME_ID_ABO)) 123 errx(1, "%s #%d: bad magic", path, i); 124 125 if (vd.magic == HAMMER2_VOLUME_ID_ABO) { 126 /* XXX: Reversed-endianness filesystem */ 127 errx(1, "%s #%d: reverse-endian filesystem detected", 128 path, i); 129 } 130 131 /* verify volume header CRC's */ 132 crc0 = vd.icrc_sects[HAMMER2_VOL_ICRC_SECT0]; 133 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC0_OFF, 134 HAMMER2_VOLUME_ICRC0_SIZE); 135 if (crc0 != crc1) 136 errx(1, "%s #%d: volume header crc mismatch sect0 %08x/%08x", 137 path, i, crc0, crc1); 138 139 crc0 = vd.icrc_sects[HAMMER2_VOL_ICRC_SECT1]; 140 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRC1_OFF, 141 HAMMER2_VOLUME_ICRC1_SIZE); 142 if (crc0 != crc1) 143 errx(1, "%s #%d: volume header crc mismatch sect1 %08x/%08x", 144 path, i, crc0, crc1); 145 146 crc0 = vd.icrc_volheader; 147 crc1 = hammer2_icrc32(p + HAMMER2_VOLUME_ICRCVH_OFF, 148 HAMMER2_VOLUME_ICRCVH_SIZE); 149 if (crc0 != crc1) 150 errx(1, "%s #%d: volume header crc mismatch vh %08x/%08x", 151 path, i, crc0, crc1); 152 153 if (zone == -1 || mirror_tid < vd.mirror_tid) { 154 bcopy(&vd, voldata, sizeof(vd)); 155 mirror_tid = vd.mirror_tid; 156 zone = i; 157 } 158 } 159 160 if (zone == -1) 161 errx(1, "%s has no valid volume headers", path); 162 return(zone); 163 } 164 165 static void 166 hammer2_err_uuid_mismatch(uuid_t *uuid1, uuid_t *uuid2, const char *id) 167 { 168 char *p1 = NULL, *p2 = NULL; 169 170 hammer2_uuid_to_str(uuid1, &p1); 171 hammer2_uuid_to_str(uuid2, &p2); 172 173 errx(1, "%s uuid mismatch %s vs %s", id, p1, p2); 174 175 free(p1); 176 free(p2); 177 } 178 179 static void 180 hammer2_add_volume(const char *path, int rdonly) 181 { 182 hammer2_volume_data_t voldata; 183 hammer2_volume_t *vol; 184 struct stat st; 185 int fd, i; 186 187 fd = open(path, rdonly ? O_RDONLY : O_RDWR); 188 if (fd == -1) 189 err(1, "open"); 190 191 if (fstat(fd, &st) == -1) 192 err(1, "fstat"); 193 if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) 194 errx(1, "Unsupported file type"); 195 196 if (hammer2_read_volume_header(fd, path, &voldata) >= 0) { 197 i = voldata.volu_id; 198 if (i < 0 || i >= HAMMER2_MAX_VOLUMES) 199 errx(1, "%s has bad volume id %d", path, i); 200 vol = &fso.volumes[i]; 201 if (vol->id != -1) 202 errx(1, "%s already initialized", path); 203 /* all headers must have the same version, nvolumes and uuid */ 204 if (!fso.nvolumes) { 205 fso.version = voldata.version; 206 fso.nvolumes = voldata.nvolumes; 207 fso.fsid = voldata.fsid; 208 fso.fstype = voldata.fstype; 209 } else { 210 if (fso.version != (int)voldata.version) 211 errx(1, "Volume version mismatch %d vs %d", 212 fso.version, (int)voldata.version); 213 if (fso.nvolumes != voldata.nvolumes) 214 errx(1, "Volume count mismatch %d vs %d", 215 fso.nvolumes, voldata.nvolumes); 216 if (!uuid_equal(&fso.fsid, &voldata.fsid, NULL)) 217 hammer2_err_uuid_mismatch(&fso.fsid, 218 &voldata.fsid, 219 "fsid"); 220 if (!uuid_equal(&fso.fstype, &voldata.fstype, NULL)) 221 hammer2_err_uuid_mismatch(&fso.fstype, 222 &voldata.fstype, 223 "fstype"); 224 } 225 /* all per-volume tests passed */ 226 hammer2_install_volume(vol, fd, i, path, 227 voldata.volu_loff[i], voldata.volu_size); 228 fso.total_size += vol->size; 229 } else { 230 errx(1, "Failed to read volume header"); 231 } 232 } 233 234 static void 235 hammer2_verify_volumes_common(const hammer2_ondisk_t *fsp) 236 { 237 const hammer2_volume_t *vol; 238 hammer2_off_t size; 239 struct stat *st; 240 const char *path; 241 int i, j, nvolumes = 0; 242 243 if (fsp->version == -1) 244 errx(1, "Bad volume version %d", fsp->version); 245 246 /* check initialized volume count */ 247 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 248 vol = &fsp->volumes[i]; 249 if (vol->id != -1) 250 nvolumes++; 251 } 252 253 /* fsp->nvolumes hasn't been verified yet, use nvolumes */ 254 st = calloc(nvolumes, sizeof(*st)); 255 256 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 257 vol = &fsp->volumes[i]; 258 if (vol->id == -1) 259 continue; 260 path = vol->path; 261 /* check volumes are unique */ 262 if (stat(path, &st[i]) != 0) 263 errx(1, "Failed to stat %s", path); 264 if (fstat(vol->fd, &st[i]) != 0) 265 errx(1, "Failed to fstat %d", vol->fd); 266 for (j = 0; j < i; ++j) { 267 if ((st[i].st_ino == st[j].st_ino) && 268 (st[i].st_dev == st[j].st_dev)) 269 errx(1, "%s specified more than once", path); 270 } 271 /* check volume fields are initialized */ 272 if (vol->fd == -1) 273 errx(1, "%s has bad fd %d", path, vol->fd); 274 if (vol->offset == (hammer2_off_t)-1) 275 errx(1, "%s has bad offset 0x%016jx", path, 276 (intmax_t)vol->offset); 277 if (vol->size == (hammer2_off_t)-1) 278 errx(1, "%s has bad size 0x%016jx", path, 279 (intmax_t)vol->size); 280 /* check volume size vs block device size */ 281 size = check_volume(vol->fd); 282 if (vol->size > size) 283 errx(1, "%s's size 0x%016jx exceeds device size 0x%016jx", 284 path, (intmax_t)vol->size, size); 285 } 286 free(st); 287 } 288 289 static void 290 hammer2_verify_volumes_1(hammer2_ondisk_t *fsp, 291 const hammer2_volume_data_t *rootvoldata) 292 { 293 const hammer2_volume_t *vol; 294 hammer2_off_t off; 295 const char *path; 296 int i, nvolumes = 0; 297 298 /* check initialized volume count */ 299 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 300 vol = &fsp->volumes[i]; 301 if (vol->id != -1) 302 nvolumes++; 303 } 304 if (nvolumes != 1) 305 errx(1, "Only 1 volume supported"); 306 if (fsp->nvolumes) 307 errx(1, "Volume count %d must be 0", fsp->nvolumes); 308 fsp->nvolumes = nvolumes; /* adjust with actual count */ 309 310 /* check volume header */ 311 if (rootvoldata) { 312 if (rootvoldata->volu_id) 313 errx(1, "Volume id %d must be 0", rootvoldata->volu_id); 314 if (rootvoldata->nvolumes) 315 errx(1, "Volume count %d must be 0", 316 rootvoldata->nvolumes); 317 if (rootvoldata->total_size) 318 errx(1, "Total size 0x%016jx must be 0", 319 (intmax_t)rootvoldata->total_size); 320 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 321 off = rootvoldata->volu_loff[i]; 322 if (off) 323 errx(1, "Volume offset[%d] 0x%016jx must be 0", 324 i, (intmax_t)off); 325 } 326 } 327 328 /* check volume */ 329 vol = &fsp->volumes[0]; 330 path = vol->path; 331 if (vol->id) 332 errx(1, "%s has non zero id %d", path, vol->id); 333 if (vol->offset) 334 errx(1, "%s has non zero offset 0x%016jx", path, 335 (intmax_t)vol->offset); 336 if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) 337 errx(1, "%s's size is not 0x%016jx aligned", path, 338 (intmax_t)HAMMER2_VOLUME_ALIGN); 339 } 340 341 static void 342 hammer2_verify_volumes_2(const hammer2_ondisk_t *fsp, 343 const hammer2_volume_data_t *rootvoldata) 344 { 345 const hammer2_volume_t *vol; 346 hammer2_off_t off; 347 const char *path; 348 int i, nvolumes = 0; 349 350 /* check initialized volume count */ 351 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 352 vol = &fsp->volumes[i]; 353 if (vol->id != -1) 354 nvolumes++; 355 } 356 if (fsp->nvolumes != nvolumes) 357 errx(1, "Volume count mismatch %d vs %d", 358 fsp->nvolumes, nvolumes); 359 360 /* check volume header */ 361 if (rootvoldata) { 362 if (rootvoldata->volu_id != HAMMER2_ROOT_VOLUME) 363 errx(1, "Volume id %d must be %d", 364 rootvoldata->volu_id, HAMMER2_ROOT_VOLUME); 365 if (rootvoldata->nvolumes != fso.nvolumes) 366 errx(1, "Volume header requires %d devices, %d specified", 367 rootvoldata->nvolumes, fso.nvolumes); 368 if (rootvoldata->total_size != fso.total_size) 369 errx(1, "Total size 0x%016jx does not equal sum of " 370 "volumes 0x%016jx", 371 rootvoldata->total_size, fso.total_size); 372 for (i = 0; i < nvolumes; ++i) { 373 off = rootvoldata->volu_loff[i]; 374 if (off == (hammer2_off_t)-1) 375 errx(1, "Volume offset[%d] 0x%016jx must not be -1", 376 i, (intmax_t)off); 377 } 378 for (i = nvolumes; i < HAMMER2_MAX_VOLUMES; ++i) { 379 off = rootvoldata->volu_loff[i]; 380 if (off != (hammer2_off_t)-1) 381 errx(1, "Volume offset[%d] 0x%016jx must be -1", 382 i, (intmax_t)off); 383 } 384 } 385 386 /* check volumes */ 387 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 388 vol = &fsp->volumes[i]; 389 if (vol->id == -1) 390 continue; 391 path = vol->path; 392 /* check offset */ 393 if (vol->offset & HAMMER2_FREEMAP_LEVEL1_MASK) 394 errx(1, "%s's offset 0x%016jx not 0x%016jx aligned", 395 path, (intmax_t)vol->offset, 396 HAMMER2_FREEMAP_LEVEL1_SIZE); 397 /* check vs previous volume */ 398 if (i) { 399 if (vol->id != (vol-1)->id + 1) 400 errx(1, "%s has inconsistent id %d", path, 401 vol->id); 402 if (vol->offset != (vol-1)->offset + (vol-1)->size) 403 errx(1, "%s has inconsistent offset 0x%016jx", 404 path, (intmax_t)vol->offset); 405 } else { /* first */ 406 if (vol->offset) 407 errx(1, "%s has non zero offset 0x%016jx", path, 408 (intmax_t)vol->offset); 409 } 410 /* check size for non-last and last volumes */ 411 if (i != fsp->nvolumes - 1) { 412 if (vol->size < HAMMER2_FREEMAP_LEVEL1_SIZE) 413 errx(1, "%s's size must be >= 0x%016jx", path, 414 (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE); 415 if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK) 416 errx(1, "%s's size is not 0x%016jx aligned", 417 path, 418 (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE); 419 } else { /* last */ 420 if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) 421 errx(1, "%s's size is not 0x%016jx aligned", 422 path, (intmax_t)HAMMER2_VOLUME_ALIGN); 423 } 424 } 425 } 426 427 void 428 hammer2_verify_volumes(hammer2_ondisk_t *fsp, 429 const hammer2_volume_data_t *rootvoldata) 430 { 431 hammer2_verify_volumes_common(fsp); 432 if (fsp->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES) 433 hammer2_verify_volumes_2(fsp, rootvoldata); 434 else 435 hammer2_verify_volumes_1(fsp, rootvoldata); 436 assert(fsp->nvolumes > 0); 437 } 438 439 void 440 hammer2_print_volumes(const hammer2_ondisk_t *fsp) 441 { 442 const hammer2_volume_t *vol; 443 int i, n, w = 0; 444 445 for (i = 0; i < fsp->nvolumes; ++i) { 446 vol = &fsp->volumes[i]; 447 n = (int)strlen(vol->path); 448 if (n > w) 449 w = n; 450 } 451 452 printf("total %-*.*s 0x%016jx 0x%016jx\n", 453 w, w, "", (intmax_t)0, (intmax_t)fsp->total_size); 454 455 for (i = 0; i < fsp->nvolumes; ++i) { 456 vol = &fsp->volumes[i]; 457 printf("volume%-2d %-*.*s 0x%016jx 0x%016jx%s\n", 458 vol->id, w, w, vol->path, (intmax_t)vol->offset, 459 (intmax_t)vol->size, 460 (vol->id == HAMMER2_ROOT_VOLUME ? 461 " (root volume)" : "")); 462 } 463 } 464 465 void 466 hammer2_init_volumes(const char *blkdevs, int rdonly) 467 { 468 hammer2_volume_data_t *rootvoldata; 469 char *p, *devpath; 470 471 if (hammer2_volumes_initialized) 472 errx(1, "Already initialized"); 473 if (!blkdevs) 474 errx(1, "NULL blkdevs"); 475 476 hammer2_init_ondisk(&fso); 477 p = strdup(blkdevs); 478 while ((devpath = p) != NULL) { 479 if ((p = strchr(p, ':')) != NULL) 480 *p++ = 0; 481 devpath = getdevpath(devpath, 0); 482 if (strchr(devpath, ':')) 483 hammer2_init_volumes(devpath, rdonly); 484 else 485 hammer2_add_volume(devpath, rdonly); 486 free(devpath); 487 } 488 free(p); 489 hammer2_volumes_initialized = 1; 490 491 rootvoldata = hammer2_read_root_volume_header(); 492 hammer2_verify_volumes(&fso, rootvoldata); 493 free(rootvoldata); 494 } 495 496 void 497 hammer2_cleanup_volumes(void) 498 { 499 hammer2_volume_t *vol; 500 int i; 501 502 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 503 vol = &fso.volumes[i]; 504 if (vol->id == -1) 505 continue; 506 hammer2_uninstall_volume(vol); 507 } 508 hammer2_volumes_initialized = 0; 509 } 510 511 typedef void (*callback)(const hammer2_volume_t*, void *data); 512 513 static int 514 hammer2_get_volume_attr(hammer2_off_t offset, callback fn, void *data) 515 { 516 hammer2_volume_t *vol; 517 int i; 518 519 assert(hammer2_volumes_initialized == 1); 520 offset &= ~HAMMER2_OFF_MASK_RADIX; 521 522 /* do binary search if users really use this many supported volumes */ 523 for (i = 0; i < fso.nvolumes; ++i) { 524 vol = &fso.volumes[i]; 525 if ((offset >= vol->offset) && 526 (offset < vol->offset + vol->size)) { 527 fn(vol, data); 528 return(0); 529 } 530 } 531 532 return(-1); 533 } 534 535 /* fd */ 536 static void 537 hammer2_volume_fd_cb(const hammer2_volume_t *vol, void *data) 538 { 539 *(int*)data = vol->fd; 540 } 541 542 int 543 hammer2_get_volume_fd(hammer2_off_t offset) 544 { 545 int ret = 0; 546 547 if (hammer2_get_volume_attr(offset, hammer2_volume_fd_cb, &ret) < 0) 548 return(-1); 549 return(ret); 550 } 551 552 int 553 hammer2_get_root_volume_fd(void) 554 { 555 return(hammer2_get_volume_fd(0)); 556 } 557 558 /* id */ 559 static void 560 hammer2_volume_id_cb(const hammer2_volume_t *vol, void *data) 561 { 562 *(int*)data = vol->id; 563 } 564 565 int 566 hammer2_get_volume_id(hammer2_off_t offset) 567 { 568 int ret = 0; 569 570 if (hammer2_get_volume_attr(offset, hammer2_volume_id_cb, &ret) < 0) 571 return(-1); 572 return(ret); 573 } 574 575 int 576 hammer2_get_root_volume_id(void) 577 { 578 return(hammer2_get_volume_id(0)); 579 } 580 581 /* path */ 582 static void 583 hammer2_volume_path_cb(const hammer2_volume_t *vol, void *data) 584 { 585 *(const char**)data = vol->path; 586 } 587 588 const char * 589 hammer2_get_volume_path(hammer2_off_t offset) 590 { 591 const char *ret = NULL; 592 593 if (hammer2_get_volume_attr(offset, hammer2_volume_path_cb, &ret) < 0) 594 return(NULL); 595 return(ret); 596 } 597 598 const char * 599 hammer2_get_root_volume_path(void) 600 { 601 return(hammer2_get_volume_path(0)); 602 } 603 604 /* offset */ 605 static void 606 hammer2_volume_offset_cb(const hammer2_volume_t *vol, void *data) 607 { 608 *(hammer2_off_t*)data = vol->offset; 609 } 610 611 hammer2_off_t 612 hammer2_get_volume_offset(hammer2_off_t offset) 613 { 614 hammer2_off_t ret = 0; 615 616 if (hammer2_get_volume_attr(offset, hammer2_volume_offset_cb, &ret) < 0) 617 return(-1); 618 return(ret); 619 } 620 621 hammer2_off_t 622 hammer2_get_root_volume_offset(void) 623 { 624 return(hammer2_get_volume_offset(0)); 625 } 626 627 /* size */ 628 static void 629 hammer2_volume_size_cb(const hammer2_volume_t *vol, void *data) 630 { 631 *(hammer2_off_t*)data = vol->size; 632 } 633 634 hammer2_off_t 635 hammer2_get_volume_size(hammer2_off_t offset) 636 { 637 hammer2_off_t ret = 0; 638 639 if (hammer2_get_volume_attr(offset, hammer2_volume_size_cb, &ret) < 0) 640 return(-1); 641 return(ret); 642 } 643 644 hammer2_off_t 645 hammer2_get_root_volume_size(void) 646 { 647 return(hammer2_get_volume_size(0)); 648 } 649 650 /* total size */ 651 hammer2_off_t 652 hammer2_get_total_size(void) 653 { 654 return(fso.total_size); 655 } 656 657 hammer2_volume_data_t* 658 hammer2_read_root_volume_header(void) 659 { 660 hammer2_volume_data_t *voldata; 661 int fd = hammer2_get_root_volume_fd(); 662 const char *path = hammer2_get_root_volume_path(); 663 664 if (fd == -1) 665 return(NULL); 666 667 voldata = calloc(1, sizeof(*voldata)); 668 if (hammer2_read_volume_header(fd, path, voldata) >= 0) 669 return(voldata); 670 else 671 errx(1, "Failed to read volume header"); 672 } 673