xref: /illumos-gate/usr/src/common/fs/ufsops.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright (c) 2016 by Delphix. All rights reserved.
26  * Copyright 2022 Oxide Computer Company
27  */
28 
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/vnode.h>
32 #include <sys/fs/ufs_fsdir.h>
33 #include <sys/fs/ufs_fs.h>
34 #include <sys/fs/ufs_inode.h>
35 #include <sys/sysmacros.h>
36 #include <sys/bootvfs.h>
37 #include <sys/filep.h>
38 #include <sys/kmem.h>
39 
40 #ifdef	_BOOT
41 #include "../common/util.h"
42 #else
43 #include <sys/sunddi.h>
44 #endif
45 
46 extern void *bkmem_alloc(size_t);
47 extern void bkmem_free(void *, size_t);
48 extern int cf_check_compressed(fileid_t *);
49 extern void cf_close(fileid_t *);
50 extern void cf_seek(fileid_t *, off_t, int);
51 extern int cf_read(fileid_t *, caddr_t, size_t);
52 
53 int bootrd_debug;
54 #ifdef _BOOT
55 #define	dprintf	if (bootrd_debug) printf
56 #else
57 #define	printf	kobj_printf
58 #define	dprintf	if (bootrd_debug) kobj_printf
59 
60 /* PRINTLIKE */
61 extern void kobj_printf(char *, ...);
62 #endif
63 
64 /*
65  * This fd is used when talking to the device file itself.
66  */
67 static fileid_t *head;
68 
69 /* Only got one of these...ergo, only 1 fs open at once */
70 /* static */
71 devid_t		*ufs_devp;
72 
73 struct dirinfo {
74 	int	loc;
75 	fileid_t *fi;
76 };
77 
78 static	int	bufs_close(int);
79 static	void	bufs_closeall(int);
80 static	ino_t	find(fileid_t *filep, char *path);
81 static	ino_t	dlook(fileid_t *filep, char *path);
82 static	daddr32_t	sbmap(fileid_t *filep, daddr32_t bn);
83 static  struct direct *readdir(struct dirinfo *dstuff);
84 static	void set_cache(int, void *, uint_t);
85 static	void *get_cache(int);
86 static	void free_cache();
87 
88 
89 /*
90  *	There is only 1 open (mounted) device at any given time.
91  *	So we can keep a single, global devp file descriptor to
92  *	use to index into the di[] array.  This is not true for the
93  *	fi[] array.  We can have more than one file open at once,
94  *	so there is no global fd for the fi[].
95  *	The user program must save the fd passed back from open()
96  *	and use it to do subsequent read()'s.
97  */
98 
99 static int
100 openi(fileid_t *filep, ino_t inode)
101 {
102 	struct dinode *dp;
103 	devid_t *devp = filep->fi_devp;
104 
105 	filep->fi_inode = get_cache((int)inode);
106 	if (filep->fi_inode != 0)
107 		return (0);
108 
109 	filep->fi_offset = 0;
110 	filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs,
111 	    itod(&devp->un_fs.di_fs, inode));
112 
113 	/* never more than 1 disk block */
114 	filep->fi_count = devp->un_fs.di_fs.fs_bsize;
115 	filep->fi_memp = 0;		/* cached read */
116 	if (diskread(filep) != 0) {
117 		return (0);
118 	}
119 
120 	dp = (struct dinode *)filep->fi_memp;
121 	filep->fi_inode = (struct inode *)
122 	    bkmem_alloc(sizeof (struct inode));
123 	bzero((char *)filep->fi_inode, sizeof (struct inode));
124 	filep->fi_inode->i_ic =
125 	    dp[itoo(&devp->un_fs.di_fs, inode)].di_un.di_icom;
126 	filep->fi_inode->i_number = inode;
127 	set_cache((int)inode, (void *)filep->fi_inode, sizeof (struct inode));
128 	return (0);
129 }
130 
131 static fileid_t *
132 find_fp(int fd)
133 {
134 	fileid_t *filep = head;
135 
136 	if (fd >= 0) {
137 		while ((filep = filep->fi_forw) != head)
138 			if (fd == filep->fi_filedes)
139 				return (filep->fi_taken ? filep : 0);
140 	}
141 
142 	return (0);
143 }
144 
145 static ino_t
146 find(fileid_t *filep, char *path)
147 {
148 	char *q;
149 	char c;
150 	ino_t inode;
151 	char lpath[MAXPATHLEN];
152 	char *lpathp = lpath;
153 	int len, r;
154 	devid_t	*devp;
155 
156 	inode = 0;
157 	if (path == NULL || *path == '\0') {
158 		printf("null path\n");
159 		return (inode);
160 	}
161 
162 	dprintf("openi: %s\n", path);
163 
164 	bzero(lpath, sizeof (lpath));
165 	bcopy(path, lpath, strlen(path));
166 	devp = filep->fi_devp;
167 	while (*lpathp) {
168 		/* if at the beginning of pathname get root inode */
169 		r = (lpathp == lpath);
170 		if (r && openi(filep, (ino_t)UFSROOTINO))
171 			return ((ino_t)0);
172 		while (*lpathp == '/')
173 			lpathp++;	/* skip leading slashes */
174 		q = lpathp;
175 		while (*q != '/' && *q != '\0')
176 			q++;		/* find end of component */
177 		c = *q;
178 		*q = '\0';		/* terminate component */
179 
180 		/* Bail out early if opening root */
181 		if (r && (*lpathp == '\0'))
182 			return ((ino_t)UFSROOTINO);
183 		if ((inode = dlook(filep, lpathp)) != 0) {
184 			if (openi(filep, inode))
185 				return ((ino_t)0);
186 			if ((filep->fi_inode->i_smode & IFMT) == IFLNK) {
187 				filep->fi_blocknum =
188 				    fsbtodb(&devp->un_fs.di_fs,
189 				    filep->fi_inode->i_db[0]);
190 				filep->fi_count = DEV_BSIZE;
191 				filep->fi_memp = 0;
192 				if (diskread(filep) != 0)
193 					return ((ino_t)0);
194 				len = strlen(filep->fi_memp);
195 				if (filep->fi_memp[0] == '/')
196 					/* absolute link */
197 					lpathp = lpath;
198 				/* copy rest of unprocessed path up */
199 				bcopy(q, lpathp + len, strlen(q + 1) + 2);
200 				/* point to unprocessed path */
201 				*(lpathp + len) = c;
202 				/* prepend link in before unprocessed path */
203 				bcopy(filep->fi_memp, lpathp, len);
204 				lpathp = lpath;
205 				continue;
206 			} else
207 				*q = c;
208 			if (c == '\0')
209 				break;
210 			lpathp = q;
211 			continue;
212 		} else {
213 			return ((ino_t)0);
214 		}
215 	}
216 	return (inode);
217 }
218 
219 static daddr32_t
220 sbmap(fileid_t *filep, daddr32_t bn)
221 {
222 	struct inode *inodep;
223 	int i, j, sh;
224 	daddr32_t nb, *bap;
225 	daddr32_t *db;
226 	devid_t	*devp;
227 
228 	devp = filep->fi_devp;
229 	inodep = filep->fi_inode;
230 	db = inodep->i_db;
231 
232 	/*
233 	 * blocks 0..NDADDR are direct blocks
234 	 */
235 	if (bn < NDADDR) {
236 		nb = db[bn];
237 		return (nb);
238 	}
239 
240 	/*
241 	 * addresses NIADDR have single and double indirect blocks.
242 	 * the first step is to determine how many levels of indirection.
243 	 */
244 	sh = 1;
245 	bn -= NDADDR;
246 	for (j = NIADDR; j > 0; j--) {
247 		sh *= NINDIR(&devp->un_fs.di_fs);
248 		if (bn < sh)
249 			break;
250 		bn -= sh;
251 	}
252 	if (j == 0) {
253 		return ((daddr32_t)0);
254 	}
255 
256 	/*
257 	 * fetch the first indirect block address from the inode
258 	 */
259 	nb = inodep->i_ib[NIADDR - j];
260 	if (nb == 0) {
261 		return ((daddr32_t)0);
262 	}
263 
264 	/*
265 	 * fetch through the indirect blocks
266 	 */
267 	for (; j <= NIADDR; j++) {
268 		filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, nb);
269 		filep->fi_count = devp->un_fs.di_fs.fs_bsize;
270 		filep->fi_memp = 0;
271 		if (diskread(filep) != 0)
272 			return (0);
273 		bap = (daddr32_t *)filep->fi_memp;
274 		sh /= NINDIR(&devp->un_fs.di_fs);
275 		i = (bn / sh) % NINDIR(&devp->un_fs.di_fs);
276 		nb = bap[i];
277 		if (nb == 0) {
278 			return ((daddr32_t)0);
279 		}
280 	}
281 	return (nb);
282 }
283 
284 static ino_t
285 dlook(fileid_t *filep, char *path)
286 {
287 	struct direct *dp;
288 	struct inode *ip;
289 	struct dirinfo dirp;
290 	int len;
291 
292 	ip = filep->fi_inode;
293 	if (path == NULL || *path == '\0')
294 		return (0);
295 
296 	dprintf("dlook: %s\n", path);
297 
298 	if ((ip->i_smode & IFMT) != IFDIR) {
299 		return (0);
300 	}
301 	if (ip->i_size == 0) {
302 		return (0);
303 	}
304 	len = strlen(path);
305 	dirp.loc = 0;
306 	dirp.fi = filep;
307 	for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
308 		if (dp->d_ino == 0)
309 			continue;
310 		if (dp->d_namlen == len && strcmp(path, dp->d_name) == 0) {
311 			return (dp->d_ino);
312 		}
313 		/* Allow "*" to print all names at that level, w/out match */
314 		if (strcmp(path, "*") == 0)
315 			dprintf("%s\n", dp->d_name);
316 	}
317 	return (0);
318 }
319 
320 /*
321  * get next entry in a directory.
322  */
323 struct direct *
324 readdir(struct dirinfo *dstuff)
325 {
326 	struct direct *dp;
327 	fileid_t *filep;
328 	daddr32_t lbn, d;
329 	int off;
330 	devid_t	*devp;
331 
332 	filep = dstuff->fi;
333 	devp = filep->fi_devp;
334 	for (;;) {
335 		if (dstuff->loc >= filep->fi_inode->i_size) {
336 			return (NULL);
337 		}
338 		off = blkoff(&devp->un_fs.di_fs, dstuff->loc);
339 		dprintf("readdir: off = 0x%x\n", off);
340 		if (off == 0) {
341 			lbn = lblkno(&devp->un_fs.di_fs, dstuff->loc);
342 			d = sbmap(filep, lbn);
343 
344 			if (d == 0)
345 				return (NULL);
346 
347 			filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, d);
348 			filep->fi_count =
349 			    blksize(&devp->un_fs.di_fs, filep->fi_inode, lbn);
350 			filep->fi_memp = 0;
351 			if (diskread(filep) != 0) {
352 				return (NULL);
353 			}
354 		}
355 		dp = (struct direct *)(filep->fi_memp + off);
356 		dstuff->loc += dp->d_reclen;
357 		if (dp->d_ino == 0)
358 			continue;
359 		dprintf("readdir: name = %s\n", dp->d_name);
360 		return (dp);
361 	}
362 }
363 
364 /*
365  * Get the next block of data from the file.  If possible, dma right into
366  * user's buffer
367  */
368 static int
369 getblock(fileid_t *filep, caddr_t buf, int count, int *rcount)
370 {
371 	struct fs *fs;
372 	caddr_t p;
373 	int off, size, diff;
374 	daddr32_t lbn;
375 	devid_t	*devp;
376 
377 	dprintf("getblock: buf 0x%p, count 0x%x\n", (void *)buf, count);
378 
379 	devp = filep->fi_devp;
380 	p = filep->fi_memp;
381 	if ((signed)filep->fi_count <= 0) {
382 
383 		/* find the amt left to be read in the file */
384 		diff = filep->fi_inode->i_size - filep->fi_offset;
385 		if (diff <= 0) {
386 			printf("Short read\n");
387 			return (-1);
388 		}
389 
390 		fs = &devp->un_fs.di_fs;
391 		/* which block (or frag) in the file do we read? */
392 		lbn = lblkno(fs, filep->fi_offset);
393 
394 		/* which physical block on the device do we read? */
395 		filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn));
396 
397 		off = blkoff(fs, filep->fi_offset);
398 
399 		/* either blksize or fragsize */
400 		size = blksize(fs, filep->fi_inode, lbn);
401 		filep->fi_count = size;
402 		filep->fi_memp = filep->fi_buf;
403 
404 		/*
405 		 * optimization if we are reading large blocks of data then
406 		 * we can go directly to user's buffer
407 		 */
408 		*rcount = 0;
409 		if (off == 0 && count >= size) {
410 			filep->fi_memp = buf;
411 			if (diskread(filep)) {
412 				return (-1);
413 			}
414 			*rcount = size;
415 			filep->fi_count = 0;
416 			return (0);
417 		} else if (diskread(filep))
418 			return (-1);
419 
420 		if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
421 			filep->fi_count = diff + off;
422 		filep->fi_count -= off;
423 		p = &filep->fi_memp[off];
424 	}
425 	filep->fi_memp = p;
426 	return (0);
427 }
428 
429 /*
430  * Get the next block of data from the file.  Don't attempt to go directly
431  * to user's buffer.
432  */
433 static int
434 getblock_noopt(fileid_t *filep)
435 {
436 	struct fs *fs;
437 	caddr_t p;
438 	int off, size, diff;
439 	daddr32_t lbn;
440 	devid_t	*devp;
441 
442 	dprintf("getblock_noopt: start\n");
443 
444 	devp = filep->fi_devp;
445 	p = filep->fi_memp;
446 	if ((signed)filep->fi_count <= 0) {
447 
448 		/* find the amt left to be read in the file */
449 		diff = filep->fi_inode->i_size - filep->fi_offset;
450 		if (diff <= 0) {
451 			printf("Short read\n");
452 			return (-1);
453 		}
454 
455 		fs = &devp->un_fs.di_fs;
456 		/* which block (or frag) in the file do we read? */
457 		lbn = lblkno(fs, filep->fi_offset);
458 
459 		/* which physical block on the device do we read? */
460 		filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn));
461 
462 		off = blkoff(fs, filep->fi_offset);
463 
464 		/* either blksize or fragsize */
465 		size = blksize(fs, filep->fi_inode, lbn);
466 		filep->fi_count = size;
467 		/* reading on a ramdisk, just get a pointer to the data */
468 		filep->fi_memp = NULL;
469 
470 		if (diskread(filep))
471 			return (-1);
472 
473 		if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
474 			filep->fi_count = diff + off;
475 		filep->fi_count -= off;
476 		p = &filep->fi_memp[off];
477 	}
478 	filep->fi_memp = p;
479 	return (0);
480 }
481 
482 
483 /*
484  *  This is the high-level read function.  It works like this.
485  *  We assume that our IO device buffers up some amount of
486  *  data and that we can get a ptr to it.  Thus we need
487  *  to actually call the device func about filesize/blocksize times
488  *  and this greatly increases our IO speed.  When we already
489  *  have data in the buffer, we just return that data (with bcopy() ).
490  */
491 
492 static ssize_t
493 bufs_read(int fd, caddr_t buf, size_t count)
494 {
495 	size_t i, j;
496 	caddr_t	n;
497 	int rcount;
498 	fileid_t *filep;
499 
500 	if (!(filep = find_fp(fd))) {
501 		return (-1);
502 	}
503 
504 	if ((filep->fi_flags & FI_COMPRESSED) == 0 &&
505 	    filep->fi_offset + count > filep->fi_inode->i_size)
506 		count = filep->fi_inode->i_size - filep->fi_offset;
507 
508 	/* that was easy */
509 	if ((i = count) == 0)
510 		return (0);
511 
512 	n = buf;
513 	while (i > 0) {
514 		if (filep->fi_flags & FI_COMPRESSED) {
515 			int rval;
516 
517 			if ((rval = cf_read(filep, buf, count)) < 0)
518 				return (0); /* encountered an error */
519 			j = (size_t)rval;
520 			if (j < i)
521 				i = j; /* short read, must have hit EOF */
522 		} else {
523 			/* If we need to reload the buffer, do so */
524 			if ((j = filep->fi_count) == 0) {
525 				(void) getblock(filep, buf, i, &rcount);
526 				i -= rcount;
527 				buf += rcount;
528 				filep->fi_offset += rcount;
529 				continue;
530 			} else {
531 				/* else just bcopy from our buffer */
532 				j = MIN(i, j);
533 				bcopy(filep->fi_memp, buf, (unsigned)j);
534 			}
535 		}
536 		buf += j;
537 		filep->fi_memp += j;
538 		filep->fi_offset += j;
539 		filep->fi_count -= j;
540 		i -= j;
541 	}
542 	return (buf - n);
543 }
544 
545 /*
546  *	This routine will open a device as it is known by the V2 OBP.
547  *	Interface Defn:
548  *	err = mountroot(string);
549  *		err = 0 on success
550  *		err = -1 on failure
551  *	string:	char string describing the properties of the device.
552  *	We must not dork with any fi[]'s here.  Save that for later.
553  */
554 
555 static int
556 bufs_mountroot(char *str)
557 {
558 	if (ufs_devp)		/* already mounted */
559 		return (0);
560 
561 	ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
562 	ufs_devp->di_taken = 1;
563 	ufs_devp->di_dcookie = 0;
564 	ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
565 	(void) strcpy(ufs_devp->di_desc, str);
566 	bzero(ufs_devp->un_fs.dummy, SBSIZE);
567 	head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
568 	head->fi_back = head->fi_forw = head;
569 	head->fi_filedes = 0;
570 	head->fi_taken = 0;
571 
572 	/* Setup read of the superblock */
573 	head->fi_devp = ufs_devp;
574 	head->fi_blocknum = SBLOCK;
575 	head->fi_count = (uint_t)SBSIZE;
576 	head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs);
577 	head->fi_offset = 0;
578 
579 	if (diskread(head)) {
580 		printf("failed to read superblock\n");
581 		(void) bufs_closeall(1);
582 		return (-1);
583 	}
584 
585 	if (ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) {
586 		dprintf("fs magic = 0x%x\n", ufs_devp->un_fs.di_fs.fs_magic);
587 		(void) bufs_closeall(1);
588 		return (-1);
589 	}
590 	dprintf("mountroot succeeded\n");
591 	return (0);
592 }
593 
594 /*
595  * Unmount the currently mounted root fs.  In practice, this means
596  * closing all open files and releasing resources.  All of this
597  * is done by closeall().
598  */
599 
600 static int
601 bufs_unmountroot(void)
602 {
603 	if (ufs_devp == NULL)
604 		return (-1);
605 
606 	(void) bufs_closeall(1);
607 
608 	return (0);
609 }
610 
611 /*
612  *	We allocate an fd here for use when talking
613  *	to the file itself.
614  */
615 
616 /*ARGSUSED*/
617 static int
618 bufs_open(char *filename, int flags)
619 {
620 	fileid_t	*filep;
621 	ino_t	inode;
622 	static int	filedes = 1;
623 
624 	dprintf("open: %s\n", filename);
625 
626 	/* build and link a new file descriptor */
627 	filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
628 	filep->fi_back = head->fi_back;
629 	filep->fi_forw = head;
630 	head->fi_back->fi_forw = filep;
631 	head->fi_back = filep;
632 	filep->fi_filedes = filedes++;
633 	filep->fi_taken = 1;
634 	filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
635 	(void) strcpy(filep->fi_path, filename);
636 	filep->fi_devp = ufs_devp; /* dev is already "mounted" */
637 	filep->fi_inode = NULL;
638 	bzero(filep->fi_buf, MAXBSIZE);
639 	filep->fi_getblock = getblock_noopt;
640 	filep->fi_flags = 0;
641 
642 	inode = find(filep, (char *)filename);
643 	if (inode == (ino_t)0) {
644 		dprintf("open: cannot find %s\n", filename);
645 		(void) bufs_close(filep->fi_filedes);
646 		return (-1);
647 	}
648 	if (openi(filep, inode)) {
649 		printf("open: cannot open %s\n", filename);
650 		(void) bufs_close(filep->fi_filedes);
651 		return (-1);
652 	}
653 
654 	filep->fi_offset = filep->fi_count = 0;
655 
656 	if (cf_check_compressed(filep) != 0)
657 		return (-1);
658 	return (filep->fi_filedes);
659 }
660 
661 /*
662  *  We don't do any IO here.
663  *  We just play games with the device pointers.
664  */
665 
666 static off_t
667 bufs_lseek(int fd, off_t addr, int whence)
668 {
669 	fileid_t *filep;
670 
671 	/* Make sure user knows what file they are talking to */
672 	if (!(filep = find_fp(fd)))
673 		return (-1);
674 
675 	if (filep->fi_flags & FI_COMPRESSED) {
676 		cf_seek(filep, addr, whence);
677 	} else {
678 		switch (whence) {
679 		case SEEK_CUR:
680 			filep->fi_offset += addr;
681 			break;
682 		case SEEK_SET:
683 			filep->fi_offset = addr;
684 			break;
685 		default:
686 		case SEEK_END:
687 			printf("lseek(): invalid whence value %d\n", whence);
688 			break;
689 		}
690 		filep->fi_blocknum = addr / DEV_BSIZE;
691 	}
692 
693 	filep->fi_count = 0;
694 
695 	return (0);
696 }
697 
698 
699 int
700 bufs_fstat(int fd, struct bootstat *stp)
701 {
702 	fileid_t	*filep;
703 	struct inode	*ip;
704 
705 	if (!(filep = find_fp(fd)))
706 		return (-1);
707 
708 	ip = filep->fi_inode;
709 
710 	stp->st_mode = 0;
711 	stp->st_size = 0;
712 
713 	if (ip == NULL)
714 		return (0);
715 
716 	switch (ip->i_smode & IFMT) {
717 	case IFLNK:
718 		stp->st_mode = S_IFLNK;
719 		break;
720 	case IFREG:
721 		stp->st_mode = S_IFREG;
722 		break;
723 	default:
724 		break;
725 	}
726 	/*
727 	 * NOTE: this size will be the compressed size for a compressed file
728 	 * This could confuse the caller since we decompress the file behind
729 	 * the scenes when the file is read.
730 	 */
731 	stp->st_size = ip->i_size;
732 	stp->st_atim.tv_sec = ip->i_atime.tv_sec;
733 	stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
734 	stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
735 	stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
736 	stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
737 	stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
738 
739 	return (0);
740 }
741 
742 
743 static int
744 bufs_close(int fd)
745 {
746 	fileid_t *filep;
747 
748 	/* Make sure user knows what file they are talking to */
749 	if (!(filep = find_fp(fd)))
750 		return (-1);
751 
752 	if (filep->fi_taken && (filep != head)) {
753 		/* Clear the ranks */
754 		bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
755 		filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
756 		filep->fi_memp = (caddr_t)0;
757 		filep->fi_devp = 0;
758 		filep->fi_taken = 0;
759 
760 		/* unlink and deallocate node */
761 		filep->fi_forw->fi_back = filep->fi_back;
762 		filep->fi_back->fi_forw = filep->fi_forw;
763 		cf_close(filep);
764 		bkmem_free((char *)filep, sizeof (fileid_t));
765 
766 		/*
767 		 * Some files are opened and closed in early boot, for example
768 		 * when doing a microcode update on the boot CPU. In that case
769 		 * the inode cache will contain memory allocated from boot
770 		 * pages, which will be invalid once kmem is initialised.
771 		 * Until kmem is ready, clear the inode cache when closing a
772 		 * file.
773 		 */
774 		if (kmem_ready == 0)
775 			free_cache();
776 
777 		return (0);
778 	} else {
779 		/* Big problem */
780 		printf("\nFile descrip %d not allocated!", fd);
781 		return (-1);
782 	}
783 }
784 
785 /*ARGSUSED*/
786 static void
787 bufs_closeall(int flag)
788 {
789 	fileid_t *filep = head;
790 
791 	while ((filep = filep->fi_forw) != head)
792 		if (filep->fi_taken)
793 			if (bufs_close(filep->fi_filedes))
794 				printf("Filesystem may be inconsistent.\n");
795 
796 	ufs_devp->di_taken = 0;
797 	bkmem_free((char *)ufs_devp, sizeof (devid_t));
798 	bkmem_free((char *)head, sizeof (fileid_t));
799 	ufs_devp = (devid_t *)NULL;
800 	head = (fileid_t *)NULL;
801 	free_cache();
802 }
803 
804 static struct cache {
805 	struct cache *next;
806 	void *data;
807 	int key;
808 	uint_t size;
809 } *icache;
810 
811 void
812 set_cache(int key, void *data, uint_t size)
813 {
814 	struct cache *entry = bkmem_alloc(sizeof (*entry));
815 	entry->key = key;
816 	entry->data = data;
817 	entry->size = size;
818 	if (icache) {
819 		entry->next = icache;
820 		icache = entry;
821 	} else {
822 		icache = entry;
823 		entry->next = 0;
824 	}
825 }
826 
827 void *
828 get_cache(int key)
829 {
830 	struct cache *entry = icache;
831 	while (entry) {
832 		if (entry->key == key)
833 			return (entry->data);
834 		entry = entry->next;
835 	}
836 	return (NULL);
837 }
838 
839 void
840 free_cache()
841 {
842 	struct cache *next, *entry = icache;
843 	while (entry) {
844 		next = entry->next;
845 		bkmem_free(entry->data, entry->size);
846 		bkmem_free(entry, sizeof (*entry));
847 		entry = next;
848 	}
849 	icache = 0;
850 }
851 
852 struct boot_fs_ops bufs_ops = {
853 	"boot_ufs",
854 	bufs_mountroot,
855 	bufs_unmountroot,
856 	bufs_open,
857 	bufs_close,
858 	bufs_read,
859 	bufs_lseek,
860 	bufs_fstat,
861 	NULL
862 };
863