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