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