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 213 if ((n = dlook(path, filep)) != 0) { 214 if (c == '\0') 215 break; 216 if (opendir(n, filep)) 217 return (0); 218 *q = c; 219 path = q; 220 continue; 221 } else { 222 return (0); 223 } 224 } 225 return ((ino_t)n); 226 } 227 228 static ino_t 229 dlook(char *s, fileid_t *filep) 230 { 231 struct hs_direct *hsdep; 232 struct direct *udp; 233 struct inode *ip; 234 struct dirstuff dirp; 235 int len; 236 237 dprintf("dlook: %s\n", s); 238 ip = filep->fi_inode; 239 if (s == NULL || *s == '\0') 240 return (0); 241 if ((ip->i_smode & IFMT) != IFDIR) { 242 return (0); 243 } 244 if (ip->i_size == 0) { 245 return (0); 246 } 247 len = strlen(s); 248 dirp.loc = 0; 249 dirp.filep = filep; 250 for (hsdep = readdir(&dirp); hsdep != NULL; hsdep = readdir(&dirp)) { 251 udp = &hsdep->hs_ufs_dir; 252 if (udp->d_namlen == 1 && 253 udp->d_name[0] == '.' && 254 udp->d_name[1] == '\0') 255 continue; 256 if (udp->d_namlen == 2 && 257 udp->d_name[0] == '.' && 258 udp->d_name[1] == '.' && 259 udp->d_name[2] == '\0') 260 continue; 261 if (udp->d_namlen == len && (strcmp(s, udp->d_name)) == 0) { 262 struct inode *ip = filep->fi_inode; 263 264 filep->fi_offset = 0; 265 filep->fi_blocknum = hdbtodb(udp->d_ino); 266 267 bzero(filep->fi_inode, sizeof (struct inode)); 268 ip->i_size = hsdep->hs_dir.ext_size; 269 ip->i_smode = hsdep->hs_dir.mode; 270 ip->i_number = udp->d_ino; 271 return (udp->d_ino); 272 } 273 } 274 return (0); 275 } 276 277 /* 278 * get next entry in a directory. 279 */ 280 static struct hs_direct * 281 readdir(struct dirstuff *dirp) 282 { 283 static struct hs_direct hsdep; 284 struct direct *udp = &hsdep.hs_ufs_dir; 285 struct inode *ip; 286 fileid_t *filep; 287 daddr_t lbn; 288 int off; 289 290 dprintf("readdir: start\n"); 291 filep = dirp->filep; 292 ip = filep->fi_inode; 293 for (;;) { 294 if (dirp->loc >= ip->i_size) { 295 return (NULL); 296 } 297 off = dirp->loc & ((1 << ISO_SECTOR_SHIFT) - 1); 298 if (off == 0) { 299 lbn = hdbtodb(dirp->loc >> ISO_SECTOR_SHIFT); 300 filep->fi_blocknum = lbn + hdbtodb(ip->i_number); 301 filep->fi_count = ISO_SECTOR_SIZE; 302 filep->fi_memp = 0; 303 if (diskread(filep)) { 304 dprintf("readdir: diskread failed\n"); 305 return (NULL); 306 } 307 } 308 dirp->loc += parse_dir(filep, off, &hsdep); 309 if (udp->d_reclen == 0 && dirp->loc <= ip->i_size) { 310 dirp->loc = roundup(dirp->loc, ISO_SECTOR_SIZE); 311 continue; 312 } 313 return (&hsdep); 314 } 315 } 316 317 static int 318 getblock(fileid_t *filep) 319 { 320 struct inode *ip = filep->fi_inode; 321 int off, size, diff; 322 daddr_t lbn; 323 324 dprintf("getblock: start\n"); 325 diff = ip->i_size - filep->fi_offset; 326 if (diff <= 0) 327 return (-1); 328 329 /* which block (or frag) in the file do we read? */ 330 lbn = hdbtodb(filep->fi_offset >> ISO_SECTOR_SHIFT); 331 filep->fi_blocknum = lbn + hdbtodb(ip->i_number); 332 333 off = filep->fi_offset & ((1 << ISO_SECTOR_SHIFT) - 1); 334 size = filep->fi_count = ISO_SECTOR_SIZE; 335 filep->fi_memp = 0; 336 if (diskread(filep)) /* Trap errors */ 337 return (-1); 338 339 if (filep->fi_offset - off + size >= ip->i_size) 340 filep->fi_count = diff + off; 341 filep->fi_count -= off; 342 filep->fi_memp += off; 343 dprintf("getblock: end\n"); 344 return (0); 345 } 346 347 static ssize_t 348 bhsfs_read(int fd, caddr_t buf, size_t count) 349 { 350 int i, j; 351 fileid_t *filep; 352 struct inode *ip; 353 caddr_t n; 354 355 dprintf("bhsfs_read %d, count 0x%lx\n", fd, count); 356 filep = find_fp(fd); 357 if (filep == NULL) 358 return (-1); 359 360 ip = filep->fi_inode; 361 n = buf; 362 if (filep->fi_offset + count > ip->i_size) 363 count = ip->i_size - filep->fi_offset; 364 365 if ((i = count) <= 0) 366 return (0); 367 368 while (i > 0) { 369 if (filep->fi_count == 0) { 370 if (getblock(filep) == -1) 371 return (0); 372 } 373 j = MIN(i, filep->fi_count); 374 bcopy(filep->fi_memp, buf, (uint_t)j); 375 buf += j; 376 filep->fi_memp += j; 377 filep->fi_offset += j; 378 filep->fi_count -= j; 379 i -= j; 380 } 381 382 dprintf("bhsfs_read: read 0x%x\n", (int)(buf - n)); 383 return (buf - n); 384 } 385 386 /*ARGSUSED*/ 387 static int 388 bhsfs_mountroot(char *str) 389 { 390 char *bufp; 391 392 if (hsfsp != NULL) 393 return (0); /* already mounted */ 394 395 dprintf("mounting ramdisk as hsfs\n"); 396 397 hsfsp = bkmem_alloc(sizeof (*hsfsp)); 398 bzero(hsfsp, sizeof (*hsfsp)); 399 head = bkmem_alloc(sizeof (*head)); 400 bzero(head, sizeof (*head)); 401 head->fi_back = head->fi_forw = head; 402 403 /* now read the superblock. */ 404 head->fi_blocknum = hdbtodb(ISO_VOLDESC_SEC); 405 head->fi_offset = 0; 406 head->fi_count = ISO_SECTOR_SIZE; 407 head->fi_memp = head->fi_buf; 408 if (diskread(head)) { 409 printf("failed to read superblock\n"); 410 bhsfs_closeall(); 411 return (-1); 412 } 413 414 /* Since RRIP is based on ISO9660, that's where we start */ 415 bufp = head->fi_buf; 416 if ((ISO_DESC_TYPE(bufp) != ISO_VD_PVD) || 417 (strncmp((const char *)ISO_std_id(bufp), ISO_ID_STRING, 418 ISO_ID_STRLEN) != 0) || (ISO_STD_VER(bufp) != ISO_ID_VER)) { 419 dprintf("volume type does not match\n"); 420 bhsfs_closeall(); 421 return (-1); 422 } 423 424 /* Now we fill in the volume descriptor */ 425 hsfsp->vol_size = ISO_VOL_SIZE(bufp); 426 hsfsp->lbn_size = ISO_BLK_SIZE(bufp); 427 hsfsp->lbn_shift = ISO_SECTOR_SHIFT; 428 hsfsp->lbn_secshift = ISO_SECTOR_SHIFT; 429 hsfsp->vol_set_size = (ushort_t)ISO_SET_SIZE(bufp); 430 hsfsp->vol_set_seq = (ushort_t)ISO_SET_SEQ(bufp); 431 432 /* Make sure we have a valid logical block size */ 433 if (hsfsp->lbn_size & ~(1 << hsfsp->lbn_shift)) { 434 printf("%d invalid logical block size\n", hsfsp->lbn_size); 435 bhsfs_closeall(); 436 return (-1); 437 } 438 439 /* Since an HSFS root could be located anywhere on the media! */ 440 root_ino = IDE_EXT_LBN(ISO_root_dir(bufp)); 441 return (0); 442 } 443 444 static int 445 bhsfs_unmountroot(void) 446 { 447 if (hsfsp == NULL) 448 return (-1); 449 450 bhsfs_closeall(); 451 452 return (0); 453 } 454 455 /* 456 * Open a file. 457 */ 458 /*ARGSUSED*/ 459 int 460 bhsfs_open(char *str, int flags) 461 { 462 static int filedes = 1; 463 464 fileid_t *filep; 465 ino_t ino; 466 467 dprintf("open %s\n", str); 468 filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t)); 469 filep->fi_back = head->fi_back; 470 filep->fi_forw = head; 471 head->fi_back->fi_forw = filep; 472 head->fi_back = filep; 473 filep->fi_filedes = filedes++; 474 filep->fi_taken = 1; 475 filep->fi_path = (char *)bkmem_alloc(strlen(str) + 1); 476 (void) strcpy(filep->fi_path, str); 477 filep->fi_inode = NULL; 478 bzero(filep->fi_buf, MAXBSIZE); 479 480 ino = find(str, filep); 481 if (ino == 0) { 482 (void) bhsfs_close(filep->fi_filedes); 483 return (-1); 484 } 485 486 filep->fi_blocknum = hdbtodb(ino); 487 filep->fi_offset = 0; 488 filep->fi_count = 0; 489 filep->fi_memp = 0; 490 491 dprintf("open done\n"); 492 return (filep->fi_filedes); 493 } 494 495 int 496 bhsfs_close(int fd) 497 { 498 fileid_t *filep; 499 500 dprintf("close %d\n", fd); 501 if (!(filep = find_fp(fd))) 502 return (-1); 503 504 if (filep->fi_taken == 0 || filep == head) { 505 printf("File descripter %d no allocated!\n", fd); 506 return (-1); 507 } 508 509 /* unlink and deallocate node */ 510 filep->fi_forw->fi_back = filep->fi_back; 511 filep->fi_back->fi_forw = filep->fi_forw; 512 if (filep->fi_inode) 513 bkmem_free(filep->fi_inode, sizeof (struct inode)); 514 bkmem_free(filep->fi_path, strlen(filep->fi_path) + 1); 515 bkmem_free((char *)filep, sizeof (fileid_t)); 516 dprintf("close done\n"); 517 return (0); 518 } 519 520 static void 521 bhsfs_closeall(void) 522 { 523 fileid_t *filep; 524 525 while ((filep = head->fi_forw) != head) 526 if (filep->fi_taken && bhsfs_close(filep->fi_filedes)) 527 printf("Filesystem may be inconsistent.\n"); 528 529 bkmem_free(hsfsp, sizeof (*hsfsp)); 530 bkmem_free(head, sizeof (fileid_t)); 531 hsfsp = NULL; 532 head = NULL; 533 } 534 535 /* 536 * This version of seek() only performs absolute seeks (whence == 0). 537 */ 538 static off_t 539 bhsfs_lseek(int fd, off_t addr, int whence) 540 { 541 fileid_t *filep; 542 543 dprintf("lseek %d, off = %lx\n", fd, addr); 544 if (!(filep = find_fp(fd))) 545 return (-1); 546 547 switch (whence) { 548 case SEEK_CUR: 549 filep->fi_offset += addr; 550 break; 551 case SEEK_SET: 552 filep->fi_offset = addr; 553 break; 554 default: 555 case SEEK_END: 556 printf("lseek(): invalid whence value %d\n", whence); 557 break; 558 } 559 560 filep->fi_blocknum = addr / DEV_BSIZE; 561 filep->fi_count = 0; 562 return (0); 563 } 564 565 /* 566 * Parse a directory entry. 567 * 568 */ 569 static uint_t 570 parse_dir(fileid_t *filep, int offset, struct hs_direct *hsdep) 571 { 572 char *bufp = (char *)(filep->fi_memp + offset); 573 struct direct *udp = &hsdep->hs_ufs_dir; /* ufs-style dir info */ 574 struct hs_direntry *hdp = &hsdep->hs_dir; /* hsfs-style dir info */ 575 uint_t ce_lbn; 576 uint_t ce_len; 577 uint_t nmlen; 578 uint_t i; 579 uchar_t c; 580 581 dprintf("parse_dir: offset = %d\n", offset); 582 /* a zero length dir entry terminates the dir block */ 583 udp->d_reclen = IDE_DIR_LEN(bufp); 584 if (udp->d_reclen == 0) 585 return (0); 586 587 /* fill in some basic hsfs info */ 588 hdp->ext_lbn = IDE_EXT_LBN(bufp); 589 hdp->ext_size = IDE_EXT_SIZE(bufp); 590 hdp->xar_len = IDE_XAR_LEN(bufp); 591 hdp->intlf_sz = IDE_INTRLV_SIZE(bufp); 592 hdp->intlf_sk = IDE_INTRLV_SKIP(bufp); 593 hdp->sym_link = NULL; 594 595 /* we use lbn of data extent as an inode # equivalent */ 596 udp->d_ino = hdp->ext_lbn; 597 598 c = IDE_FLAGS(bufp); 599 if (IDE_REGULAR_FILE(c)) { 600 hdp->type = VREG; 601 hdp->mode = IFREG; 602 hdp->nlink = 1; 603 } else if (IDE_REGULAR_DIR(c)) { 604 hdp->type = VDIR; 605 hdp->mode = IFDIR; 606 hdp->nlink = 2; 607 } else { 608 printf("pd(): file type=0x%x unknown.\n", c); 609 } 610 611 /* 612 * Massage hsfs name, recognizing special entries for . and .. 613 * else lopping off version junk. 614 */ 615 616 /* Some initial conditions */ 617 nmlen = IDE_NAME_LEN(bufp); 618 c = *IDE_NAME(bufp); 619 /* Special Case: Current Directory */ 620 if (nmlen == 1 && c == '\0') { 621 udp->d_name[0] = '.'; 622 udp->d_name[1] = '\0'; 623 udp->d_namlen = 1; 624 /* Special Case: Parent Directory */ 625 } else if (nmlen == 1 && c == '\001') { 626 udp->d_name[0] = '.'; 627 udp->d_name[1] = '.'; 628 udp->d_name[2] = '\0'; 629 udp->d_namlen = 2; 630 /* Other file name */ 631 } else { 632 udp->d_namlen = 0; 633 for (i = 0; i < nmlen; i++) { 634 c = *(IDE_name(bufp)+i); 635 if (c == ';') 636 break; 637 else if (c == ' ') 638 continue; 639 else 640 udp->d_name[udp->d_namlen++] = c; 641 } 642 udp->d_name[udp->d_namlen] = '\0'; 643 } 644 645 /* System Use Fields */ 646 ce_len = IDE_SUA_LEN(bufp); 647 648 if (ce_len == 0) 649 return (udp->d_reclen); 650 651 /* there is an SUA for this dir entry; go parse it */ 652 ce_lbn = parse_susp((char *)IDE_sys_use_area(bufp), &ce_len, hsdep); 653 654 if (ce_lbn) { 655 /* 656 * store away current position in dir, 657 * as we will be using the iobuf to reading SUA. 658 */ 659 daddr_t save_bn = filep->fi_blocknum; 660 daddr_t save_offset = filep->fi_offset; 661 caddr_t save_ma = filep->fi_memp; 662 int save_cc = filep->fi_count; 663 do { 664 filep->fi_count = ISO_SECTOR_SIZE; 665 filep->fi_offset = 0; 666 filep->fi_blocknum = hdbtodb(ce_lbn); 667 filep->fi_memp = 0; 668 if (diskread(filep)) { 669 printf("failed to read cont. area\n"); 670 ce_len = 0; 671 ce_lbn = 0; 672 break; 673 } 674 ce_lbn = parse_susp(filep->fi_memp, &ce_len, 675 hsdep); 676 } while (ce_lbn); 677 filep->fi_count = save_cc; 678 filep->fi_offset = save_offset; 679 filep->fi_blocknum = save_bn; 680 filep->fi_memp = save_ma; 681 } 682 return (udp->d_reclen); 683 } 684 685 /* 686 * Parse the System Use Fields in this System Use Area. 687 * Return blk number of continuation/SUA, or 0 if no continuation/not a SUA. 688 */ 689 static uint_t 690 parse_susp(char *bufp, uint_t *len, struct hs_direct *hsdep) 691 { 692 struct direct *udp = &hsdep->hs_ufs_dir; /* ufs-style info */ 693 char *susp; 694 uint_t cur_off = 0; 695 uint_t blk_len = *len; 696 uint_t susp_len = 0; 697 uint_t ce_lbn = 0; 698 uint_t i; 699 700 dprintf("parse_susp: len = %d\n", *len); 701 while (cur_off < blk_len) { 702 susp = (char *)(bufp + cur_off); 703 704 /* 705 * A null entry, or an entry with zero length 706 * terminates the SUSP. 707 */ 708 if (susp[0] == '\0' || susp[1] == '\0' || 709 (susp_len = SUF_LEN(susp)) == 0) 710 break; 711 712 /* 713 * Compare current entry to all known signatures. 714 */ 715 for (i = 0; i < hsfs_num_sig; i++) 716 if (strncmp(hsfs_sig_tab[i], susp, SUF_SIG_LEN) == 0) 717 break; 718 switch (i) { 719 case SUSP_CE_IX: 720 /* 721 * CE signature: continuation of SUSP. 722 * will want to return new lbn, len. 723 */ 724 ce_lbn = CE_BLK_LOC(susp); 725 *len = CE_CONT_LEN(susp); 726 break; 727 case RRIP_NM_IX: 728 /* NM signature: POSIX-style file name */ 729 if (!RRIP_NAME_FLAGS(susp)) { 730 udp->d_namlen = RRIP_NAME_LEN(susp); 731 bcopy((char *)RRIP_name(susp), 732 udp->d_name, udp->d_namlen); 733 udp->d_name[udp->d_namlen] = '\0'; 734 } 735 break; 736 case HSFS_NUM_SIG: 737 /* couldn't find a legit susp, terminate loop */ 738 case SUSP_ST_IX: 739 /* ST signature: terminates SUSP */ 740 return (ce_lbn); 741 case SUSP_SP_IX: 742 case RRIP_RR_IX: 743 default: 744 break; 745 } 746 cur_off += susp_len; 747 } 748 return (ce_lbn); 749 } 750 751 struct boot_fs_ops bhsfs_ops = { 752 "boot_hsfs", 753 bhsfs_mountroot, 754 bhsfs_unmountroot, 755 bhsfs_open, 756 bhsfs_close, 757 bhsfs_read, 758 bhsfs_lseek, 759 NULL 760 }; 761