8990 /opt/onbld/gk is useless
[unleashed.git] / usr / src / common / fs / ufsops.c
blob52e716f1deaa4882928a260ee1c5d37f3f7e9966
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
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>
38 #ifdef _BOOT
39 #include "../common/util.h"
40 #else
41 #include <sys/sunddi.h>
42 #endif
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);
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
58 /* PRINTLIKE */
59 extern void kobj_printf(char *, ...);
60 #endif
63 * This fd is used when talking to the device file itself.
65 static fileid_t *head;
67 /* Only got one of these...ergo, only 1 fs open at once */
68 /* static */
69 devid_t *ufs_devp;
71 struct dirinfo {
72 int loc;
73 fileid_t *fi;
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();
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.
97 static int
98 openi(fileid_t *filep, ino_t inode)
100 struct dinode *dp;
101 devid_t *devp = filep->fi_devp;
103 filep->fi_inode = get_cache((int)inode);
104 if (filep->fi_inode != 0)
105 return (0);
107 filep->fi_offset = 0;
108 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs,
109 itod(&devp->un_fs.di_fs, inode));
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);
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);
129 static fileid_t *
130 find_fp(int fd)
132 fileid_t *filep = head;
134 if (fd >= 0) {
135 while ((filep = filep->fi_forw) != head)
136 if (fd == filep->fi_filedes)
137 return (filep->fi_taken ? filep : 0);
140 return (0);
143 static ino_t
144 find(fileid_t *filep, char *path)
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;
154 if (path == NULL || *path == '\0') {
155 printf("null path\n");
156 return ((ino_t)0);
159 dprintf("openi: %s\n", path);
161 bzero(lpath, sizeof (lpath));
162 bcopy(path, lpath, strlen(path));
163 devp = filep->fi_devp;
164 while (*lpathp) {
165 /* if at the beginning of pathname get root inode */
166 r = (lpathp == lpath);
167 if (r && openi(filep, (ino_t)UFSROOTINO))
168 return ((ino_t)0);
169 while (*lpathp == '/')
170 lpathp++; /* skip leading slashes */
171 q = lpathp;
172 while (*q != '/' && *q != '\0')
173 q++; /* find end of component */
174 c = *q;
175 *q = '\0'; /* terminate component */
177 /* Bail out early if opening root */
178 if (r && (*lpathp == '\0'))
179 return ((ino_t)UFSROOTINO);
180 if ((inode = dlook(filep, lpathp)) != 0) {
181 if (openi(filep, inode))
182 return ((ino_t)0);
183 if ((filep->fi_inode->i_smode & IFMT) == IFLNK) {
184 filep->fi_blocknum =
185 fsbtodb(&devp->un_fs.di_fs,
186 filep->fi_inode->i_db[0]);
187 filep->fi_count = DEV_BSIZE;
188 filep->fi_memp = 0;
189 if (diskread(filep) != 0)
190 return ((ino_t)0);
191 len = strlen(filep->fi_memp);
192 if (filep->fi_memp[0] == '/')
193 /* absolute link */
194 lpathp = lpath;
195 /* copy rest of unprocessed path up */
196 bcopy(q, lpathp + len, strlen(q + 1) + 2);
197 /* point to unprocessed path */
198 *(lpathp + len) = c;
199 /* prepend link in before unprocessed path */
200 bcopy(filep->fi_memp, lpathp, len);
201 lpathp = lpath;
202 continue;
203 } else
204 *q = c;
205 if (c == '\0')
206 break;
207 lpathp = q;
208 continue;
209 } else {
210 return ((ino_t)0);
213 return (inode);
216 static daddr32_t
217 sbmap(fileid_t *filep, daddr32_t bn)
219 struct inode *inodep;
220 int i, j, sh;
221 daddr32_t nb, *bap;
222 daddr32_t *db;
223 devid_t *devp;
225 devp = filep->fi_devp;
226 inodep = filep->fi_inode;
227 db = inodep->i_db;
230 * blocks 0..NDADDR are direct blocks
232 if (bn < NDADDR) {
233 nb = db[bn];
234 return (nb);
238 * addresses NIADDR have single and double indirect blocks.
239 * the first step is to determine how many levels of indirection.
241 sh = 1;
242 bn -= NDADDR;
243 for (j = NIADDR; j > 0; j--) {
244 sh *= NINDIR(&devp->un_fs.di_fs);
245 if (bn < sh)
246 break;
247 bn -= sh;
249 if (j == 0) {
250 return ((daddr32_t)0);
254 * fetch the first indirect block address from the inode
256 nb = inodep->i_ib[NIADDR - j];
257 if (nb == 0) {
258 return ((daddr32_t)0);
262 * fetch through the indirect blocks
264 for (; j <= NIADDR; j++) {
265 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, nb);
266 filep->fi_count = devp->un_fs.di_fs.fs_bsize;
267 filep->fi_memp = 0;
268 if (diskread(filep) != 0)
269 return (0);
270 bap = (daddr32_t *)filep->fi_memp;
271 sh /= NINDIR(&devp->un_fs.di_fs);
272 i = (bn / sh) % NINDIR(&devp->un_fs.di_fs);
273 nb = bap[i];
274 if (nb == 0) {
275 return ((daddr32_t)0);
278 return (nb);
281 static ino_t
282 dlook(fileid_t *filep, char *path)
284 struct direct *dp;
285 struct inode *ip;
286 struct dirinfo dirp;
287 int len;
289 ip = filep->fi_inode;
290 if (path == NULL || *path == '\0')
291 return (0);
293 dprintf("dlook: %s\n", path);
295 if ((ip->i_smode & IFMT) != IFDIR) {
296 return (0);
298 if (ip->i_size == 0) {
299 return (0);
301 len = strlen(path);
302 dirp.loc = 0;
303 dirp.fi = filep;
304 for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
305 if (dp->d_ino == 0)
306 continue;
307 if (dp->d_namlen == len && strcmp(path, dp->d_name) == 0) {
308 return (dp->d_ino);
310 /* Allow "*" to print all names at that level, w/out match */
311 if (strcmp(path, "*") == 0)
312 dprintf("%s\n", dp->d_name);
314 return (0);
318 * get next entry in a directory.
320 struct direct *
321 readdir(struct dirinfo *dstuff)
323 struct direct *dp;
324 fileid_t *filep;
325 daddr32_t lbn, d;
326 int off;
327 devid_t *devp;
329 filep = dstuff->fi;
330 devp = filep->fi_devp;
331 for (;;) {
332 if (dstuff->loc >= filep->fi_inode->i_size) {
333 return (NULL);
335 off = blkoff(&devp->un_fs.di_fs, dstuff->loc);
336 dprintf("readdir: off = 0x%x\n", off);
337 if (off == 0) {
338 lbn = lblkno(&devp->un_fs.di_fs, dstuff->loc);
339 d = sbmap(filep, lbn);
341 if (d == 0)
342 return (NULL);
344 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, d);
345 filep->fi_count =
346 blksize(&devp->un_fs.di_fs, filep->fi_inode, lbn);
347 filep->fi_memp = 0;
348 if (diskread(filep) != 0) {
349 return (NULL);
352 dp = (struct direct *)(filep->fi_memp + off);
353 dstuff->loc += dp->d_reclen;
354 if (dp->d_ino == 0)
355 continue;
356 dprintf("readdir: name = %s\n", dp->d_name);
357 return (dp);
362 * Get the next block of data from the file. If possible, dma right into
363 * user's buffer
365 static int
366 getblock(fileid_t *filep, caddr_t buf, int count, int *rcount)
368 struct fs *fs;
369 caddr_t p;
370 int off, size, diff;
371 daddr32_t lbn;
372 devid_t *devp;
374 dprintf("getblock: buf 0x%p, count 0x%x\n", (void *)buf, count);
376 devp = filep->fi_devp;
377 p = filep->fi_memp;
378 if ((signed)filep->fi_count <= 0) {
380 /* find the amt left to be read in the file */
381 diff = filep->fi_inode->i_size - filep->fi_offset;
382 if (diff <= 0) {
383 printf("Short read\n");
384 return (-1);
387 fs = &devp->un_fs.di_fs;
388 /* which block (or frag) in the file do we read? */
389 lbn = lblkno(fs, filep->fi_offset);
391 /* which physical block on the device do we read? */
392 filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn));
394 off = blkoff(fs, filep->fi_offset);
396 /* either blksize or fragsize */
397 size = blksize(fs, filep->fi_inode, lbn);
398 filep->fi_count = size;
399 filep->fi_memp = filep->fi_buf;
402 * optimization if we are reading large blocks of data then
403 * we can go directly to user's buffer
405 *rcount = 0;
406 if (off == 0 && count >= size) {
407 filep->fi_memp = buf;
408 if (diskread(filep)) {
409 return (-1);
411 *rcount = size;
412 filep->fi_count = 0;
413 return (0);
414 } else if (diskread(filep))
415 return (-1);
417 if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
418 filep->fi_count = diff + off;
419 filep->fi_count -= off;
420 p = &filep->fi_memp[off];
422 filep->fi_memp = p;
423 return (0);
427 * Get the next block of data from the file. Don't attempt to go directly
428 * to user's buffer.
430 static int
431 getblock_noopt(fileid_t *filep)
433 struct fs *fs;
434 caddr_t p;
435 int off, size, diff;
436 daddr32_t lbn;
437 devid_t *devp;
439 dprintf("getblock_noopt: start\n");
441 devp = filep->fi_devp;
442 p = filep->fi_memp;
443 if ((signed)filep->fi_count <= 0) {
445 /* find the amt left to be read in the file */
446 diff = filep->fi_inode->i_size - filep->fi_offset;
447 if (diff <= 0) {
448 printf("Short read\n");
449 return (-1);
452 fs = &devp->un_fs.di_fs;
453 /* which block (or frag) in the file do we read? */
454 lbn = lblkno(fs, filep->fi_offset);
456 /* which physical block on the device do we read? */
457 filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn));
459 off = blkoff(fs, filep->fi_offset);
461 /* either blksize or fragsize */
462 size = blksize(fs, filep->fi_inode, lbn);
463 filep->fi_count = size;
464 /* reading on a ramdisk, just get a pointer to the data */
465 filep->fi_memp = NULL;
467 if (diskread(filep))
468 return (-1);
470 if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
471 filep->fi_count = diff + off;
472 filep->fi_count -= off;
473 p = &filep->fi_memp[off];
475 filep->fi_memp = p;
476 return (0);
481 * This is the high-level read function. It works like this.
482 * We assume that our IO device buffers up some amount of
483 * data and that we can get a ptr to it. Thus we need
484 * to actually call the device func about filesize/blocksize times
485 * and this greatly increases our IO speed. When we already
486 * have data in the buffer, we just return that data (with bcopy() ).
489 static ssize_t
490 bufs_read(int fd, caddr_t buf, size_t count)
492 size_t i, j;
493 caddr_t n;
494 int rcount;
495 fileid_t *filep;
497 if (!(filep = find_fp(fd))) {
498 return (-1);
501 if ((filep->fi_flags & FI_COMPRESSED) == 0 &&
502 filep->fi_offset + count > filep->fi_inode->i_size)
503 count = filep->fi_inode->i_size - filep->fi_offset;
505 /* that was easy */
506 if ((i = count) == 0)
507 return (0);
509 n = buf;
510 while (i > 0) {
511 if (filep->fi_flags & FI_COMPRESSED) {
512 if ((j = cf_read(filep, buf, count)) < 0)
513 return (0); /* encountered an error */
514 if (j < i)
515 i = j; /* short read, must have hit EOF */
516 } else {
517 /* If we need to reload the buffer, do so */
518 if ((j = filep->fi_count) == 0) {
519 (void) getblock(filep, buf, i, &rcount);
520 i -= rcount;
521 buf += rcount;
522 filep->fi_offset += rcount;
523 continue;
524 } else {
525 /* else just bcopy from our buffer */
526 j = MIN(i, j);
527 bcopy(filep->fi_memp, buf, (unsigned)j);
530 buf += j;
531 filep->fi_memp += j;
532 filep->fi_offset += j;
533 filep->fi_count -= j;
534 i -= j;
536 return (buf - n);
540 * This routine will open a device as it is known by the V2 OBP.
541 * Interface Defn:
542 * err = mountroot(string);
543 * err = 0 on success
544 * err = -1 on failure
545 * string: char string describing the properties of the device.
546 * We must not dork with any fi[]'s here. Save that for later.
549 static int
550 bufs_mountroot(char *str)
552 if (ufs_devp) /* already mounted */
553 return (0);
555 ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
556 ufs_devp->di_taken = 1;
557 ufs_devp->di_dcookie = 0;
558 ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
559 (void) strcpy(ufs_devp->di_desc, str);
560 bzero(ufs_devp->un_fs.dummy, SBSIZE);
561 head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
562 head->fi_back = head->fi_forw = head;
563 head->fi_filedes = 0;
564 head->fi_taken = 0;
566 /* Setup read of the superblock */
567 head->fi_devp = ufs_devp;
568 head->fi_blocknum = SBLOCK;
569 head->fi_count = (uint_t)SBSIZE;
570 head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs);
571 head->fi_offset = 0;
573 if (diskread(head)) {
574 printf("failed to read superblock\n");
575 (void) bufs_closeall(1);
576 return (-1);
579 if (ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) {
580 dprintf("fs magic = 0x%x\n", ufs_devp->un_fs.di_fs.fs_magic);
581 (void) bufs_closeall(1);
582 return (-1);
584 dprintf("mountroot succeeded\n");
585 return (0);
589 * Unmount the currently mounted root fs. In practice, this means
590 * closing all open files and releasing resources. All of this
591 * is done by closeall().
594 static int
595 bufs_unmountroot(void)
597 if (ufs_devp == NULL)
598 return (-1);
600 (void) bufs_closeall(1);
602 return (0);
606 * We allocate an fd here for use when talking
607 * to the file itself.
610 /*ARGSUSED*/
611 static int
612 bufs_open(char *filename, int flags)
614 fileid_t *filep;
615 ino_t inode;
616 static int filedes = 1;
618 dprintf("open: %s\n", filename);
620 /* build and link a new file descriptor */
621 filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
622 filep->fi_back = head->fi_back;
623 filep->fi_forw = head;
624 head->fi_back->fi_forw = filep;
625 head->fi_back = filep;
626 filep->fi_filedes = filedes++;
627 filep->fi_taken = 1;
628 filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
629 (void) strcpy(filep->fi_path, filename);
630 filep->fi_devp = ufs_devp; /* dev is already "mounted" */
631 filep->fi_inode = NULL;
632 bzero(filep->fi_buf, MAXBSIZE);
633 filep->fi_getblock = getblock_noopt;
634 filep->fi_flags = 0;
636 inode = find(filep, (char *)filename);
637 if (inode == (ino_t)0) {
638 dprintf("open: cannot find %s\n", filename);
639 (void) bufs_close(filep->fi_filedes);
640 return (-1);
642 if (openi(filep, inode)) {
643 printf("open: cannot open %s\n", filename);
644 (void) bufs_close(filep->fi_filedes);
645 return (-1);
648 filep->fi_offset = filep->fi_count = 0;
650 if (cf_check_compressed(filep) != 0)
651 return (-1);
652 return (filep->fi_filedes);
656 * We don't do any IO here.
657 * We just play games with the device pointers.
660 static off_t
661 bufs_lseek(int fd, off_t addr, int whence)
663 fileid_t *filep;
665 /* Make sure user knows what file they are talking to */
666 if (!(filep = find_fp(fd)))
667 return (-1);
669 if (filep->fi_flags & FI_COMPRESSED) {
670 cf_seek(filep, addr, whence);
671 } else {
672 switch (whence) {
673 case SEEK_CUR:
674 filep->fi_offset += addr;
675 break;
676 case SEEK_SET:
677 filep->fi_offset = addr;
678 break;
679 default:
680 case SEEK_END:
681 printf("lseek(): invalid whence value %d\n", whence);
682 break;
684 filep->fi_blocknum = addr / DEV_BSIZE;
687 filep->fi_count = 0;
689 return (0);
694 bufs_fstat(int fd, struct bootstat *stp)
696 fileid_t *filep;
697 struct inode *ip;
699 if (!(filep = find_fp(fd)))
700 return (-1);
702 ip = filep->fi_inode;
704 stp->st_mode = 0;
705 stp->st_size = 0;
707 if (ip == NULL)
708 return (0);
710 switch (ip->i_smode & IFMT) {
711 case IFLNK:
712 stp->st_mode = S_IFLNK;
713 break;
714 case IFREG:
715 stp->st_mode = S_IFREG;
716 break;
717 default:
718 break;
721 * NOTE: this size will be the compressed size for a compressed file
722 * This could confuse the caller since we decompress the file behind
723 * the scenes when the file is read.
725 stp->st_size = ip->i_size;
726 stp->st_atim.tv_sec = ip->i_atime.tv_sec;
727 stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
728 stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
729 stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
730 stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
731 stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
733 return (0);
737 static int
738 bufs_close(int fd)
740 fileid_t *filep;
742 /* Make sure user knows what file they are talking to */
743 if (!(filep = find_fp(fd)))
744 return (-1);
746 if (filep->fi_taken && (filep != head)) {
747 /* Clear the ranks */
748 bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
749 filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
750 filep->fi_memp = (caddr_t)0;
751 filep->fi_devp = 0;
752 filep->fi_taken = 0;
754 /* unlink and deallocate node */
755 filep->fi_forw->fi_back = filep->fi_back;
756 filep->fi_back->fi_forw = filep->fi_forw;
757 cf_close(filep);
758 bkmem_free((char *)filep, sizeof (fileid_t));
760 return (0);
761 } else {
762 /* Big problem */
763 printf("\nFile descrip %d not allocated!", fd);
764 return (-1);
768 /*ARGSUSED*/
769 static void
770 bufs_closeall(int flag)
772 fileid_t *filep = head;
774 while ((filep = filep->fi_forw) != head)
775 if (filep->fi_taken)
776 if (bufs_close(filep->fi_filedes))
777 printf("Filesystem may be inconsistent.\n");
779 ufs_devp->di_taken = 0;
780 bkmem_free((char *)ufs_devp, sizeof (devid_t));
781 bkmem_free((char *)head, sizeof (fileid_t));
782 ufs_devp = (devid_t *)NULL;
783 head = (fileid_t *)NULL;
784 free_cache();
787 static struct cache {
788 struct cache *next;
789 void *data;
790 int key;
791 uint_t size;
792 } *icache;
794 void
795 set_cache(int key, void *data, uint_t size)
797 struct cache *entry = bkmem_alloc(sizeof (*entry));
798 entry->key = key;
799 entry->data = data;
800 entry->size = size;
801 if (icache) {
802 entry->next = icache;
803 icache = entry;
804 } else {
805 icache = entry;
806 entry->next = 0;
810 void *
811 get_cache(int key)
813 struct cache *entry = icache;
814 while (entry) {
815 if (entry->key == key)
816 return (entry->data);
817 entry = entry->next;
819 return (NULL);
822 void
823 free_cache()
825 struct cache *next, *entry = icache;
826 while (entry) {
827 next = entry->next;
828 bkmem_free(entry->data, entry->size);
829 bkmem_free(entry, sizeof (*entry));
830 entry = next;
832 icache = 0;
835 struct boot_fs_ops bufs_ops = {
836 "boot_ufs",
837 bufs_mountroot,
838 bufs_unmountroot,
839 bufs_open,
840 bufs_close,
841 bufs_read,
842 bufs_lseek,
843 bufs_fstat,
844 NULL