1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Basic file system reading code for standalone I/O system. 31 * Simulates a primitive UNIX I/O system (read(), write(), open(), etc). 32 * Does not support writes. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/sysmacros.h> 37 #include <sys/vnode.h> 38 #include <sys/fs/ufs_fsdir.h> 39 #include <sys/fs/ufs_fs.h> 40 #include <sys/fs/ufs_inode.h> 41 42 #include <sys/fs/hsfs_spec.h> 43 #include <sys/fs/hsfs_isospec.h> 44 #include <sys/fs/hsfs_node.h> 45 #include <sys/fs/hsfs_susp.h> 46 #include <sys/fs/hsfs_rrip.h> 47 #include <sys/bootvfs.h> 48 #include <sys/filep.h> 49 50 #ifdef _BOOT 51 #include "../common/util.h" 52 #else 53 #include <sys/sunddi.h> 54 #endif 55 56 #define hdbtodb(n) ((ISO_SECTOR_SIZE / DEV_BSIZE) * (n)) 57 58 #define HSFS_NUM_SIG 14 59 60 #define SUSP_SP_IX 0 61 #define SUSP_CE_IX 1 62 #define SUSP_PD_IX 2 63 #define SUSP_ST_IX 3 64 #define SUSP_ER_IX 4 65 #define RRIP_PX_IX 5 66 #define RRIP_PN_IX 6 67 #define RRIP_SL_IX 7 68 #define RRIP_CL_IX 8 69 #define RRIP_PL_IX 9 70 #define RRIP_RE_IX 10 71 #define RRIP_RF_IX 11 72 #define RRIP_RR_IX 12 73 #define RRIP_NM_IX 13 74 75 #ifdef _BOOT 76 #define dprintf if (bootrd_debug) printf 77 #else 78 #define printf kobj_printf 79 #define dprintf if (bootrd_debug) kobj_printf 80 81 /* PRINTFLIKE1 */ 82 extern void kobj_printf(char *, ...); 83 #endif 84 85 extern int bootrd_debug; 86 extern void *bkmem_alloc(size_t); 87 extern void bkmem_free(void *, size_t); 88 89 struct dirstuff { 90 int loc; 91 fileid_t *filep; 92 }; 93 94 struct hs_direct { 95 struct direct hs_ufs_dir; 96 struct hs_direntry hs_dir; 97 }; 98 99 static uint_t root_ino = 0; 100 static struct hs_volume *hsfsp; 101 static fileid_t *head; 102 103 static char *hsfs_sig_tab[] = { 104 SUSP_SP, 105 SUSP_CE, 106 SUSP_PD, 107 SUSP_ST, 108 SUSP_ER, 109 RRIP_PX, 110 RRIP_PN, 111 RRIP_SL, 112 RRIP_CL, 113 RRIP_PL, 114 RRIP_RE, 115 RRIP_TF, 116 RRIP_RR, 117 RRIP_NM 118 }; 119 120 static int hsfs_num_sig = sizeof (hsfs_sig_tab) / sizeof (hsfs_sig_tab[0]); 121 122 /* 123 * Local prototypes 124 */ 125 static struct hs_direct *readdir(struct dirstuff *); 126 static uint_t parse_dir(fileid_t *, int, struct hs_direct *); 127 static uint_t parse_susp(char *, uint_t *, struct hs_direct *); 128 static ino_t dlook(char *, fileid_t *); 129 static int opendir(ino_t, fileid_t *); 130 static ino_t find(char *, fileid_t *); 131 132 static int bhsfs_mountroot(char *str); 133 static int bhsfs_unmountroot(void); 134 static int bhsfs_open(char *str, int flags); 135 static int bhsfs_close(int fd); 136 static void bhsfs_closeall(void); 137 static ssize_t bhsfs_read(int fdesc, char *buf, size_t count); 138 static off_t bhsfs_lseek(int fdesc, off_t addr, int whence); 139 140 static fileid_t * 141 find_fp(int fd) 142 { 143 fileid_t *filep = head; 144 145 if (fd >= 0) { 146 while ((filep = filep->fi_forw) != head) 147 if (fd == filep->fi_filedes) 148 return (filep->fi_taken ? filep : 0); 149 } 150 151 return (0); 152 } 153 154 static int 155 opendir(ino_t inode, fileid_t *filep) 156 { 157 struct hs_direct hsdep; 158 159 dprintf("opendir: inode = %ld\n", inode); 160 /* Set up the IO request */ 161 filep->fi_offset = 0; 162 filep->fi_blocknum = hdbtodb(inode); 163 filep->fi_count = ISO_SECTOR_SIZE; 164 filep->fi_memp = 0; 165 166 if (diskread(filep)) 167 return (0); 168 169 filep->fi_offset = 0; 170 filep->fi_blocknum = hdbtodb(inode); 171 172 if (inode != root_ino) 173 return (0); 174 175 if (parse_dir(filep, 0, &hsdep) > 0) { 176 struct inode *ip; 177 178 ip = filep->fi_inode; 179 if (ip == NULL) 180 ip = filep->fi_inode = bkmem_alloc(sizeof (*ip)); 181 182 ip->i_size = hsdep.hs_dir.ext_size; 183 ip->i_smode = hsdep.hs_dir.mode; 184 ip->i_number = inode; 185 return (0); 186 } 187 return (1); 188 } 189 190 static ino_t 191 find(char *path, fileid_t *filep) 192 { 193 char *q; 194 char c; 195 ino_t n; 196 197 dprintf("find: %s\n", path); 198 if (path == NULL || *path == '\0') 199 return (0); 200 201 if (opendir(root_ino, filep)) 202 return (0); 203 204 while (*path) { 205 while (*path == '/') 206 path++; 207 q = path; 208 while (*q != '/' && *q != '\0') 209 q++; 210 c = *q; 211 *q = '\0'; 212 n = dlook(path, filep); 213 *q = c; 214 path = q; 215 216 if (n != 0) { 217 if (c == '\0') 218 break; 219 if (opendir(n, filep)) 220 return (0); 221 continue; 222 } else { 223 return (0); 224 } 225 } 226 return ((ino_t)n); 227 } 228 229 static ino_t 230 dlook(char *s, fileid_t *filep) 231 { 232 struct hs_direct *hsdep; 233 struct direct *udp; 234 struct inode *ip; 235 struct dirstuff dirp; 236 int len; 237 238 dprintf("dlook: %s\n", s); 239 ip = filep->fi_inode; 240 if (s == NULL || *s == '\0') 241 return (0); 242 if ((ip->i_smode & IFMT) != IFDIR) { 243 return (0); 244 } 245 if (ip->i_size == 0) { 246 return (0); 247 } 248 len = strlen(s); 249 dirp.loc = 0; 250 dirp.filep = filep; 251 for (hsdep = readdir(&dirp); hsdep != NULL; hsdep = readdir(&dirp)) { 252 udp = &hsdep->hs_ufs_dir; 253 if (udp->d_namlen == 1 && 254 udp->d_name[0] == '.' && 255 udp->d_name[1] == '\0') 256 continue; 257 if (udp->d_namlen == 2 && 258 udp->d_name[0] == '.' && 259 udp->d_name[1] == '.' && 260 udp->d_name[2] == '\0') 261 continue; 262 if (udp->d_namlen == len && (strcmp(s, udp->d_name)) == 0) { 263 struct inode *ip = filep->fi_inode; 264 265 filep->fi_offset = 0; 266 filep->fi_blocknum = hdbtodb(udp->d_ino); 267 268 bzero(filep->fi_inode, sizeof (struct inode)); 269 ip->i_size = hsdep->hs_dir.ext_size; 270 ip->i_smode = hsdep->hs_dir.mode; 271 ip->i_number = udp->d_ino; 272 return (udp->d_ino); 273 } 274 } 275 return (0); 276 } 277 278 /* 279 * get next entry in a directory. 280 */ 281 static struct hs_direct * 282 readdir(struct dirstuff *dirp) 283 { 284 static struct hs_direct hsdep; 285 struct direct *udp = &hsdep.hs_ufs_dir; 286 struct inode *ip; 287 fileid_t *filep; 288 daddr_t lbn; 289 int off; 290 291 dprintf("readdir: start\n"); 292 filep = dirp->filep; 293 ip = filep->fi_inode; 294 for (;;) { 295 if (dirp->loc >= ip->i_size) { 296 return (NULL); 297 } 298 off = dirp->loc & ((1 << ISO_SECTOR_SHIFT) - 1); 299 if (off == 0) { 300 lbn = hdbtodb(dirp->loc >> ISO_SECTOR_SHIFT); 301 filep->fi_blocknum = lbn + hdbtodb(ip->i_number); 302 filep->fi_count = ISO_SECTOR_SIZE; 303 filep->fi_memp = 0; 304 if (diskread(filep)) { 305 dprintf("readdir: diskread failed\n"); 306 return (NULL); 307 } 308 } 309 dirp->loc += parse_dir(filep, off, &hsdep); 310 if (udp->d_reclen == 0 && dirp->loc <= ip->i_size) { 311 dirp->loc = roundup(dirp->loc, ISO_SECTOR_SIZE); 312 continue; 313 } 314 return (&hsdep); 315 } 316 } 317 318 static int 319 getblock(fileid_t *filep) 320 { 321 struct inode *ip = filep->fi_inode; 322 int off, size, diff; 323 daddr_t lbn; 324 325 dprintf("getblock: start\n"); 326 diff = ip->i_size - filep->fi_offset; 327 if (diff <= 0) 328 return (-1); 329 330 /* which block (or frag) in the file do we read? */ 331 lbn = hdbtodb(filep->fi_offset >> ISO_SECTOR_SHIFT); 332 filep->fi_blocknum = lbn + hdbtodb(ip->i_number); 333 334 off = filep->fi_offset & ((1 << ISO_SECTOR_SHIFT) - 1); 335 size = filep->fi_count = ISO_SECTOR_SIZE; 336 filep->fi_memp = 0; 337 if (diskread(filep)) /* Trap errors */ 338 return (-1); 339 340 if (filep->fi_offset - off + size >= ip->i_size) 341 filep->fi_count = diff + off; 342 filep->fi_count -= off; 343 filep->fi_memp += off; 344 dprintf("getblock: end\n"); 345 return (0); 346 } 347 348 static ssize_t 349 bhsfs_read(int fd, caddr_t buf, size_t count) 350 { 351 int i, j; 352 fileid_t *filep; 353 struct inode *ip; 354 caddr_t n; 355 356 dprintf("bhsfs_read %d, count 0x%lx\n", fd, count); 357 filep = find_fp(fd); 358 if (filep == NULL) 359 return (-1); 360 361 ip = filep->fi_inode; 362 n = buf; 363 if (filep->fi_offset + count > ip->i_size) 364 count = ip->i_size - filep->fi_offset; 365 366 if ((i = count) <= 0) 367 return (0); 368 369 while (i > 0) { 370 if (filep->fi_count == 0) { 371 if (getblock(filep) == -1) 372 return (0); 373 } 374 j = MIN(i, filep->fi_count); 375 bcopy(filep->fi_memp, buf, (uint_t)j); 376 buf += j; 377 filep->fi_memp += j; 378 filep->fi_offset += j; 379 filep->fi_count -= j; 380 i -= j; 381 } 382 383 dprintf("bhsfs_read: read 0x%x\n", (int)(buf - n)); 384 return (buf - n); 385 } 386 387 /*ARGSUSED*/ 388 static int 389 bhsfs_mountroot(char *str) 390 { 391 char *bufp; 392 393 if (hsfsp != NULL) 394 return (0); /* already mounted */ 395 396 dprintf("mounting ramdisk as hsfs\n"); 397 398 hsfsp = bkmem_alloc(sizeof (*hsfsp)); 399 bzero(hsfsp, sizeof (*hsfsp)); 400 head = bkmem_alloc(sizeof (*head)); 401 bzero(head, sizeof (*head)); 402 head->fi_back = head->fi_forw = head; 403 404 /* now read the superblock. */ 405 head->fi_blocknum = hdbtodb(ISO_VOLDESC_SEC); 406 head->fi_offset = 0; 407 head->fi_count = ISO_SECTOR_SIZE; 408 head->fi_memp = head->fi_buf; 409 if (diskread(head)) { 410 printf("failed to read superblock\n"); 411 bhsfs_closeall(); 412 return (-1); 413 } 414 415 /* Since RRIP is based on ISO9660, that's where we start */ 416 bufp = head->fi_buf; 417 if ((ISO_DESC_TYPE(bufp) != ISO_VD_PVD) || 418 (strncmp((const char *)ISO_std_id(bufp), ISO_ID_STRING, 419 ISO_ID_STRLEN) != 0) || (ISO_STD_VER(bufp) != ISO_ID_VER)) { 420 dprintf("volume type does not match\n"); 421 bhsfs_closeall(); 422 return (-1); 423 } 424 425 /* Now we fill in the volume descriptor */ 426 hsfsp->vol_size = ISO_VOL_SIZE(bufp); 427 hsfsp->lbn_size = ISO_BLK_SIZE(bufp); 428 hsfsp->lbn_shift = ISO_SECTOR_SHIFT; 429 hsfsp->lbn_secshift = ISO_SECTOR_SHIFT; 430 hsfsp->vol_set_size = (ushort_t)ISO_SET_SIZE(bufp); 431 hsfsp->vol_set_seq = (ushort_t)ISO_SET_SEQ(bufp); 432 433 /* Make sure we have a valid logical block size */ 434 if (hsfsp->lbn_size & ~(1 << hsfsp->lbn_shift)) { 435 printf("%d invalid logical block size\n", hsfsp->lbn_size); 436 bhsfs_closeall(); 437 return (-1); 438 } 439 440 /* Since an HSFS root could be located anywhere on the media! */ 441 root_ino = IDE_EXT_LBN(ISO_root_dir(bufp)); 442 return (0); 443 } 444 445 static int 446 bhsfs_unmountroot(void) 447 { 448 if (hsfsp == NULL) 449 return (-1); 450 451 bhsfs_closeall(); 452 453 return (0); 454 } 455 456 /* 457 * Open a file. 458 */ 459 /*ARGSUSED*/ 460 int 461 bhsfs_open(char *str, int flags) 462 { 463 static int filedes = 1; 464 465 fileid_t *filep; 466 ino_t ino; 467 468 dprintf("open %s\n", str); 469 filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t)); 470 filep->fi_back = head->fi_back; 471 filep->fi_forw = head; 472 head->fi_back->fi_forw = filep; 473 head->fi_back = filep; 474 filep->fi_filedes = filedes++; 475 filep->fi_taken = 1; 476 filep->fi_path = (char *)bkmem_alloc(strlen(str) + 1); 477 (void) strcpy(filep->fi_path, str); 478 filep->fi_inode = NULL; 479 bzero(filep->fi_buf, MAXBSIZE); 480 481 ino = find(str, filep); 482 if (ino == 0) { 483 (void) bhsfs_close(filep->fi_filedes); 484 return (-1); 485 } 486 487 filep->fi_blocknum = hdbtodb(ino); 488 filep->fi_offset = 0; 489 filep->fi_count = 0; 490 filep->fi_memp = 0; 491 492 dprintf("open done\n"); 493 return (filep->fi_filedes); 494 } 495 496 int 497 bhsfs_close(int fd) 498 { 499 fileid_t *filep; 500 501 dprintf("close %d\n", fd); 502 if (!(filep = find_fp(fd))) 503 return (-1); 504 505 if (filep->fi_taken == 0 || filep == head) { 506 printf("File descripter %d no allocated!\n", fd); 507 return (-1); 508 } 509 510 /* unlink and deallocate node */ 511 filep->fi_forw->fi_back = filep->fi_back; 512 filep->fi_back->fi_forw = filep->fi_forw; 513 if (filep->fi_inode) 514 bkmem_free(filep->fi_inode, sizeof (struct inode)); 515 bkmem_free(filep->fi_path, strlen(filep->fi_path) + 1); 516 bkmem_free((char *)filep, sizeof (fileid_t)); 517 dprintf("close done\n"); 518 return (0); 519 } 520 521 static void 522 bhsfs_closeall(void) 523 { 524 fileid_t *filep; 525 526 while ((filep = head->fi_forw) != head) 527 if (filep->fi_taken && bhsfs_close(filep->fi_filedes)) 528 printf("Filesystem may be inconsistent.\n"); 529 530 bkmem_free(hsfsp, sizeof (*hsfsp)); 531 bkmem_free(head, sizeof (fileid_t)); 532 hsfsp = NULL; 533 head = NULL; 534 } 535 536 /* 537 * This version of seek() only performs absolute seeks (whence == 0). 538 */ 539 static off_t 540 bhsfs_lseek(int fd, off_t addr, int whence) 541 { 542 fileid_t *filep; 543 544 dprintf("lseek %d, off = %lx\n", fd, addr); 545 if (!(filep = find_fp(fd))) 546 return (-1); 547 548 switch (whence) { 549 case SEEK_CUR: 550 filep->fi_offset += addr; 551 break; 552 case SEEK_SET: 553 filep->fi_offset = addr; 554 break; 555 default: 556 case SEEK_END: 557 printf("lseek(): invalid whence value %d\n", whence); 558 break; 559 } 560 561 filep->fi_blocknum = addr / DEV_BSIZE; 562 filep->fi_count = 0; 563 return (0); 564 } 565 566 /* 567 * Parse a directory entry. 568 * 569 */ 570 static uint_t 571 parse_dir(fileid_t *filep, int offset, struct hs_direct *hsdep) 572 { 573 char *bufp = (char *)(filep->fi_memp + offset); 574 struct direct *udp = &hsdep->hs_ufs_dir; /* ufs-style dir info */ 575 struct hs_direntry *hdp = &hsdep->hs_dir; /* hsfs-style dir info */ 576 uint_t ce_lbn; 577 uint_t ce_len; 578 uint_t nmlen; 579 uint_t i; 580 uchar_t c; 581 582 dprintf("parse_dir: offset = %d\n", offset); 583 /* a zero length dir entry terminates the dir block */ 584 udp->d_reclen = IDE_DIR_LEN(bufp); 585 if (udp->d_reclen == 0) 586 return (0); 587 588 /* fill in some basic hsfs info */ 589 hdp->ext_lbn = IDE_EXT_LBN(bufp); 590 hdp->ext_size = IDE_EXT_SIZE(bufp); 591 hdp->xar_len = IDE_XAR_LEN(bufp); 592 hdp->intlf_sz = IDE_INTRLV_SIZE(bufp); 593 hdp->intlf_sk = IDE_INTRLV_SKIP(bufp); 594 hdp->sym_link = NULL; 595 596 /* we use lbn of data extent as an inode # equivalent */ 597 udp->d_ino = hdp->ext_lbn; 598 599 c = IDE_FLAGS(bufp); 600 if (IDE_REGULAR_FILE(c)) { 601 hdp->type = VREG; 602 hdp->mode = IFREG; 603 hdp->nlink = 1; 604 } else if (IDE_REGULAR_DIR(c)) { 605 hdp->type = VDIR; 606 hdp->mode = IFDIR; 607 hdp->nlink = 2; 608 } else { 609 printf("pd(): file type=0x%x unknown.\n", c); 610 } 611 612 /* 613 * Massage hsfs name, recognizing special entries for . and .. 614 * else lopping off version junk. 615 */ 616 617 /* Some initial conditions */ 618 nmlen = IDE_NAME_LEN(bufp); 619 c = *IDE_NAME(bufp); 620 /* Special Case: Current Directory */ 621 if (nmlen == 1 && c == '\0') { 622 udp->d_name[0] = '.'; 623 udp->d_name[1] = '\0'; 624 udp->d_namlen = 1; 625 /* Special Case: Parent Directory */ 626 } else if (nmlen == 1 && c == '\001') { 627 udp->d_name[0] = '.'; 628 udp->d_name[1] = '.'; 629 udp->d_name[2] = '\0'; 630 udp->d_namlen = 2; 631 /* Other file name */ 632 } else { 633 udp->d_namlen = 0; 634 for (i = 0; i < nmlen; i++) { 635 c = *(IDE_name(bufp)+i); 636 if (c == ';') 637 break; 638 else if (c == ' ') 639 continue; 640 else 641 udp->d_name[udp->d_namlen++] = c; 642 } 643 udp->d_name[udp->d_namlen] = '\0'; 644 } 645 646 /* System Use Fields */ 647 ce_len = IDE_SUA_LEN(bufp); 648 649 if (ce_len == 0) 650 return (udp->d_reclen); 651 652 /* there is an SUA for this dir entry; go parse it */ 653 ce_lbn = parse_susp((char *)IDE_sys_use_area(bufp), &ce_len, hsdep); 654 655 if (ce_lbn) { 656 /* 657 * store away current position in dir, 658 * as we will be using the iobuf to reading SUA. 659 */ 660 daddr_t save_bn = filep->fi_blocknum; 661 daddr_t save_offset = filep->fi_offset; 662 caddr_t save_ma = filep->fi_memp; 663 int save_cc = filep->fi_count; 664 do { 665 filep->fi_count = ISO_SECTOR_SIZE; 666 filep->fi_offset = 0; 667 filep->fi_blocknum = hdbtodb(ce_lbn); 668 filep->fi_memp = 0; 669 if (diskread(filep)) { 670 printf("failed to read cont. area\n"); 671 ce_len = 0; 672 ce_lbn = 0; 673 break; 674 } 675 ce_lbn = parse_susp(filep->fi_memp, &ce_len, 676 hsdep); 677 } while (ce_lbn); 678 filep->fi_count = save_cc; 679 filep->fi_offset = save_offset; 680 filep->fi_blocknum = save_bn; 681 filep->fi_memp = save_ma; 682 } 683 return (udp->d_reclen); 684 } 685 686 /* 687 * Parse the System Use Fields in this System Use Area. 688 * Return blk number of continuation/SUA, or 0 if no continuation/not a SUA. 689 */ 690 static uint_t 691 parse_susp(char *bufp, uint_t *len, struct hs_direct *hsdep) 692 { 693 struct direct *udp = &hsdep->hs_ufs_dir; /* ufs-style info */ 694 char *susp; 695 uint_t cur_off = 0; 696 uint_t blk_len = *len; 697 uint_t susp_len = 0; 698 uint_t ce_lbn = 0; 699 uint_t i; 700 701 dprintf("parse_susp: len = %d\n", *len); 702 while (cur_off < blk_len) { 703 susp = (char *)(bufp + cur_off); 704 705 /* 706 * A null entry, or an entry with zero length 707 * terminates the SUSP. 708 */ 709 if (susp[0] == '\0' || susp[1] == '\0' || 710 (susp_len = SUF_LEN(susp)) == 0) 711 break; 712 713 /* 714 * Compare current entry to all known signatures. 715 */ 716 for (i = 0; i < hsfs_num_sig; i++) 717 if (strncmp(hsfs_sig_tab[i], susp, SUF_SIG_LEN) == 0) 718 break; 719 switch (i) { 720 case SUSP_CE_IX: 721 /* 722 * CE signature: continuation of SUSP. 723 * will want to return new lbn, len. 724 */ 725 ce_lbn = CE_BLK_LOC(susp); 726 *len = CE_CONT_LEN(susp); 727 break; 728 case RRIP_NM_IX: 729 /* NM signature: POSIX-style file name */ 730 if (!RRIP_NAME_FLAGS(susp)) { 731 udp->d_namlen = RRIP_NAME_LEN(susp); 732 bcopy((char *)RRIP_name(susp), 733 udp->d_name, udp->d_namlen); 734 udp->d_name[udp->d_namlen] = '\0'; 735 } 736 break; 737 case HSFS_NUM_SIG: 738 /* couldn't find a legit susp, terminate loop */ 739 case SUSP_ST_IX: 740 /* ST signature: terminates SUSP */ 741 return (ce_lbn); 742 case SUSP_SP_IX: 743 case RRIP_RR_IX: 744 default: 745 break; 746 } 747 cur_off += susp_len; 748 } 749 return (ce_lbn); 750 } 751 752 struct boot_fs_ops bhsfs_ops = { 753 "boot_hsfs", 754 bhsfs_mountroot, 755 bhsfs_unmountroot, 756 bhsfs_open, 757 bhsfs_close, 758 bhsfs_read, 759 bhsfs_lseek, 760 NULL 761 }; 762