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... */
56static uint_t	sua_offset = 0;
57
58/* The root inode on an HSFS filesystem can be anywhere! */
59static uint_t	root_ino = 0;		/* This is both a flag and a value */
60
61static fileid_t *head;
62
63/* Only got one of these...ergo, only 1 fs open at once */
64static devid_t *devp;
65
66struct dirinfo {
67	int 	loc;
68	fileid_t *fi;
69};
70
71struct hs_direct {
72    struct	direct	hs_ufs_dir;
73    struct	hs_direntry hs_dir;
74};
75
76/*
77 *  Function prototypes
78 */
79
80static int	boot_hsfs_mountroot(char *str);
81static int	boot_hsfs_unmountroot(void);
82static int	boot_hsfs_open(char *filename, int flags);
83static int	boot_hsfs_close(int fd);
84static ssize_t	boot_hsfs_read(int fd, caddr_t buf, size_t size);
85static off_t	boot_hsfs_lseek(int, off_t, int);
86static int	boot_hsfs_fstat(int fd, struct bootstat *stp);
87static void	boot_hsfs_closeall(int flag);
88static int	boot_hsfs_getdents(int fd, struct dirent *dep, unsigned size);
89
90struct 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
103static 	ino_t	find(fileid_t *, char *);
104static	ino_t	dlook(fileid_t *, char *);
105static	int	opendir(fileid_t *, ino_t);
106static	struct	hs_direct *readdir(struct dirinfo *);
107static	uint_t	parse_dir(fileid_t *, int, struct hs_direct *);
108static	uint_t	parse_susp(char *, uint_t *, struct hs_direct *);
109static	void	hs_seti(fileid_t *,  struct hs_direct *, ino_t);
110static void	hs_dodates(enum hs_vol_type, struct hs_direntry *, char *);
111static 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
123static int
124opendir(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
155static ino_t
156find(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
205static fileid_t *
206find_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
219static ino_t
220dlook(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 */
279static struct hs_direct *
280readdir(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 */
318static int
319getblock(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
401static ssize_t
402boot_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
456static int
457boot_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
553int
554boot_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*/
570static int
571boot_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
612static int
613boot_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*/
658static off_t
659boot_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
673static int
674boot_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*/
704static void
705boot_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
734static uint_t
735parse_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
858static uint_t
859parse_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
952static void
953hs_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
978static void
979print_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
990static int
991boot_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
1056static void
1057hs_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 */
1079void
1080hs_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 */
1114void
1115hs_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 */
1141static time_t cum_sec[] = {
1142	0x0, 0x28de80, 0x4dc880, 0x76a700, 0x9e3400, 0xc71280,
1143	0xee9f80, 0x1177e00, 0x1405c80, 0x167e980, 0x190c800, 0x1b85500
1144};
1145static 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 */
1159static time_t
1160hs_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