xref: /illumos-gate/usr/src/common/fs/hsfs.c (revision 49218d4f8e4d84d1c08aeb267bcf6e451f2056dc)
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