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