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 TESTING 45 #include <sys/types.h> 46 #include <sys/stat.h> 47 #include <sys/uuid.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <stddef.h> 51 #include <stdint.h> 52 #include <unistd.h> 53 #include <fcntl.h> 54 #include <string.h> 55 #include <strings.h> 56 #include <errno.h> 57 #endif 58 59 #ifdef LIBSTAND 60 #include "stand.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, key | 0xFFFFU, 472 &bres, (void **)&data); 473 if (bytes < 0) 474 break; 475 if (bres.type == 0) 476 break; 477 switch (bres.type) { 478 case HAMMER2_BREF_TYPE_DIRENT: 479 if (bres.embed.dirent.namlen != len) 480 break; 481 if (bres.embed.dirent.namlen <= 482 sizeof(bres.check.buf)) { 483 if (memcmp(path, bres.check.buf, len)) 484 break; 485 } else { 486 if (memcmp(path, data, len)) 487 break; 488 } 489 490 /* 491 * Found, resolve inode. This will set 492 * ino similarly to HAMMER2_BREF_TYPE_INODE 493 * and adjust bres, which path continuation 494 * needs. 495 */ 496 *bref = hfs->sroot; 497 bytes = h2lookup(hfs, bref, 498 bres.embed.dirent.inum, 499 bres.embed.dirent.inum, 500 &bres, (void **)&ino); 501 if (inop) 502 *inop = ino; 503 goto found; 504 break; /* NOT REACHED */ 505 case HAMMER2_BREF_TYPE_INODE: 506 ino = data; 507 if (ino->meta.name_len != len) 508 break; 509 if (memcmp(path, ino->filename, len) == 0) { 510 if (inop) 511 *inop = ino; 512 goto found; 513 } 514 break; 515 } 516 if ((bres.key & 0xFFFF) == 0xFFFF) { 517 bres.type = 0; 518 break; 519 } 520 key = bres.key + 1; 521 } 522 found: 523 524 /* 525 * Lookup failure 526 */ 527 if (bytes < 0 || bres.type == 0) { 528 bref->data_off = (hammer2_off_t)-1; 529 ino = NULL; 530 break; 531 } 532 533 /* 534 * Check path continuance, inode must be a directory or 535 * we fail. 536 */ 537 path += len; 538 if (*path && ino->meta.type != HAMMER2_OBJTYPE_DIRECTORY) { 539 bref->data_off = (hammer2_off_t)-1; 540 break; 541 } 542 *bref = bres; 543 } 544 } 545 546 static 547 ssize_t 548 h2readfile(struct hammer2_fs *hfs, hammer2_blockref_t *bref, 549 off_t off, off_t filesize, void *buf, size_t len) 550 { 551 hammer2_blockref_t bres; 552 ssize_t total; 553 ssize_t bytes; 554 ssize_t zfill; 555 char *data; 556 557 /* 558 * EOF edge cases 559 */ 560 if (off >= filesize) 561 return (0); 562 if (off + len > filesize) 563 len = filesize - off; 564 565 /* 566 * Loop until done 567 */ 568 total = 0; 569 while (len) { 570 /* 571 * Find closest bres >= requested offset. 572 */ 573 bytes = h2lookup(hfs, bref, off, off + len - 1, 574 &bres, (void **)&data); 575 576 if (bytes < 0) { 577 if (total == 0) 578 total = -1; 579 break; 580 } 581 582 /* 583 * Load the data into the buffer. First handle a degenerate 584 * zero-fill case. 585 */ 586 if (bytes == 0) { 587 bzero(buf, len); 588 total += len; 589 break; 590 } 591 592 /* 593 * Returned record overlaps to the left of the requested 594 * position. It must overlap in this case or h2lookup() 595 * would have returned something else. 596 */ 597 if (bres.key < off) { 598 data += off - bres.key; 599 bytes -= off - bres.key; 600 } 601 602 /* 603 * Returned record overlaps to the right of the requested 604 * position, handle zero-fill. Again h2lookup() only returns 605 * this case if there is an actual overlap. 606 */ 607 if (bres.key > off) { 608 zfill = (ssize_t)(bres.key - off); 609 bzero(buf, zfill); 610 len -= zfill; 611 off += zfill; 612 total += zfill; 613 buf = (char *)buf + zfill; 614 } 615 616 /* 617 * Trim returned request before copying. 618 */ 619 if (bytes > len) 620 bytes = len; 621 bcopy(data, buf, bytes); 622 len -= bytes; 623 off += bytes; 624 total += bytes; 625 buf = (char *)buf + bytes; 626 } 627 return (total); 628 } 629 630 static 631 int 632 h2init(struct hammer2_fs *hfs) 633 { 634 #if 0 635 uint32_t crc0; 636 #endif 637 hammer2_tid_t best_tid = 0; 638 void *data; 639 off_t off; 640 int best; 641 int i; 642 int r; 643 644 /* 645 * Find the best volume header. 646 * 647 * WARNING BIOS BUGS: It looks like some BIOSes will implode when 648 * given a disk offset beyond the EOM. XXX We need to probe the 649 * size of the media and limit our accesses, until then we have 650 * to give up if the first volume header does not have a hammer2 651 * signature. 652 * 653 * XXX Probably still going to be problems w/ HAMMER2 volumes on 654 * media which is too small w/certain BIOSes. 655 */ 656 best = -1; 657 for (i = 0; i < HAMMER2_NUM_VOLHDRS; ++i) { 658 off = i * HAMMER2_ZONE_BYTES64; 659 if (i) 660 no_io_error = 1; 661 if (h2read(hfs, &media, sizeof(media.voldata), off)) 662 break; 663 if (media.voldata.magic != HAMMER2_VOLUME_ID_HBO) 664 break; 665 if (best < 0 || best_tid < media.voldata.mirror_tid) { 666 best = i; 667 best_tid = media.voldata.mirror_tid; 668 } 669 } 670 no_io_error = 0; 671 if (best < 0) 672 return(-1); 673 674 /* 675 * Reload the best volume header and set up the blockref. 676 * We messed with media, clear the cache before continuing. 677 */ 678 off = best * HAMMER2_ZONE_BYTES64; 679 if (h2read(hfs, &media, sizeof(media.voldata), off)) 680 return(-1); 681 hfs->sroot.type = HAMMER2_BREF_TYPE_VOLUME; 682 hfs->sroot.data_off = off; 683 hfs->sroot_blockset = media.voldata.sroot_blockset; 684 h2lookup(hfs, NULL, 0, 0, NULL, NULL); 685 686 /* 687 * Lookup sroot/BOOT and clear the cache again. 688 */ 689 r = h2lookup(hfs, &hfs->sroot, 690 HAMMER2_SROOT_KEY, HAMMER2_SROOT_KEY, 691 &hfs->sroot, &data); 692 if (r <= 0) 693 return(-1); 694 h2lookup(hfs, NULL, 0, 0, NULL, NULL); 695 r = h2lookup(hfs, &hfs->sroot, 696 HAMMER2_BOOT_KEY, HAMMER2_BOOT_KEY, 697 &hfs->sroot, &data); 698 if (r <= 0) { 699 printf("hammer2: 'BOOT' PFS not found\n"); 700 return(-1); 701 } 702 h2lookup(hfs, NULL, 0, 0, NULL, NULL); 703 704 return (0); 705 } 706 707 /************************************************************************ 708 * BOOT2 SUPPORT * 709 ************************************************************************ 710 * 711 */ 712 #ifdef BOOT2 713 714 static struct hammer2_fs hfs; 715 716 static int 717 boot2_hammer2_init(void) 718 { 719 if (h2init(&hfs)) 720 return(-1); 721 return(0); 722 } 723 724 static boot2_ino_t 725 boot2_hammer2_lookup(const char *path) 726 { 727 hammer2_blockref_t bref; 728 729 h2resolve(&hfs, path, &bref, NULL); 730 return ((boot2_ino_t)bref.data_off); 731 } 732 733 static ssize_t 734 boot2_hammer2_read(boot2_ino_t ino, void *buf, size_t len) 735 { 736 hammer2_blockref_t bref; 737 ssize_t total; 738 739 bzero(&bref, sizeof(bref)); 740 bref.type = HAMMER2_BREF_TYPE_INODE; 741 bref.data_off = ino; 742 743 total = h2readfile(&hfs, &bref, fs_off, 0x7FFFFFFF, buf, len); 744 if (total > 0) 745 fs_off += total; 746 return total; 747 } 748 749 const struct boot2_fsapi boot2_hammer2_api = { 750 .fsinit = boot2_hammer2_init, 751 .fslookup = boot2_hammer2_lookup, 752 .fsread = boot2_hammer2_read 753 }; 754 755 #endif 756 757 /************************************************************************ 758 * BOOT2 SUPPORT * 759 ************************************************************************ 760 * 761 */ 762 #ifdef LIBSTAND 763 764 struct hfile { 765 struct hammer2_fs hfs; 766 hammer2_blockref_t bref; 767 int64_t fsize; 768 uint32_t mode; 769 uint8_t type; 770 }; 771 772 static 773 int 774 hammer2_get_dtype(uint8_t type) 775 { 776 switch(type) { 777 case HAMMER2_OBJTYPE_DIRECTORY: 778 return(DT_DIR); 779 case HAMMER2_OBJTYPE_REGFILE: 780 return(DT_REG); 781 case HAMMER2_OBJTYPE_FIFO: 782 return(DT_FIFO); 783 case HAMMER2_OBJTYPE_CDEV: 784 return(DT_CHR); 785 case HAMMER2_OBJTYPE_BDEV: 786 return(DT_BLK); 787 case HAMMER2_OBJTYPE_SOFTLINK: 788 return(DT_LNK); 789 case HAMMER2_OBJTYPE_SOCKET: 790 return(DT_SOCK); 791 default: 792 return(DT_UNKNOWN); 793 } 794 } 795 796 static 797 mode_t 798 hammer2_get_mode(uint8_t type) 799 { 800 switch(type) { 801 case HAMMER2_OBJTYPE_DIRECTORY: 802 return(S_IFDIR); 803 case HAMMER2_OBJTYPE_REGFILE: 804 return(S_IFREG); 805 case HAMMER2_OBJTYPE_FIFO: 806 return(S_IFIFO); 807 case HAMMER2_OBJTYPE_CDEV: 808 return(S_IFCHR); 809 case HAMMER2_OBJTYPE_BDEV: 810 return(S_IFBLK); 811 case HAMMER2_OBJTYPE_SOFTLINK: 812 return(S_IFLNK); 813 case HAMMER2_OBJTYPE_SOCKET: 814 return(S_IFSOCK); 815 default: 816 return(0); 817 } 818 } 819 820 static int 821 hammer2_open(const char *path, struct open_file *f) 822 { 823 struct hfile *hf = malloc(sizeof(*hf)); 824 hammer2_inode_data_t *ipdata; 825 826 bzero(hf, sizeof(*hf)); 827 f->f_offset = 0; 828 f->f_fsdata = hf; 829 hf->hfs.f = f; 830 831 if (h2init(&hf->hfs)) { 832 f->f_fsdata = NULL; 833 free(hf); 834 errno = ENOENT; 835 return(-1); 836 } 837 h2resolve(&hf->hfs, path, &hf->bref, &ipdata); 838 if (hf->bref.data_off == (hammer2_off_t)-1 || 839 (hf->bref.type != HAMMER2_BREF_TYPE_INODE && 840 hf->bref.type != HAMMER2_BREF_TYPE_VOLUME)) { 841 f->f_fsdata = NULL; 842 free(hf); 843 errno = ENOENT; 844 return(-1); 845 } 846 if (ipdata) { 847 hf->fsize = ipdata->meta.size; 848 hf->type = ipdata->meta.type; 849 hf->mode = ipdata->meta.mode | 850 hammer2_get_mode(ipdata->meta.type); 851 } else { 852 hf->fsize = 0; 853 hf->type = HAMMER2_OBJTYPE_DIRECTORY; 854 hf->mode = 0755 | S_IFDIR; 855 } 856 return(0); 857 } 858 859 static int 860 hammer2_close(struct open_file *f) 861 { 862 struct hfile *hf = f->f_fsdata; 863 864 f->f_fsdata = NULL; 865 if (hf) 866 free(hf); 867 return (0); 868 } 869 870 static int 871 hammer2_read(struct open_file *f, void *buf, size_t len, size_t *resid) 872 { 873 struct hfile *hf = f->f_fsdata; 874 ssize_t total; 875 int rc = 0; 876 877 total = h2readfile(&hf->hfs, &hf->bref, 878 f->f_offset, hf->fsize, buf, len); 879 if (total < 0) { 880 rc = EIO; 881 total = 0; 882 } else { 883 f->f_offset += total; 884 rc = 0; 885 } 886 *resid = len - total; 887 return rc; 888 } 889 890 static off_t 891 hammer2_seek(struct open_file *f, off_t offset, int whence) 892 { 893 struct hfile *hf = f->f_fsdata; 894 895 switch (whence) { 896 case SEEK_SET: 897 f->f_offset = offset; 898 break; 899 case SEEK_CUR: 900 f->f_offset += offset; 901 break; 902 case SEEK_END: 903 f->f_offset = hf->fsize - offset; 904 break; 905 default: 906 return (-1); 907 } 908 return (f->f_offset); 909 } 910 911 static int 912 hammer2_stat(struct open_file *f, struct stat *st) 913 { 914 struct hfile *hf = f->f_fsdata; 915 916 st->st_mode = hf->mode; 917 st->st_uid = 0; 918 st->st_gid = 0; 919 st->st_size = hf->fsize; 920 921 return (0); 922 } 923 924 static int 925 hammer2_readdir(struct open_file *f, struct dirent *den) 926 { 927 struct hfile *hf = f->f_fsdata; 928 hammer2_blockref_t bres; 929 hammer2_inode_data_t *ipdata; 930 void *data; 931 int bytes; 932 933 for (;;) { 934 bytes = h2lookup(&hf->hfs, &hf->bref, 935 f->f_offset | HAMMER2_DIRHASH_VISIBLE, 936 HAMMER2_KEY_MAX, 937 &bres, (void **)&data); 938 if (bytes < 0) 939 break; 940 switch (bres.type) { 941 case HAMMER2_BREF_TYPE_INODE: 942 ipdata = data; 943 den->d_namlen = ipdata->meta.name_len; 944 den->d_type = hammer2_get_dtype(ipdata->meta.type); 945 den->d_ino = ipdata->meta.inum; 946 bcopy(ipdata->filename, den->d_name, den->d_namlen); 947 den->d_name[den->d_namlen] = 0; 948 break; 949 case HAMMER2_BREF_TYPE_DIRENT: 950 den->d_namlen = bres.embed.dirent.namlen; 951 den->d_type = hammer2_get_dtype(bres.embed.dirent.type); 952 den->d_ino = bres.embed.dirent.inum; 953 if (den->d_namlen <= sizeof(bres.check.buf)) { 954 bcopy(bres.check.buf, 955 den->d_name, 956 den->d_namlen); 957 } else { 958 bcopy(data, den->d_name, den->d_namlen); 959 } 960 den->d_name[den->d_namlen] = 0; 961 break; 962 default: 963 den->d_namlen = 1; 964 den->d_type = 965 hammer2_get_dtype(HAMMER2_OBJTYPE_REGFILE); 966 den->d_name[0] = '?'; 967 den->d_name[1] = 0; 968 break; 969 } 970 971 f->f_offset = bres.key + 1; 972 973 return(0); 974 } 975 return ENOENT; 976 } 977 978 struct fs_ops hammer2_fsops = { 979 "hammer2", 980 hammer2_open, 981 hammer2_close, 982 hammer2_read, 983 null_write, 984 hammer2_seek, 985 hammer2_stat, 986 hammer2_readdir 987 }; 988 989 #endif 990