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