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, "%s already initialized", path); 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 if (fsp->nvolumes) 334 errx(1, "Volume count %d must be 0", fsp->nvolumes); 335 fsp->nvolumes = nvolumes; /* adjust with actual count */ 336 337 /* check volume header */ 338 if (rootvoldata) { 339 if (rootvoldata->volu_id) 340 errx(1, "Volume id %d must be 0", rootvoldata->volu_id); 341 if (rootvoldata->nvolumes) 342 errx(1, "Volume count %d must be 0", 343 rootvoldata->nvolumes); 344 if (rootvoldata->total_size) 345 errx(1, "Total size 0x%016jx must be 0", 346 (intmax_t)rootvoldata->total_size); 347 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 348 off = rootvoldata->volu_loff[i]; 349 if (off) 350 errx(1, "Volume offset[%d] 0x%016jx must be 0", 351 i, (intmax_t)off); 352 } 353 } 354 355 /* check volume */ 356 vol = &fsp->volumes[0]; 357 path = vol->path; 358 if (vol->id) 359 errx(1, "%s has non zero id %d", path, vol->id); 360 if (vol->offset) 361 errx(1, "%s has non zero offset 0x%016jx", path, 362 (intmax_t)vol->offset); 363 if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) 364 errx(1, "%s's size is not 0x%016jx aligned", path, 365 (intmax_t)HAMMER2_VOLUME_ALIGN); 366 } 367 368 static void 369 hammer2_verify_volumes_2(const hammer2_ondisk_t *fsp, 370 const hammer2_volume_data_t *rootvoldata) 371 { 372 const hammer2_volume_t *vol; 373 hammer2_off_t off; 374 const char *path; 375 int i, nvolumes = 0; 376 377 /* check initialized volume count */ 378 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 379 vol = &fsp->volumes[i]; 380 if (vol->id != -1) 381 nvolumes++; 382 } 383 if (fsp->nvolumes != nvolumes) 384 errx(1, "Volume count mismatch %d vs %d", 385 fsp->nvolumes, nvolumes); 386 387 /* check volume header */ 388 if (rootvoldata) { 389 if (rootvoldata->volu_id != HAMMER2_ROOT_VOLUME) 390 errx(1, "Volume id %d must be %d", 391 rootvoldata->volu_id, HAMMER2_ROOT_VOLUME); 392 if (rootvoldata->nvolumes != fso.nvolumes) 393 errx(1, "Volume header requires %d devices, %d specified", 394 rootvoldata->nvolumes, fso.nvolumes); 395 if (rootvoldata->total_size != fso.total_size) 396 errx(1, "Total size 0x%016jx does not equal sum of " 397 "volumes 0x%016jx", 398 rootvoldata->total_size, fso.total_size); 399 for (i = 0; i < nvolumes; ++i) { 400 off = rootvoldata->volu_loff[i]; 401 if (off == (hammer2_off_t)-1) 402 errx(1, "Volume offset[%d] 0x%016jx must not be -1", 403 i, (intmax_t)off); 404 } 405 for (i = nvolumes; i < HAMMER2_MAX_VOLUMES; ++i) { 406 off = rootvoldata->volu_loff[i]; 407 if (off != (hammer2_off_t)-1) 408 errx(1, "Volume offset[%d] 0x%016jx must be -1", 409 i, (intmax_t)off); 410 } 411 } 412 413 /* check volumes */ 414 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 415 vol = &fsp->volumes[i]; 416 if (vol->id == -1) 417 continue; 418 path = vol->path; 419 /* check offset */ 420 if (vol->offset & HAMMER2_FREEMAP_LEVEL1_MASK) 421 errx(1, "%s's offset 0x%016jx not 0x%016jx aligned", 422 path, (intmax_t)vol->offset, 423 HAMMER2_FREEMAP_LEVEL1_SIZE); 424 /* check vs previous volume */ 425 if (i) { 426 if (vol->id != (vol-1)->id + 1) 427 errx(1, "%s has inconsistent id %d", path, 428 vol->id); 429 if (vol->offset != (vol-1)->offset + (vol-1)->size) 430 errx(1, "%s has inconsistent offset 0x%016jx", 431 path, (intmax_t)vol->offset); 432 } else { /* first */ 433 if (vol->offset) 434 errx(1, "%s has non zero offset 0x%016jx", path, 435 (intmax_t)vol->offset); 436 } 437 /* check size for non-last and last volumes */ 438 if (i != fsp->nvolumes - 1) { 439 if (vol->size < HAMMER2_FREEMAP_LEVEL1_SIZE) 440 errx(1, "%s's size must be >= 0x%016jx", path, 441 (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE); 442 if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK) 443 errx(1, "%s's size is not 0x%016jx aligned", 444 path, 445 (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE); 446 } else { /* last */ 447 if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) 448 errx(1, "%s's size is not 0x%016jx aligned", 449 path, (intmax_t)HAMMER2_VOLUME_ALIGN); 450 } 451 } 452 } 453 454 void 455 hammer2_verify_volumes(hammer2_ondisk_t *fsp, 456 const hammer2_volume_data_t *rootvoldata) 457 { 458 hammer2_verify_volumes_common(fsp); 459 if (fsp->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES) 460 hammer2_verify_volumes_2(fsp, rootvoldata); 461 else 462 hammer2_verify_volumes_1(fsp, rootvoldata); 463 assert(fsp->nvolumes > 0); 464 } 465 466 void 467 hammer2_print_volumes(const hammer2_ondisk_t *fsp) 468 { 469 const hammer2_volume_t *vol; 470 int i, n, w = 0; 471 472 for (i = 0; i < fsp->nvolumes; ++i) { 473 vol = &fsp->volumes[i]; 474 n = (int)strlen(vol->path); 475 if (n > w) 476 w = n; 477 } 478 479 printf("total %-*.*s 0x%016jx 0x%016jx\n", 480 w, w, "", (intmax_t)0, (intmax_t)fsp->total_size); 481 482 for (i = 0; i < fsp->nvolumes; ++i) { 483 vol = &fsp->volumes[i]; 484 printf("volume%-2d %-*.*s 0x%016jx 0x%016jx%s\n", 485 vol->id, w, w, vol->path, (intmax_t)vol->offset, 486 (intmax_t)vol->size, 487 (vol->id == HAMMER2_ROOT_VOLUME ? 488 " (root volume)" : "")); 489 } 490 } 491 492 void 493 hammer2_init_volumes(const char *blkdevs, int rdonly) 494 { 495 hammer2_volume_data_t *rootvoldata; 496 char *p, *devpath; 497 498 if (hammer2_volumes_initialized) 499 errx(1, "Already initialized"); 500 if (!blkdevs) 501 errx(1, "NULL blkdevs"); 502 503 hammer2_init_ondisk(&fso); 504 p = strdup(blkdevs); 505 while ((devpath = p) != NULL) { 506 if ((p = strchr(p, ':')) != NULL) 507 *p++ = 0; 508 devpath = getdevpath(devpath, 0); 509 if (strchr(devpath, ':')) 510 hammer2_init_volumes(devpath, rdonly); 511 else 512 hammer2_add_volume(devpath, rdonly); 513 free(devpath); 514 } 515 free(p); 516 hammer2_volumes_initialized = 1; 517 518 rootvoldata = hammer2_read_root_volume_header(); 519 hammer2_verify_volumes(&fso, rootvoldata); 520 free(rootvoldata); 521 } 522 523 void 524 hammer2_cleanup_volumes(void) 525 { 526 hammer2_volume_t *vol; 527 int i; 528 529 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 530 vol = &fso.volumes[i]; 531 if (vol->id == -1) 532 continue; 533 hammer2_uninstall_volume(vol); 534 } 535 hammer2_volumes_initialized = 0; 536 } 537 538 typedef void (*callback)(const hammer2_volume_t*, void *data); 539 540 static int 541 hammer2_get_volume_attr(hammer2_off_t offset, callback fn, void *data) 542 { 543 hammer2_volume_t *vol; 544 int i; 545 546 assert(hammer2_volumes_initialized == 1); 547 offset &= ~HAMMER2_OFF_MASK_RADIX; 548 549 /* do binary search if users really use this many supported volumes */ 550 for (i = 0; i < fso.nvolumes; ++i) { 551 vol = &fso.volumes[i]; 552 if ((offset >= vol->offset) && 553 (offset < vol->offset + vol->size)) { 554 fn(vol, data); 555 return(0); 556 } 557 } 558 559 return(-1); 560 } 561 562 /* fd */ 563 static void 564 hammer2_volume_fd_cb(const hammer2_volume_t *vol, void *data) 565 { 566 *(int*)data = vol->fd; 567 } 568 569 int 570 hammer2_get_volume_fd(hammer2_off_t offset) 571 { 572 int ret = 0; 573 574 if (hammer2_get_volume_attr(offset, hammer2_volume_fd_cb, &ret) < 0) 575 return(-1); 576 return(ret); 577 } 578 579 int 580 hammer2_get_root_volume_fd(void) 581 { 582 return(hammer2_get_volume_fd(0)); 583 } 584 585 /* id */ 586 static void 587 hammer2_volume_id_cb(const hammer2_volume_t *vol, void *data) 588 { 589 *(int*)data = vol->id; 590 } 591 592 int 593 hammer2_get_volume_id(hammer2_off_t offset) 594 { 595 int ret = 0; 596 597 if (hammer2_get_volume_attr(offset, hammer2_volume_id_cb, &ret) < 0) 598 return(-1); 599 return(ret); 600 } 601 602 int 603 hammer2_get_root_volume_id(void) 604 { 605 return(hammer2_get_volume_id(0)); 606 } 607 608 /* path */ 609 static void 610 hammer2_volume_path_cb(const hammer2_volume_t *vol, void *data) 611 { 612 *(const char**)data = vol->path; 613 } 614 615 const char * 616 hammer2_get_volume_path(hammer2_off_t offset) 617 { 618 const char *ret = NULL; 619 620 if (hammer2_get_volume_attr(offset, hammer2_volume_path_cb, &ret) < 0) 621 return(NULL); 622 return(ret); 623 } 624 625 const char * 626 hammer2_get_root_volume_path(void) 627 { 628 return(hammer2_get_volume_path(0)); 629 } 630 631 /* offset */ 632 static void 633 hammer2_volume_offset_cb(const hammer2_volume_t *vol, void *data) 634 { 635 *(hammer2_off_t*)data = vol->offset; 636 } 637 638 hammer2_off_t 639 hammer2_get_volume_offset(hammer2_off_t offset) 640 { 641 hammer2_off_t ret = 0; 642 643 if (hammer2_get_volume_attr(offset, hammer2_volume_offset_cb, &ret) < 0) 644 return(-1); 645 return(ret); 646 } 647 648 hammer2_off_t 649 hammer2_get_root_volume_offset(void) 650 { 651 return(hammer2_get_volume_offset(0)); 652 } 653 654 /* size */ 655 static void 656 hammer2_volume_size_cb(const hammer2_volume_t *vol, void *data) 657 { 658 *(hammer2_off_t*)data = vol->size; 659 } 660 661 hammer2_off_t 662 hammer2_get_volume_size(hammer2_off_t offset) 663 { 664 hammer2_off_t ret = 0; 665 666 if (hammer2_get_volume_attr(offset, hammer2_volume_size_cb, &ret) < 0) 667 return(-1); 668 return(ret); 669 } 670 671 hammer2_off_t 672 hammer2_get_root_volume_size(void) 673 { 674 return(hammer2_get_volume_size(0)); 675 } 676 677 /* total size */ 678 hammer2_off_t 679 hammer2_get_total_size(void) 680 { 681 return(fso.total_size); 682 } 683 684 hammer2_volume_data_t* 685 hammer2_read_root_volume_header(void) 686 { 687 hammer2_volume_data_t *voldata; 688 int fd = hammer2_get_root_volume_fd(); 689 const char *path = hammer2_get_root_volume_path(); 690 691 if (fd == -1) 692 return(NULL); 693 694 voldata = calloc(1, sizeof(*voldata)); 695 if (hammer2_read_volume_header(fd, path, voldata) >= 0) 696 return(voldata); 697 else 698 errx(1, "Failed to read volume header"); 699 } 700