xref: /illumos-gate/usr/src/common/fs/hsfs.c (revision 2b24ab6b3865caeede9eeb9db6b83e1d89dcd1ea)
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 2007 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 extern int cf_check_compressed(fileid_t *);
88 extern void cf_close(fileid_t *);
89 extern void cf_seek(fileid_t *, off_t, int);
90 extern int cf_read(fileid_t *, caddr_t, size_t);
91 
92 struct dirstuff {
93 	int loc;
94 	fileid_t *filep;
95 };
96 
97 struct hs_direct {
98     struct	direct  hs_ufs_dir;
99     struct	hs_direntry hs_dir;
100 };
101 
102 static uint_t root_ino = 0;
103 static struct hs_volume *hsfsp;
104 static fileid_t *head;
105 
106 static char *hsfs_sig_tab[] = {
107 	SUSP_SP,
108 	SUSP_CE,
109 	SUSP_PD,
110 	SUSP_ST,
111 	SUSP_ER,
112 	RRIP_PX,
113 	RRIP_PN,
114 	RRIP_SL,
115 	RRIP_CL,
116 	RRIP_PL,
117 	RRIP_RE,
118 	RRIP_TF,
119 	RRIP_RR,
120 	RRIP_NM
121 };
122 
123 static int hsfs_num_sig = sizeof (hsfs_sig_tab) / sizeof (hsfs_sig_tab[0]);
124 
125 /*
126  *  Local prototypes
127  */
128 static struct hs_direct *readdir(struct dirstuff *);
129 static uint_t parse_dir(fileid_t *, int, struct hs_direct *);
130 static uint_t parse_susp(char *, uint_t *, struct hs_direct *);
131 static ino_t dlook(char *, fileid_t *);
132 static int opendir(ino_t, fileid_t *);
133 static ino_t find(char *, fileid_t *);
134 
135 static int bhsfs_mountroot(char *str);
136 static int bhsfs_unmountroot(void);
137 static int bhsfs_open(char *str, int flags);
138 static int bhsfs_close(int fd);
139 static void bhsfs_closeall(void);
140 static ssize_t bhsfs_read(int fdesc, char *buf, size_t count);
141 static off_t bhsfs_lseek(int fdesc, off_t addr, int whence);
142 static int bhsfs_fstat(int fdesc, struct bootstat *stp);
143 
144 static fileid_t *
145 find_fp(int fd)
146 {
147 	fileid_t *filep = head;
148 
149 	if (fd >= 0) {
150 		while ((filep = filep->fi_forw) != head)
151 			if (fd == filep->fi_filedes)
152 				return (filep->fi_taken ? filep : 0);
153 	}
154 
155 	return (0);
156 }
157 
158 static int
159 opendir(ino_t inode, fileid_t *filep)
160 {
161 	struct hs_direct hsdep;
162 
163 	dprintf("opendir: inode = %ld\n", inode);
164 	/* Set up the IO request */
165 	filep->fi_offset = 0;
166 	filep->fi_blocknum = hdbtodb(inode);
167 	filep->fi_count = ISO_SECTOR_SIZE;
168 	filep->fi_memp = 0;
169 
170 	if (diskread(filep))
171 		return (0);
172 
173 	filep->fi_offset = 0;
174 	filep->fi_blocknum = hdbtodb(inode);
175 
176 	if (inode != root_ino)
177 	    return (0);
178 
179 	if (parse_dir(filep, 0, &hsdep) > 0) {
180 		struct inode *ip;
181 
182 		ip = filep->fi_inode;
183 		if (ip == NULL)
184 			ip = filep->fi_inode = bkmem_alloc(sizeof (*ip));
185 
186 		ip->i_size = hsdep.hs_dir.ext_size;
187 		ip->i_smode = hsdep.hs_dir.mode;
188 		ip->i_number = inode;
189 		return (0);
190 	}
191 	return (1);
192 }
193 
194 static ino_t
195 find(char *path, fileid_t *filep)
196 {
197 	char *q;
198 	char c;
199 	ino_t n;
200 
201 	dprintf("find: %s\n", path);
202 	if (path == NULL || *path == '\0')
203 		return (0);
204 
205 	if (opendir(root_ino, filep))
206 		return (0);
207 
208 	while (*path) {
209 		while (*path == '/')
210 			path++;
211 		q = path;
212 		while (*q != '/' && *q != '\0')
213 			q++;
214 		c = *q;
215 		*q = '\0';
216 		n = dlook(path, filep);
217 		*q = c;
218 		path = q;
219 
220 		if (n != 0) {
221 			if (c == '\0')
222 				break;
223 			if (opendir(n, filep))
224 				return (0);
225 			continue;
226 		} else {
227 			return (0);
228 		}
229 	}
230 	return ((ino_t)n);
231 }
232 
233 static ino_t
234 dlook(char *s, fileid_t *filep)
235 {
236 	struct hs_direct *hsdep;
237 	struct direct *udp;
238 	struct inode *ip;
239 	struct dirstuff dirp;
240 	int len;
241 
242 	dprintf("dlook: %s\n", s);
243 	ip = filep->fi_inode;
244 	if (s == NULL || *s == '\0')
245 		return (0);
246 	if ((ip->i_smode & IFMT) != IFDIR) {
247 		return (0);
248 	}
249 	if (ip->i_size == 0) {
250 		return (0);
251 	}
252 	len = strlen(s);
253 	dirp.loc = 0;
254 	dirp.filep = filep;
255 	for (hsdep = readdir(&dirp); hsdep != NULL; hsdep = readdir(&dirp)) {
256 		udp = &hsdep->hs_ufs_dir;
257 		if (udp->d_namlen == 1 &&
258 		    udp->d_name[0] == '.' &&
259 		    udp->d_name[1] == '\0')
260 			continue;
261 		if (udp->d_namlen == 2 &&
262 		    udp->d_name[0] == '.' &&
263 		    udp->d_name[1] == '.' &&
264 		    udp->d_name[2] == '\0')
265 			continue;
266 		if (udp->d_namlen == len && (strcmp(s, udp->d_name)) == 0) {
267 			struct inode *ip = filep->fi_inode;
268 
269 			filep->fi_offset = 0;
270 			filep->fi_blocknum = hdbtodb(udp->d_ino);
271 
272 			bzero(filep->fi_inode, sizeof (struct inode));
273 			ip->i_size = hsdep->hs_dir.ext_size;
274 			ip->i_smode = hsdep->hs_dir.mode;
275 			ip->i_number = udp->d_ino;
276 			return (udp->d_ino);
277 		}
278 	}
279 	return (0);
280 }
281 
282 /*
283  * get next entry in a directory.
284  */
285 static struct hs_direct *
286 readdir(struct dirstuff *dirp)
287 {
288 	static struct hs_direct hsdep;
289 	struct direct *udp = &hsdep.hs_ufs_dir;
290 	struct inode *ip;
291 	fileid_t *filep;
292 	daddr_t lbn;
293 	int off;
294 
295 	dprintf("readdir: start\n");
296 	filep = dirp->filep;
297 	ip = filep->fi_inode;
298 	for (;;) {
299 		if (dirp->loc >= ip->i_size) {
300 			return (NULL);
301 		}
302 		off = dirp->loc & ((1 << ISO_SECTOR_SHIFT) - 1);
303 		if (off == 0) {
304 			lbn = hdbtodb(dirp->loc >> ISO_SECTOR_SHIFT);
305 			filep->fi_blocknum = lbn + hdbtodb(ip->i_number);
306 			filep->fi_count = ISO_SECTOR_SIZE;
307 			filep->fi_memp = 0;
308 			if (diskread(filep)) {
309 				dprintf("readdir: diskread failed\n");
310 				return (NULL);
311 			}
312 		}
313 		dirp->loc += parse_dir(filep, off, &hsdep);
314 		if (udp->d_reclen == 0 && dirp->loc <= ip->i_size) {
315 			dirp->loc = roundup(dirp->loc, ISO_SECTOR_SIZE);
316 			continue;
317 		}
318 		return (&hsdep);
319 	}
320 }
321 
322 static int
323 getblock(fileid_t *filep)
324 {
325 	struct inode *ip = filep->fi_inode;
326 	int off, size, diff;
327 	daddr_t lbn;
328 
329 	dprintf("getblock: start\n");
330 	diff = ip->i_size - filep->fi_offset;
331 	if (diff <= 0)
332 		return (-1);
333 
334 	/* which block (or frag) in the file do we read? */
335 	lbn = hdbtodb(filep->fi_offset >> ISO_SECTOR_SHIFT);
336 	filep->fi_blocknum = lbn + hdbtodb(ip->i_number);
337 
338 	off = filep->fi_offset & ((1 << ISO_SECTOR_SHIFT) - 1);
339 	size = filep->fi_count = ISO_SECTOR_SIZE;
340 	filep->fi_memp = 0;
341 	if (diskread(filep))	/* Trap errors */
342 		return (-1);
343 
344 	if (filep->fi_offset - off + size >= ip->i_size)
345 		filep->fi_count = diff + off;
346 	filep->fi_count -= off;
347 	filep->fi_memp += off;
348 	dprintf("getblock: end\n");
349 	return (0);
350 }
351 
352 static ssize_t
353 bhsfs_read(int fd, caddr_t buf, size_t count)
354 {
355 	int i, j;
356 	fileid_t *filep;
357 	struct inode *ip;
358 	caddr_t n;
359 
360 	dprintf("bhsfs_read %d, ", fd);
361 	dprintf("count 0x%lx\n", count);
362 	filep = find_fp(fd);
363 	if (filep == NULL)
364 		return (-1);
365 
366 	ip = filep->fi_inode;
367 	n = buf;
368 	if ((filep->fi_flags & FI_COMPRESSED) == 0 &&
369 	    filep->fi_offset + count > ip->i_size)
370 		count = ip->i_size - filep->fi_offset;
371 
372 	if ((i = count) <= 0)
373 		return (0);
374 
375 	while (i > 0) {
376 		if (filep->fi_flags & FI_COMPRESSED) {
377 			if ((j = cf_read(filep, buf, count)) < 0)
378 				return (0); /* encountered an error */
379 			if (j < i)
380 				i = j; /* short read, must have hit EOF */
381 		} else {
382 			if (filep->fi_count == 0) {
383 				if (getblock(filep) == -1)
384 					return (0);
385 			}
386 			j = MIN(i, filep->fi_count);
387 			bcopy(filep->fi_memp, buf, (uint_t)j);
388 		}
389 		filep->fi_memp += j;
390 		filep->fi_offset += j;
391 		filep->fi_count -= j;
392 		buf += j;
393 		i -= j;
394 	}
395 
396 	dprintf("bhsfs_read: read 0x%x\n", (int)(buf - n));
397 	return (buf - n);
398 }
399 
400 /*ARGSUSED*/
401 static int
402 bhsfs_mountroot(char *str)
403 {
404 	char *bufp;
405 
406 	if (hsfsp != NULL)
407 		return (0);	/* already mounted */
408 
409 	dprintf("mounting ramdisk as hsfs\n");
410 
411 	hsfsp = bkmem_alloc(sizeof (*hsfsp));
412 	bzero(hsfsp, sizeof (*hsfsp));
413 	head = bkmem_alloc(sizeof (*head));
414 	bzero(head, sizeof (*head));
415 	head->fi_back = head->fi_forw = head;
416 
417 	/* now read the superblock. */
418 	head->fi_blocknum = hdbtodb(ISO_VOLDESC_SEC);
419 	head->fi_offset = 0;
420 	head->fi_count = ISO_SECTOR_SIZE;
421 	head->fi_memp = head->fi_buf;
422 	if (diskread(head)) {
423 		printf("failed to read superblock\n");
424 		bhsfs_closeall();
425 		return (-1);
426 	}
427 
428 	/* Since RRIP is based on ISO9660, that's where we start */
429 	bufp = head->fi_buf;
430 	if ((ISO_DESC_TYPE(bufp) != ISO_VD_PVD) ||
431 	    (strncmp((const char *)ISO_std_id(bufp), ISO_ID_STRING,
432 	    ISO_ID_STRLEN) != 0) || (ISO_STD_VER(bufp) != ISO_ID_VER)) {
433 		dprintf("volume type does not match\n");
434 		bhsfs_closeall();
435 		return (-1);
436 	}
437 
438 	/* Now we fill in the volume descriptor */
439 	hsfsp->vol_size = ISO_VOL_SIZE(bufp);
440 	hsfsp->lbn_size = ISO_BLK_SIZE(bufp);
441 	hsfsp->lbn_shift = ISO_SECTOR_SHIFT;
442 	hsfsp->lbn_secshift = ISO_SECTOR_SHIFT;
443 	hsfsp->vol_set_size = (ushort_t)ISO_SET_SIZE(bufp);
444 	hsfsp->vol_set_seq = (ushort_t)ISO_SET_SEQ(bufp);
445 
446 	/* Make sure we have a valid logical block size */
447 	if (hsfsp->lbn_size & ~(1 << hsfsp->lbn_shift)) {
448 		printf("%d invalid logical block size\n", hsfsp->lbn_size);
449 		bhsfs_closeall();
450 		return (-1);
451 	}
452 
453 	/* Since an HSFS root could be located anywhere on the media! */
454 	root_ino = IDE_EXT_LBN(ISO_root_dir(bufp));
455 	return (0);
456 }
457 
458 static int
459 bhsfs_unmountroot(void)
460 {
461 	if (hsfsp == NULL)
462 		return (-1);
463 
464 	bhsfs_closeall();
465 
466 	return (0);
467 }
468 
469 /*
470  * Open a file.
471  */
472 /*ARGSUSED*/
473 int
474 bhsfs_open(char *str, int flags)
475 {
476 	static int filedes = 1;
477 
478 	fileid_t *filep;
479 	ino_t ino;
480 
481 	dprintf("open %s\n", str);
482 	filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
483 	filep->fi_back = head->fi_back;
484 	filep->fi_forw = head;
485 	head->fi_back->fi_forw = filep;
486 	head->fi_back = filep;
487 	filep->fi_filedes = filedes++;
488 	filep->fi_taken = 1;
489 	filep->fi_path = (char *)bkmem_alloc(strlen(str) + 1);
490 	(void) strcpy(filep->fi_path, str);
491 	filep->fi_inode = NULL;
492 	bzero(filep->fi_buf, MAXBSIZE);
493 	filep->fi_getblock = getblock;
494 	filep->fi_flags = 0;
495 
496 	ino = find(str, filep);
497 	if (ino == 0) {
498 		(void) bhsfs_close(filep->fi_filedes);
499 		return (-1);
500 	}
501 
502 	filep->fi_blocknum = hdbtodb(ino);
503 	filep->fi_offset = 0;
504 	filep->fi_count = 0;
505 	filep->fi_memp = 0;
506 
507 	if (cf_check_compressed(filep) != 0)
508 		return (-1);
509 	dprintf("open done\n");
510 	return (filep->fi_filedes);
511 }
512 
513 int
514 bhsfs_close(int fd)
515 {
516 	fileid_t *filep;
517 
518 	dprintf("close %d\n", fd);
519 	if (!(filep = find_fp(fd)))
520 		return (-1);
521 
522 	if (filep->fi_taken == 0 || filep == head) {
523 		printf("File descripter %d not allocated!\n", fd);
524 		return (-1);
525 	}
526 
527 	cf_close(filep);
528 	/* unlink and deallocate node */
529 	filep->fi_forw->fi_back = filep->fi_back;
530 	filep->fi_back->fi_forw = filep->fi_forw;
531 	if (filep->fi_inode)
532 		bkmem_free(filep->fi_inode, sizeof (struct inode));
533 	bkmem_free(filep->fi_path, strlen(filep->fi_path) + 1);
534 	bkmem_free((char *)filep, sizeof (fileid_t));
535 	dprintf("close done\n");
536 	return (0);
537 }
538 
539 static void
540 bhsfs_closeall(void)
541 {
542 	fileid_t *filep;
543 
544 	while ((filep = head->fi_forw) != head)
545 		if (filep->fi_taken && bhsfs_close(filep->fi_filedes))
546 			printf("Filesystem may be inconsistent.\n");
547 
548 	bkmem_free(hsfsp, sizeof (*hsfsp));
549 	bkmem_free(head, sizeof (fileid_t));
550 	hsfsp = NULL;
551 	head = NULL;
552 }
553 
554 /*
555  * This version of seek() only performs absolute seeks (whence == 0).
556  */
557 static off_t
558 bhsfs_lseek(int fd, off_t addr, int whence)
559 {
560 	fileid_t *filep;
561 
562 	dprintf("lseek %d, ", fd);
563 	dprintf("off = %lx\n", addr);
564 	if (!(filep = find_fp(fd)))
565 		return (-1);
566 
567 	if (filep->fi_flags & FI_COMPRESSED) {
568 		cf_seek(filep, addr, whence);
569 	} else {
570 		switch (whence) {
571 		case SEEK_CUR:
572 			filep->fi_offset += addr;
573 			break;
574 		case SEEK_SET:
575 			filep->fi_offset = addr;
576 			break;
577 		default:
578 		case SEEK_END:
579 			printf("lseek(): invalid whence value %d\n", whence);
580 			break;
581 		}
582 		filep->fi_blocknum = addr / DEV_BSIZE;
583 	}
584 
585 	filep->fi_count = 0;
586 	return (0);
587 }
588 
589 static int
590 bhsfs_fstat(int fd, struct bootstat *stp)
591 {
592 	fileid_t	*filep;
593 	struct inode	*ip;
594 
595 	if (!(filep = find_fp(fd)))
596 		return (-1);
597 
598 	ip = filep->fi_inode;
599 
600 	stp->st_mode = 0;
601 	stp->st_size = 0;
602 
603 	if (ip == NULL)
604 		return (0);
605 
606 	switch (ip->i_smode & IFMT) {
607 	case IFDIR:
608 		stp->st_mode = S_IFDIR;
609 		break;
610 	case IFREG:
611 		stp->st_mode = S_IFREG;
612 		break;
613 	default:
614 		break;
615 	}
616 	/*
617 	 * NOTE: this size will be the compressed size for a compressed file
618 	 * This could confuse the caller since we decompress the file behind
619 	 * the scenes when the file is read.
620 	 */
621 	stp->st_size = ip->i_size;
622 
623 	/* file times */
624 	stp->st_atim.tv_sec = ip->i_atime.tv_sec;
625 	stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
626 	stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
627 	stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
628 	stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
629 	stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
630 
631 	return (0);
632 
633 }
634 
635 
636 /*
637  * Parse a directory entry.
638  *
639  */
640 static uint_t
641 parse_dir(fileid_t *filep, int offset, struct hs_direct *hsdep)
642 {
643 	char *bufp = (char *)(filep->fi_memp + offset);
644 	struct direct *udp = &hsdep->hs_ufs_dir;  /* ufs-style dir info */
645 	struct hs_direntry *hdp = &hsdep->hs_dir; /* hsfs-style dir info */
646 	uint_t ce_lbn;
647 	uint_t ce_len;
648 	uint_t nmlen;
649 	uint_t i;
650 	uchar_t c;
651 
652 	dprintf("parse_dir: offset = %d\n", offset);
653 	/* a zero length dir entry terminates the dir block */
654 	udp->d_reclen = IDE_DIR_LEN(bufp);
655 	if (udp->d_reclen == 0)
656 		return (0);
657 
658 	/* fill in some basic hsfs info */
659 	hdp->ext_lbn  = IDE_EXT_LBN(bufp);
660 	hdp->ext_size = IDE_EXT_SIZE(bufp);
661 	hdp->xar_len  = IDE_XAR_LEN(bufp);
662 	hdp->intlf_sz = IDE_INTRLV_SIZE(bufp);
663 	hdp->intlf_sk = IDE_INTRLV_SKIP(bufp);
664 	hdp->sym_link = NULL;
665 
666 	/* we use lbn of data extent as an inode # equivalent */
667 	udp->d_ino	= hdp->ext_lbn;
668 
669 	c = IDE_FLAGS(bufp);
670 	if (IDE_REGULAR_FILE(c)) {
671 		hdp->type = VREG;
672 		hdp->mode = IFREG;
673 		hdp->nlink = 1;
674 	} else if (IDE_REGULAR_DIR(c)) {
675 		hdp->type = VDIR;
676 		hdp->mode = IFDIR;
677 		hdp->nlink = 2;
678 	} else {
679 		printf("pd(): file type=0x%x unknown.\n", c);
680 	}
681 
682 	/*
683 	 * Massage hsfs name, recognizing special entries for . and ..
684 	 * else lopping off version junk.
685 	 */
686 
687 	/* Some initial conditions */
688 	nmlen = IDE_NAME_LEN(bufp);
689 	c = *IDE_NAME(bufp);
690 	/* Special Case: Current Directory */
691 	if (nmlen == 1 && c == '\0') {
692 		udp->d_name[0] = '.';
693 		udp->d_name[1] = '\0';
694 		udp->d_namlen = 1;
695 	/* Special Case: Parent Directory */
696 	} else if (nmlen == 1 && c == '\001') {
697 		udp->d_name[0] = '.';
698 		udp->d_name[1] = '.';
699 		udp->d_name[2] = '\0';
700 		udp->d_namlen = 2;
701 	/* Other file name */
702 	} else {
703 		udp->d_namlen = 0;
704 		for (i = 0; i < nmlen; i++) {
705 			c = *(IDE_name(bufp)+i);
706 			if (c == ';')
707 				break;
708 			else if (c == ' ')
709 				continue;
710 			else
711 				udp->d_name[udp->d_namlen++] = c;
712 		}
713 		udp->d_name[udp->d_namlen] = '\0';
714 	}
715 
716 	/* System Use Fields */
717 	ce_len = IDE_SUA_LEN(bufp);
718 
719 	if (ce_len == 0)
720 		return (udp->d_reclen);
721 
722 	/* there is an SUA for this dir entry; go parse it */
723 	ce_lbn = parse_susp((char *)IDE_sys_use_area(bufp), &ce_len, hsdep);
724 
725 	if (ce_lbn) {
726 		/*
727 		 * store away current position in dir,
728 		 * as we will be using the iobuf to reading SUA.
729 		 */
730 		daddr_t save_bn = filep->fi_blocknum;
731 		daddr_t save_offset = filep->fi_offset;
732 		caddr_t save_ma = filep->fi_memp;
733 		int save_cc = filep->fi_count;
734 		do {
735 			filep->fi_count = ISO_SECTOR_SIZE;
736 			filep->fi_offset = 0;
737 			filep->fi_blocknum = hdbtodb(ce_lbn);
738 			filep->fi_memp = 0;
739 			if (diskread(filep)) {
740 				printf("failed to read cont. area\n");
741 				ce_len = 0;
742 				ce_lbn = 0;
743 				break;
744 			}
745 			ce_lbn = parse_susp(filep->fi_memp, &ce_len,
746 			    hsdep);
747 		} while (ce_lbn);
748 		filep->fi_count = save_cc;
749 		filep->fi_offset = save_offset;
750 		filep->fi_blocknum = save_bn;
751 		filep->fi_memp = save_ma;
752 	}
753 	return (udp->d_reclen);
754 }
755 
756 /*
757  * Parse the System Use Fields in this System Use Area.
758  * Return blk number of continuation/SUA, or 0 if no continuation/not a SUA.
759  */
760 static uint_t
761 parse_susp(char *bufp, uint_t *len, struct hs_direct *hsdep)
762 {
763 	struct direct *udp = &hsdep->hs_ufs_dir; /* ufs-style info */
764 	char *susp;
765 	uint_t cur_off = 0;
766 	uint_t blk_len = *len;
767 	uint_t susp_len = 0;
768 	uint_t ce_lbn = 0;
769 	uint_t i;
770 
771 	dprintf("parse_susp: len = %d\n", *len);
772 	while (cur_off < blk_len) {
773 		susp = (char *)(bufp + cur_off);
774 
775 		/*
776 		 * A null entry, or an entry with zero length
777 		 * terminates the SUSP.
778 		 */
779 		if (susp[0] == '\0' || susp[1] == '\0' ||
780 		    (susp_len = SUF_LEN(susp)) == 0)
781 			break;
782 
783 		/*
784 		 * Compare current entry to all known signatures.
785 		 */
786 		for (i = 0; i < hsfs_num_sig; i++)
787 			if (strncmp(hsfs_sig_tab[i], susp, SUF_SIG_LEN) == 0)
788 				break;
789 		switch (i) {
790 		case SUSP_CE_IX:
791 			/*
792 			 * CE signature: continuation of SUSP.
793 			 * will want to return new lbn, len.
794 			 */
795 			ce_lbn = CE_BLK_LOC(susp);
796 			*len = CE_CONT_LEN(susp);
797 			break;
798 		case RRIP_NM_IX:
799 			/* NM signature: POSIX-style file name */
800 			if (!RRIP_NAME_FLAGS(susp)) {
801 				udp->d_namlen = RRIP_NAME_LEN(susp);
802 				bcopy((char *)RRIP_name(susp),
803 				    udp->d_name, udp->d_namlen);
804 				udp->d_name[udp->d_namlen] = '\0';
805 			}
806 			break;
807 		case HSFS_NUM_SIG:
808 			/* couldn't find a legit susp, terminate loop */
809 		case SUSP_ST_IX:
810 			/* ST signature: terminates SUSP */
811 			return (ce_lbn);
812 		case SUSP_SP_IX:
813 		case RRIP_RR_IX:
814 		default:
815 			break;
816 		}
817 		cur_off += susp_len;
818 	}
819 	return (ce_lbn);
820 }
821 
822 struct boot_fs_ops bhsfs_ops = {
823 	"boot_hsfs",
824 	bhsfs_mountroot,
825 	bhsfs_unmountroot,
826 	bhsfs_open,
827 	bhsfs_close,
828 	bhsfs_read,
829 	bhsfs_lseek,
830 	bhsfs_fstat,
831 	NULL
832 };
833