xref: /illumos-gate/usr/src/common/fs/ufsops.c (revision d4039345c8fe6e54a31d17d91e86e393fdcf401b)
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 
44 extern void *bkmem_alloc(size_t);
45 extern void bkmem_free(void *, size_t);
46 extern int cf_check_compressed(fileid_t *);
47 extern void cf_close(fileid_t *);
48 extern void cf_seek(fileid_t *, off_t, int);
49 extern int cf_read(fileid_t *, caddr_t, size_t);
50 
51 int 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 */
59 extern void kobj_printf(char *, ...);
60 #endif
61 
62 /*
63  * This fd is used when talking to the device file itself.
64  */
65 static fileid_t *head;
66 
67 /* Only got one of these...ergo, only 1 fs open at once */
68 /* static */
69 devid_t		*ufs_devp;
70 
71 struct dirinfo {
72 	int	loc;
73 	fileid_t *fi;
74 };
75 
76 static	int	bufs_close(int);
77 static	void	bufs_closeall(int);
78 static	ino_t	find(fileid_t *filep, char *path);
79 static	ino_t	dlook(fileid_t *filep, char *path);
80 static	daddr32_t	sbmap(fileid_t *filep, daddr32_t bn);
81 static  struct direct *readdir(struct dirinfo *dstuff);
82 static	void set_cache(int, void *, uint_t);
83 static	void *get_cache(int);
84 static	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 
97 static int
98 openi(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 
129 static fileid_t *
130 find_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 
143 static ino_t
144 find(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 
217 static daddr32_t
218 sbmap(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 
282 static ino_t
283 dlook(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  */
321 struct direct *
322 readdir(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  */
366 static int
367 getblock(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  */
431 static int
432 getblock_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 
490 static ssize_t
491 bufs_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 			int rval;
514 
515 			if ((rval = cf_read(filep, buf, count)) < 0)
516 				return (0); /* encountered an error */
517 			j = (size_t)rval;
518 			if (j < i)
519 				i = j; /* short read, must have hit EOF */
520 		} else {
521 			/* If we need to reload the buffer, do so */
522 			if ((j = filep->fi_count) == 0) {
523 				(void) getblock(filep, buf, i, &rcount);
524 				i -= rcount;
525 				buf += rcount;
526 				filep->fi_offset += rcount;
527 				continue;
528 			} else {
529 				/* else just bcopy from our buffer */
530 				j = MIN(i, j);
531 				bcopy(filep->fi_memp, buf, (unsigned)j);
532 			}
533 		}
534 		buf += j;
535 		filep->fi_memp += j;
536 		filep->fi_offset += j;
537 		filep->fi_count -= j;
538 		i -= j;
539 	}
540 	return (buf - n);
541 }
542 
543 /*
544  *	This routine will open a device as it is known by the V2 OBP.
545  *	Interface Defn:
546  *	err = mountroot(string);
547  *		err = 0 on success
548  *		err = -1 on failure
549  *	string:	char string describing the properties of the device.
550  *	We must not dork with any fi[]'s here.  Save that for later.
551  */
552 
553 static int
554 bufs_mountroot(char *str)
555 {
556 	if (ufs_devp)		/* already mounted */
557 		return (0);
558 
559 	ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
560 	ufs_devp->di_taken = 1;
561 	ufs_devp->di_dcookie = 0;
562 	ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
563 	(void) strcpy(ufs_devp->di_desc, str);
564 	bzero(ufs_devp->un_fs.dummy, SBSIZE);
565 	head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
566 	head->fi_back = head->fi_forw = head;
567 	head->fi_filedes = 0;
568 	head->fi_taken = 0;
569 
570 	/* Setup read of the superblock */
571 	head->fi_devp = ufs_devp;
572 	head->fi_blocknum = SBLOCK;
573 	head->fi_count = (uint_t)SBSIZE;
574 	head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs);
575 	head->fi_offset = 0;
576 
577 	if (diskread(head)) {
578 		printf("failed to read superblock\n");
579 		(void) bufs_closeall(1);
580 		return (-1);
581 	}
582 
583 	if (ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) {
584 		dprintf("fs magic = 0x%x\n", ufs_devp->un_fs.di_fs.fs_magic);
585 		(void) bufs_closeall(1);
586 		return (-1);
587 	}
588 	dprintf("mountroot succeeded\n");
589 	return (0);
590 }
591 
592 /*
593  * Unmount the currently mounted root fs.  In practice, this means
594  * closing all open files and releasing resources.  All of this
595  * is done by closeall().
596  */
597 
598 static int
599 bufs_unmountroot(void)
600 {
601 	if (ufs_devp == NULL)
602 		return (-1);
603 
604 	(void) bufs_closeall(1);
605 
606 	return (0);
607 }
608 
609 /*
610  *	We allocate an fd here for use when talking
611  *	to the file itself.
612  */
613 
614 /*ARGSUSED*/
615 static int
616 bufs_open(char *filename, int flags)
617 {
618 	fileid_t	*filep;
619 	ino_t	inode;
620 	static int	filedes = 1;
621 
622 	dprintf("open: %s\n", filename);
623 
624 	/* build and link a new file descriptor */
625 	filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
626 	filep->fi_back = head->fi_back;
627 	filep->fi_forw = head;
628 	head->fi_back->fi_forw = filep;
629 	head->fi_back = filep;
630 	filep->fi_filedes = filedes++;
631 	filep->fi_taken = 1;
632 	filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
633 	(void) strcpy(filep->fi_path, filename);
634 	filep->fi_devp = ufs_devp; /* dev is already "mounted" */
635 	filep->fi_inode = NULL;
636 	bzero(filep->fi_buf, MAXBSIZE);
637 	filep->fi_getblock = getblock_noopt;
638 	filep->fi_flags = 0;
639 
640 	inode = find(filep, (char *)filename);
641 	if (inode == (ino_t)0) {
642 		dprintf("open: cannot find %s\n", filename);
643 		(void) bufs_close(filep->fi_filedes);
644 		return (-1);
645 	}
646 	if (openi(filep, inode)) {
647 		printf("open: cannot open %s\n", filename);
648 		(void) bufs_close(filep->fi_filedes);
649 		return (-1);
650 	}
651 
652 	filep->fi_offset = filep->fi_count = 0;
653 
654 	if (cf_check_compressed(filep) != 0)
655 		return (-1);
656 	return (filep->fi_filedes);
657 }
658 
659 /*
660  *  We don't do any IO here.
661  *  We just play games with the device pointers.
662  */
663 
664 static off_t
665 bufs_lseek(int fd, off_t addr, int whence)
666 {
667 	fileid_t *filep;
668 
669 	/* Make sure user knows what file they are talking to */
670 	if (!(filep = find_fp(fd)))
671 		return (-1);
672 
673 	if (filep->fi_flags & FI_COMPRESSED) {
674 		cf_seek(filep, addr, whence);
675 	} else {
676 		switch (whence) {
677 		case SEEK_CUR:
678 			filep->fi_offset += addr;
679 			break;
680 		case SEEK_SET:
681 			filep->fi_offset = addr;
682 			break;
683 		default:
684 		case SEEK_END:
685 			printf("lseek(): invalid whence value %d\n", whence);
686 			break;
687 		}
688 		filep->fi_blocknum = addr / DEV_BSIZE;
689 	}
690 
691 	filep->fi_count = 0;
692 
693 	return (0);
694 }
695 
696 
697 int
698 bufs_fstat(int fd, struct bootstat *stp)
699 {
700 	fileid_t	*filep;
701 	struct inode	*ip;
702 
703 	if (!(filep = find_fp(fd)))
704 		return (-1);
705 
706 	ip = filep->fi_inode;
707 
708 	stp->st_mode = 0;
709 	stp->st_size = 0;
710 
711 	if (ip == NULL)
712 		return (0);
713 
714 	switch (ip->i_smode & IFMT) {
715 	case IFLNK:
716 		stp->st_mode = S_IFLNK;
717 		break;
718 	case IFREG:
719 		stp->st_mode = S_IFREG;
720 		break;
721 	default:
722 		break;
723 	}
724 	/*
725 	 * NOTE: this size will be the compressed size for a compressed file
726 	 * This could confuse the caller since we decompress the file behind
727 	 * the scenes when the file is read.
728 	 */
729 	stp->st_size = ip->i_size;
730 	stp->st_atim.tv_sec = ip->i_atime.tv_sec;
731 	stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
732 	stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
733 	stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
734 	stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
735 	stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
736 
737 	return (0);
738 }
739 
740 
741 static int
742 bufs_close(int fd)
743 {
744 	fileid_t *filep;
745 
746 	/* Make sure user knows what file they are talking to */
747 	if (!(filep = find_fp(fd)))
748 		return (-1);
749 
750 	if (filep->fi_taken && (filep != head)) {
751 		/* Clear the ranks */
752 		bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
753 		filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
754 		filep->fi_memp = (caddr_t)0;
755 		filep->fi_devp = 0;
756 		filep->fi_taken = 0;
757 
758 		/* unlink and deallocate node */
759 		filep->fi_forw->fi_back = filep->fi_back;
760 		filep->fi_back->fi_forw = filep->fi_forw;
761 		cf_close(filep);
762 		bkmem_free((char *)filep, sizeof (fileid_t));
763 
764 		return (0);
765 	} else {
766 		/* Big problem */
767 		printf("\nFile descrip %d not allocated!", fd);
768 		return (-1);
769 	}
770 }
771 
772 /*ARGSUSED*/
773 static void
774 bufs_closeall(int flag)
775 {
776 	fileid_t *filep = head;
777 
778 	while ((filep = filep->fi_forw) != head)
779 		if (filep->fi_taken)
780 			if (bufs_close(filep->fi_filedes))
781 				printf("Filesystem may be inconsistent.\n");
782 
783 	ufs_devp->di_taken = 0;
784 	bkmem_free((char *)ufs_devp, sizeof (devid_t));
785 	bkmem_free((char *)head, sizeof (fileid_t));
786 	ufs_devp = (devid_t *)NULL;
787 	head = (fileid_t *)NULL;
788 	free_cache();
789 }
790 
791 static struct cache {
792 	struct cache *next;
793 	void *data;
794 	int key;
795 	uint_t size;
796 } *icache;
797 
798 void
799 set_cache(int key, void *data, uint_t size)
800 {
801 	struct cache *entry = bkmem_alloc(sizeof (*entry));
802 	entry->key = key;
803 	entry->data = data;
804 	entry->size = size;
805 	if (icache) {
806 		entry->next = icache;
807 		icache = entry;
808 	} else {
809 		icache = entry;
810 		entry->next = 0;
811 	}
812 }
813 
814 void *
815 get_cache(int key)
816 {
817 	struct cache *entry = icache;
818 	while (entry) {
819 		if (entry->key == key)
820 			return (entry->data);
821 		entry = entry->next;
822 	}
823 	return (NULL);
824 }
825 
826 void
827 free_cache()
828 {
829 	struct cache *next, *entry = icache;
830 	while (entry) {
831 		next = entry->next;
832 		bkmem_free(entry->data, entry->size);
833 		bkmem_free(entry, sizeof (*entry));
834 		entry = next;
835 	}
836 	icache = 0;
837 }
838 
839 struct boot_fs_ops bufs_ops = {
840 	"boot_ufs",
841 	bufs_mountroot,
842 	bufs_unmountroot,
843 	bufs_open,
844 	bufs_close,
845 	bufs_read,
846 	bufs_lseek,
847 	bufs_fstat,
848 	NULL
849 };
850