xref: /illumos-gate/usr/src/stand/lib/fs/hsfs/hsfsops.c (revision d8ab6e129d75d7c3f21a7909bf811a3de65faea8)
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 #include <sys/param.h>
27 #include <sys/vnode.h>
28 #include <sys/fs/ufs_fsdir.h>
29 #include <sys/fs/ufs_fs.h>
30 #include <sys/fs/ufs_inode.h>
31 #include <sys/sysmacros.h>
32 #include <sys/promif.h>
33 #include <sys/filep.h>
34 #include <sys/salib.h>
35 #include <sys/sacache.h>
36 
37 #include <sys/fs/hsfs_spec.h>
38 #include <sys/fs/hsfs_isospec.h>
39 #include <sys/fs/hsfs_node.h>
40 #include <sys/fs/hsfs_susp.h>
41 #include <sys/fs/hsfs_rrip.h>
42 
43 #include "hsfs_sig.h"
44 
45 #include <sys/stat.h>
46 #include <sys/bootvfs.h>
47 #include <sys/bootconf.h>
48 #include <sys/bootdebug.h>
49 
50 #define	hdbtodb(n)	((ISO_SECTOR_SIZE / DEV_BSIZE) * (n))
51 
52 #define	THE_EPOCH	1970
53 #define	END_OF_TIME	2099
54 
55 /* May not need this... */
56 static uint_t	sua_offset = 0;
57 
58 /* The root inode on an HSFS filesystem can be anywhere! */
59 static uint_t	root_ino = 0;		/* This is both a flag and a value */
60 
61 static fileid_t *head;
62 
63 /* Only got one of these...ergo, only 1 fs open at once */
64 static devid_t *devp;
65 
66 struct dirinfo {
67 	int 	loc;
68 	fileid_t *fi;
69 };
70 
71 struct hs_direct {
72     struct	direct	hs_ufs_dir;
73     struct	hs_direntry hs_dir;
74 };
75 
76 /*
77  *  Function prototypes
78  */
79 
80 static int	boot_hsfs_mountroot(char *str);
81 static int	boot_hsfs_unmountroot(void);
82 static int	boot_hsfs_open(char *filename, int flags);
83 static int	boot_hsfs_close(int fd);
84 static ssize_t	boot_hsfs_read(int fd, caddr_t buf, size_t size);
85 static off_t	boot_hsfs_lseek(int, off_t, int);
86 static int	boot_hsfs_fstat(int fd, struct bootstat *stp);
87 static void	boot_hsfs_closeall(int flag);
88 static int	boot_hsfs_getdents(int fd, struct dirent *dep, unsigned size);
89 
90 struct boot_fs_ops boot_hsfs_ops = {
91 	"hsfs",
92 	boot_hsfs_mountroot,
93 	boot_hsfs_unmountroot,
94 	boot_hsfs_open,
95 	boot_hsfs_close,
96 	boot_hsfs_read,
97 	boot_hsfs_lseek,
98 	boot_hsfs_fstat,
99 	boot_hsfs_closeall,
100 	boot_hsfs_getdents
101 };
102 
103 static 	ino_t	find(fileid_t *, char *);
104 static	ino_t	dlook(fileid_t *, char *);
105 static	int	opendir(fileid_t *, ino_t);
106 static	struct	hs_direct *readdir(struct dirinfo *);
107 static	uint_t	parse_dir(fileid_t *, int, struct hs_direct *);
108 static	uint_t	parse_susp(char *, uint_t *, struct hs_direct *);
109 static	void	hs_seti(fileid_t *,  struct hs_direct *, ino_t);
110 static void	hs_dodates(enum hs_vol_type, struct hs_direntry *, char *);
111 static time_t	hs_date_to_gmtime(int, int, int, int);
112 
113 /*
114  *	There is only 1 open (mounted) device at any given time.
115  *	So we can keep a single, global devp file descriptor to
116  *	use to index into the di[] array.  This is not true for the
117  *	fi[] array.  We can have more than one file open at once,
118  *	so there is no global fd for the fi[].
119  *	The user program must save the fd passed back from open()
120  *	and use it to do subsequent read()'s.
121  */
122 
123 static int
124 opendir(fileid_t *filep, ino_t inode)
125 {
126 	struct hs_direct hsdep;
127 	int retval;
128 
129 	/* Set up the saio request */
130 	filep->fi_offset = 0;
131 	filep->fi_blocknum = hdbtodb(inode);
132 	filep->fi_count = ISO_SECTOR_SIZE;
133 
134 	/* Maybe the block is in the disk block cache */
135 	if ((filep->fi_memp = get_bcache(filep)) == NULL) {
136 		/* Not in the block cache so read it from disk */
137 		if (retval = set_bcache(filep)) {
138 			return (retval);
139 		}
140 	}
141 
142 	filep->fi_offset = 0;
143 	filep->fi_blocknum = hdbtodb(inode);
144 
145 	if (inode != root_ino)
146 		return (0);
147 
148 	if ((int)(parse_dir(filep, 0, &hsdep)) > 0) {
149 		hs_seti(filep, &hsdep, inode);
150 		return (0);
151 	}
152 	return (1);
153 }
154 
155 static ino_t
156 find(fileid_t *filep, char *path)
157 {
158 	register char *q;
159 	char c;
160 	ino_t inode;
161 
162 	if (path == NULL || *path == '\0') {
163 		printf("null path\n");
164 		return (0);
165 	}
166 
167 	if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
168 		printf("find(): path=<%s>\n", path);
169 
170 	/* Read the ROOT directory */
171 	if (opendir(filep, inode = root_ino)) {
172 		printf("find(): root_ino opendir() failed!\n");
173 		return ((ino_t)-1);
174 	}
175 
176 	while (*path) {
177 		while (*path == '/')
178 			path++;
179 		if (*(q = path) == '\0')
180 			break;
181 		while (*q != '/' && *q != '\0')
182 			q++;
183 		c = *q;
184 		*q = '\0';
185 
186 		if ((inode = dlook(filep, path)) != 0) {
187 			if (c == '\0')
188 				break;
189 			if (opendir(filep, inode)) {
190 				printf("find(): opendir(%d) failed!\n", inode);
191 				*q = c;
192 				return ((ino_t)-1);
193 			}
194 			*q = c;
195 			path = q;
196 			continue;
197 		} else {
198 			*q = c;
199 			return (0);
200 		}
201 	}
202 	return (inode);
203 }
204 
205 static fileid_t *
206 find_fp(int fd)
207 {
208 	fileid_t *filep = head;
209 
210 	if (fd >= 0) {
211 		while ((filep = filep->fi_forw) != head)
212 			if (fd == filep->fi_filedes)
213 				return (filep->fi_taken ? filep : 0);
214 	}
215 
216 	return (0);
217 }
218 
219 static ino_t
220 dlook(fileid_t *filep, char *path)
221 {
222 	int dv = filep->fi_devp->di_dcookie;
223 	register struct hs_direct *hsdep;
224 	register struct direct *udp;
225 	register struct inode *ip;
226 	struct dirinfo dirp;
227 	register int len;
228 	ino_t in;
229 
230 	ip = filep->fi_inode;
231 	if (path == NULL || *path == '\0')
232 		return (0);
233 	if ((ip->i_smode & IFMT) != IFDIR) {
234 		return (0);
235 	}
236 	if (ip->i_size == 0) {
237 		return (0);
238 	}
239 	len = strlen(path);
240 	/* first look through the directory entry cache */
241 	if (in = get_dcache(dv, path, ip->i_number)) {
242 		if ((filep->fi_inode = get_icache(dv, in)) != NULL) {
243 			filep->fi_offset = 0;
244 			filep->fi_blocknum = hdbtodb(in);
245 			return (in);
246 		}
247 	}
248 	dirp.loc = 0;
249 	dirp.fi = 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(path, udp->d_name) == 0)) {
262 			set_dcache(dv, path, ip->i_number, udp->d_ino);
263 			hs_seti(filep, hsdep, udp->d_ino);
264 			filep->fi_offset = 0;
265 			filep->fi_blocknum = hdbtodb(udp->d_ino);
266 			/* put this entry into the cache */
267 			return (udp->d_ino);
268 		}
269 		/* Allow "*" to print all names at that level, w/out match */
270 		if (strcmp(path, "*") == 0)
271 			printf("%s\n", udp->d_name);
272 	}
273 	return (0);
274 }
275 
276 /*
277  * get next entry in a directory.
278  */
279 static struct hs_direct *
280 readdir(struct dirinfo *dirp)
281 {
282 	static struct hs_direct hsdep;
283 	register struct direct *udp = &hsdep.hs_ufs_dir;
284 	register struct inode *ip;
285 	register fileid_t *filep;
286 	register daddr_t lbn;
287 	register int off;
288 
289 	filep = dirp->fi;
290 	ip = filep->fi_inode;
291 	for (;;) {
292 		if (dirp->loc >= ip->i_size) {
293 			return (NULL);
294 		}
295 		off = dirp->loc & ((1 << ISO_SECTOR_SHIFT) - 1);
296 		if (off == 0) {
297 			lbn = hdbtodb(dirp->loc >> ISO_SECTOR_SHIFT);
298 			filep->fi_blocknum = lbn + hdbtodb(ip->i_number);
299 			filep->fi_count = ISO_SECTOR_SIZE;
300 			/* check the block cache */
301 			if ((filep->fi_memp = get_bcache(filep)) == 0)
302 				if (set_bcache(filep))
303 					return ((struct hs_direct *)-1);
304 		}
305 		dirp->loc += parse_dir(filep, off, &hsdep);
306 		if (udp->d_reclen == 0 && dirp->loc <= ip->i_size) {
307 			dirp->loc = roundup(dirp->loc, ISO_SECTOR_SIZE);
308 			continue;
309 		}
310 		return (&hsdep);
311 	}
312 }
313 
314 /*
315  * Get the next block of data from the file.  If possible, dma right into
316  * user's buffer
317  */
318 static int
319 getblock(fileid_t *filep, caddr_t buf, int count, int *rcount)
320 {
321 	register struct inode *ip;
322 	register caddr_t p;
323 	register int off, size, diff;
324 	register daddr_t lbn;
325 	static int	pos;
326 	static char 	ind[] = "|/-\\";	/* that's entertainment? */
327 	static int	blks_read;
328 
329 	ip = filep->fi_inode;
330 	p = filep->fi_memp;
331 	if ((signed)filep->fi_count <= 0) {
332 
333 		/* find the amt left to be read in the file */
334 		diff = ip->i_size - filep->fi_offset;
335 		if (diff <= 0) {
336 			printf("Short read\n");
337 			return (-1);
338 		}
339 
340 		/* which block (or frag) in the file do we read? */
341 		lbn = hdbtodb(filep->fi_offset >> ISO_SECTOR_SHIFT);
342 
343 		/* which physical block on the device do we read? */
344 		filep->fi_blocknum = lbn + hdbtodb(ip->i_number);
345 
346 		off = filep->fi_offset & ((1 << ISO_SECTOR_SHIFT) - 1);
347 
348 		size = sizeof (filep->fi_buf);
349 		if (size > ISO_SECTOR_SIZE)
350 			size = ISO_SECTOR_SIZE;
351 
352 		filep->fi_count = size;
353 		filep->fi_memp = filep->fi_buf;
354 
355 		/*
356 		 * optimization if we are reading large blocks of data then
357 		 * we can go directly to user's buffer
358 		 */
359 		*rcount = 0;
360 		if (off == 0 && count >= size) {
361 			filep->fi_memp = buf;
362 			if (diskread(filep)) {
363 				return (-1);
364 			}
365 			*rcount = size;
366 			filep->fi_count = 0;
367 			read_opt++;
368 			if ((blks_read++ & 0x3) == 0)
369 				printf("%c\b", ind[pos++ & 3]);
370 			return (0);
371 		} else
372 			if (diskread(filep))
373 				return (-1);
374 
375 		/*
376 		 * round and round she goes (though not on every block..
377 		 * - OBP's take a fair bit of time to actually print stuff)
378 		 */
379 		if ((blks_read++ & 0x3) == 0)
380 			printf("%c\b", ind[pos++ & 3]);
381 
382 		if (filep->fi_offset - off + size >= ip->i_size)
383 			filep->fi_count = diff + off;
384 		filep->fi_count -= off;
385 		p = &filep->fi_memp[off];
386 	}
387 	filep->fi_memp = p;
388 	return (0);
389 }
390 
391 
392 /*
393  *  This is the high-level read function.  It works like this.
394  *  We assume that our IO device buffers up some amount of
395  *  data ant that we can get a ptr to it.  Thus we need
396  *  to actually call the device func about filesize/blocksize times
397  *  and this greatly increases our IO speed.  When we already
398  *  have data in the buffer, we just return that data (with bcopy() ).
399  */
400 
401 static ssize_t
402 boot_hsfs_read(int fd, caddr_t buf, size_t count)
403 {
404 	size_t i, j;
405 	struct inode *ip;
406 	caddr_t	n;
407 	fileid_t *filep;
408 	int rcount;
409 
410 	if (!(filep = find_fp(fd))) {
411 		return (-1);
412 	}
413 
414 	ip = filep->fi_inode;
415 
416 	if (filep->fi_offset + count > ip->i_size)
417 		count = ip->i_size - filep->fi_offset;
418 
419 	/* that was easy */
420 	if ((i = count) == 0)
421 		return (0);
422 
423 	n = buf;
424 	while (i > 0) {
425 		/* If we need to reload the buffer, do so */
426 		if ((j = filep->fi_count) == 0) {
427 			getblock(filep, buf, i, &rcount);
428 			i -= rcount;
429 			buf += rcount;
430 			filep->fi_offset += rcount;
431 		} else {
432 			/* else just bcopy from our buffer */
433 			j = MIN(i, j);
434 			bcopy(filep->fi_memp, buf, (unsigned)j);
435 			buf += j;
436 			filep->fi_memp += j;
437 			filep->fi_offset += j;
438 			filep->fi_count -= j;
439 			i -= j;
440 		}
441 	}
442 	return (buf - n);
443 }
444 
445 /*
446  *	This routine will open a device as it is known by the
447  *	V2 OBP.
448  *	Interface Defn:
449  *	err = mountroot(string);
450  *	err:	0 on success
451  *		-1 on failure
452  *	string:	char string describing the properties of the device.
453  *	We must not dork with any fi[]'s here.  Save that for later.
454  */
455 
456 static int
457 boot_hsfs_mountroot(char *str)
458 {
459 	ihandle_t	h;
460 	struct hs_volume *fsp;
461 	char 		*bufp;
462 
463 	if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
464 		printf("mountroot()\n");
465 
466 	/*
467 	 * If already mounted, just return success.
468 	 */
469 	if (root_ino != 0) {
470 		return (0);
471 	}
472 
473 	h = prom_open(str);
474 
475 	if (h == 0) {
476 		printf("Cannot open %s\n", str);
477 		return (-1);
478 	}
479 
480 	devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
481 	devp->di_taken = 1;
482 	devp->di_dcookie = h;
483 	devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
484 	(void) strcpy(devp->di_desc, str);
485 	bzero(devp->un_fs.dummy, sizeof (devp->un_fs.dummy));
486 	head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
487 	head->fi_back = head->fi_forw = head;
488 	head->fi_filedes = 0;
489 	head->fi_taken = 0;
490 
491 	/* Setup read of the "superblock" */
492 	bzero(head->fi_buf, sizeof (head->fi_buf));
493 	head->fi_devp = devp;
494 	head->fi_blocknum = hdbtodb(ISO_VOLDESC_SEC);
495 	head->fi_count = ISO_SECTOR_SIZE;
496 	head->fi_memp = head->fi_buf;
497 	head->fi_offset = 0;
498 
499 	if (diskread(head)) {
500 		printf("mountroot(): read super block failed!\n");
501 		boot_hsfs_closeall(1);
502 		return (-1);
503 	}
504 
505 	bufp = head->fi_memp;
506 	fsp = (struct hs_volume *)devp->un_fs.dummy;
507 	/* Since RRIP is based on ISO9660, that's where we start */
508 
509 	if (ISO_DESC_TYPE(bufp) != ISO_VD_PVD ||
510 	    strncmp((char *)(ISO_std_id(bufp)), (char *)(ISO_ID_STRING),
511 	    ISO_ID_STRLEN) != 0 || ISO_STD_VER(bufp) != ISO_ID_VER) {
512 		boot_hsfs_closeall(1);
513 		return (-1);
514 	}
515 
516 	/* Now we fill in the volume descriptor */
517 	fsp->vol_size = ISO_VOL_SIZE(bufp);
518 	fsp->lbn_size = ISO_BLK_SIZE(bufp);
519 	fsp->lbn_shift = ISO_SECTOR_SHIFT;
520 	fsp->lbn_secshift = ISO_SECTOR_SHIFT;
521 	fsp->vol_set_size = (ushort_t)ISO_SET_SIZE(bufp);
522 	fsp->vol_set_seq = (ushort_t)ISO_SET_SEQ(bufp);
523 
524 	/* Make sure we have a valid logical block size */
525 	if (fsp->lbn_size & ~(1 << fsp->lbn_shift)) {
526 		printf("%d byte logical block size invalid.\n", fsp->lbn_size);
527 		boot_hsfs_closeall(1);
528 		return (-1);
529 	}
530 
531 	/* Since an HSFS root could be located anywhere on the media! */
532 	root_ino = IDE_EXT_LBN(ISO_root_dir(bufp));
533 
534 	if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE)) {
535 		int	i;
536 
537 		printf("root_ino=%d\n", root_ino);
538 		printf("ID=");
539 		for (i = 0; i < ISO_ID_STRLEN; i++)
540 			printf("%c", *(ISO_std_id(bufp)+i));
541 		printf(" VS=%d\n", fsp->vol_size);
542 	}
543 
544 	return (0);
545 }
546 
547 /*
548  * Unmount the currently mounted root fs.  In practice, this means
549  * closing all open files and releasing resources.  All of this
550  * is done by boot_hsfs_closeall().
551  */
552 
553 int
554 boot_hsfs_unmountroot(void)
555 {
556 	if (root_ino == 0)
557 		return (-1);
558 
559 	boot_hsfs_closeall(1);
560 
561 	return (0);
562 }
563 
564 /*
565  *	We allocate an fd here for use when talking
566  *	to the file itself.
567  */
568 
569 /*ARGSUSED*/
570 static int
571 boot_hsfs_open(char *filename, int flags)
572 {
573 	fileid_t	*filep;
574 	ino_t		inode;
575 	static int	filedes = 1;
576 
577 	/* build and link a new file descriptor */
578 	filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
579 	filep->fi_back = head->fi_back;
580 	filep->fi_forw = head;
581 	head->fi_back->fi_forw = filep;
582 	head->fi_back = filep;
583 
584 	filep->fi_filedes = filedes++;
585 	filep->fi_taken = 1;
586 	filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
587 	(void) strcpy(filep->fi_path, filename);
588 	filep->fi_devp = devp; /* dev is already "mounted" */
589 
590 	filep->fi_inode = 0;
591 
592 	inode = find(filep, filename);
593 	if (inode == (ino_t)0) {
594 		if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
595 			printf("open(%s) ENOENT\n", filename);
596 		(void) boot_hsfs_close(filep->fi_filedes);
597 		return (-1);
598 	}
599 
600 	filep->fi_blocknum = hdbtodb(inode);
601 	filep->fi_offset = filep->fi_count = 0;
602 
603 	if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
604 		printf("open(%s) fd=%d\n", filename, filep->fi_filedes);
605 	return (filep->fi_filedes);
606 }
607 
608 /*
609  * hsfs_fstat() only supports size, mode and times at present time.
610  */
611 
612 static int
613 boot_hsfs_fstat(int fd, struct bootstat *stp)
614 {
615 	fileid_t	*filep;
616 	struct inode	*ip;
617 
618 	if (!(filep = find_fp(fd)))
619 		return (-1);
620 
621 	ip = filep->fi_inode;
622 
623 	stp->st_mode = 0;
624 	stp->st_size = 0;
625 
626 	if (ip == NULL)
627 		return (0);
628 
629 	switch (ip->i_smode & IFMT) {
630 	case IFDIR:
631 		stp->st_mode = S_IFDIR;
632 		break;
633 	case IFREG:
634 		stp->st_mode = S_IFREG;
635 		break;
636 	default:
637 		break;
638 	}
639 	stp->st_size = ip->i_size;
640 
641 	/* file times */
642 	stp->st_atim.tv_sec = ip->i_atime.tv_sec;
643 	stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
644 	stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
645 	stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
646 	stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
647 	stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
648 
649 	return (0);
650 }
651 
652 /*
653  *  We don't do any IO here.
654  *  We just play games with the device pointers.
655  */
656 
657 /*ARGSUSED*/
658 static off_t
659 boot_hsfs_lseek(int fd, off_t addr, int whence)
660 {
661 	fileid_t *filep;
662 
663 	if (!(filep = find_fp(fd)))
664 		return (-1);
665 
666 	filep->fi_offset = addr;
667 	filep->fi_blocknum = addr / DEV_BSIZE;
668 	filep->fi_count = 0;
669 
670 	return (0);
671 }
672 
673 static int
674 boot_hsfs_close(int fd)
675 {
676 	fileid_t *filep;
677 
678 	if ((boothowto & RB_DEBUG) && (boothowto & RB_VERBOSE))
679 		printf("close(%d)\n", fd);
680 
681 	if (filep = find_fp(fd)) {
682 		/* Clear the ranks */
683 		bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
684 		filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
685 		filep->fi_memp = (caddr_t)0;
686 		filep->fi_devp = 0;
687 		filep->fi_taken = 0;
688 
689 		/* unlink and deallocate node */
690 		filep->fi_forw->fi_back = filep->fi_back;
691 		filep->fi_back->fi_forw = filep->fi_forw;
692 		bkmem_free((char *)filep, sizeof (fileid_t));
693 
694 		return (0);
695 	} else {
696 		/* Big problem */
697 		printf("\nFile descrip %d not allocated!", fd);
698 		return (-1);
699 	}
700 }
701 
702 /* closeall is now idempotent */
703 /*ARGSUSED*/
704 static void
705 boot_hsfs_closeall(int flag)
706 {
707 	fileid_t	*filep = head;
708 	extern int verbosemode;
709 
710 	if (devp == NULL) {
711 		if (head)
712 			prom_panic("boot_hsfs_closeall: head != NULL.\n");
713 		return;
714 	}
715 
716 	while ((filep = filep->fi_forw) != head)
717 		if (filep->fi_taken)
718 			if (boot_hsfs_close(filep->fi_filedes))
719 				prom_panic("Filesystem may be inconsistent.\n");
720 
721 
722 	release_cache(devp->di_dcookie);
723 	(void) prom_close(devp->di_dcookie);
724 	devp->di_taken = 0;
725 	if (verbosemode)
726 		print_cache_data();
727 	bkmem_free((char *)devp, sizeof (devid_t));
728 	bkmem_free((char *)head, sizeof (fileid_t));
729 	root_ino = 0;
730 	devp = NULL;
731 	head = NULL;
732 }
733 
734 static uint_t
735 parse_dir(fileid_t *filep, int offset, struct hs_direct *hsdep)
736 {
737 	char *bufp = (char *)(filep->fi_memp + offset);
738 	struct direct *udp = &hsdep->hs_ufs_dir;
739 	struct hs_direntry *hdp = &hsdep->hs_dir;
740 	uint_t ce_lbn;
741 	uint_t ce_len;
742 	uint_t nmlen;
743 	uint_t i;
744 	uchar_t c;
745 	int ret_code = 0;
746 
747 	if ((udp->d_reclen = IDE_DIR_LEN(bufp)) == 0)
748 		return (0);
749 
750 	hdp->ext_lbn  = IDE_EXT_LBN(bufp);
751 	hdp->ext_size = IDE_EXT_SIZE(bufp);
752 	hs_dodates(HS_VOL_TYPE_ISO, hdp, bufp);
753 	hdp->xar_len  = IDE_XAR_LEN(bufp);
754 	hdp->intlf_sz = IDE_INTRLV_SIZE(bufp);
755 	hdp->intlf_sk = IDE_INTRLV_SKIP(bufp);
756 	hdp->sym_link = NULL;
757 
758 	udp->d_ino = hdp->ext_lbn;
759 
760 	c = IDE_FLAGS(bufp);
761 	if (IDE_REGULAR_FILE(c)) {
762 		hdp->type = VREG;
763 		hdp->mode = IFREG;
764 		hdp->nlink = 1;
765 	} else if (IDE_REGULAR_DIR(c)) {
766 		hdp->type = VDIR;
767 		hdp->mode = IFDIR;
768 		hdp->nlink = 2;
769 	} else {
770 		printf("parse_dir(): file type=0x%x unknown.\n", c);
771 		return ((uint_t)-1);
772 	}
773 
774 	/* Some initial conditions */
775 	nmlen = IDE_NAME_LEN(bufp);
776 	c = *IDE_NAME(bufp);
777 	/* Special Case: Current Directory */
778 	if (nmlen == 1 && c == '\0') {
779 		udp->d_name[0] = '.';
780 		udp->d_name[1] = '\0';
781 		udp->d_namlen = 1;
782 	/* Special Case: Parent Directory */
783 	} else if (nmlen == 1 && c == '\001') {
784 		udp->d_name[0] = '.';
785 		udp->d_name[1] = '.';
786 		udp->d_name[2] = '\0';
787 		udp->d_namlen = 2;
788 	/* Other file name */
789 	} else {
790 		udp->d_namlen = 0;
791 		for (i = 0; i < nmlen; i++) {
792 			c = *(IDE_name(bufp)+i);
793 			if (c == ';')
794 				break;
795 			else if (c == ' ')
796 				continue;
797 			else
798 				udp->d_name[udp->d_namlen++] = c;
799 		}
800 		udp->d_name[udp->d_namlen] = '\0';
801 	}
802 	/* System Use Fields */
803 	ce_len = IDE_SUA_LEN(bufp);
804 	ce_lbn = 0;
805 	if ((int)(ce_len) > 0) {
806 		ce_lbn = parse_susp((char *)IDE_sys_use_area(bufp),
807 		    &ce_len, hsdep);
808 		while (ce_lbn) {
809 			daddr_t save_blocknum = filep->fi_blocknum;
810 			daddr_t save_offset = filep->fi_offset;
811 			caddr_t save_memp = filep->fi_memp;
812 			uint_t save_count = filep->fi_count;
813 
814 #ifdef	noisy
815 			print_io_req(filep, "parse_dir(): [I]");
816 #endif	/* noisy */
817 
818 			filep->fi_blocknum = hdbtodb(ce_lbn);
819 			filep->fi_offset = 0;
820 			filep->fi_count = ISO_SECTOR_SIZE;
821 
822 #ifdef	noisy
823 			print_io_req(filep, "parse_dir(): [0]");
824 #endif	/* noisy */
825 
826 			if ((filep->fi_memp = get_bcache(filep)) == 0)
827 				ret_code = set_bcache(filep);
828 
829 #ifdef	noisy
830 			print_io_req(filep, "parse_dir(): [1]");
831 #endif	/* noisy */
832 
833 			if (ret_code) {
834 				filep->fi_blocknum = save_blocknum;
835 				filep->fi_offset = save_offset;
836 				filep->fi_memp = save_memp;
837 				filep->fi_count = save_count;
838 				printf("parse_dir(): "
839 				    "set_bcache() failed (%d)\n", ret_code);
840 				break;
841 			}
842 			ce_lbn = parse_susp(filep->fi_memp, &ce_len, hsdep);
843 
844 			filep->fi_blocknum = save_blocknum;
845 			filep->fi_offset = save_offset;
846 			filep->fi_memp = save_memp;
847 			filep->fi_count = save_count;
848 
849 #ifdef	noisy
850 			print_io_req(filep, "parse_dir(): [2]");
851 #endif	/* noisy */
852 		}
853 	}
854 
855 	return (udp->d_reclen);
856 }
857 
858 static uint_t
859 parse_susp(char *bufp, uint_t *ce_len, struct hs_direct *hsdep)
860 {
861 	struct direct *udp = &hsdep->hs_ufs_dir;
862 	uchar_t *susp;
863 	uint_t cur_off = 0;
864 	uint_t blk_len = *ce_len;
865 	uint_t susp_len = 0;
866 	uint_t ce_lbn = 0;
867 	uint_t i;
868 
869 	while (cur_off < blk_len) {
870 		susp = (uchar_t *)(bufp + cur_off);
871 		if (susp[0] == '\0' || susp[1] == '\0')
872 			break;
873 		susp_len = SUF_LEN(susp);
874 		if (susp_len == 0)
875 			break;
876 		for (i = 0; i < hsfs_num_sig; i++) {
877 			if (strncmp(hsfs_sig_tab[i],
878 			    (char *)susp, SUF_SIG_LEN) == 0) {
879 #ifdef	noisy
880 				if ((boothowto & RB_DEBUG) &&
881 				    (boothowto & RB_VERBOSE))
882 					printf("  SUSP_%c%c %d\n",
883 					    susp[0], susp[1], susp_len);
884 #endif	/* noisy */
885 				switch (i) {
886 				case SUSP_SP_IX:
887 					if (CHECK_BYTES_OK(susp)) {
888 						sua_offset =
889 						    SP_SUA_OFFSET(susp);
890 #ifdef	lint
891 						/* this may not be needed */
892 						i = (int)sua_offset;
893 #endif	/* lint */
894 					}
895 					break;
896 
897 				case SUSP_CE_IX:
898 					ce_lbn = CE_BLK_LOC(susp);
899 					*ce_len = CE_CONT_LEN(susp);
900 #ifdef	noisy
901 					if ((boothowto & RB_DEBUG) &&
902 					    (boothowto & RB_VERBOSE))
903 						printf("parse_susp(): "
904 						    "CE: ce_lbn = %d "
905 						    "ce_len=%d\n",
906 						    ce_lbn, *ce_len);
907 #endif	/* noisy */
908 					break;
909 
910 				case SUSP_ST_IX:
911 					printf("parse_susp(): ST: returning "
912 					    "%d\n", ce_lbn);
913 					return (ce_lbn);
914 
915 				case RRIP_SL_IX:
916 #ifdef	noisy
917 					if ((boothowto & RB_DEBUG) &&
918 					    (boothowto & RB_VERBOSE))
919 						printf("parse_susp(): "
920 						    "******* SL *******\n");
921 #endif	/* noisy */
922 					break;
923 
924 				case RRIP_RR_IX:
925 					break;
926 
927 				case RRIP_NM_IX:
928 					if (!RRIP_NAME_FLAGS(susp)) {
929 						udp->d_namlen =
930 						    RRIP_NAME_LEN(susp);
931 						bcopy((char *)RRIP_name(susp),
932 						    (char *)udp->d_name,
933 						    udp->d_namlen);
934 						udp->d_name
935 						    [udp->d_namlen] = '\0';
936 					}
937 					break;
938 				}
939 			cur_off += susp_len;
940 			break;
941 			}
942 		}
943 		if (i > hsfs_num_sig) {
944 			printf("parse_susp(): Bad SUSP\n");
945 			cur_off = blk_len;
946 			break;
947 		}
948 	}
949 	return (ce_lbn);
950 }
951 
952 static void
953 hs_seti(fileid_t *filep, struct hs_direct *hsdep, ino_t inode)
954 {
955 	register struct inode *ip;
956 	int dv = filep->fi_devp->di_dcookie;
957 
958 	/* Try the inode cache first */
959 	if ((filep->fi_inode = get_icache(dv, inode)) != NULL)
960 		return;
961 
962 	filep->fi_inode = (struct inode *)bkmem_alloc(sizeof (struct inode));
963 	ip = filep->fi_inode;
964 	bzero((char *)ip, sizeof (struct inode));
965 	ip->i_size = hsdep->hs_dir.ext_size;
966 	ip->i_smode = hsdep->hs_dir.mode;
967 	ip->i_number = inode;
968 	ip->i_atime.tv_sec = hsdep->hs_dir.adate.tv_sec;
969 	ip->i_atime.tv_usec = hsdep->hs_dir.adate.tv_usec;
970 	ip->i_ctime.tv_sec = hsdep->hs_dir.cdate.tv_sec;
971 	ip->i_ctime.tv_usec = hsdep->hs_dir.cdate.tv_usec;
972 	ip->i_mtime.tv_sec = hsdep->hs_dir.mdate.tv_sec;
973 	ip->i_mtime.tv_usec = hsdep->hs_dir.mdate.tv_usec;
974 	set_icache(dv, inode, ip, sizeof (struct inode));
975 }
976 
977 #ifdef	noisy
978 static void
979 print_io_req(fileid_t *filep, char *str)
980 {
981 	printf("%s o=%d b=%d c=%d m=%x\n",
982 	    str,
983 	    filep->fi_offset,
984 	    filep->fi_blocknum,
985 	    filep->fi_count,
986 	    (uint_t)filep->fi_memp);
987 }
988 #endif	/* noisy */
989 
990 static int
991 boot_hsfs_getdents(int fd, struct dirent *dep, unsigned size)
992 {
993 	/*
994 	 * Read directory entries from the file open on "fd" into the
995 	 * "size"-byte buffer at "dep" until the buffer is exhausted
996 	 * or we reach EOF on the directory.  Returns the number of
997 	 * entries read.
998 	 */
999 	int n;
1000 	int cnt = 0;
1001 	struct dirinfo dir;
1002 	struct hs_direct *hdp;
1003 	unsigned long oldoff, oldblok;
1004 
1005 #define	SLOP (sizeof (struct dirent) - offsetof(struct dirent, d_name[1]))
1006 
1007 	if (!(dir.fi = find_fp(fd)) ||
1008 	    ((dir.fi->fi_inode->i_smode & IFMT) != IFDIR)) {
1009 		/*
1010 		 *  Bogus file descriptor, bail out now!
1011 		 */
1012 		return (-1);
1013 	}
1014 
1015 	oldoff = dir.loc = dir.fi->fi_offset;
1016 	oldblok = dir.fi->fi_blocknum;
1017 
1018 	for (hdp = readdir(&dir); hdp; hdp = readdir(&dir)) {
1019 		/*
1020 		 * Compute name length and break loop if there's not
1021 		 * enough space in the output buffer for the next
1022 		 * entry.
1023 		 *
1024 		 *  NOTE: "SLOP" is the number of bytes inserted into the dirent
1025 		 *	  struct's "d_name" field by the compiler to preserve
1026 		 *	  alignment.
1027 		 */
1028 		n = strlen(hdp->hs_ufs_dir.d_name);
1029 		n = roundup((sizeof (struct dirent) + ((n > SLOP) ? n : 0)),
1030 		    sizeof (off_t));
1031 
1032 		if (n > size) {
1033 			dir.fi->fi_blocknum = oldblok;
1034 			dir.fi->fi_offset = oldoff;
1035 			break;
1036 		}
1037 
1038 		oldblok = dir.fi->fi_blocknum;
1039 		oldoff = dir.loc;
1040 		size -= n;
1041 		cnt += 1;
1042 
1043 		(void) strcpy(dep->d_name, hdp->hs_ufs_dir.d_name);
1044 		dep->d_ino = hdp->hs_ufs_dir.d_ino;
1045 		dep->d_off = dir.loc;
1046 		dep->d_reclen = (unsigned short)n;
1047 
1048 		dep = (struct dirent *)((char *)dep + n);
1049 	}
1050 
1051 #undef SLOP
1052 
1053 	return (cnt);
1054 }
1055 
1056 static void
1057 hs_dodates(enum hs_vol_type type, struct hs_direntry *hdp, char *bufp)
1058 {
1059 	if (type == HS_VOL_TYPE_HS) {
1060 		hs_parse_dirdate(HDE_cdate(bufp), &hdp->cdate);
1061 		hs_parse_dirdate(HDE_cdate(bufp), &hdp->adate);
1062 		hs_parse_dirdate(HDE_cdate(bufp), &hdp->mdate);
1063 	} else if (type == HS_VOL_TYPE_ISO) {
1064 		hs_parse_dirdate(IDE_cdate(bufp), &hdp->cdate);
1065 		hs_parse_dirdate(IDE_cdate(bufp), &hdp->adate);
1066 		hs_parse_dirdate(IDE_cdate(bufp), &hdp->mdate);
1067 	} else
1068 		prom_panic("hs_dodates:  bad volume type");
1069 }
1070 
1071 /*
1072  * hs_parse_dirdate
1073  *
1074  * Parse the short 'directory-format' date into a Unix timeval.
1075  * This is the date format used in Directory Entries.
1076  *
1077  * If the date is not representable, make something up.
1078  */
1079 void
1080 hs_parse_dirdate(uchar_t *dp, struct timeval *tvp)
1081 {
1082 	int year, month, day, hour, minute, sec, gmtoff;
1083 
1084 	year = HDE_DATE_YEAR(dp);
1085 	month = HDE_DATE_MONTH(dp);
1086 	day = HDE_DATE_DAY(dp);
1087 	hour = HDE_DATE_HOUR(dp);
1088 	minute = HDE_DATE_MIN(dp);
1089 	sec = HDE_DATE_SEC(dp);
1090 	gmtoff = HDE_DATE_GMTOFF(dp);
1091 
1092 	tvp->tv_usec = 0;
1093 	if (year < THE_EPOCH) {
1094 		tvp->tv_sec = 0;
1095 	} else {
1096 		tvp->tv_sec = hs_date_to_gmtime(year, month, day, gmtoff);
1097 		if (tvp->tv_sec != -1) {
1098 			tvp->tv_sec += ((hour * 60) + minute) * 60 + sec;
1099 		}
1100 	}
1101 
1102 	return;
1103 
1104 }
1105 
1106 /*
1107  * hs_parse_longdate
1108  *
1109  * Parse the long 'user-oriented' date into a Unix timeval.
1110  * This is the date format used in the Volume Descriptor.
1111  *
1112  * If the date is not representable, make something up.
1113  */
1114 void
1115 hs_parse_longdate(uchar_t *dp, struct timeval *tvp)
1116 {
1117 	int year, month, day, hour, minute, sec, gmtoff;
1118 
1119 	year = HSV_DATE_YEAR(dp);
1120 	month = HSV_DATE_MONTH(dp);
1121 	day = HSV_DATE_DAY(dp);
1122 	hour = HSV_DATE_HOUR(dp);
1123 	minute = HSV_DATE_MIN(dp);
1124 	sec = HSV_DATE_SEC(dp);
1125 	gmtoff = HSV_DATE_GMTOFF(dp);
1126 
1127 	tvp->tv_usec = 0;
1128 	if (year < THE_EPOCH) {
1129 		tvp->tv_sec = 0;
1130 	} else {
1131 		tvp->tv_sec = hs_date_to_gmtime(year, month, day, gmtoff);
1132 		if (tvp->tv_sec != -1) {
1133 			tvp->tv_sec += ((hour * 60) + minute) * 60 + sec;
1134 			tvp->tv_usec = HSV_DATE_HSEC(dp) * 10000;
1135 		}
1136 	}
1137 
1138 }
1139 
1140 /* cumulative number of seconds per month,  non-leap and leap-year versions */
1141 static time_t cum_sec[] = {
1142 	0x0, 0x28de80, 0x4dc880, 0x76a700, 0x9e3400, 0xc71280,
1143 	0xee9f80, 0x1177e00, 0x1405c80, 0x167e980, 0x190c800, 0x1b85500
1144 };
1145 static time_t cum_sec_leap[] = {
1146 	0x0, 0x28de80, 0x4f1a00, 0x77f880, 0x9f8580, 0xc86400,
1147 	0xeff100, 0x118cf80, 0x141ae00, 0x1693b00, 0x1921980, 0x1b9a680
1148 };
1149 #define	SEC_PER_DAY	0x15180
1150 #define	SEC_PER_YEAR	0x1e13380
1151 
1152 /*
1153  * hs_date_to_gmtime
1154  *
1155  * Convert year(1970-2099)/month(1-12)/day(1-31) to seconds-since-1970/1/1.
1156  *
1157  * Returns -1 if the date is out of range.
1158  */
1159 static time_t
1160 hs_date_to_gmtime(int year, int mon, int day, int gmtoff)
1161 {
1162 	time_t sum;
1163 	time_t *cp;
1164 	int y;
1165 
1166 	if ((year < THE_EPOCH) || (year > END_OF_TIME) ||
1167 	    (mon < 1) || (mon > 12) ||
1168 	    (day < 1) || (day > 31))
1169 		return (-1);
1170 
1171 	/*
1172 	 * Figure seconds until this year and correct for leap years.
1173 	 * Note: 2000 is a leap year but not 2100.
1174 	 */
1175 	y = year - THE_EPOCH;
1176 	sum = y * SEC_PER_YEAR;
1177 	sum += ((y + 1) / 4) * SEC_PER_DAY;
1178 	/*
1179 	 * Point to the correct table for this year and
1180 	 * add in seconds until this month.
1181 	 */
1182 	cp = ((y + 2) % 4) ? cum_sec : cum_sec_leap;
1183 	sum += cp[mon - 1];
1184 	/*
1185 	 * Add in seconds until 0:00 of this day.
1186 	 * (days-per-month validation is not done here)
1187 	 */
1188 	sum += (day - 1) * SEC_PER_DAY;
1189 	sum -= (gmtoff * 15 * 60);
1190 	return (sum);
1191 }
1192