xref: /titanic_51/usr/src/common/fs/hsfs.c (revision facf4a8d7b59fde89a8662b4f4c73a758e6c402c)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Basic file system reading code for standalone I/O system.
30  * Simulates a primitive UNIX I/O system (read(), write(), open(), etc).
31  * Does not support writes.
32  */
33 
34 #include <sys/param.h>
35 #include <sys/sysmacros.h>
36 #include <sys/vnode.h>
37 #include <sys/fs/ufs_fsdir.h>
38 #include <sys/fs/ufs_fs.h>
39 #include <sys/fs/ufs_inode.h>
40 
41 #include <sys/fs/hsfs_spec.h>
42 #include <sys/fs/hsfs_isospec.h>
43 #include <sys/fs/hsfs_node.h>
44 #include <sys/fs/hsfs_susp.h>
45 #include <sys/fs/hsfs_rrip.h>
46 #include <sys/bootvfs.h>
47 #include <sys/filep.h>
48 
49 #ifdef	_BOOT
50 #include "../common/util.h"
51 #else
52 #include <sys/sunddi.h>
53 #endif
54 
55 #define	hdbtodb(n)	((ISO_SECTOR_SIZE / DEV_BSIZE) * (n))
56 
57 #define	HSFS_NUM_SIG    14
58 
59 #define	SUSP_SP_IX	0
60 #define	SUSP_CE_IX	1
61 #define	SUSP_PD_IX	2
62 #define	SUSP_ST_IX	3
63 #define	SUSP_ER_IX	4
64 #define	RRIP_PX_IX	5
65 #define	RRIP_PN_IX	6
66 #define	RRIP_SL_IX	7
67 #define	RRIP_CL_IX	8
68 #define	RRIP_PL_IX	9
69 #define	RRIP_RE_IX	10
70 #define	RRIP_RF_IX	11
71 #define	RRIP_RR_IX	12
72 #define	RRIP_NM_IX	13
73 
74 #ifdef	_BOOT
75 #define	dprintf	if (bootrd_debug) printf
76 #else
77 #define	printf	kobj_printf
78 #define	dprintf	if (bootrd_debug) kobj_printf
79 
80 /* PRINTFLIKE1 */
81 extern void kobj_printf(char *, ...);
82 #endif
83 
84 extern int bootrd_debug;
85 extern void *bkmem_alloc(size_t);
86 extern void bkmem_free(void *, size_t);
87 
88 struct dirstuff {
89 	int loc;
90 	fileid_t *filep;
91 };
92 
93 struct hs_direct {
94     struct	direct  hs_ufs_dir;
95     struct	hs_direntry hs_dir;
96 };
97 
98 static uint_t root_ino = 0;
99 static struct hs_volume *hsfsp;
100 static fileid_t *head;
101 
102 static char *hsfs_sig_tab[] = {
103 	SUSP_SP,
104 	SUSP_CE,
105 	SUSP_PD,
106 	SUSP_ST,
107 	SUSP_ER,
108 	RRIP_PX,
109 	RRIP_PN,
110 	RRIP_SL,
111 	RRIP_CL,
112 	RRIP_PL,
113 	RRIP_RE,
114 	RRIP_TF,
115 	RRIP_RR,
116 	RRIP_NM
117 };
118 
119 static int hsfs_num_sig = sizeof (hsfs_sig_tab) / sizeof (hsfs_sig_tab[0]);
120 
121 /*
122  *  Local prototypes
123  */
124 static struct hs_direct *readdir(struct dirstuff *);
125 static uint_t parse_dir(fileid_t *, int, struct hs_direct *);
126 static uint_t parse_susp(char *, uint_t *, struct hs_direct *);
127 static ino_t dlook(char *, fileid_t *);
128 static int opendir(ino_t, fileid_t *);
129 static ino_t find(char *, fileid_t *);
130 
131 static int bhsfs_mountroot(char *str);
132 static int bhsfs_unmountroot(void);
133 static int bhsfs_open(char *str, int flags);
134 static int bhsfs_close(int fd);
135 static void bhsfs_closeall(void);
136 static ssize_t bhsfs_read(int fdesc, char *buf, size_t count);
137 static off_t bhsfs_lseek(int fdesc, off_t addr, int whence);
138 static int bhsfs_fstat(int fdesc, struct bootstat *stp);
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 static int
567 bhsfs_fstat(int fd, struct bootstat *stp)
568 {
569 	fileid_t	*filep;
570 	struct inode	*ip;
571 
572 	if (!(filep = find_fp(fd)))
573 		return (-1);
574 
575 	ip = filep->fi_inode;
576 
577 	stp->st_mode = 0;
578 	stp->st_size = 0;
579 
580 	if (ip == NULL)
581 		return (0);
582 
583 	switch (ip->i_smode & IFMT) {
584 	case IFDIR:
585 		stp->st_mode = S_IFDIR;
586 		break;
587 	case IFREG:
588 		stp->st_mode = S_IFREG;
589 		break;
590 	default:
591 		break;
592 	}
593 	stp->st_size = ip->i_size;
594 
595 	/* file times */
596 	stp->st_atim.tv_sec = ip->i_atime.tv_sec;
597 	stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
598 	stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
599 	stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
600 	stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
601 	stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
602 
603 	return (0);
604 
605 }
606 
607 
608 /*
609  * Parse a directory entry.
610  *
611  */
612 static uint_t
613 parse_dir(fileid_t *filep, int offset, struct hs_direct *hsdep)
614 {
615 	char *bufp = (char *)(filep->fi_memp + offset);
616 	struct direct *udp = &hsdep->hs_ufs_dir;  /* ufs-style dir info */
617 	struct hs_direntry *hdp = &hsdep->hs_dir; /* hsfs-style dir info */
618 	uint_t ce_lbn;
619 	uint_t ce_len;
620 	uint_t nmlen;
621 	uint_t i;
622 	uchar_t c;
623 
624 	dprintf("parse_dir: offset = %d\n", offset);
625 	/* a zero length dir entry terminates the dir block */
626 	udp->d_reclen = IDE_DIR_LEN(bufp);
627 	if (udp->d_reclen == 0)
628 		return (0);
629 
630 	/* fill in some basic hsfs info */
631 	hdp->ext_lbn  = IDE_EXT_LBN(bufp);
632 	hdp->ext_size = IDE_EXT_SIZE(bufp);
633 	hdp->xar_len  = IDE_XAR_LEN(bufp);
634 	hdp->intlf_sz = IDE_INTRLV_SIZE(bufp);
635 	hdp->intlf_sk = IDE_INTRLV_SKIP(bufp);
636 	hdp->sym_link = NULL;
637 
638 	/* we use lbn of data extent as an inode # equivalent */
639 	udp->d_ino	= hdp->ext_lbn;
640 
641 	c = IDE_FLAGS(bufp);
642 	if (IDE_REGULAR_FILE(c)) {
643 		hdp->type = VREG;
644 		hdp->mode = IFREG;
645 		hdp->nlink = 1;
646 	} else if (IDE_REGULAR_DIR(c)) {
647 		hdp->type = VDIR;
648 		hdp->mode = IFDIR;
649 		hdp->nlink = 2;
650 	} else {
651 		printf("pd(): file type=0x%x unknown.\n", c);
652 	}
653 
654 	/*
655 	 * Massage hsfs name, recognizing special entries for . and ..
656 	 * else lopping off version junk.
657 	 */
658 
659 	/* Some initial conditions */
660 	nmlen = IDE_NAME_LEN(bufp);
661 	c = *IDE_NAME(bufp);
662 	/* Special Case: Current Directory */
663 	if (nmlen == 1 && c == '\0') {
664 		udp->d_name[0] = '.';
665 		udp->d_name[1] = '\0';
666 		udp->d_namlen = 1;
667 	/* Special Case: Parent Directory */
668 	} else if (nmlen == 1 && c == '\001') {
669 		udp->d_name[0] = '.';
670 		udp->d_name[1] = '.';
671 		udp->d_name[2] = '\0';
672 		udp->d_namlen = 2;
673 	/* Other file name */
674 	} else {
675 		udp->d_namlen = 0;
676 		for (i = 0; i < nmlen; i++) {
677 			c = *(IDE_name(bufp)+i);
678 			if (c == ';')
679 				break;
680 			else if (c == ' ')
681 				continue;
682 			else
683 				udp->d_name[udp->d_namlen++] = c;
684 		}
685 		udp->d_name[udp->d_namlen] = '\0';
686 	}
687 
688 	/* System Use Fields */
689 	ce_len = IDE_SUA_LEN(bufp);
690 
691 	if (ce_len == 0)
692 		return (udp->d_reclen);
693 
694 	/* there is an SUA for this dir entry; go parse it */
695 	ce_lbn = parse_susp((char *)IDE_sys_use_area(bufp), &ce_len, hsdep);
696 
697 	if (ce_lbn) {
698 		/*
699 		 * store away current position in dir,
700 		 * as we will be using the iobuf to reading SUA.
701 		 */
702 		daddr_t save_bn = filep->fi_blocknum;
703 		daddr_t save_offset = filep->fi_offset;
704 		caddr_t save_ma = filep->fi_memp;
705 		int save_cc = filep->fi_count;
706 		do {
707 			filep->fi_count = ISO_SECTOR_SIZE;
708 			filep->fi_offset = 0;
709 			filep->fi_blocknum = hdbtodb(ce_lbn);
710 			filep->fi_memp = 0;
711 			if (diskread(filep)) {
712 				printf("failed to read cont. area\n");
713 				ce_len = 0;
714 				ce_lbn = 0;
715 				break;
716 			}
717 			ce_lbn = parse_susp(filep->fi_memp, &ce_len,
718 			    hsdep);
719 		} while (ce_lbn);
720 		filep->fi_count = save_cc;
721 		filep->fi_offset = save_offset;
722 		filep->fi_blocknum = save_bn;
723 		filep->fi_memp = save_ma;
724 	}
725 	return (udp->d_reclen);
726 }
727 
728 /*
729  * Parse the System Use Fields in this System Use Area.
730  * Return blk number of continuation/SUA, or 0 if no continuation/not a SUA.
731  */
732 static uint_t
733 parse_susp(char *bufp, uint_t *len, struct hs_direct *hsdep)
734 {
735 	struct direct *udp = &hsdep->hs_ufs_dir; /* ufs-style info */
736 	char *susp;
737 	uint_t cur_off = 0;
738 	uint_t blk_len = *len;
739 	uint_t susp_len = 0;
740 	uint_t ce_lbn = 0;
741 	uint_t i;
742 
743 	dprintf("parse_susp: len = %d\n", *len);
744 	while (cur_off < blk_len) {
745 		susp = (char *)(bufp + cur_off);
746 
747 		/*
748 		 * A null entry, or an entry with zero length
749 		 * terminates the SUSP.
750 		 */
751 		if (susp[0] == '\0' || susp[1] == '\0' ||
752 		    (susp_len = SUF_LEN(susp)) == 0)
753 			break;
754 
755 		/*
756 		 * Compare current entry to all known signatures.
757 		 */
758 		for (i = 0; i < hsfs_num_sig; i++)
759 			if (strncmp(hsfs_sig_tab[i], susp, SUF_SIG_LEN) == 0)
760 				break;
761 		switch (i) {
762 		case SUSP_CE_IX:
763 			/*
764 			 * CE signature: continuation of SUSP.
765 			 * will want to return new lbn, len.
766 			 */
767 			ce_lbn = CE_BLK_LOC(susp);
768 			*len = CE_CONT_LEN(susp);
769 			break;
770 		case RRIP_NM_IX:
771 			/* NM signature: POSIX-style file name */
772 			if (!RRIP_NAME_FLAGS(susp)) {
773 				udp->d_namlen = RRIP_NAME_LEN(susp);
774 				bcopy((char *)RRIP_name(susp),
775 				    udp->d_name, udp->d_namlen);
776 				udp->d_name[udp->d_namlen] = '\0';
777 			}
778 			break;
779 		case HSFS_NUM_SIG:
780 			/* couldn't find a legit susp, terminate loop */
781 		case SUSP_ST_IX:
782 			/* ST signature: terminates SUSP */
783 			return (ce_lbn);
784 		case SUSP_SP_IX:
785 		case RRIP_RR_IX:
786 		default:
787 			break;
788 		}
789 		cur_off += susp_len;
790 	}
791 	return (ce_lbn);
792 }
793 
794 struct boot_fs_ops bhsfs_ops = {
795 	"boot_hsfs",
796 	bhsfs_mountroot,
797 	bhsfs_unmountroot,
798 	bhsfs_open,
799 	bhsfs_close,
800 	bhsfs_read,
801 	bhsfs_lseek,
802 	bhsfs_fstat,
803 	NULL
804 };
805