1 /* 2 * Copyright (c) 2011-2013 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@dragonflybsd.org> 6 * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 3. Neither the name of The DragonFly Project nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific, prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 26 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 30 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 32 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #if !defined(BOOT2) && !defined(TESTING) 37 #define LIBSTAND 1 38 #endif 39 40 #ifdef BOOT2 41 #include "boot2.h" 42 #endif 43 44 #ifdef LIBSTAND 45 #include "stand.h" 46 #endif 47 48 #ifdef TESTING 49 #include <sys/types.h> 50 #include <sys/stat.h> 51 #include <sys/uuid.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <stddef.h> 55 #include <stdint.h> 56 #include <unistd.h> 57 #include <fcntl.h> 58 #include <string.h> 59 #include <strings.h> 60 #include <errno.h> 61 #endif 62 63 #include <machine/param.h> /* for DEV_BSHIFT */ 64 #include <vfs/hammer2/hammer2_disk.h> 65 66 uint32_t iscsi_crc32(const void *buf, size_t size); 67 uint32_t iscsi_crc32_ext(const void *buf, size_t size, uint32_t ocrc); 68 69 static hammer2_media_data_t media; 70 static hammer2_blockref_t saved_base; 71 72 #define hammer2_icrc32(buf, size) iscsi_crc32(buf, size) 73 74 struct hammer2_fs { 75 hammer2_blockref_t sroot; 76 hammer2_blockset_t sroot_blockset; 77 #if defined(TESTING) 78 int fd; 79 #elif defined(LIBSTAND) 80 struct open_file *f; 81 #elif defined(BOOT2) 82 /* BOOT2 doesn't use a descriptor */ 83 #else 84 #error "hammer2: unknown library API" 85 #endif 86 }; 87 88 struct hammer2_inode { 89 struct hammer2_inode_data ino; /* raw inode data */ 90 off_t doff; /* disk inode offset */ 91 }; 92 93 #ifdef BOOT2 94 95 static void 96 bzero(void *buf, size_t size) 97 { 98 for (size_t i = 0; i < size; i++) 99 ((char *)buf)[i] = 0; 100 } 101 102 static void 103 bcopy(void *src, void *dst, size_t size) 104 { 105 memcpy(dst, src, size); 106 } 107 108 #if 0 109 static size_t 110 strlen(const char *s) 111 { 112 size_t l = 0; 113 for (; *s != 0; s++) 114 l++; 115 return (l); 116 } 117 #endif 118 119 static int 120 memcmp(const void *a, const void *b, size_t len) 121 { 122 for (size_t p = 0; p < len; p++) { 123 int r = ((const char *)a)[p] - ((const char *)b)[p]; 124 if (r != 0) 125 return (r); 126 } 127 128 return (0); 129 } 130 131 #endif 132 133 static 134 off_t 135 blockoff(hammer2_blockref_t *bref) 136 { 137 return(bref->data_off & ~HAMMER2_OFF_MASK_RADIX); 138 } 139 140 static 141 size_t 142 blocksize(hammer2_blockref_t *bref) 143 { 144 size_t bytes; 145 146 bytes = (size_t)(bref->data_off & HAMMER2_OFF_MASK_RADIX); 147 if (bytes) 148 bytes = (size_t)1 << bytes; 149 return bytes; 150 } 151 152 static 153 hammer2_key_t 154 hammer2_dirhash(const unsigned char *name, size_t len) 155 { 156 const unsigned char *aname = name; 157 uint32_t crcx; 158 uint64_t key; 159 size_t i; 160 size_t j; 161 162 key = 0; 163 164 /* 165 * m32 166 */ 167 crcx = 0; 168 for (i = j = 0; i < len; ++i) { 169 if (aname[i] == '.' || 170 aname[i] == '-' || 171 aname[i] == '_' || 172 aname[i] == '~') { 173 if (i != j) 174 crcx += hammer2_icrc32(aname + j, i - j); 175 j = i + 1; 176 } 177 } 178 if (i != j) 179 crcx += hammer2_icrc32(aname + j, i - j); 180 181 /* 182 * The directory hash utilizes the top 32 bits of the 64-bit key. 183 * Bit 63 must be set to 1. 184 */ 185 crcx |= 0x80000000U; 186 key |= (uint64_t)crcx << 32; 187 188 /* 189 * l16 - crc of entire filename 190 * 191 * This crc reduces degenerate hash collision conditions 192 */ 193 crcx = hammer2_icrc32(aname, len); 194 crcx = crcx ^ (crcx << 16); 195 key |= crcx & 0xFFFF0000U; 196 197 /* 198 * Set bit 15. This allows readdir to strip bit 63 so a positive 199 * 64-bit cookie/offset can always be returned, and still guarantee 200 * that the values 0x0000-0x7FFF are available for artificial entries. 201 * ('.' and '..'). 202 */ 203 key |= 0x8000U; 204 205 return (key); 206 } 207 208 /* 209 * Low level read 210 */ 211 static 212 int 213 h2read(struct hammer2_fs *hfs, void *buf, size_t nbytes, off_t off) 214 { 215 #if defined(LIBSTAND) 216 size_t rlen; 217 #endif 218 int rc; 219 220 #if defined(TESTING) 221 rc = pread(hfs->fd, &media, nbytes, off); 222 if (rc == (int)nbytes) 223 rc = 0; 224 else 225 rc = -1; 226 #elif defined(LIBSTAND) 227 rc = hfs->f->f_dev->dv_strategy(hfs->f->f_devdata, F_READ, 228 off >> DEV_BSHIFT, nbytes, 229 buf, &rlen); 230 if (rc || rlen != nbytes) 231 rc = -1; 232 #elif defined(BOOT2) 233 /* BIOS interface may barf on 64KB reads */ 234 rc = 0; 235 while (nbytes > 16384) { 236 rc = dskread(buf, off >> DEV_BSHIFT, 16384 >> DEV_BSHIFT); 237 nbytes -= 16384; 238 buf = (char *)buf + 16384; 239 off += 16384; 240 } 241 if (nbytes) 242 rc = dskread(buf, off >> DEV_BSHIFT, nbytes >> DEV_BSHIFT); 243 if (rc) 244 rc = -1; 245 #else 246 #error "hammer2: unknown library API" 247 #endif 248 return rc; 249 } 250 251 /* 252 * Common code 253 * 254 * Initialize for HAMMER2 filesystem access given a hammer2_fs with 255 * its device file descriptor initialized. 256 */ 257 258 /* 259 * Lookup within the block specified by (*base), loading the block from disk 260 * if necessary. Locate the first key within the requested range and 261 * recursively run through indirect blocks as needed. The caller may loop 262 * by setting key_beg to *key_ret. 263 * 264 * Returns 0 if nothing could be found and the key range has been exhausted. 265 * returns -1 if a disk error occured. Otherwise returns the size of the 266 * data block and returns the data block in *pptr and bref in *bref_ret. 267 * 268 * NOTE! When reading file data, the returned bref's key will be the nearest 269 * data block we looked up. The file read procedure must handle any 270 * zero-fill or skip. However, we will truncate the return value to 271 * the file size. 272 */ 273 static int 274 h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base, 275 hammer2_key_t key_beg, hammer2_key_t key_end, 276 hammer2_blockref_t *bref_ret, void **pptr) 277 { 278 hammer2_blockref_t *bref; 279 hammer2_blockref_t best; 280 hammer2_key_t scan_beg; 281 hammer2_key_t scan_end; 282 int i; 283 int rc; 284 int count; 285 int dev_boff; 286 int dev_bsize; 287 288 if (base == NULL) { 289 saved_base.data_off = (hammer2_off_t)-1; 290 return(0); 291 } 292 if (base->data_off == (hammer2_off_t)-1) { 293 bref_ret->type = 0; 294 return(-1); 295 } 296 297 /* 298 * Calculate the number of blockrefs to scan 299 */ 300 switch(base->type) { 301 case HAMMER2_BREF_TYPE_VOLUME: 302 count = HAMMER2_SET_COUNT; 303 break; 304 case HAMMER2_BREF_TYPE_INODE: 305 count = HAMMER2_SET_COUNT; 306 break; 307 case HAMMER2_BREF_TYPE_INDIRECT: 308 count = blocksize(base) / sizeof(hammer2_blockref_t); 309 break; 310 default: 311 count = 0; 312 break; 313 } 314 315 /* 316 * Find the best candidate (the lowest blockref within the specified 317 * range). The array can be fully set associative so make no ordering 318 * assumptions. 319 */ 320 again: 321 best.key = HAMMER2_KEY_MAX; 322 best.type = 0; 323 324 for (i = 0; i < count; ++i) { 325 /* 326 * [re]load when returning from our recursion 327 */ 328 if (base->type != HAMMER2_BREF_TYPE_VOLUME && 329 base->data_off != saved_base.data_off) { 330 if (blocksize(base) && h2read(hfs, &media, 331 blocksize(base), 332 blockoff(base))) { 333 bref_ret->type = 0; 334 return(-1); 335 } 336 saved_base = *base; 337 } 338 339 /* 340 * Special case embedded file data 341 */ 342 if (base->type == HAMMER2_BREF_TYPE_INODE) { 343 if (media.ipdata.meta.op_flags & 344 HAMMER2_OPFLAG_DIRECTDATA) { 345 *pptr = media.ipdata.u.data; 346 bref_ret->type = HAMMER2_BREF_TYPE_DATA; 347 bref_ret->key = 0; 348 return HAMMER2_EMBEDDED_BYTES; 349 } 350 } 351 352 /* 353 * Calculate the bref in our scan. 354 */ 355 switch(base->type) { 356 case HAMMER2_BREF_TYPE_VOLUME: 357 bref = &hfs->sroot_blockset.blockref[i]; 358 break; 359 case HAMMER2_BREF_TYPE_INODE: 360 bref = &media.ipdata.u.blockset.blockref[i]; 361 break; 362 case HAMMER2_BREF_TYPE_INDIRECT: 363 bref = &media.npdata[i]; 364 break; 365 } 366 if (bref->type == 0) 367 continue; 368 if (bref->key > best.key) 369 continue; 370 scan_beg = bref->key; 371 scan_end = scan_beg + ((hammer2_key_t)1 << bref->keybits) - 1; 372 if (scan_end >= key_beg && scan_beg <= key_end) { 373 best = *bref; 374 } 375 } 376 377 /* 378 * Figure out what to do with the results. 379 */ 380 switch(best.type) { 381 case 0: 382 /* 383 * Return 0 384 */ 385 bref_ret->type = 0; 386 rc = 0; 387 break; 388 case HAMMER2_BREF_TYPE_INDIRECT: 389 /* 390 * Matched an indirect block. If the block turns out to 391 * contain nothing we continue the iteration, otherwise 392 * we return the data from the recursion. 393 * 394 * Be sure to handle the overflow case when recalculating 395 * key_beg. 396 */ 397 rc = h2lookup(hfs, &best, key_beg, key_end, bref_ret, pptr); 398 if (rc == 0) { 399 key_beg = best.key + 400 ((hammer2_key_t)1 << best.keybits); 401 if (key_beg > best.key && key_beg <= key_end) 402 goto again; 403 } 404 break; 405 case HAMMER2_BREF_TYPE_DIRENT: 406 case HAMMER2_BREF_TYPE_INODE: 407 case HAMMER2_BREF_TYPE_DATA: 408 /* 409 * Terminal match. Leaf elements might not be data-aligned. 410 */ 411 dev_bsize = blocksize(&best); 412 if (dev_bsize) { 413 if (dev_bsize < HAMMER2_LBUFSIZE) 414 dev_bsize = HAMMER2_LBUFSIZE; 415 dev_boff = blockoff(&best) - 416 (blockoff(&best) & ~HAMMER2_LBUFMASK64); 417 if (h2read(hfs, &media, 418 dev_bsize, 419 blockoff(&best) - dev_boff)) { 420 return(-1); 421 } 422 } 423 saved_base.data_off = (hammer2_off_t)-1; 424 *bref_ret = best; 425 *pptr = media.buf + dev_boff; 426 rc = blocksize(&best); 427 break; 428 } 429 return(rc); 430 } 431 432 static 433 void 434 h2resolve(struct hammer2_fs *hfs, const char *path, 435 hammer2_blockref_t *bref, hammer2_inode_data_t **inop) 436 { 437 hammer2_blockref_t bres; 438 hammer2_inode_data_t *ino; 439 hammer2_key_t key; 440 void *data; 441 ssize_t bytes; 442 size_t len; 443 444 /* 445 * Start point (superroot) 446 */ 447 ino = NULL; 448 *bref = hfs->sroot; 449 if (inop) 450 *inop = NULL; 451 452 /* 453 * Iterate path elements 454 */ 455 while (*path) { 456 while (*path == '/') 457 ++path; 458 if (*path == 0) /* terminal */ 459 break; 460 461 /* 462 * Calculate path element and look for it in the directory 463 */ 464 for (len = 0; path[len]; ++len) { 465 if (path[len] == '/') 466 break; 467 } 468 key = hammer2_dirhash(path, len); 469 for (;;) { 470 bytes = h2lookup(hfs, bref, 471 key, 472 key | HAMMER2_DIRHASH_LOMASK, 473 &bres, (void **)&data); 474 if (bytes < 0) 475 break; 476 if (bres.type == 0) 477 break; 478 switch (bres.type) { 479 case HAMMER2_BREF_TYPE_DIRENT: 480 if (bres.embed.dirent.namlen != len) 481 break; 482 if (bres.embed.dirent.namlen <= 483 sizeof(bres.check.buf)) { 484 if (memcmp(path, bres.check.buf, len)) 485 break; 486 } else { 487 if (memcmp(path, data, len)) 488 break; 489 } 490 491 /* 492 * Found, resolve inode. This will set 493 * ino similarly to HAMMER2_BREF_TYPE_INODE 494 * and adjust bres, which path continuation 495 * needs. 496 */ 497 *bref = hfs->sroot; 498 bytes = h2lookup(hfs, bref, 499 bres.embed.dirent.inum, 500 bres.embed.dirent.inum, 501 &bres, (void **)&ino); 502 if (inop) 503 *inop = ino; 504 goto found; 505 break; /* NOT REACHED */ 506 case HAMMER2_BREF_TYPE_INODE: 507 ino = data; 508 if (ino->meta.name_len != len) 509 break; 510 if (memcmp(path, ino->filename, len) == 0) { 511 if (inop) 512 *inop = ino; 513 goto found; 514 } 515 break; 516 } 517 if ((bres.key & 0xFFFF) == 0xFFFF) { 518 bres.type = 0; 519 break; 520 } 521 key = bres.key + 1; 522 } 523 found: 524 525 /* 526 * Lookup failure 527 */ 528 if (bytes < 0 || bres.type == 0) { 529 bref->data_off = (hammer2_off_t)-1; 530 ino = NULL; 531 break; 532 } 533 534 /* 535 * Check path continuance, inode must be a directory or 536 * we fail. 537 */ 538 path += len; 539 if (*path && ino->meta.type != HAMMER2_OBJTYPE_DIRECTORY) { 540 bref->data_off = (hammer2_off_t)-1; 541 break; 542 } 543 *bref = bres; 544 } 545 } 546 547 static 548 ssize_t 549 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref, 550 off_t off, off_t filesize, void *buf, size_t len) 551 { 552 hammer2_blockref_t bres; 553 ssize_t total; 554 ssize_t bytes; 555 ssize_t zfill; 556 char *data; 557 558 /* 559 * EOF edge cases 560 */ 561 if (off >= filesize) 562 return (0); 563 if (off + len > filesize) 564 len = filesize - off; 565 566 /* 567 * Loop until done 568 */ 569 total = 0; 570 while (len) { 571 /* 572 * Find closest bres >= requested offset. 573 */ 574 bytes = h2lookup(hfs, bref, off, off + len - 1, 575 &bres, (void **)&data); 576 577 if (bytes < 0) { 578 if (total == 0) 579 total = -1; 580 break; 581 } 582 583 /* 584 * Load the data into the buffer. First handle a degenerate 585 * zero-fill case. 586 */ 587 if (bytes == 0) { 588 bzero(buf, len); 589 total += len; 590 break; 591 } 592 593 /* 594 * Returned record overlaps to the left of the requested 595 * position. It must overlap in this case or h2lookup() 596 * would have returned something else. 597 */ 598 if (bres.key < off) { 599 data += off - bres.key; 600 bytes -= off - bres.key; 601 } 602 603 /* 604 * Returned record overlaps to the right of the requested 605 * position, handle zero-fill. Again h2lookup() only returns 606 * this case if there is an actual overlap. 607 */ 608 if (bres.key > off) { 609 zfill = (ssize_t)(bres.key - off); 610 bzero(buf, zfill); 611 len -= zfill; 612 off += zfill; 613 total += zfill; 614 buf = (char *)buf + zfill; 615 } 616 617 /* 618 * Trim returned request before copying. 619 */ 620 if (bytes > len) 621 bytes = len; 622 bcopy(data, buf, bytes); 623 len -= bytes; 624 off += bytes; 625 total += bytes; 626 buf = (char *)buf + bytes; 627 } 628 return (total); 629 } 630 631 static 632 int 633 h2init(struct hammer2_fs *hfs) 634 { 635 #if 0 636 uint32_t crc0; 637 #endif 638 hammer2_tid_t best_tid = 0; 639 void *data; 640 off_t off; 641 int best; 642 int i; 643 int r; 644 645 /* 646 * Find the best volume header. 647 * 648 * WARNING BIOS BUGS: It looks like some BIOSes will implode when 649 * given a disk offset beyond the EOM. XXX We need to probe the 650 * size of the media and limit our accesses, until then we have 651 * to give up if the first volume header does not have a hammer2 652 * signature. 653 * 654 * XXX Probably still going to be problems w/ HAMMER2 volumes on 655 * media which is too small w/certain BIOSes. 656 */ 657 best = -1; 658 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 659 off = i * HAMMER2_ZONE_BYTES64; 660 if (i) 661 no_io_error = 1; 662 if (h2read(hfs, &media, sizeof(media.voldata), off)) 663 break; 664 if (media.voldata.magic != HAMMER2_VOLUME_ID_HBO) 665 break; 666 if (best < 0 || best_tid < media.voldata.mirror_tid) { 667 best = i; 668 best_tid = media.voldata.mirror_tid; 669 } 670 } 671 no_io_error = 0; 672 if (best < 0) 673 return(-1); 674 675 /* 676 * Reload the best volume header and set up the blockref. 677 * We messed with media, clear the cache before continuing. 678 */ 679 off = best * HAMMER2_ZONE_BYTES64; 680 if (h2read(hfs, &media, sizeof(media.voldata), off)) 681 return(-1); 682 hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME; 683 hfs->sroot.data_off = off; 684 hfs->sroot_blockset = media.voldata.sroot_blockset; 685 h2lookup(hfs, NULL, 0, 0, NULL, NULL); 686 687 /* 688 * Lookup sroot/BOOT and clear the cache again. 689 */ 690 r = h2lookup(hfs, &hfs->sroot, 691 HAMMER2_SROOT_KEY, HAMMER2_SROOT_KEY, 692 &hfs->sroot, &data); 693 if (r <= 0) 694 return(-1); 695 h2lookup(hfs, NULL, 0, 0, NULL, NULL); 696 r = h2lookup(hfs, &hfs->sroot, 697 HAMMER2_BOOT_KEY, 698 HAMMER2_BOOT_KEY | HAMMER2_DIRHASH_LOMASK, 699 &hfs->sroot, &data); 700 if (r <= 0) { 701 printf("hammer2: 'BOOT' PFS not found\n"); 702 return(-1); 703 } 704 h2lookup(hfs, NULL, 0, 0, NULL, NULL); 705 706 return (0); 707 } 708 709 /************************************************************************ 710 * BOOT2 SUPPORT * 711 ************************************************************************ 712 * 713 */ 714 #ifdef BOOT2 715 716 static struct hammer2_fs hfs; 717 718 static int 719 boot2_hammer2_init(void) 720 { 721 if (h2init(&hfs)) 722 return(-1); 723 return(0); 724 } 725 726 static boot2_ino_t 727 boot2_hammer2_lookup(const char *path) 728 { 729 hammer2_blockref_t bref; 730 731 h2resolve(&hfs, path, &bref, NULL); 732 return ((boot2_ino_t)bref.data_off); 733 } 734 735 static ssize_t 736 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len) 737 { 738 hammer2_blockref_t bref; 739 ssize_t total; 740 741 bzero(&bref, sizeof(bref)); 742 bref.type = HAMMER2_BREF_TYPE_INODE; 743 bref.data_off = ino; 744 745 total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len); 746 if (total > 0) 747 fs_off += total; 748 return total; 749 } 750 751 const struct boot2_fsapi boot2_hammer2_api = { 752 .fsinit = boot2_hammer2_init, 753 .fslookup = boot2_hammer2_lookup, 754 .fsread = boot2_hammer2_read 755 }; 756 757 #endif 758 759 /************************************************************************ 760 * LIBSTAND SUPPORT * 761 ************************************************************************ 762 * 763 */ 764 #ifdef LIBSTAND 765 766 struct hfile { 767 struct hammer2_fs hfs; 768 hammer2_blockref_t bref; 769 int64_t fsize; 770 uint32_t mode; 771 uint8_t type; 772 }; 773 774 static 775 int 776 hammer2_get_dtype(uint8_t type) 777 { 778 switch(type) { 779 case HAMMER2_OBJTYPE_DIRECTORY: 780 return(DT_DIR); 781 case HAMMER2_OBJTYPE_REGFILE: 782 return(DT_REG); 783 case HAMMER2_OBJTYPE_FIFO: 784 return(DT_FIFO); 785 case HAMMER2_OBJTYPE_CDEV: 786 return(DT_CHR); 787 case HAMMER2_OBJTYPE_BDEV: 788 return(DT_BLK); 789 case HAMMER2_OBJTYPE_SOFTLINK: 790 return(DT_LNK); 791 case HAMMER2_OBJTYPE_SOCKET: 792 return(DT_SOCK); 793 default: 794 return(DT_UNKNOWN); 795 } 796 } 797 798 static 799 mode_t 800 hammer2_get_mode(uint8_t type) 801 { 802 switch(type) { 803 case HAMMER2_OBJTYPE_DIRECTORY: 804 return(S_IFDIR); 805 case HAMMER2_OBJTYPE_REGFILE: 806 return(S_IFREG); 807 case HAMMER2_OBJTYPE_FIFO: 808 return(S_IFIFO); 809 case HAMMER2_OBJTYPE_CDEV: 810 return(S_IFCHR); 811 case HAMMER2_OBJTYPE_BDEV: 812 return(S_IFBLK); 813 case HAMMER2_OBJTYPE_SOFTLINK: 814 return(S_IFLNK); 815 case HAMMER2_OBJTYPE_SOCKET: 816 return(S_IFSOCK); 817 default: 818 return(0); 819 } 820 } 821 822 static int 823 hammer2_open(const char *path, struct open_file *f) 824 { 825 struct hfile *hf = malloc(sizeof(*hf)); 826 hammer2_inode_data_t *ipdata; 827 828 bzero(hf, sizeof(*hf)); 829 f->f_offset = 0; 830 f->f_fsdata = hf; 831 hf->hfs.f = f; 832 833 if (h2init(&hf->hfs)) { 834 f->f_fsdata = NULL; 835 free(hf); 836 errno = ENOENT; 837 return(-1); 838 } 839 h2resolve(&hf->hfs, path, &hf->bref, &ipdata); 840 if (hf->bref.data_off == (hammer2_off_t)-1 || 841 (hf->bref.type != HAMMER2_BREF_TYPE_INODE && 842 hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) { 843 f->f_fsdata = NULL; 844 free(hf); 845 errno = ENOENT; 846 return(-1); 847 } 848 if (ipdata) { 849 hf->fsize = ipdata->meta.size; 850 hf->type = ipdata->meta.type; 851 hf->mode = ipdata->meta.mode | 852 hammer2_get_mode(ipdata->meta.type); 853 } else { 854 hf->fsize = 0; 855 hf->type = HAMMER2_OBJTYPE_DIRECTORY; 856 hf->mode = 0755 | S_IFDIR; 857 } 858 return(0); 859 } 860 861 static int 862 hammer2_close(struct open_file *f) 863 { 864 struct hfile *hf = f->f_fsdata; 865 866 f->f_fsdata = NULL; 867 if (hf) 868 free(hf); 869 return (0); 870 } 871 872 static int 873 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid) 874 { 875 struct hfile *hf = f->f_fsdata; 876 ssize_t total; 877 int rc = 0; 878 879 total = h2readfile(&hf->hfs, &hf->bref, 880 f->f_offset, hf->fsize, buf, len); 881 if (total < 0) { 882 rc = EIO; 883 total = 0; 884 } else { 885 f->f_offset += total; 886 rc = 0; 887 } 888 *resid = len - total; 889 return rc; 890 } 891 892 static off_t 893 hammer2_seek(struct open_file *f, off_t offset, int whence) 894 { 895 struct hfile *hf = f->f_fsdata; 896 897 switch (whence) { 898 case SEEK_SET: 899 f->f_offset = offset; 900 break; 901 case SEEK_CUR: 902 f->f_offset += offset; 903 break; 904 case SEEK_END: 905 f->f_offset = hf->fsize - offset; 906 break; 907 default: 908 return (-1); 909 } 910 return (f->f_offset); 911 } 912 913 static int 914 hammer2_stat(struct open_file *f, struct stat *st) 915 { 916 struct hfile *hf = f->f_fsdata; 917 918 st->st_mode = hf->mode; 919 st->st_uid = 0; 920 st->st_gid = 0; 921 st->st_size = hf->fsize; 922 923 return (0); 924 } 925 926 static int 927 hammer2_readdir(struct open_file *f, struct dirent *den) 928 { 929 struct hfile *hf = f->f_fsdata; 930 hammer2_blockref_t bres; 931 hammer2_inode_data_t *ipdata; 932 void *data; 933 int bytes; 934 935 for (;;) { 936 bytes = h2lookup(&hf->hfs, &hf->bref, 937 f->f_offset | HAMMER2_DIRHASH_VISIBLE, 938 HAMMER2_KEY_MAX, 939 &bres, (void **)&data); 940 if (bytes < 0) 941 break; 942 switch (bres.type) { 943 case HAMMER2_BREF_TYPE_INODE: 944 ipdata = data; 945 den->d_namlen = ipdata->meta.name_len; 946 den->d_type = hammer2_get_dtype(ipdata->meta.type); 947 den->d_ino = ipdata->meta.inum; 948 bcopy(ipdata->filename, den->d_name, den->d_namlen); 949 den->d_name[den->d_namlen] = 0; 950 break; 951 case HAMMER2_BREF_TYPE_DIRENT: 952 den->d_namlen = bres.embed.dirent.namlen; 953 den->d_type = hammer2_get_dtype(bres.embed.dirent.type); 954 den->d_ino = bres.embed.dirent.inum; 955 if (den->d_namlen <= sizeof(bres.check.buf)) { 956 bcopy(bres.check.buf, 957 den->d_name, 958 den->d_namlen); 959 } else { 960 bcopy(data, den->d_name, den->d_namlen); 961 } 962 den->d_name[den->d_namlen] = 0; 963 break; 964 default: 965 den->d_namlen = 1; 966 den->d_type = 967 hammer2_get_dtype(HAMMER2_OBJTYPE_REGFILE); 968 den->d_name[0] = '?'; 969 den->d_name[1] = 0; 970 break; 971 } 972 973 f->f_offset = bres.key + 1; 974 975 return(0); 976 } 977 return ENOENT; 978 } 979 980 struct fs_ops hammer2_fsops = { 981 "hammer2", 982 hammer2_open, 983 hammer2_close, 984 hammer2_read, 985 null_write, 986 hammer2_seek, 987 hammer2_stat, 988 hammer2_readdir 989 }; 990 991 #endif 992