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_add_volume(const char *path, int rdonly) 167 { 168 hammer2_volume_data_t voldata; 169 hammer2_volume_t *vol; 170 struct stat st; 171 int fd, i; 172 173 fd = open(path, rdonly ? O_RDONLY : O_RDWR); 174 if (fd == -1) 175 err(1, "open"); 176 177 if (fstat(fd, &st) == -1) 178 err(1, "fstat"); 179 if (!S_ISCHR(st.st_mode) && !S_ISREG(st.st_mode)) 180 errx(1, "Unsupported file type"); 181 182 if (hammer2_read_volume_header(fd, path, &voldata) >= 0) { 183 i = voldata.volu_id; 184 if (i < 0 || i >= HAMMER2_MAX_VOLUMES) 185 errx(1, "%s has bad volume id %d", path, i); 186 vol = &fso.volumes[i]; 187 if (vol->id != -1) 188 errx(1, "%s already initialized", path); 189 /* all headers must have the same version, nvolumes and uuid */ 190 if (!fso.nvolumes) { 191 fso.version = voldata.version; 192 fso.nvolumes = voldata.nvolumes; 193 fso.fsid = voldata.fsid; 194 fso.fstype = voldata.fstype; 195 } else { 196 if (fso.version != (int)voldata.version) 197 errx(1, "Volume version mismatch %d vs %d", 198 fso.version, (int)voldata.version); 199 if (fso.nvolumes != voldata.nvolumes) 200 errx(1, "Volume count mismatch %d vs %d", 201 fso.nvolumes, voldata.nvolumes); 202 if (!uuid_equal(&fso.fsid, &voldata.fsid, NULL)) 203 errx(1, "fsid uuid mismatch"); 204 if (!uuid_equal(&fso.fstype, &voldata.fstype, NULL)) 205 errx(1, "fstype uuid mismatch"); 206 } 207 /* all per-volume tests passed */ 208 hammer2_install_volume(vol, fd, i, path, 209 voldata.volu_loff[i], voldata.volu_size); 210 fso.total_size += vol->size; 211 } else { 212 errx(1, "Failed to read volume header"); 213 } 214 } 215 216 static void 217 hammer2_verify_volumes_common(const hammer2_ondisk_t *fsp) 218 { 219 const hammer2_volume_t *vol; 220 hammer2_off_t size; 221 struct stat *st; 222 const char *path; 223 int i, j, nvolumes = 0; 224 225 if (fsp->version == -1) 226 errx(1, "Bad volume version %d", fsp->version); 227 228 /* check initialized volume count */ 229 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 230 vol = &fsp->volumes[i]; 231 if (vol->id != -1) 232 nvolumes++; 233 } 234 235 /* fsp->nvolumes hasn't been verified yet, use nvolumes */ 236 st = calloc(nvolumes, sizeof(*st)); 237 238 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 239 vol = &fsp->volumes[i]; 240 if (vol->id == -1) 241 continue; 242 path = vol->path; 243 /* check volumes are unique */ 244 if (stat(path, &st[i]) != 0) 245 errx(1, "Failed to stat %s", path); 246 if (fstat(vol->fd, &st[i]) != 0) 247 errx(1, "Failed to fstat %d", vol->fd); 248 for (j = 0; j < i; ++j) { 249 if ((st[i].st_ino == st[j].st_ino) && 250 (st[i].st_dev == st[j].st_dev)) 251 errx(1, "%s specified more than once", path); 252 } 253 /* check volume fields are initialized */ 254 if (vol->fd == -1) 255 errx(1, "%s has bad fd %d", path, vol->fd); 256 if (vol->offset == (hammer2_off_t)-1) 257 errx(1, "%s has bad offset 0x%016jx", path, 258 (intmax_t)vol->offset); 259 if (vol->size == (hammer2_off_t)-1) 260 errx(1, "%s has bad size 0x%016jx", path, 261 (intmax_t)vol->size); 262 /* check volume size vs block device size */ 263 size = check_volume(vol->fd); 264 if (vol->size > size) 265 errx(1, "%s's size 0x%016jx exceeds device size 0x%016jx", 266 path, (intmax_t)vol->size, size); 267 } 268 free(st); 269 } 270 271 static void 272 hammer2_verify_volumes_1(hammer2_ondisk_t *fsp, 273 const hammer2_volume_data_t *rootvoldata) 274 { 275 const hammer2_volume_t *vol; 276 hammer2_off_t off; 277 const char *path; 278 int i, nvolumes = 0; 279 280 /* check initialized volume count */ 281 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 282 vol = &fsp->volumes[i]; 283 if (vol->id != -1) 284 nvolumes++; 285 } 286 if (nvolumes != 1) 287 errx(1, "Only 1 volume supported"); 288 if (fsp->nvolumes) 289 errx(1, "Volume count %d must be 0", fsp->nvolumes); 290 fsp->nvolumes = nvolumes; /* adjust with actual count */ 291 292 /* check volume header */ 293 if (rootvoldata) { 294 if (rootvoldata->volu_id) 295 errx(1, "Volume id %d must be 0", rootvoldata->volu_id); 296 if (rootvoldata->nvolumes) 297 errx(1, "Volume count %d must be 0", 298 rootvoldata->nvolumes); 299 if (rootvoldata->total_size) 300 errx(1, "Total size 0x%016jx must be 0", 301 (intmax_t)rootvoldata->total_size); 302 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 303 off = rootvoldata->volu_loff[i]; 304 if (off) 305 errx(1, "Volume offset[%d] 0x%016jx must be 0", 306 i, (intmax_t)off); 307 } 308 } 309 310 /* check volume */ 311 vol = &fsp->volumes[0]; 312 path = vol->path; 313 if (vol->id) 314 errx(1, "%s has non zero id %d", path, vol->id); 315 if (vol->offset) 316 errx(1, "%s has non zero offset 0x%016jx", path, 317 (intmax_t)vol->offset); 318 if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) 319 errx(1, "%s's size is not 0x%016jx aligned", path, 320 (intmax_t)HAMMER2_VOLUME_ALIGN); 321 } 322 323 static void 324 hammer2_verify_volumes_2(const hammer2_ondisk_t *fsp, 325 const hammer2_volume_data_t *rootvoldata) 326 { 327 const hammer2_volume_t *vol; 328 hammer2_off_t off; 329 const char *path; 330 int i, nvolumes = 0; 331 332 /* check initialized volume count */ 333 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 334 vol = &fsp->volumes[i]; 335 if (vol->id != -1) 336 nvolumes++; 337 } 338 if (fsp->nvolumes != nvolumes) 339 errx(1, "Volume count mismatch %d vs %d", 340 fsp->nvolumes, nvolumes); 341 342 /* check volume header */ 343 if (rootvoldata) { 344 if (rootvoldata->volu_id != HAMMER2_ROOT_VOLUME) 345 errx(1, "Volume id %d must be %d", 346 rootvoldata->volu_id, HAMMER2_ROOT_VOLUME); 347 if (rootvoldata->nvolumes != fso.nvolumes) 348 errx(1, "Volume header requires %d devices, %d specified", 349 rootvoldata->nvolumes, fso.nvolumes); 350 if (rootvoldata->total_size != fso.total_size) 351 errx(1, "Total size 0x%016jx does not equal sum of " 352 "volumes 0x%016jx", 353 rootvoldata->total_size, fso.total_size); 354 for (i = 0; i < nvolumes; ++i) { 355 off = rootvoldata->volu_loff[i]; 356 if (off == (hammer2_off_t)-1) 357 errx(1, "Volume offset[%d] 0x%016jx must not be -1", 358 i, (intmax_t)off); 359 } 360 for (i = nvolumes; i < HAMMER2_MAX_VOLUMES; ++i) { 361 off = rootvoldata->volu_loff[i]; 362 if (off != (hammer2_off_t)-1) 363 errx(1, "Volume offset[%d] 0x%016jx must be -1", 364 i, (intmax_t)off); 365 } 366 } 367 368 /* check volumes */ 369 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 370 vol = &fsp->volumes[i]; 371 if (vol->id == -1) 372 continue; 373 path = vol->path; 374 /* check offset */ 375 if (vol->offset & HAMMER2_FREEMAP_LEVEL1_MASK) 376 errx(1, "%s's offset 0x%016jx not 0x%016jx aligned", 377 path, (intmax_t)vol->offset, 378 HAMMER2_FREEMAP_LEVEL1_SIZE); 379 /* check vs previous volume */ 380 if (i) { 381 if (vol->id != (vol-1)->id + 1) 382 errx(1, "%s has inconsistent id %d", path, 383 vol->id); 384 if (vol->offset != (vol-1)->offset + (vol-1)->size) 385 errx(1, "%s has inconsistent offset 0x%016jx", 386 path, (intmax_t)vol->offset); 387 } else { /* first */ 388 if (vol->offset) 389 errx(1, "%s has non zero offset 0x%016jx", path, 390 (intmax_t)vol->offset); 391 } 392 /* check size for non-last and last volumes */ 393 if (i != fsp->nvolumes - 1) { 394 if (vol->size < HAMMER2_FREEMAP_LEVEL1_SIZE) 395 errx(1, "%s's size must be >= 0x%016jx", path, 396 (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE); 397 if (vol->size & HAMMER2_FREEMAP_LEVEL1_MASK) 398 errx(1, "%s's size is not 0x%016jx aligned", 399 path, 400 (intmax_t)HAMMER2_FREEMAP_LEVEL1_SIZE); 401 } else { /* last */ 402 if (vol->size & HAMMER2_VOLUME_ALIGNMASK64) 403 errx(1, "%s's size is not 0x%016jx aligned", 404 path, (intmax_t)HAMMER2_VOLUME_ALIGN); 405 } 406 } 407 } 408 409 void 410 hammer2_verify_volumes(hammer2_ondisk_t *fsp, 411 const hammer2_volume_data_t *rootvoldata) 412 { 413 hammer2_verify_volumes_common(fsp); 414 if (fsp->version >= HAMMER2_VOL_VERSION_MULTI_VOLUMES) 415 hammer2_verify_volumes_2(fsp, rootvoldata); 416 else 417 hammer2_verify_volumes_1(fsp, rootvoldata); 418 assert(fsp->nvolumes > 0); 419 } 420 421 void 422 hammer2_print_volumes(const hammer2_ondisk_t *fsp) 423 { 424 const hammer2_volume_t *vol; 425 int i, n, w = 0; 426 427 for (i = 0; i < fsp->nvolumes; ++i) { 428 vol = &fsp->volumes[i]; 429 n = (int)strlen(vol->path); 430 if (n > w) 431 w = n; 432 } 433 434 printf("total %-*.*s 0x%016jx 0x%016jx\n", 435 w, w, "", (intmax_t)0, (intmax_t)fsp->total_size); 436 437 for (i = 0; i < fsp->nvolumes; ++i) { 438 vol = &fsp->volumes[i]; 439 printf("volume%-2d %-*.*s 0x%016jx 0x%016jx%s\n", 440 vol->id, w, w, vol->path, (intmax_t)vol->offset, 441 (intmax_t)vol->size, 442 (vol->id == HAMMER2_ROOT_VOLUME ? 443 " (root volume)" : "")); 444 } 445 } 446 447 void 448 hammer2_init_volumes(const char *blkdevs, int rdonly) 449 { 450 hammer2_volume_data_t *rootvoldata; 451 char *p, *devpath; 452 453 if (hammer2_volumes_initialized) 454 errx(1, "Already initialized"); 455 if (!blkdevs) 456 errx(1, "NULL blkdevs"); 457 458 hammer2_init_ondisk(&fso); 459 p = strdup(blkdevs); 460 while ((devpath = p) != NULL) { 461 if ((p = strchr(p, ':')) != NULL) 462 *p++ = 0; 463 devpath = getdevpath(devpath, 0); 464 if (strchr(devpath, ':')) 465 hammer2_init_volumes(devpath, rdonly); 466 else 467 hammer2_add_volume(devpath, rdonly); 468 free(devpath); 469 } 470 free(p); 471 hammer2_volumes_initialized = 1; 472 473 rootvoldata = hammer2_read_root_volume_header(); 474 hammer2_verify_volumes(&fso, rootvoldata); 475 free(rootvoldata); 476 } 477 478 void 479 hammer2_cleanup_volumes(void) 480 { 481 hammer2_volume_t *vol; 482 int i; 483 484 for (i = 0; i < HAMMER2_MAX_VOLUMES; ++i) { 485 vol = &fso.volumes[i]; 486 if (vol->id == -1) 487 continue; 488 hammer2_uninstall_volume(vol); 489 } 490 hammer2_volumes_initialized = 0; 491 } 492 493 typedef void (*callback)(const hammer2_volume_t*, void *data); 494 495 static int 496 hammer2_get_volume_attr(hammer2_off_t offset, callback fn, void *data) 497 { 498 hammer2_volume_t *vol; 499 int i; 500 501 assert(hammer2_volumes_initialized == 1); 502 offset &= ~HAMMER2_OFF_MASK_RADIX; 503 504 /* do binary search if users really use this many supported volumes */ 505 for (i = 0; i < fso.nvolumes; ++i) { 506 vol = &fso.volumes[i]; 507 if ((offset >= vol->offset) && 508 (offset < vol->offset + vol->size)) { 509 fn(vol, data); 510 return(0); 511 } 512 } 513 514 return(-1); 515 } 516 517 /* fd */ 518 static void 519 hammer2_volume_fd_cb(const hammer2_volume_t *vol, void *data) 520 { 521 *(int*)data = vol->fd; 522 } 523 524 int 525 hammer2_get_volume_fd(hammer2_off_t offset) 526 { 527 int ret = 0; 528 529 if (hammer2_get_volume_attr(offset, hammer2_volume_fd_cb, &ret) < 0) 530 return(-1); 531 return(ret); 532 } 533 534 int 535 hammer2_get_root_volume_fd(void) 536 { 537 return(hammer2_get_volume_fd(0)); 538 } 539 540 /* id */ 541 static void 542 hammer2_volume_id_cb(const hammer2_volume_t *vol, void *data) 543 { 544 *(int*)data = vol->id; 545 } 546 547 int 548 hammer2_get_volume_id(hammer2_off_t offset) 549 { 550 int ret = 0; 551 552 if (hammer2_get_volume_attr(offset, hammer2_volume_id_cb, &ret) < 0) 553 return(-1); 554 return(ret); 555 } 556 557 int 558 hammer2_get_root_volume_id(void) 559 { 560 return(hammer2_get_volume_id(0)); 561 } 562 563 /* path */ 564 static void 565 hammer2_volume_path_cb(const hammer2_volume_t *vol, void *data) 566 { 567 *(const char**)data = vol->path; 568 } 569 570 const char * 571 hammer2_get_volume_path(hammer2_off_t offset) 572 { 573 const char *ret = NULL; 574 575 if (hammer2_get_volume_attr(offset, hammer2_volume_path_cb, &ret) < 0) 576 return(NULL); 577 return(ret); 578 } 579 580 const char * 581 hammer2_get_root_volume_path(void) 582 { 583 return(hammer2_get_volume_path(0)); 584 } 585 586 /* offset */ 587 static void 588 hammer2_volume_offset_cb(const hammer2_volume_t *vol, void *data) 589 { 590 *(hammer2_off_t*)data = vol->offset; 591 } 592 593 hammer2_off_t 594 hammer2_get_volume_offset(hammer2_off_t offset) 595 { 596 hammer2_off_t ret = 0; 597 598 if (hammer2_get_volume_attr(offset, hammer2_volume_offset_cb, &ret) < 0) 599 return(-1); 600 return(ret); 601 } 602 603 hammer2_off_t 604 hammer2_get_root_volume_offset(void) 605 { 606 return(hammer2_get_volume_offset(0)); 607 } 608 609 /* size */ 610 static void 611 hammer2_volume_size_cb(const hammer2_volume_t *vol, void *data) 612 { 613 *(hammer2_off_t*)data = vol->size; 614 } 615 616 hammer2_off_t 617 hammer2_get_volume_size(hammer2_off_t offset) 618 { 619 hammer2_off_t ret = 0; 620 621 if (hammer2_get_volume_attr(offset, hammer2_volume_size_cb, &ret) < 0) 622 return(-1); 623 return(ret); 624 } 625 626 hammer2_off_t 627 hammer2_get_root_volume_size(void) 628 { 629 return(hammer2_get_volume_size(0)); 630 } 631 632 /* total size */ 633 hammer2_off_t 634 hammer2_get_total_size(void) 635 { 636 return(fso.total_size); 637 } 638 639 hammer2_volume_data_t* 640 hammer2_read_root_volume_header(void) 641 { 642 hammer2_volume_data_t *voldata; 643 int fd = hammer2_get_root_volume_fd(); 644 const char *path = hammer2_get_root_volume_path(); 645 646 if (fd == -1) 647 return(NULL); 648 649 voldata = calloc(1, sizeof(*voldata)); 650 if (hammer2_read_volume_header(fd, path, voldata) >= 0) 651 return(voldata); 652 else 653 errx(1, "Failed to read volume header"); 654 } 655