xref: /illumos-gate/usr/src/stand/lib/fs/ufs/ufsops.c (revision d2ec54f7875f7e05edd56195adbeb593c947763f)
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 2007 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 #ifndef	i386
459 	static int	pos;
460 	static char 	ind[] = "|/-\\";	/* that's entertainment? */
461 	static int	blks_read;
462 #endif
463 	devp = filep->fi_devp;
464 	p = filep->fi_memp;
465 	if ((signed)filep->fi_count <= 0) {
466 
467 		/* find the amt left to be read in the file */
468 		diff = filep->fi_inode->i_size - filep->fi_offset;
469 		if (diff <= 0) {
470 			printf("Short read\n");
471 			return (-1);
472 		}
473 
474 		fs = &devp->un_fs.di_fs;
475 		/* which block (or frag) in the file do we read? */
476 		lbn = lblkno(fs, filep->fi_offset);
477 
478 		/* which physical block on the device do we read? */
479 		fsbn = sbmap(filep, lbn);
480 
481 		/*
482 		 * zero fsbn -> unallocated hole.
483 		 * negative fsbn -> allocated but uninitialized.
484 		 * since we only read from the fs, treat both the same.
485 		 */
486 		zeroize = (fsbn <= 0);
487 
488 		filep->fi_blocknum = fsbtodb(fs, fsbn);
489 
490 		off = blkoff(fs, filep->fi_offset);
491 
492 		/* either blksize or fragsize */
493 		size = blksize(fs, filep->fi_inode, lbn);
494 		filep->fi_count = size;
495 		filep->fi_memp = filep->fi_buf;
496 
497 		/*
498 		 * optimization if we are reading large blocks of data then
499 		 * we can go directly to user's buffer
500 		 */
501 		*rcount = 0;
502 		if (off == 0 && count >= size) {
503 			filep->fi_memp = buf;
504 			if (zeroize) {
505 				bzero(buf, size);
506 			} else if (diskread(filep)) {
507 				return (-1);
508 			}
509 			*rcount = size;
510 			filep->fi_count = 0;
511 			read_opt++;
512 #ifndef	i386
513 			if ((blks_read++ & 0x3) == 0)
514 				printf("%c\b", ind[pos++ & 3]);
515 #endif
516 			return (0);
517 		} else {
518 			if (zeroize) {
519 				bzero(filep->fi_memp, size);
520 			} else if (diskread(filep))
521 				return (-1);
522 		}
523 
524 		/*
525 		 * round and round she goes (though not on every block..
526 		 * - OBP's take a fair bit of time to actually print stuff)
527 		 * On x86, the screen oriented bootconf program doesn't
528 		 * want this noise...
529 		 */
530 #ifndef	i386
531 		if ((blks_read++ & 0x3) == 0)
532 			printf("%c\b", ind[pos++ & 3]);
533 #endif
534 
535 		if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
536 			filep->fi_count = diff + off;
537 		filep->fi_count -= off;
538 		p = &filep->fi_memp[off];
539 	}
540 	filep->fi_memp = p;
541 	return (0);
542 }
543 
544 
545 /*
546  *  This is the high-level read function.  It works like this.
547  *  We assume that our IO device buffers up some amount of
548  *  data and that we can get a ptr to it.  Thus we need
549  *  to actually call the device func about filesize/blocksize times
550  *  and this greatly increases our IO speed.  When we already
551  *  have data in the buffer, we just return that data (with bcopy() ).
552  */
553 
554 static ssize_t
555 boot_ufs_read(int fd, caddr_t buf, size_t count)
556 {
557 	size_t i, j;
558 	caddr_t	n;
559 	int rcount;
560 	fileid_t *filep;
561 
562 	if (!(filep = find_fp(fd))) {
563 		return (-1);
564 	}
565 
566 	if (filep->fi_offset + count > filep->fi_inode->i_size)
567 		count = filep->fi_inode->i_size - filep->fi_offset;
568 
569 	/* that was easy */
570 	if ((i = count) == 0)
571 		return (0);
572 
573 	n = buf;
574 	while (i > 0) {
575 		/* If we need to reload the buffer, do so */
576 		if ((j = filep->fi_count) == 0) {
577 			getblock(filep, buf, i, &rcount);
578 			i -= rcount;
579 			buf += rcount;
580 			filep->fi_offset += rcount;
581 		} else {
582 			/* else just bcopy from our buffer */
583 			j = MIN(i, j);
584 			bcopy(filep->fi_memp, buf, (unsigned)j);
585 			buf += j;
586 			filep->fi_memp += j;
587 			filep->fi_offset += j;
588 			filep->fi_count -= j;
589 			i -= j;
590 		}
591 	}
592 	return (buf - n);
593 }
594 
595 /*
596  *	This routine will open a device as it is known by the V2 OBP.
597  *	Interface Defn:
598  *	err = boot_ufs_mountroot(string);
599  *		err = 0 on success
600  *		err = -1 on failure
601  *	string:	char string describing the properties of the device.
602  *	We must not dork with any fi[]'s here.  Save that for later.
603  */
604 
605 static int
606 boot_ufs_mountroot(char *str)
607 {
608 	int	h;
609 
610 	/*
611 	 * Open the device and setup the read of the ufs superblock
612 	 * only the first time mountroot is called.  Subsequent calls
613 	 * to mountroot succeed immediatly
614 	 */
615 	if (ufs_devp == NULL) {
616 
617 		/*
618 		 * Encode the knowledge that we normally boot from the 'a'
619 		 * slice of the leaf device on the OBP path; we also permit
620 		 * a 'nolabel' device, i.e. the entire device.  Since v2path
621 		 * points to 'str' as well, changing str should have the
622 		 * desired result.
623 		 */
624 		if (strchr(str, ':') == NULL) {
625 			(void) strcat(str, ":a");
626 		}
627 		h = prom_open(str);
628 		if (h == 0) {
629 			printf("Cannot open %s\n", str);
630 			return (-1);
631 		}
632 
633 		ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
634 		ufs_devp->di_taken = 1;
635 		ufs_devp->di_dcookie = h;
636 		ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
637 		(void) strcpy(ufs_devp->di_desc, str);
638 		bzero(ufs_devp->un_fs.dummy, SBSIZE);
639 		head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
640 		head->fi_back = head->fi_forw = head;
641 		head->fi_filedes = 0;
642 		head->fi_taken = 0;
643 
644 		/* Setup read of the superblock */
645 		head->fi_devp = ufs_devp;
646 		head->fi_blocknum = SBLOCK;
647 		head->fi_count = (uint_t)SBSIZE;
648 		head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs);
649 		head->fi_offset = 0;
650 
651 		if (diskread(head) ||
652 		    ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) {
653 			boot_ufs_closeall(1);
654 			return (-1);
655 		}
656 		lufs_boot_init(head);
657 	}
658 	return (0);
659 }
660 
661 /*
662  * Unmount the currently mounted root fs.  In practice, this means
663  * closing all open files and releasing resources.  All of this
664  * is done by boot_ufs_closeall().
665  */
666 
667 int
668 boot_ufs_unmountroot(void)
669 {
670 	if (ufs_devp == NULL)
671 		return (-1);
672 
673 	boot_ufs_closeall(1);
674 
675 	return (0);
676 }
677 
678 /*
679  *	We allocate an fd here for use when talking
680  *	to the file itself.
681  */
682 
683 /*ARGSUSED*/
684 static int
685 boot_ufs_open(char *filename, int flags)
686 {
687 	fileid_t	*filep;
688 	ino_t	inode;
689 	static int	filedes = 1;
690 
691 	/* build and link a new file descriptor */
692 	filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
693 	filep->fi_back = head->fi_back;
694 	filep->fi_forw = head;
695 	head->fi_back->fi_forw = filep;
696 	head->fi_back = filep;
697 	filep->fi_filedes = filedes++;
698 	filep->fi_taken = 1;
699 	filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
700 	(void) strcpy(filep->fi_path, filename);
701 	filep->fi_devp = ufs_devp; /* dev is already "mounted" */
702 	filep->fi_inode = NULL;
703 	bzero(filep->fi_buf, MAXBSIZE);
704 
705 	inode = find(filep, filename);
706 	if (inode == (ino_t)0) {
707 		boot_ufs_close(filep->fi_filedes);
708 		return (-1);
709 	}
710 	if (openi(filep, inode)) {
711 		boot_ufs_close(filep->fi_filedes);
712 		return (-1);
713 	}
714 
715 	filep->fi_offset = filep->fi_count = 0;
716 
717 	return (filep->fi_filedes);
718 }
719 
720 /*
721  *  We don't do any IO here.
722  *  We just play games with the device pointers.
723  */
724 
725 static off_t
726 boot_ufs_lseek(int fd, off_t addr, int whence)
727 {
728 	fileid_t *filep;
729 
730 	/* Make sure user knows what file he is talking to */
731 	if (!(filep = find_fp(fd)))
732 		return (-1);
733 
734 	switch (whence) {
735 	case SEEK_CUR:
736 		filep->fi_offset += addr;
737 		break;
738 	case SEEK_SET:
739 		filep->fi_offset = addr;
740 		break;
741 	default:
742 	case SEEK_END:
743 		printf("ufs_lseek(): invalid whence value %d\n", whence);
744 		break;
745 	}
746 
747 	filep->fi_blocknum = addr / DEV_BSIZE;
748 	filep->fi_count = 0;
749 
750 	return (0);
751 }
752 
753 /*
754  * ufs_fstat() only supports size, mode, and times at present time.
755  */
756 
757 static int
758 boot_ufs_fstat(int fd, struct bootstat *stp)
759 {
760 	fileid_t	*filep;
761 	struct inode	*ip;
762 
763 	if (!(filep = find_fp(fd)))
764 		return (-1);
765 
766 	ip = filep->fi_inode;
767 
768 	stp->st_mode = 0;
769 	stp->st_size = 0;
770 
771 	if (ip == NULL)
772 		return (0);
773 
774 	switch (ip->i_smode & IFMT) {
775 	case IFDIR:
776 		stp->st_mode = S_IFDIR;
777 		break;
778 	case IFLNK:
779 		stp->st_mode = S_IFLNK;
780 		break;
781 	case IFREG:
782 		stp->st_mode = S_IFREG;
783 		break;
784 	default:
785 		break;
786 	}
787 	stp->st_size = ip->i_size;
788 	stp->st_atim.tv_sec = ip->i_atime.tv_sec;
789 	stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
790 	stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
791 	stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
792 	stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
793 	stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
794 
795 	return (0);
796 }
797 
798 static int
799 boot_ufs_close(int fd)
800 {
801 	fileid_t *filep;
802 
803 	/* Make sure user knows what file he is talking to */
804 	if (!(filep = find_fp(fd)))
805 		return (-1);
806 
807 	if (filep->fi_taken && (filep != head)) {
808 		/* Clear the ranks */
809 		bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
810 		filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
811 		filep->fi_memp = (caddr_t)0;
812 		filep->fi_devp = 0;
813 		filep->fi_taken = 0;
814 
815 		/* unlink and deallocate node */
816 		filep->fi_forw->fi_back = filep->fi_back;
817 		filep->fi_back->fi_forw = filep->fi_forw;
818 		bkmem_free((char *)filep, sizeof (fileid_t));
819 
820 		return (0);
821 	} else {
822 		/* Big problem */
823 		printf("\nFile descrip %d not allocated!", fd);
824 		return (-1);
825 	}
826 }
827 
828 /* closeall is now idempotent */
829 /*ARGSUSED*/
830 static void
831 boot_ufs_closeall(int flag)
832 {
833 	fileid_t *filep = head;
834 
835 	if (ufs_devp == NULL) {
836 		if (head)
837 			prom_panic("boot_ufs_closeall: head != NULL.\n");
838 		return;
839 	}
840 
841 	while ((filep = filep->fi_forw) != head)
842 		if (filep->fi_taken)
843 			if (boot_ufs_close(filep->fi_filedes))
844 				prom_panic("Filesystem may be inconsistent.\n");
845 
846 
847 	release_cache(ufs_devp->di_dcookie);
848 	(void) prom_close(ufs_devp->di_dcookie);
849 	ufs_devp->di_taken = 0;
850 	if (verbosemode & print_cache_stats)
851 		print_cache_data();
852 	lufs_closeall();
853 	bkmem_free((char *)ufs_devp, sizeof (devid_t));
854 	bkmem_free((char *)head, sizeof (fileid_t));
855 	ufs_devp = (devid_t *)NULL;
856 	head = (fileid_t *)NULL;
857 }
858 
859 static int
860 boot_ufs_getdents(int fd, struct dirent *dep, unsigned size)
861 {
862 	/*
863 	 * Read directory entries from the file open on "fd" into the
864 	 * "size"-byte buffer at "dep" until the buffer is exhausted
865 	 * or we reach EOF on the directory.  Returns the number of
866 	 * entries read.
867 	 */
868 	int n;
869 	fileid_t *fp;
870 	unsigned long oldoff, oldblok;
871 
872 #define	SLOP (sizeof (struct dirent) - (int)&((struct dirent *)0)->d_name[1])
873 
874 	if (fp = find_fp(fd)) {
875 		/*
876 		 *  File is open, check type to make sure it's a directory.
877 		 */
878 
879 		while ((fp->fi_inode->i_smode & IFMT) == IFLNK) {
880 			/*
881 			 * If file is a symbolic link, we'll follow
882 			 * it JIC it points to a directory!
883 			 */
884 			fileid_t fx;
885 			char pn[MAXPATHLEN];
886 			fp->fi_count = DEV_BSIZE;
887 			fp->fi_blocknum = fsbtodb(&fp->fi_devp->un_fs.di_fs,
888 			    fp->fi_inode->i_db[0]);
889 
890 			/*
891 			 * Return failure if:
892 			 * (a) we get an I/O error reading the path name.
893 			 * (b) the path name points to a non-existant file,
894 			 * (c) we get an I/O error reading the target inode.
895 			 */
896 			if ((fp->fi_memp = get_bcache(fp)) == NULL) {
897 				if (set_bcache(fp))
898 					return (-1);
899 				lufs_merge_deltas(fp);
900 			}
901 			if (!(n = find(&fx, strcpy(pn, fp->fi_memp))) ||
902 			    openi(fp = &fx, n)) {
903 				return (-1);
904 			}
905 		}
906 
907 		if ((fp->fi_inode->i_smode & IFMT) == IFDIR) {
908 			/*
909 			 * If target file is a directory, go ahead
910 			 * and read it.  This consists of making
911 			 * repeated calls to readdir() until we reach
912 			 * end-of-file or run out of buffer space.
913 			 */
914 			int cnt = 0;
915 			struct direct *dp;
916 			struct dirinfo dir;
917 
918 			dir.fi = fp;
919 			oldblok = fp->fi_blocknum;
920 			dir.loc = oldoff = fp->fi_offset;
921 
922 			for (dp = readdir(&dir); dp; dp = readdir(&dir)) {
923 				/*
924 				 *  Read all directory entries in the file ...
925 				 */
926 
927 				if (dp->d_ino) {
928 					/*
929 					 *  Next entry is valid.
930 					 * Compute name length and
931 					 * break loop if there's not
932 					 * enough space in the output
933 					 * buffer for the next entry.
934 					 *
935 					 *  NOTE: "SLOP" is the number
936 					 * of bytes inserted into the
937 					 * dirent struct's "d_name"
938 					 * field by the compiler to
939 					 * preserve alignment.
940 					 */
941 					dep->d_ino = dp->d_ino;
942 					n = strlen(dp->d_name);
943 					n = roundup((sizeof (struct dirent) +
944 					    ((n > SLOP) ? n : 0)),
945 					    sizeof (off_t));
946 
947 					if (n > size)
948 						break; /* user buffer is full */
949 
950 					oldblok = fp->fi_blocknum;
951 					oldoff = dir.loc;
952 					size -= n;
953 					cnt += 1;
954 
955 					(void) strcpy(dep->d_name, dp->d_name);
956 					dep->d_off = dir.loc;
957 					dep->d_reclen = (ushort_t)n;
958 
959 					dep = (struct dirent *)
960 					    ((char *)dep + n);
961 				}
962 			}
963 			/*
964 			 * Remember where we left off for next time
965 			 */
966 			fp->fi_blocknum = oldblok;
967 			fp->fi_offset = oldoff;
968 
969 			return (cnt);
970 		}
971 	}
972 
973 #undef SLOP
974 
975 	return (-1);
976 }
977