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 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
25 */
26
27#include <sys/param.h>
28#include <sys/vnode.h>
29#include <sys/fs/ufs_fsdir.h>
30#include <sys/fs/ufs_fs.h>
31#include <sys/fs/ufs_inode.h>
32#include <sys/sysmacros.h>
33#include <sys/promif.h>
34
35#include <sys/stat.h>
36#include <sys/bootvfs.h>
37#include <sys/bootdebug.h>
38#include <sys/salib.h>
39#include <sys/sacache.h>
40
41
42int print_cache_stats = 0;
43
44/*
45 * This fd is used when talking to the device file itself.
46 */
47static fileid_t *head;
48/*
49 * hooks into ufs logging support
50 */
51extern void	lufs_boot_init(fileid_t *);
52extern void	lufs_closeall(void);
53extern void	lufs_merge_deltas(fileid_t *);
54
55/* Only got one of these...ergo, only 1 fs open at once */
56/* static */
57devid_t		*ufs_devp;
58
59struct dirinfo {
60	int 	loc;
61	fileid_t *fi;
62};
63
64/*
65 *  Function prototypes
66 */
67static int	boot_ufs_mountroot(char *str);
68static int	boot_ufs_unmountroot(void);
69static int	boot_ufs_open(char *filename, int flags);
70static int	boot_ufs_close(int fd);
71static ssize_t	boot_ufs_read(int fd, caddr_t buf, size_t size);
72static off_t	boot_ufs_lseek(int, off_t, int);
73static int	boot_ufs_fstat(int fd, struct bootstat *stp);
74static void	boot_ufs_closeall(int flag);
75static int	boot_ufs_getdents(int fd, struct dirent *dep, unsigned size);
76
77struct boot_fs_ops boot_ufs_ops = {
78	"ufs",
79	boot_ufs_mountroot,
80	boot_ufs_unmountroot,
81	boot_ufs_open,
82	boot_ufs_close,
83	boot_ufs_read,
84	boot_ufs_lseek,
85	boot_ufs_fstat,
86	boot_ufs_closeall,
87	boot_ufs_getdents
88};
89
90static 	ino_t	find(fileid_t *filep, char *path);
91static	ino_t	dlook(fileid_t *filep, char *path);
92static 	daddr32_t	sbmap(fileid_t *filep, daddr32_t bn);
93static  struct direct *readdir(struct dirinfo *dstuff);
94
95/* These are the pools of buffers, etc. */
96#define	NBUFS	(NIADDR+1)
97/* Compilers like to play with alignment, so force the issue here */
98static union {
99	char		*blk[NBUFS];
100	daddr32_t		*dummy;
101} b;
102daddr32_t		blknos[NBUFS];
103
104/*
105 *	There is only 1 open (mounted) device at any given time.
106 *	So we can keep a single, global devp file descriptor to
107 *	use to index into the di[] array.  This is not true for the
108 *	fi[] array.  We can have more than one file open at once,
109 *	so there is no global fd for the fi[].
110 *	The user program must save the fd passed back from open()
111 *	and use it to do subsequent read()'s.
112 */
113
114static int
115openi(fileid_t *filep, ino_t inode)
116{
117	int retval;
118	struct dinode *dp;
119	devid_t *devp = filep->fi_devp;
120
121	/* Try the inode cache first */
122	if ((filep->fi_inode = get_icache(devp->di_dcookie, inode)) != NULL)
123		return (0);
124	/* Nope, not there so lets read it off the disk. */
125	filep->fi_offset = 0;
126	filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs,
127	    itod(&devp->un_fs.di_fs, inode));
128
129	/* never more than 1 disk block */
130	filep->fi_count = devp->un_fs.di_fs.fs_bsize;
131	filep->fi_memp = filep->fi_buf;
132
133	/* Maybe the block is in the disk block cache */
134	if ((filep->fi_memp = get_bcache(filep)) == NULL) {
135		/* Not in the block cache so read it from disk */
136		if (retval = set_bcache(filep))
137			return (retval);
138		lufs_merge_deltas(filep);
139	}
140
141	dp = (struct dinode *)filep->fi_memp;
142	filep->fi_inode = (struct inode *)
143	    bkmem_alloc(sizeof (struct inode));
144	bzero((char *)filep->fi_inode, sizeof (struct inode));
145	filep->fi_inode->i_ic =
146	    dp[itoo(&devp->un_fs.di_fs, inode)].di_un.di_icom;
147	filep->fi_inode->i_number = inode;
148	if (set_ricache(devp->di_dcookie, inode, (void *)filep->fi_inode,
149	    sizeof (struct inode)))
150		filep->fi_inode->i_flag = FI_NOCACHE;
151	return (0);
152}
153
154static fileid_t *
155find_fp(int fd)
156{
157	fileid_t *filep = head;
158
159	if (fd >= 0) {
160		while ((filep = filep->fi_forw) != head)
161			if (fd == filep->fi_filedes)
162				return (filep->fi_taken ? filep : 0);
163	}
164
165	return (0);
166}
167
168static ino_t
169find(fileid_t *filep, char *path)
170{
171	char *q;
172	char c;
173	ino_t inode;
174	char lpath[MAXPATHLEN];
175	char *lpathp = lpath;
176	int len, r;
177	devid_t	*devp;
178
179	if (path == NULL || *path == '\0') {
180		printf("null path\n");
181		return ((ino_t)0);
182	}
183
184	bzero(lpath, sizeof (lpath));
185	bcopy(path, lpath, strlen(path));
186	devp = filep->fi_devp;
187	while (*lpathp) {
188		/* if at the beginning of pathname get root inode */
189		r = (lpathp == lpath);
190		if (r && openi(filep, (ino_t)UFSROOTINO))
191			return ((ino_t)0);
192		while (*lpathp == '/')
193			lpathp++;	/* skip leading slashes */
194		q = lpathp;
195		while (*q != '/' && *q != '\0')
196			q++;		/* find end of component */
197		c = *q;
198		*q = '\0';		/* terminate component */
199
200		/* Bail out early if opening root */
201		if (r && (*lpathp == '\0'))
202			return ((ino_t)UFSROOTINO);
203		if ((inode = dlook(filep, lpathp)) != 0) {
204			if (openi(filep, inode))
205				return ((ino_t)0);
206			if ((filep->fi_inode->i_smode & IFMT) == IFLNK) {
207				filep->fi_blocknum =
208				    fsbtodb(&devp->un_fs.di_fs,
209				    filep->fi_inode->i_db[0]);
210				filep->fi_count = DEV_BSIZE;
211				/* check the block cache */
212				if ((filep->fi_memp =
213				    get_bcache(filep)) == NULL) {
214					if (set_bcache(filep))
215						return ((ino_t)0);
216					lufs_merge_deltas(filep);
217				}
218				len = strlen(filep->fi_memp);
219				if (filep->fi_memp[0] == '/')
220					/* absolute link */
221					lpathp = lpath;
222				/* copy rest of unprocessed path up */
223				bcopy(q, lpathp + len, strlen(q + 1) + 2);
224				/* point to unprocessed path */
225				*(lpathp + len) = c;
226				/* prepend link in before unprocessed path */
227				bcopy(filep->fi_memp, lpathp, len);
228				lpathp = lpath;
229				continue;
230			} else
231				*q = c;
232			if (c == '\0')
233				break;
234			lpathp = q;
235			continue;
236		} else {
237			return ((ino_t)0);
238		}
239	}
240	return (inode);
241}
242
243/*
244 * Map <file, file logical block> into a file system block number.
245 * Reads indirect blocks as needed to find the block.  Returns zero when
246 * block isn't there; returns negative fsbn when block is uninitialized.
247 */
248static daddr32_t
249sbmap(fileid_t *filep, daddr32_t bn)
250{
251	struct inode *inodep;
252	int i, j, sh;
253	daddr32_t nb, *bap;
254	daddr32_t *db;
255	devid_t	*devp;
256
257	devp = filep->fi_devp;
258	inodep = filep->fi_inode;
259	db = inodep->i_db;
260
261	/*
262	 * blocks 0..NDADDR are direct blocks
263	 */
264	if (bn < NDADDR) {
265		nb = db[bn];
266		return (nb);
267	}
268
269	/*
270	 * addresses NIADDR have single and double indirect blocks.
271	 * the first step is to determine how many levels of indirection.
272	 */
273	sh = 1;
274	bn -= NDADDR;
275	for (j = NIADDR; j > 0; j--) {
276		sh *= NINDIR(&devp->un_fs.di_fs);
277		if (bn < sh)
278			break;
279		bn -= sh;
280	}
281	if (j == 0) {
282		return ((daddr32_t)0);
283	}
284
285	/*
286	 * fetch the first indirect block address from the inode
287	 */
288	nb = inodep->i_ib[NIADDR - j];
289	if (nb == 0) {
290		return ((daddr32_t)0);
291	}
292
293	/*
294	 * fetch through the indirect blocks
295	 */
296	for (; j <= NIADDR; j++) {
297		if (blknos[j] != nb) {
298			filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, nb);
299			filep->fi_count = devp->un_fs.di_fs.fs_bsize;
300			/* First look through the disk block cache */
301			if ((filep->fi_memp = get_bcache(filep)) == NULL) {
302				if (set_bcache(filep)) /* Gotta do I/O */
303					return (0);
304				lufs_merge_deltas(filep);
305			}
306			b.blk[j] = filep->fi_memp;
307			blknos[j] = nb;
308		}
309		bap = (daddr32_t *)b.blk[j];
310		sh /= NINDIR(&devp->un_fs.di_fs);
311		i = (bn / sh) % NINDIR(&devp->un_fs.di_fs);
312		nb = bap[i];
313		if (nb == 0) {
314			return ((daddr32_t)0);
315		}
316	}
317	return (nb);
318}
319
320static ino_t
321dlook(fileid_t *filep, char *path)
322{
323	devid_t *devp = filep->fi_devp;
324	struct direct *dp;
325	struct inode *ip;
326	struct dirinfo dirp;
327	int len;
328	ino_t in;
329#ifdef DEBUG
330	static int warned = 0;
331#endif
332
333	ip = filep->fi_inode;
334	if (path == NULL || *path == '\0')
335		return (0);
336	if ((ip->i_smode & IFMT) != IFDIR)
337		return (0);
338	if (ip->i_size == 0)
339		return (0);
340	len = strlen(path);
341
342	/*
343	 * First look through the directory entry cache
344	 */
345	if ((in = get_dcache(devp->di_dcookie, path, ip->i_number)) != 0)
346		return (in);
347
348	/*
349	 * If the entire directory is cached, return failure
350	 */
351	if (ip->i_flag & FI_CACHED)
352		return (0);
353
354	/*
355	 * Otherwise, read the entire directory into the cache
356	 */
357	in = 0;
358	dirp.loc = 0;
359	dirp.fi = filep;
360	if (!(ip->i_flag & FI_NOCACHE))
361		ip->i_flag |= FI_CACHED;
362	for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
363		if (dp->d_ino == 0)
364			continue;
365		if (dp->d_namlen == len && strcmp(path, dp->d_name) == 0)
366			in = dp->d_ino;
367
368		/*
369		 * Allow "*" to print all names at that level, w/out match
370		 */
371		if (strcmp(path, "*") == 0)
372			printf("%s\n", dp->d_name);
373
374		if (ip->i_flag & FI_NOCACHE)
375			continue;
376
377		/*
378		 * Put this entry into the cache.  If the entry has been
379		 * partially cached, check before inserting.  This should be
380		 * rare if sized correctly
381		 */
382		if ((ip->i_flag & FI_PARTIAL_CACHE) &&
383		    (get_dcache(devp->di_dcookie, dp->d_name, dp->d_ino) != 0))
384			continue;
385
386		if (set_rdcache(devp->di_dcookie, dp->d_name, ip->i_number,
387		    dp->d_ino)) {
388			ip->i_flag &= ~FI_CACHED;
389			ip->i_flag |= FI_PARTIAL_CACHE;
390#ifdef DEBUG
391			if (!warned) {
392				printf("ufsboot: directory cache too small\n");
393				warned++;
394			}
395#endif
396		}
397	}
398	return (in);
399}
400
401/*
402 * get next entry in a directory.
403 */
404struct direct *
405readdir(struct dirinfo *dstuff)
406{
407	struct direct *dp;
408	fileid_t *filep;
409	daddr32_t lbn, d;
410	int off;
411	devid_t	*devp;
412
413	filep = dstuff->fi;
414	devp = filep->fi_devp;
415	for (;;) {
416		if (dstuff->loc >= filep->fi_inode->i_size) {
417			return (NULL);
418		}
419		off = blkoff(&devp->un_fs.di_fs, dstuff->loc);
420		if (off == 0) {
421			lbn = lblkno(&devp->un_fs.di_fs, dstuff->loc);
422			d = sbmap(filep, lbn);
423
424			if (d <= 0)
425				return (NULL);
426
427			filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, d);
428			filep->fi_count =
429			    blksize(&devp->un_fs.di_fs, filep->fi_inode, lbn);
430			/* check the block cache */
431			if ((filep->fi_memp = get_bcache(filep)) == NULL) {
432				if (set_bcache(filep))
433					return (NULL);
434				lufs_merge_deltas(filep);
435			}
436		}
437		dp = (struct direct *)(filep->fi_memp + off);
438		dstuff->loc += dp->d_reclen;
439		if (dp->d_ino == 0)
440			continue;
441		return (dp);
442	}
443}
444
445/*
446 * Get the next block of data from the file.  If possible, dma right into
447 * user's buffer
448 */
449static int
450getblock(fileid_t *filep, caddr_t buf, int count, int *rcount)
451{
452	struct fs *fs;
453	caddr_t p;
454	int off, size, diff, zeroize;
455	daddr32_t lbn, fsbn;
456	devid_t	*devp;
457	static int	pos;
458	static char 	ind[] = "|/-\\";	/* that's entertainment? */
459	static int	blks_read;
460	devp = filep->fi_devp;
461	p = filep->fi_memp;
462	if ((signed)filep->fi_count <= 0) {
463
464		/* find the amt left to be read in the file */
465		diff = filep->fi_inode->i_size - filep->fi_offset;
466		if (diff <= 0) {
467			printf("Short read\n");
468			return (-1);
469		}
470
471		fs = &devp->un_fs.di_fs;
472		/* which block (or frag) in the file do we read? */
473		lbn = lblkno(fs, filep->fi_offset);
474
475		/* which physical block on the device do we read? */
476		fsbn = sbmap(filep, lbn);
477
478		/*
479		 * zero fsbn -> unallocated hole.
480		 * negative fsbn -> allocated but uninitialized.
481		 * since we only read from the fs, treat both the same.
482		 */
483		zeroize = (fsbn <= 0);
484
485		filep->fi_blocknum = fsbtodb(fs, fsbn);
486
487		off = blkoff(fs, filep->fi_offset);
488
489		/* either blksize or fragsize */
490		size = blksize(fs, filep->fi_inode, lbn);
491		filep->fi_count = size;
492		filep->fi_memp = filep->fi_buf;
493
494		/*
495		 * optimization if we are reading large blocks of data then
496		 * we can go directly to user's buffer
497		 */
498		*rcount = 0;
499		if (off == 0 && count >= size) {
500			filep->fi_memp = buf;
501			if (zeroize) {
502				bzero(buf, size);
503			} else if (diskread(filep)) {
504				return (-1);
505			}
506			*rcount = size;
507			filep->fi_count = 0;
508			read_opt++;
509			if ((blks_read++ & 0x3) == 0)
510				printf("%c\b", ind[pos++ & 3]);
511			return (0);
512		} else {
513			if (zeroize) {
514				bzero(filep->fi_memp, size);
515			} else if (diskread(filep))
516				return (-1);
517		}
518
519		/*
520		 * round and round she goes (though not on every block..
521		 * - OBP's take a fair bit of time to actually print stuff)
522		 * On x86, the screen oriented bootconf program doesn't
523		 * want this noise...
524		 */
525		if ((blks_read++ & 0x3) == 0)
526			printf("%c\b", ind[pos++ & 3]);
527
528		if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
529			filep->fi_count = diff + off;
530		filep->fi_count -= off;
531		p = &filep->fi_memp[off];
532	}
533	filep->fi_memp = p;
534	return (0);
535}
536
537
538/*
539 *  This is the high-level read function.  It works like this.
540 *  We assume that our IO device buffers up some amount of
541 *  data and that we can get a ptr to it.  Thus we need
542 *  to actually call the device func about filesize/blocksize times
543 *  and this greatly increases our IO speed.  When we already
544 *  have data in the buffer, we just return that data (with bcopy() ).
545 */
546
547static ssize_t
548boot_ufs_read(int fd, caddr_t buf, size_t count)
549{
550	size_t i, j;
551	caddr_t	n;
552	int rcount;
553	fileid_t *filep;
554
555	if (!(filep = find_fp(fd))) {
556		return (-1);
557	}
558
559	if (filep->fi_offset + count > filep->fi_inode->i_size)
560		count = filep->fi_inode->i_size - filep->fi_offset;
561
562	/* that was easy */
563	if ((i = count) == 0)
564		return (0);
565
566	n = buf;
567	while (i > 0) {
568		/* If we need to reload the buffer, do so */
569		if ((j = filep->fi_count) == 0) {
570			(void) getblock(filep, buf, i, &rcount);
571			i -= rcount;
572			buf += rcount;
573			filep->fi_offset += rcount;
574		} else {
575			/* else just bcopy from our buffer */
576			j = MIN(i, j);
577			bcopy(filep->fi_memp, buf, (unsigned)j);
578			buf += j;
579			filep->fi_memp += j;
580			filep->fi_offset += j;
581			filep->fi_count -= j;
582			i -= j;
583		}
584	}
585	return (buf - n);
586}
587
588/*
589 *	This routine will open a device as it is known by the V2 OBP.
590 *	Interface Defn:
591 *	err = boot_ufs_mountroot(string);
592 *		err = 0 on success
593 *		err = -1 on failure
594 *	string:	char string describing the properties of the device.
595 *	We must not dork with any fi[]'s here.  Save that for later.
596 */
597
598static int
599boot_ufs_mountroot(char *str)
600{
601	int	h;
602
603	/*
604	 * Open the device and setup the read of the ufs superblock
605	 * only the first time mountroot is called.  Subsequent calls
606	 * to mountroot succeed immediatly
607	 */
608	if (ufs_devp == NULL) {
609
610		/*
611		 * Encode the knowledge that we normally boot from the 'a'
612		 * slice of the leaf device on the OBP path; we also permit
613		 * a 'nolabel' device, i.e. the entire device.  Since v2path
614		 * points to 'str' as well, changing str should have the
615		 * desired result.
616		 */
617		if (strchr(str, ':') == NULL) {
618			(void) strcat(str, ":a");
619		}
620		h = prom_open(str);
621		if (h == 0) {
622			printf("Cannot open %s\n", str);
623			return (-1);
624		}
625
626		ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
627		ufs_devp->di_taken = 1;
628		ufs_devp->di_dcookie = h;
629		ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
630		(void) strcpy(ufs_devp->di_desc, str);
631		bzero(ufs_devp->un_fs.dummy, SBSIZE);
632		head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
633		head->fi_back = head->fi_forw = head;
634		head->fi_filedes = 0;
635		head->fi_taken = 0;
636
637		/* Setup read of the superblock */
638		head->fi_devp = ufs_devp;
639		head->fi_blocknum = SBLOCK;
640		head->fi_count = (uint_t)SBSIZE;
641		head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs);
642		head->fi_offset = 0;
643
644		if (diskread(head) ||
645		    ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) {
646			boot_ufs_closeall(1);
647			return (-1);
648		}
649		lufs_boot_init(head);
650	}
651	return (0);
652}
653
654/*
655 * Unmount the currently mounted root fs.  In practice, this means
656 * closing all open files and releasing resources.  All of this
657 * is done by boot_ufs_closeall().
658 */
659
660int
661boot_ufs_unmountroot(void)
662{
663	if (ufs_devp == NULL)
664		return (-1);
665
666	boot_ufs_closeall(1);
667
668	return (0);
669}
670
671/*
672 *	We allocate an fd here for use when talking
673 *	to the file itself.
674 */
675
676/*ARGSUSED*/
677static int
678boot_ufs_open(char *filename, int flags)
679{
680	fileid_t	*filep;
681	ino_t	inode;
682	static int	filedes = 1;
683
684	/* build and link a new file descriptor */
685	filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
686	filep->fi_back = head->fi_back;
687	filep->fi_forw = head;
688	head->fi_back->fi_forw = filep;
689	head->fi_back = filep;
690	filep->fi_filedes = filedes++;
691	filep->fi_taken = 1;
692	filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
693	(void) strcpy(filep->fi_path, filename);
694	filep->fi_devp = ufs_devp; /* dev is already "mounted" */
695	filep->fi_inode = NULL;
696	bzero(filep->fi_buf, MAXBSIZE);
697
698	inode = find(filep, filename);
699	if (inode == (ino_t)0) {
700		(void) boot_ufs_close(filep->fi_filedes);
701		return (-1);
702	}
703	if (openi(filep, inode)) {
704		(void) boot_ufs_close(filep->fi_filedes);
705		return (-1);
706	}
707
708	filep->fi_offset = filep->fi_count = 0;
709
710	return (filep->fi_filedes);
711}
712
713/*
714 *  We don't do any IO here.
715 *  We just play games with the device pointers.
716 */
717
718static off_t
719boot_ufs_lseek(int fd, off_t addr, int whence)
720{
721	fileid_t *filep;
722
723	/* Make sure user knows what file they're talking to */
724	if (!(filep = find_fp(fd)))
725		return (-1);
726
727	switch (whence) {
728	case SEEK_CUR:
729		filep->fi_offset += addr;
730		break;
731	case SEEK_SET:
732		filep->fi_offset = addr;
733		break;
734	default:
735	case SEEK_END:
736		printf("ufs_lseek(): invalid whence value %d\n", whence);
737		break;
738	}
739
740	filep->fi_blocknum = addr / DEV_BSIZE;
741	filep->fi_count = 0;
742
743	return (0);
744}
745
746/*
747 * ufs_fstat() only supports size, mode, and times at present time.
748 */
749
750static int
751boot_ufs_fstat(int fd, struct bootstat *stp)
752{
753	fileid_t	*filep;
754	struct inode	*ip;
755
756	if (!(filep = find_fp(fd)))
757		return (-1);
758
759	ip = filep->fi_inode;
760
761	stp->st_mode = 0;
762	stp->st_size = 0;
763
764	if (ip == NULL)
765		return (0);
766
767	switch (ip->i_smode & IFMT) {
768	case IFDIR:
769		stp->st_mode = S_IFDIR;
770		break;
771	case IFLNK:
772		stp->st_mode = S_IFLNK;
773		break;
774	case IFREG:
775		stp->st_mode = S_IFREG;
776		break;
777	default:
778		break;
779	}
780	stp->st_size = ip->i_size;
781	stp->st_atim.tv_sec = ip->i_atime.tv_sec;
782	stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
783	stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
784	stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
785	stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
786	stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
787
788	return (0);
789}
790
791static int
792boot_ufs_close(int fd)
793{
794	fileid_t *filep;
795
796	/* Make sure user knows what file they're talking to */
797	if (!(filep = find_fp(fd)))
798		return (-1);
799
800	if (filep->fi_taken && (filep != head)) {
801		/* Clear the ranks */
802		bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
803		filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
804		filep->fi_memp = (caddr_t)0;
805		filep->fi_devp = 0;
806		filep->fi_taken = 0;
807
808		/* unlink and deallocate node */
809		filep->fi_forw->fi_back = filep->fi_back;
810		filep->fi_back->fi_forw = filep->fi_forw;
811		bkmem_free((char *)filep, sizeof (fileid_t));
812
813		return (0);
814	} else {
815		/* Big problem */
816		printf("\nFile descrip %d not allocated!", fd);
817		return (-1);
818	}
819}
820
821/* closeall is now idempotent */
822/*ARGSUSED*/
823static void
824boot_ufs_closeall(int flag)
825{
826	fileid_t *filep = head;
827
828	if (ufs_devp == NULL) {
829		if (head)
830			prom_panic("boot_ufs_closeall: head != NULL.\n");
831		return;
832	}
833
834	while ((filep = filep->fi_forw) != head)
835		if (filep->fi_taken)
836			if (boot_ufs_close(filep->fi_filedes))
837				prom_panic("Filesystem may be inconsistent.\n");
838
839
840	release_cache(ufs_devp->di_dcookie);
841	(void) prom_close(ufs_devp->di_dcookie);
842	ufs_devp->di_taken = 0;
843	if (verbosemode & print_cache_stats)
844		print_cache_data();
845	lufs_closeall();
846	bkmem_free((char *)ufs_devp, sizeof (devid_t));
847	bkmem_free((char *)head, sizeof (fileid_t));
848	ufs_devp = (devid_t *)NULL;
849	head = (fileid_t *)NULL;
850}
851
852static int
853boot_ufs_getdents(int fd, struct dirent *dep, unsigned size)
854{
855	/*
856	 * Read directory entries from the file open on "fd" into the
857	 * "size"-byte buffer at "dep" until the buffer is exhausted
858	 * or we reach EOF on the directory.  Returns the number of
859	 * entries read.
860	 */
861	int n;
862	fileid_t *fp;
863	unsigned long oldoff, oldblok;
864
865#define	SLOP (sizeof (struct dirent) - offsetof(struct dirent, d_name[1]))
866
867	if (fp = find_fp(fd)) {
868		/*
869		 *  File is open, check type to make sure it's a directory.
870		 */
871
872		while ((fp->fi_inode->i_smode & IFMT) == IFLNK) {
873			/*
874			 * If file is a symbolic link, we'll follow
875			 * it JIC it points to a directory!
876			 */
877			fileid_t fx;
878			char pn[MAXPATHLEN];
879			fp->fi_count = DEV_BSIZE;
880			fp->fi_blocknum = fsbtodb(&fp->fi_devp->un_fs.di_fs,
881			    fp->fi_inode->i_db[0]);
882
883			/*
884			 * Return failure if:
885			 * (a) we get an I/O error reading the path name.
886			 * (b) the path name points to a non-existant file,
887			 * (c) we get an I/O error reading the target inode.
888			 */
889			if ((fp->fi_memp = get_bcache(fp)) == NULL) {
890				if (set_bcache(fp))
891					return (-1);
892				lufs_merge_deltas(fp);
893			}
894			if (!(n = find(&fx, strcpy(pn, fp->fi_memp))) ||
895			    openi(fp = &fx, n)) {
896				return (-1);
897			}
898		}
899
900		if ((fp->fi_inode->i_smode & IFMT) == IFDIR) {
901			/*
902			 * If target file is a directory, go ahead
903			 * and read it.  This consists of making
904			 * repeated calls to readdir() until we reach
905			 * end-of-file or run out of buffer space.
906			 */
907			int cnt = 0;
908			struct direct *dp;
909			struct dirinfo dir;
910
911			dir.fi = fp;
912			oldblok = fp->fi_blocknum;
913			dir.loc = oldoff = fp->fi_offset;
914
915			for (dp = readdir(&dir); dp; dp = readdir(&dir)) {
916				/*
917				 *  Read all directory entries in the file ...
918				 */
919
920				if (dp->d_ino) {
921					/*
922					 *  Next entry is valid.
923					 * Compute name length and
924					 * break loop if there's not
925					 * enough space in the output
926					 * buffer for the next entry.
927					 *
928					 *  NOTE: "SLOP" is the number
929					 * of bytes inserted into the
930					 * dirent struct's "d_name"
931					 * field by the compiler to
932					 * preserve alignment.
933					 */
934					dep->d_ino = dp->d_ino;
935					n = strlen(dp->d_name);
936					n = roundup((sizeof (struct dirent) +
937					    ((n > SLOP) ? n : 0)),
938					    sizeof (off_t));
939
940					if (n > size)
941						break; /* user buffer is full */
942
943					oldblok = fp->fi_blocknum;
944					oldoff = dir.loc;
945					size -= n;
946					cnt += 1;
947
948					(void) strlcpy(dep->d_name, dp->d_name,
949					    strlen(dp->d_name) + 1);
950					dep->d_off = dir.loc;
951					dep->d_reclen = (ushort_t)n;
952
953					dep = (struct dirent *)
954					    ((char *)dep + n);
955				}
956			}
957			/*
958			 * Remember where we left off for next time
959			 */
960			fp->fi_blocknum = oldblok;
961			fp->fi_offset = oldoff;
962
963			return (cnt);
964		}
965	}
966
967#undef SLOP
968
969	return (-1);
970}
971