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 devp = filep->fi_devp; 222 inodep = filep->fi_inode; 223 db = inodep->i_db; 224 225 /* 226 * blocks 0..NDADDR are direct blocks 227 */ 228 if (bn < NDADDR) { 229 nb = db[bn]; 230 return (nb); 231 } 232 233 /* 234 * addresses NIADDR have single and double indirect blocks. 235 * the first step is to determine how many levels of indirection. 236 */ 237 sh = 1; 238 bn -= NDADDR; 239 for (j = NIADDR; j > 0; j--) { 240 sh *= NINDIR(&devp->un_fs.di_fs); 241 if (bn < sh) 242 break; 243 bn -= sh; 244 } 245 if (j == 0) { 246 return ((daddr32_t)0); 247 } 248 249 /* 250 * fetch the first indirect block address from the inode 251 */ 252 nb = inodep->i_ib[NIADDR - j]; 253 if (nb == 0) { 254 return ((daddr32_t)0); 255 } 256 257 /* 258 * fetch through the indirect blocks 259 */ 260 for (; j <= NIADDR; j++) { 261 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, nb); 262 filep->fi_count = devp->un_fs.di_fs.fs_bsize; 263 filep->fi_memp = 0; 264 if (diskread(filep) != 0) 265 return (0); 266 bap = (daddr32_t *)filep->fi_memp; 267 sh /= NINDIR(&devp->un_fs.di_fs); 268 i = (bn / sh) % NINDIR(&devp->un_fs.di_fs); 269 nb = bap[i]; 270 if (nb == 0) { 271 return ((daddr32_t)0); 272 } 273 } 274 return (nb); 275 } 276 277 static ino_t 278 dlook(fileid_t *filep, char *path) 279 { 280 struct direct *dp; 281 struct inode *ip; 282 struct dirinfo dirp; 283 int len; 284 285 ip = filep->fi_inode; 286 if (path == NULL || *path == '\0') 287 return (0); 288 289 dprintf("dlook: %s\n", path); 290 291 if ((ip->i_smode & IFMT) != IFDIR) { 292 return (0); 293 } 294 if (ip->i_size == 0) { 295 return (0); 296 } 297 len = strlen(path); 298 dirp.loc = 0; 299 dirp.fi = filep; 300 for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) { 301 if (dp->d_ino == 0) 302 continue; 303 if (dp->d_namlen == len && strcmp(path, dp->d_name) == 0) { 304 return (dp->d_ino); 305 } 306 /* Allow "*" to print all names at that level, w/out match */ 307 if (strcmp(path, "*") == 0) 308 dprintf("%s\n", dp->d_name); 309 } 310 return (0); 311 } 312 313 /* 314 * get next entry in a directory. 315 */ 316 struct direct * 317 readdir(struct dirinfo *dstuff) 318 { 319 struct direct *dp; 320 fileid_t *filep; 321 daddr32_t lbn, d; 322 int off; 323 devid_t *devp; 324 325 filep = dstuff->fi; 326 devp = filep->fi_devp; 327 for (;;) { 328 if (dstuff->loc >= filep->fi_inode->i_size) { 329 return (NULL); 330 } 331 off = blkoff(&devp->un_fs.di_fs, dstuff->loc); 332 dprintf("readdir: off = 0x%x\n", off); 333 if (off == 0) { 334 lbn = lblkno(&devp->un_fs.di_fs, dstuff->loc); 335 d = sbmap(filep, lbn); 336 337 if (d == 0) 338 return (NULL); 339 340 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, d); 341 filep->fi_count = 342 blksize(&devp->un_fs.di_fs, filep->fi_inode, lbn); 343 filep->fi_memp = 0; 344 if (diskread(filep) != 0) { 345 return (NULL); 346 } 347 } 348 dp = (struct direct *)(filep->fi_memp + off); 349 dstuff->loc += dp->d_reclen; 350 if (dp->d_ino == 0) 351 continue; 352 dprintf("readdir: name = %s\n", dp->d_name); 353 return (dp); 354 } 355 } 356 357 /* 358 * Get the next block of data from the file. If possible, dma right into 359 * user's buffer 360 */ 361 static int 362 getblock(fileid_t *filep, caddr_t buf, int count, int *rcount) 363 { 364 struct fs *fs; 365 caddr_t p; 366 int off, size, diff; 367 daddr32_t lbn; 368 devid_t *devp; 369 370 dprintf("getblock: buf 0x%p, count 0x%x\n", (void *)buf, count); 371 372 devp = filep->fi_devp; 373 p = filep->fi_memp; 374 if ((signed)filep->fi_count <= 0) { 375 376 /* find the amt left to be read in the file */ 377 diff = filep->fi_inode->i_size - filep->fi_offset; 378 if (diff <= 0) { 379 printf("Short read\n"); 380 return (-1); 381 } 382 383 fs = &devp->un_fs.di_fs; 384 /* which block (or frag) in the file do we read? */ 385 lbn = lblkno(fs, filep->fi_offset); 386 387 /* which physical block on the device do we read? */ 388 filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn)); 389 390 off = blkoff(fs, filep->fi_offset); 391 392 /* either blksize or fragsize */ 393 size = blksize(fs, filep->fi_inode, lbn); 394 filep->fi_count = size; 395 filep->fi_memp = filep->fi_buf; 396 397 /* 398 * optimization if we are reading large blocks of data then 399 * we can go directly to user's buffer 400 */ 401 *rcount = 0; 402 if (off == 0 && count >= size) { 403 filep->fi_memp = buf; 404 if (diskread(filep)) { 405 return (-1); 406 } 407 *rcount = size; 408 filep->fi_count = 0; 409 return (0); 410 } else if (diskread(filep)) 411 return (-1); 412 413 if (filep->fi_offset - off + size >= filep->fi_inode->i_size) 414 filep->fi_count = diff + off; 415 filep->fi_count -= off; 416 p = &filep->fi_memp[off]; 417 } 418 filep->fi_memp = p; 419 return (0); 420 } 421 422 423 /* 424 * This is the high-level read function. It works like this. 425 * We assume that our IO device buffers up some amount of 426 * data and that we can get a ptr to it. Thus we need 427 * to actually call the device func about filesize/blocksize times 428 * and this greatly increases our IO speed. When we already 429 * have data in the buffer, we just return that data (with bcopy() ). 430 */ 431 432 static ssize_t 433 bufs_read(int fd, caddr_t buf, size_t count) 434 { 435 size_t i, j; 436 caddr_t n; 437 int rcount; 438 fileid_t *filep; 439 440 if (!(filep = find_fp(fd))) { 441 return (-1); 442 } 443 444 if (filep->fi_offset + count > filep->fi_inode->i_size) 445 count = filep->fi_inode->i_size - filep->fi_offset; 446 447 /* that was easy */ 448 if ((i = count) == 0) 449 return (0); 450 451 n = buf; 452 while (i > 0) { 453 /* If we need to reload the buffer, do so */ 454 if ((j = filep->fi_count) == 0) { 455 (void) getblock(filep, buf, i, &rcount); 456 i -= rcount; 457 buf += rcount; 458 filep->fi_offset += rcount; 459 } else { 460 /* else just bcopy from our buffer */ 461 j = MIN(i, j); 462 bcopy(filep->fi_memp, buf, (unsigned)j); 463 buf += j; 464 filep->fi_memp += j; 465 filep->fi_offset += j; 466 filep->fi_count -= j; 467 i -= j; 468 } 469 } 470 return (buf - n); 471 } 472 473 /* 474 * This routine will open a device as it is known by the V2 OBP. 475 * Interface Defn: 476 * err = mountroot(string); 477 * err = 0 on success 478 * err = -1 on failure 479 * string: char string describing the properties of the device. 480 * We must not dork with any fi[]'s here. Save that for later. 481 */ 482 483 static int 484 bufs_mountroot(char *str) 485 { 486 if (ufs_devp) /* already mounted */ 487 return (0); 488 489 ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t)); 490 ufs_devp->di_taken = 1; 491 ufs_devp->di_dcookie = 0; 492 ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1); 493 (void) strcpy(ufs_devp->di_desc, str); 494 bzero(ufs_devp->un_fs.dummy, SBSIZE); 495 head = (fileid_t *)bkmem_alloc(sizeof (fileid_t)); 496 head->fi_back = head->fi_forw = head; 497 head->fi_filedes = 0; 498 head->fi_taken = 0; 499 500 /* Setup read of the superblock */ 501 head->fi_devp = ufs_devp; 502 head->fi_blocknum = SBLOCK; 503 head->fi_count = (uint_t)SBSIZE; 504 head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs); 505 head->fi_offset = 0; 506 507 if (diskread(head)) { 508 printf("failed to read superblock\n"); 509 (void) bufs_closeall(1); 510 return (-1); 511 } 512 513 if (ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) { 514 dprintf("fs magic = 0x%x\n", ufs_devp->un_fs.di_fs.fs_magic); 515 (void) bufs_closeall(1); 516 return (-1); 517 } 518 dprintf("mountroot succeeded\n"); 519 return (0); 520 } 521 522 /* 523 * Unmount the currently mounted root fs. In practice, this means 524 * closing all open files and releasing resources. All of this 525 * is done by closeall(). 526 */ 527 528 static int 529 bufs_unmountroot(void) 530 { 531 if (ufs_devp == NULL) 532 return (-1); 533 534 (void) bufs_closeall(1); 535 536 return (0); 537 } 538 539 /* 540 * We allocate an fd here for use when talking 541 * to the file itself. 542 */ 543 544 /*ARGSUSED*/ 545 static int 546 bufs_open(char *filename, int flags) 547 { 548 fileid_t *filep; 549 ino_t inode; 550 static int filedes = 1; 551 552 dprintf("open: %s\n", filename); 553 554 /* build and link a new file descriptor */ 555 filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t)); 556 filep->fi_back = head->fi_back; 557 filep->fi_forw = head; 558 head->fi_back->fi_forw = filep; 559 head->fi_back = filep; 560 filep->fi_filedes = filedes++; 561 filep->fi_taken = 1; 562 filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1); 563 (void) strcpy(filep->fi_path, filename); 564 filep->fi_devp = ufs_devp; /* dev is already "mounted" */ 565 filep->fi_inode = NULL; 566 bzero(filep->fi_buf, MAXBSIZE); 567 568 inode = find(filep, (char *)filename); 569 if (inode == (ino_t)0) { 570 dprintf("open: cannot find %s\n", filename); 571 (void) bufs_close(filep->fi_filedes); 572 return (-1); 573 } 574 if (openi(filep, inode)) { 575 printf("open: cannot open %s\n", filename); 576 (void) bufs_close(filep->fi_filedes); 577 return (-1); 578 } 579 580 filep->fi_offset = filep->fi_count = 0; 581 582 return (filep->fi_filedes); 583 } 584 585 /* 586 * We don't do any IO here. 587 * We just play games with the device pointers. 588 */ 589 590 static off_t 591 bufs_lseek(int fd, off_t addr, int whence) 592 { 593 fileid_t *filep; 594 595 /* Make sure user knows what file he is talking to */ 596 if (!(filep = find_fp(fd))) 597 return (-1); 598 599 switch (whence) { 600 case SEEK_CUR: 601 filep->fi_offset += addr; 602 break; 603 case SEEK_SET: 604 filep->fi_offset = addr; 605 break; 606 default: 607 case SEEK_END: 608 printf("lseek(): invalid whence value %d\n", whence); 609 break; 610 } 611 612 filep->fi_blocknum = addr / DEV_BSIZE; 613 filep->fi_count = 0; 614 615 return (0); 616 } 617 618 619 int 620 bufs_fstat(int fd, struct bootstat *stp) 621 { 622 fileid_t *filep; 623 struct inode *ip; 624 625 if (!(filep = find_fp(fd))) 626 return (-1); 627 628 ip = filep->fi_inode; 629 630 stp->st_mode = 0; 631 stp->st_size = 0; 632 633 if (ip == NULL) 634 return (0); 635 636 switch (ip->i_smode & IFMT) { 637 case IFLNK: 638 stp->st_mode = S_IFLNK; 639 break; 640 case IFREG: 641 stp->st_mode = S_IFREG; 642 break; 643 default: 644 break; 645 } 646 stp->st_size = ip->i_size; 647 stp->st_atim.tv_sec = ip->i_atime.tv_sec; 648 stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000; 649 stp->st_mtim.tv_sec = ip->i_mtime.tv_sec; 650 stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000; 651 stp->st_ctim.tv_sec = ip->i_ctime.tv_sec; 652 stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000; 653 654 return (0); 655 } 656 657 658 static int 659 bufs_close(int fd) 660 { 661 fileid_t *filep; 662 663 /* Make sure user knows what file he is talking to */ 664 if (!(filep = find_fp(fd))) 665 return (-1); 666 667 if (filep->fi_taken && (filep != head)) { 668 /* Clear the ranks */ 669 bkmem_free(filep->fi_path, strlen(filep->fi_path)+1); 670 filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0; 671 filep->fi_memp = (caddr_t)0; 672 filep->fi_devp = 0; 673 filep->fi_taken = 0; 674 675 /* unlink and deallocate node */ 676 filep->fi_forw->fi_back = filep->fi_back; 677 filep->fi_back->fi_forw = filep->fi_forw; 678 bkmem_free((char *)filep, sizeof (fileid_t)); 679 680 return (0); 681 } else { 682 /* Big problem */ 683 printf("\nFile descrip %d not allocated!", fd); 684 return (-1); 685 } 686 } 687 688 /*ARGSUSED*/ 689 static void 690 bufs_closeall(int flag) 691 { 692 fileid_t *filep = head; 693 694 while ((filep = filep->fi_forw) != head) 695 if (filep->fi_taken) 696 if (bufs_close(filep->fi_filedes)) 697 printf("Filesystem may be inconsistent.\n"); 698 699 ufs_devp->di_taken = 0; 700 bkmem_free((char *)ufs_devp, sizeof (devid_t)); 701 bkmem_free((char *)head, sizeof (fileid_t)); 702 ufs_devp = (devid_t *)NULL; 703 head = (fileid_t *)NULL; 704 free_cache(); 705 } 706 707 static struct cache { 708 struct cache *next; 709 void *data; 710 int key; 711 uint_t size; 712 } *icache; 713 714 void 715 set_cache(int key, void *data, uint_t size) 716 { 717 struct cache *entry = bkmem_alloc(sizeof (*entry)); 718 entry->key = key; 719 entry->data = data; 720 entry->size = size; 721 if (icache) { 722 entry->next = icache; 723 icache = entry; 724 } else { 725 icache = entry; 726 entry->next = 0; 727 } 728 } 729 730 void * 731 get_cache(int key) 732 { 733 struct cache *entry = icache; 734 while (entry) { 735 if (entry->key == key) 736 return (entry->data); 737 entry = entry->next; 738 } 739 return (NULL); 740 } 741 742 void 743 free_cache() 744 { 745 struct cache *next, *entry = icache; 746 while (entry) { 747 next = entry->next; 748 bkmem_free(entry->data, entry->size); 749 bkmem_free(entry, sizeof (*entry)); 750 entry = next; 751 } 752 icache = 0; 753 } 754 755 struct boot_fs_ops bufs_ops = { 756 "boot_ufs", 757 bufs_mountroot, 758 bufs_unmountroot, 759 bufs_open, 760 bufs_close, 761 bufs_read, 762 bufs_lseek, 763 bufs_fstat, 764 NULL 765 }; 766