xref: /illumos-gate/usr/src/stand/lib/fs/ufs/ufsops.c (revision d1aea6f139360e9e7f1504facb24f8521047b15c)
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 
42 int print_cache_stats = 0;
43 
44 /*
45  * This fd is used when talking to the device file itself.
46  */
47 static fileid_t *head;
48 /*
49  * hooks into ufs logging support
50  */
51 extern void	lufs_boot_init(fileid_t *);
52 extern void	lufs_closeall(void);
53 extern void	lufs_merge_deltas(fileid_t *);
54 
55 /* Only got one of these...ergo, only 1 fs open at once */
56 /* static */
57 devid_t		*ufs_devp;
58 
59 struct dirinfo {
60 	int 	loc;
61 	fileid_t *fi;
62 };
63 
64 /*
65  *  Function prototypes
66  */
67 static int	boot_ufs_mountroot(char *str);
68 static int	boot_ufs_unmountroot(void);
69 static int	boot_ufs_open(char *filename, int flags);
70 static int	boot_ufs_close(int fd);
71 static ssize_t	boot_ufs_read(int fd, caddr_t buf, size_t size);
72 static off_t	boot_ufs_lseek(int, off_t, int);
73 static int	boot_ufs_fstat(int fd, struct bootstat *stp);
74 static void	boot_ufs_closeall(int flag);
75 static int	boot_ufs_getdents(int fd, struct dirent *dep, unsigned size);
76 
77 struct 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 
90 static 	ino_t	find(fileid_t *filep, char *path);
91 static	ino_t	dlook(fileid_t *filep, char *path);
92 static 	daddr32_t	sbmap(fileid_t *filep, daddr32_t bn);
93 static  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 */
98 static union {
99 	char		*blk[NBUFS];
100 	daddr32_t		*dummy;
101 } b;
102 daddr32_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 
114 static int
115 openi(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 
154 static fileid_t *
155 find_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 
168 static ino_t
169 find(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  */
248 static daddr32_t
249 sbmap(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 
320 static ino_t
321 dlook(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  */
404 struct direct *
405 readdir(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  */
449 static int
450 getblock(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 
547 static ssize_t
548 boot_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 			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 
598 static int
599 boot_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 
660 int
661 boot_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*/
677 static int
678 boot_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 		boot_ufs_close(filep->fi_filedes);
701 		return (-1);
702 	}
703 	if (openi(filep, inode)) {
704 		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 
718 static off_t
719 boot_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 
750 static int
751 boot_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 
791 static int
792 boot_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*/
823 static void
824 boot_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 
852 static int
853 boot_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) strcpy(dep->d_name, dp->d_name);
949 					dep->d_off = dir.loc;
950 					dep->d_reclen = (ushort_t)n;
951 
952 					dep = (struct dirent *)
953 					    ((char *)dep + n);
954 				}
955 			}
956 			/*
957 			 * Remember where we left off for next time
958 			 */
959 			fp->fi_blocknum = oldblok;
960 			fp->fi_offset = oldoff;
961 
962 			return (cnt);
963 		}
964 	}
965 
966 #undef SLOP
967 
968 	return (-1);
969 }
970