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