xref: /titanic_52/usr/src/common/fs/hsfs.c (revision 9fb1159054bb89619213a7b2be87ea3bca25fcce)
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