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