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