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