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 2004 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/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/promif.h> 36 37 #include <sys/stat.h> 38 #include <sys/bootvfs.h> 39 #include <sys/bootdebug.h> 40 #include <sys/salib.h> 41 #include <sys/sacache.h> 42 43 44 int print_cache_stats = 0; 45 46 /* 47 * This fd is used when talking to the device file itself. 48 */ 49 static fileid_t *head; 50 /* 51 * hooks into ufs logging support 52 */ 53 extern void lufs_boot_init(fileid_t *); 54 extern void lufs_closeall(void); 55 extern void lufs_merge_deltas(fileid_t *); 56 57 /* Only got one of these...ergo, only 1 fs open at once */ 58 /* static */ 59 devid_t *ufs_devp; 60 61 struct dirinfo { 62 int loc; 63 fileid_t *fi; 64 }; 65 66 /* 67 * Function prototypes 68 */ 69 static int boot_ufs_mountroot(char *str); 70 static int boot_ufs_unmountroot(void); 71 static int boot_ufs_open(char *filename, int flags); 72 static int boot_ufs_close(int fd); 73 static ssize_t boot_ufs_read(int fd, caddr_t buf, size_t size); 74 static off_t boot_ufs_lseek(int, off_t, int); 75 static int boot_ufs_fstat(int fd, struct bootstat *stp); 76 static void boot_ufs_closeall(int flag); 77 static int boot_ufs_getdents(int fd, struct dirent *dep, unsigned size); 78 79 struct boot_fs_ops boot_ufs_ops = { 80 "ufs", 81 boot_ufs_mountroot, 82 boot_ufs_unmountroot, 83 boot_ufs_open, 84 boot_ufs_close, 85 boot_ufs_read, 86 boot_ufs_lseek, 87 boot_ufs_fstat, 88 boot_ufs_closeall, 89 boot_ufs_getdents 90 }; 91 92 static ino_t find(fileid_t *filep, char *path); 93 static ino_t dlook(fileid_t *filep, char *path); 94 static daddr32_t sbmap(fileid_t *filep, daddr32_t bn); 95 static struct direct *readdir(struct dirinfo *dstuff); 96 97 /* These are the pools of buffers, etc. */ 98 #define NBUFS (NIADDR+1) 99 /* Compilers like to play with alignment, so force the issue here */ 100 static union { 101 char *blk[NBUFS]; 102 daddr32_t *dummy; 103 } b; 104 daddr32_t blknos[NBUFS]; 105 106 /* 107 * There is only 1 open (mounted) device at any given time. 108 * So we can keep a single, global devp file descriptor to 109 * use to index into the di[] array. This is not true for the 110 * fi[] array. We can have more than one file open at once, 111 * so there is no global fd for the fi[]. 112 * The user program must save the fd passed back from open() 113 * and use it to do subsequent read()'s. 114 */ 115 116 static int 117 openi(fileid_t *filep, ino_t inode) 118 { 119 int retval; 120 struct dinode *dp; 121 devid_t *devp = filep->fi_devp; 122 123 /* Try the inode cache first */ 124 if ((filep->fi_inode = get_icache(devp->di_dcookie, inode)) != NULL) 125 return (0); 126 /* Nope, not there so lets read it off the disk. */ 127 filep->fi_offset = 0; 128 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, 129 itod(&devp->un_fs.di_fs, inode)); 130 131 /* never more than 1 disk block */ 132 filep->fi_count = devp->un_fs.di_fs.fs_bsize; 133 filep->fi_memp = filep->fi_buf; 134 135 /* Maybe the block is in the disk block cache */ 136 if ((filep->fi_memp = get_bcache(filep)) == NULL) { 137 /* Not in the block cache so read it from disk */ 138 if (retval = set_bcache(filep)) 139 return (retval); 140 lufs_merge_deltas(filep); 141 } 142 143 dp = (struct dinode *)filep->fi_memp; 144 filep->fi_inode = (struct inode *) 145 bkmem_alloc(sizeof (struct inode)); 146 bzero((char *)filep->fi_inode, sizeof (struct inode)); 147 filep->fi_inode->i_ic = 148 dp[itoo(&devp->un_fs.di_fs, inode)].di_un.di_icom; 149 filep->fi_inode->i_number = inode; 150 if (set_ricache(devp->di_dcookie, inode, (void *)filep->fi_inode, 151 sizeof (struct inode))) 152 filep->fi_inode->i_flag = FI_NOCACHE; 153 return (0); 154 } 155 156 static fileid_t * 157 find_fp(int fd) 158 { 159 fileid_t *filep = head; 160 161 if (fd >= 0) { 162 while ((filep = filep->fi_forw) != head) 163 if (fd == filep->fi_filedes) 164 return (filep->fi_taken ? filep : 0); 165 } 166 167 return (0); 168 } 169 170 static ino_t 171 find(fileid_t *filep, char *path) 172 { 173 char *q; 174 char c; 175 ino_t inode; 176 char lpath[MAXPATHLEN]; 177 char *lpathp = lpath; 178 int len, r; 179 devid_t *devp; 180 181 if (path == NULL || *path == '\0') { 182 printf("null path\n"); 183 return ((ino_t)0); 184 } 185 186 bzero(lpath, sizeof (lpath)); 187 bcopy(path, lpath, strlen(path)); 188 devp = filep->fi_devp; 189 while (*lpathp) { 190 /* if at the beginning of pathname get root inode */ 191 r = (lpathp == lpath); 192 if (r && openi(filep, (ino_t)UFSROOTINO)) 193 return ((ino_t)0); 194 while (*lpathp == '/') 195 lpathp++; /* skip leading slashes */ 196 q = lpathp; 197 while (*q != '/' && *q != '\0') 198 q++; /* find end of component */ 199 c = *q; 200 *q = '\0'; /* terminate component */ 201 202 /* Bail out early if opening root */ 203 if (r && (*lpathp == '\0')) 204 return ((ino_t)UFSROOTINO); 205 if ((inode = dlook(filep, lpathp)) != 0) { 206 if (openi(filep, inode)) 207 return ((ino_t)0); 208 if ((filep->fi_inode->i_smode & IFMT) == IFLNK) { 209 filep->fi_blocknum = 210 fsbtodb(&devp->un_fs.di_fs, 211 filep->fi_inode->i_db[0]); 212 filep->fi_count = DEV_BSIZE; 213 /* check the block cache */ 214 if ((filep->fi_memp = get_bcache(filep)) 215 == NULL) { 216 if (set_bcache(filep)) 217 return ((ino_t)0); 218 lufs_merge_deltas(filep); 219 } 220 len = strlen(filep->fi_memp); 221 if (filep->fi_memp[0] == '/') 222 /* absolute link */ 223 lpathp = lpath; 224 /* copy rest of unprocessed path up */ 225 bcopy(q, lpathp + len, strlen(q + 1) + 2); 226 /* point to unprocessed path */ 227 *(lpathp + len) = c; 228 /* prepend link in before unprocessed path */ 229 bcopy(filep->fi_memp, lpathp, len); 230 lpathp = lpath; 231 continue; 232 } else 233 *q = c; 234 if (c == '\0') 235 break; 236 lpathp = q; 237 continue; 238 } else { 239 return ((ino_t)0); 240 } 241 } 242 return (inode); 243 } 244 245 static daddr32_t 246 sbmap(fileid_t *filep, daddr32_t bn) 247 { 248 struct inode *inodep; 249 int i, j, sh; 250 daddr32_t nb, *bap; 251 daddr32_t *db; 252 devid_t *devp; 253 254 devp = filep->fi_devp; 255 inodep = filep->fi_inode; 256 db = inodep->i_db; 257 258 /* 259 * blocks 0..NDADDR are direct blocks 260 */ 261 if (bn < NDADDR) { 262 nb = db[bn]; 263 return (nb); 264 } 265 266 /* 267 * addresses NIADDR have single and double indirect blocks. 268 * the first step is to determine how many levels of indirection. 269 */ 270 sh = 1; 271 bn -= NDADDR; 272 for (j = NIADDR; j > 0; j--) { 273 sh *= NINDIR(&devp->un_fs.di_fs); 274 if (bn < sh) 275 break; 276 bn -= sh; 277 } 278 if (j == 0) { 279 return ((daddr32_t)0); 280 } 281 282 /* 283 * fetch the first indirect block address from the inode 284 */ 285 nb = inodep->i_ib[NIADDR - j]; 286 if (nb == 0) { 287 return ((daddr32_t)0); 288 } 289 290 /* 291 * fetch through the indirect blocks 292 */ 293 for (; j <= NIADDR; j++) { 294 if (blknos[j] != nb) { 295 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, nb); 296 filep->fi_count = devp->un_fs.di_fs.fs_bsize; 297 /* First look through the disk block cache */ 298 if ((filep->fi_memp = get_bcache(filep)) == NULL) { 299 if (set_bcache(filep)) /* Gotta do I/O */ 300 return (0); 301 lufs_merge_deltas(filep); 302 } 303 b.blk[j] = filep->fi_memp; 304 blknos[j] = nb; 305 } 306 bap = (daddr32_t *)b.blk[j]; 307 sh /= NINDIR(&devp->un_fs.di_fs); 308 i = (bn / sh) % NINDIR(&devp->un_fs.di_fs); 309 nb = bap[i]; 310 if (nb == 0) { 311 return ((daddr32_t)0); 312 } 313 } 314 return (nb); 315 } 316 317 static ino_t 318 dlook(fileid_t *filep, char *path) 319 { 320 devid_t *devp = filep->fi_devp; 321 struct direct *dp; 322 struct inode *ip; 323 struct dirinfo dirp; 324 int len; 325 ino_t in; 326 #ifdef DEBUG 327 static int warned = 0; 328 #endif 329 330 ip = filep->fi_inode; 331 if (path == NULL || *path == '\0') 332 return (0); 333 if ((ip->i_smode & IFMT) != IFDIR) 334 return (0); 335 if (ip->i_size == 0) 336 return (0); 337 len = strlen(path); 338 339 /* 340 * First look through the directory entry cache 341 */ 342 if ((in = get_dcache(devp->di_dcookie, path, ip->i_number)) != 0) 343 return (in); 344 345 /* 346 * If the entire directory is cached, return failure 347 */ 348 if (ip->i_flag & FI_CACHED) 349 return (0); 350 351 /* 352 * Otherwise, read the entire directory into the cache 353 */ 354 in = 0; 355 dirp.loc = 0; 356 dirp.fi = filep; 357 if (!(ip->i_flag & FI_NOCACHE)) 358 ip->i_flag |= FI_CACHED; 359 for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) { 360 if (dp->d_ino == 0) 361 continue; 362 if (dp->d_namlen == len && strcmp(path, dp->d_name) == 0) 363 in = dp->d_ino; 364 365 /* 366 * Allow "*" to print all names at that level, w/out match 367 */ 368 if (strcmp(path, "*") == 0) 369 printf("%s\n", dp->d_name); 370 371 if (ip->i_flag & FI_NOCACHE) 372 continue; 373 374 /* 375 * Put this entry into the cache. If the entry has been 376 * partially cached, check before inserting. This should be 377 * rare if sized correctly 378 */ 379 if ((ip->i_flag & FI_PARTIAL_CACHE) && 380 (get_dcache(devp->di_dcookie, dp->d_name, dp->d_ino) != 0)) 381 continue; 382 383 if (set_rdcache(devp->di_dcookie, dp->d_name, ip->i_number, 384 dp->d_ino)) { 385 ip->i_flag &= ~FI_CACHED; 386 ip->i_flag |= FI_PARTIAL_CACHE; 387 #ifdef DEBUG 388 if (!warned) { 389 printf("ufsboot: directory cache too small\n"); 390 warned++; 391 } 392 #endif 393 } 394 } 395 return (in); 396 } 397 398 /* 399 * get next entry in a directory. 400 */ 401 struct direct * 402 readdir(struct dirinfo *dstuff) 403 { 404 struct direct *dp; 405 fileid_t *filep; 406 daddr32_t lbn, d; 407 int off; 408 devid_t *devp; 409 410 filep = dstuff->fi; 411 devp = filep->fi_devp; 412 for (;;) { 413 if (dstuff->loc >= filep->fi_inode->i_size) { 414 return (NULL); 415 } 416 off = blkoff(&devp->un_fs.di_fs, dstuff->loc); 417 if (off == 0) { 418 lbn = lblkno(&devp->un_fs.di_fs, dstuff->loc); 419 d = sbmap(filep, lbn); 420 421 if (d == 0) 422 return (NULL); 423 424 filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, d); 425 filep->fi_count = 426 blksize(&devp->un_fs.di_fs, filep->fi_inode, lbn); 427 /* check the block cache */ 428 if ((filep->fi_memp = get_bcache(filep)) == NULL) { 429 if (set_bcache(filep)) 430 return (NULL); 431 lufs_merge_deltas(filep); 432 } 433 } 434 dp = (struct direct *)(filep->fi_memp + off); 435 dstuff->loc += dp->d_reclen; 436 if (dp->d_ino == 0) 437 continue; 438 return (dp); 439 } 440 } 441 442 /* 443 * Get the next block of data from the file. If possible, dma right into 444 * user's buffer 445 */ 446 static int 447 getblock(fileid_t *filep, caddr_t buf, int count, int *rcount) 448 { 449 struct fs *fs; 450 caddr_t p; 451 int off, size, diff; 452 daddr32_t lbn; 453 devid_t *devp; 454 #ifndef i386 455 static int pos; 456 static char ind[] = "|/-\\"; /* that's entertainment? */ 457 static int blks_read; 458 #endif 459 devp = filep->fi_devp; 460 p = filep->fi_memp; 461 if ((signed)filep->fi_count <= 0) { 462 463 /* find the amt left to be read in the file */ 464 diff = filep->fi_inode->i_size - filep->fi_offset; 465 if (diff <= 0) { 466 printf("Short read\n"); 467 return (-1); 468 } 469 470 fs = &devp->un_fs.di_fs; 471 /* which block (or frag) in the file do we read? */ 472 lbn = lblkno(fs, filep->fi_offset); 473 474 /* which physical block on the device do we read? */ 475 filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn)); 476 477 off = blkoff(fs, filep->fi_offset); 478 479 /* either blksize or fragsize */ 480 size = blksize(fs, filep->fi_inode, lbn); 481 filep->fi_count = size; 482 filep->fi_memp = filep->fi_buf; 483 484 /* 485 * optimization if we are reading large blocks of data then 486 * we can go directly to user's buffer 487 */ 488 *rcount = 0; 489 if (off == 0 && count >= size) { 490 filep->fi_memp = buf; 491 if (diskread(filep)) { 492 return (-1); 493 } 494 *rcount = size; 495 filep->fi_count = 0; 496 read_opt++; 497 #ifndef i386 498 if ((blks_read++ & 0x3) == 0) 499 printf("%c\b", ind[pos++ & 3]); 500 #endif 501 return (0); 502 } else 503 if (diskread(filep)) 504 return (-1); 505 506 /* 507 * round and round she goes (though not on every block.. 508 * - OBP's take a fair bit of time to actually print stuff) 509 * On x86, the screen oriented bootconf program doesn't 510 * want this noise... 511 */ 512 #ifndef i386 513 if ((blks_read++ & 0x3) == 0) 514 printf("%c\b", ind[pos++ & 3]); 515 #endif 516 517 if (filep->fi_offset - off + size >= filep->fi_inode->i_size) 518 filep->fi_count = diff + off; 519 filep->fi_count -= off; 520 p = &filep->fi_memp[off]; 521 } 522 filep->fi_memp = p; 523 return (0); 524 } 525 526 527 /* 528 * This is the high-level read function. It works like this. 529 * We assume that our IO device buffers up some amount of 530 * data and that we can get a ptr to it. Thus we need 531 * to actually call the device func about filesize/blocksize times 532 * and this greatly increases our IO speed. When we already 533 * have data in the buffer, we just return that data (with bcopy() ). 534 */ 535 536 static ssize_t 537 boot_ufs_read(int fd, caddr_t buf, size_t count) 538 { 539 size_t i, j; 540 caddr_t n; 541 int rcount; 542 fileid_t *filep; 543 544 if (!(filep = find_fp(fd))) { 545 return (-1); 546 } 547 548 if (filep->fi_offset + count > filep->fi_inode->i_size) 549 count = filep->fi_inode->i_size - filep->fi_offset; 550 551 /* that was easy */ 552 if ((i = count) == 0) 553 return (0); 554 555 n = buf; 556 while (i > 0) { 557 /* If we need to reload the buffer, do so */ 558 if ((j = filep->fi_count) == 0) { 559 getblock(filep, buf, i, &rcount); 560 i -= rcount; 561 buf += rcount; 562 filep->fi_offset += rcount; 563 } else { 564 /* else just bcopy from our buffer */ 565 j = MIN(i, j); 566 bcopy(filep->fi_memp, buf, (unsigned)j); 567 buf += j; 568 filep->fi_memp += j; 569 filep->fi_offset += j; 570 filep->fi_count -= j; 571 i -= j; 572 } 573 } 574 return (buf - n); 575 } 576 577 /* 578 * This routine will open a device as it is known by the V2 OBP. 579 * Interface Defn: 580 * err = boot_ufs_mountroot(string); 581 * err = 0 on success 582 * err = -1 on failure 583 * string: char string describing the properties of the device. 584 * We must not dork with any fi[]'s here. Save that for later. 585 */ 586 587 static int 588 boot_ufs_mountroot(char *str) 589 { 590 int h; 591 592 /* 593 * Open the device and setup the read of the ufs superblock 594 * only the first time mountroot is called. Subsequent calls 595 * to mountroot succeed immediatly 596 */ 597 if (ufs_devp == NULL) { 598 599 /* 600 * Encode the knowledge that we normally boot from the 'a' 601 * slice of the leaf device on the OBP path; we also permit 602 * a 'nolabel' device, i.e. the entire device. Since v2path 603 * points to 'str' as well, changing str should have the 604 * desired result. 605 */ 606 if (strchr(str, ':') == NULL) { 607 (void) strcat(str, ":a"); 608 } 609 h = prom_open(str); 610 if (h == 0) { 611 printf("Cannot open %s\n", str); 612 return (-1); 613 } 614 615 ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t)); 616 ufs_devp->di_taken = 1; 617 ufs_devp->di_dcookie = h; 618 ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1); 619 (void) strcpy(ufs_devp->di_desc, str); 620 bzero(ufs_devp->un_fs.dummy, SBSIZE); 621 head = (fileid_t *)bkmem_alloc(sizeof (fileid_t)); 622 head->fi_back = head->fi_forw = head; 623 head->fi_filedes = 0; 624 head->fi_taken = 0; 625 626 /* Setup read of the superblock */ 627 head->fi_devp = ufs_devp; 628 head->fi_blocknum = SBLOCK; 629 head->fi_count = (uint_t)SBSIZE; 630 head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs); 631 head->fi_offset = 0; 632 633 if (diskread(head) || 634 ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) { 635 boot_ufs_closeall(1); 636 return (-1); 637 } 638 lufs_boot_init(head); 639 } 640 return (0); 641 } 642 643 /* 644 * Unmount the currently mounted root fs. In practice, this means 645 * closing all open files and releasing resources. All of this 646 * is done by boot_ufs_closeall(). 647 */ 648 649 int 650 boot_ufs_unmountroot(void) 651 { 652 if (ufs_devp == NULL) 653 return (-1); 654 655 boot_ufs_closeall(1); 656 657 return (0); 658 } 659 660 /* 661 * We allocate an fd here for use when talking 662 * to the file itself. 663 */ 664 665 /*ARGSUSED*/ 666 static int 667 boot_ufs_open(char *filename, int flags) 668 { 669 fileid_t *filep; 670 ino_t inode; 671 static int filedes = 1; 672 673 /* build and link a new file descriptor */ 674 filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t)); 675 filep->fi_back = head->fi_back; 676 filep->fi_forw = head; 677 head->fi_back->fi_forw = filep; 678 head->fi_back = filep; 679 filep->fi_filedes = filedes++; 680 filep->fi_taken = 1; 681 filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1); 682 (void) strcpy(filep->fi_path, filename); 683 filep->fi_devp = ufs_devp; /* dev is already "mounted" */ 684 filep->fi_inode = NULL; 685 bzero(filep->fi_buf, MAXBSIZE); 686 687 inode = find(filep, filename); 688 if (inode == (ino_t)0) { 689 boot_ufs_close(filep->fi_filedes); 690 return (-1); 691 } 692 if (openi(filep, inode)) { 693 boot_ufs_close(filep->fi_filedes); 694 return (-1); 695 } 696 697 filep->fi_offset = filep->fi_count = 0; 698 699 return (filep->fi_filedes); 700 } 701 702 /* 703 * We don't do any IO here. 704 * We just play games with the device pointers. 705 */ 706 707 static off_t 708 boot_ufs_lseek(int fd, off_t addr, int whence) 709 { 710 fileid_t *filep; 711 712 /* Make sure user knows what file he is talking to */ 713 if (!(filep = find_fp(fd))) 714 return (-1); 715 716 switch (whence) { 717 case SEEK_CUR: 718 filep->fi_offset += addr; 719 break; 720 case SEEK_SET: 721 filep->fi_offset = addr; 722 break; 723 default: 724 case SEEK_END: 725 printf("ufs_lseek(): invalid whence value %d\n", whence); 726 break; 727 } 728 729 filep->fi_blocknum = addr / DEV_BSIZE; 730 filep->fi_count = 0; 731 732 return (0); 733 } 734 735 /* 736 * ufs_fstat() only supports size, mode, and times at present time. 737 */ 738 739 static int 740 boot_ufs_fstat(int fd, struct bootstat *stp) 741 { 742 fileid_t *filep; 743 struct inode *ip; 744 745 if (!(filep = find_fp(fd))) 746 return (-1); 747 748 ip = filep->fi_inode; 749 750 stp->st_mode = 0; 751 stp->st_size = 0; 752 753 if (ip == NULL) 754 return (0); 755 756 switch (ip->i_smode & IFMT) { 757 case IFDIR: 758 stp->st_mode = S_IFDIR; 759 break; 760 case IFLNK: 761 stp->st_mode = S_IFLNK; 762 break; 763 case IFREG: 764 stp->st_mode = S_IFREG; 765 break; 766 default: 767 break; 768 } 769 stp->st_size = ip->i_size; 770 stp->st_atim.tv_sec = ip->i_atime.tv_sec; 771 stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000; 772 stp->st_mtim.tv_sec = ip->i_mtime.tv_sec; 773 stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000; 774 stp->st_ctim.tv_sec = ip->i_ctime.tv_sec; 775 stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000; 776 777 return (0); 778 } 779 780 static int 781 boot_ufs_close(int fd) 782 { 783 fileid_t *filep; 784 785 /* Make sure user knows what file he is talking to */ 786 if (!(filep = find_fp(fd))) 787 return (-1); 788 789 if (filep->fi_taken && (filep != head)) { 790 /* Clear the ranks */ 791 bkmem_free(filep->fi_path, strlen(filep->fi_path)+1); 792 filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0; 793 filep->fi_memp = (caddr_t)0; 794 filep->fi_devp = 0; 795 filep->fi_taken = 0; 796 797 /* unlink and deallocate node */ 798 filep->fi_forw->fi_back = filep->fi_back; 799 filep->fi_back->fi_forw = filep->fi_forw; 800 bkmem_free((char *)filep, sizeof (fileid_t)); 801 802 return (0); 803 } else { 804 /* Big problem */ 805 printf("\nFile descrip %d not allocated!", fd); 806 return (-1); 807 } 808 } 809 810 /*ARGSUSED*/ 811 static void 812 boot_ufs_closeall(int flag) 813 { 814 fileid_t *filep = head; 815 816 while ((filep = filep->fi_forw) != head) 817 if (filep->fi_taken) 818 if (boot_ufs_close(filep->fi_filedes)) 819 prom_panic("Filesystem may be inconsistent.\n"); 820 821 release_cache(ufs_devp->di_dcookie); 822 (void) prom_close(ufs_devp->di_dcookie); 823 ufs_devp->di_taken = 0; 824 if (verbosemode & print_cache_stats) 825 print_cache_data(); 826 lufs_closeall(); 827 bkmem_free((char *)ufs_devp, sizeof (devid_t)); 828 bkmem_free((char *)head, sizeof (fileid_t)); 829 ufs_devp = (devid_t *)NULL; 830 head = (fileid_t *)NULL; 831 } 832 833 static int 834 boot_ufs_getdents(int fd, struct dirent *dep, unsigned size) 835 { 836 /* 837 * Read directory entries from the file open on "fd" into the 838 * "size"-byte buffer at "dep" until the buffer is exhausted 839 * or we reach EOF on the directory. Returns the number of 840 * entries read. 841 */ 842 int n; 843 fileid_t *fp; 844 unsigned long oldoff, oldblok; 845 846 #define SLOP (sizeof (struct dirent) - (int)&((struct dirent *)0)->d_name[1]) 847 848 if (fp = find_fp(fd)) { 849 /* 850 * File is open, check type to make sure it's a directory. 851 */ 852 853 while ((fp->fi_inode->i_smode & IFMT) == IFLNK) { 854 /* 855 * If file is a symbolic link, we'll follow 856 * it JIC it points to a directory! 857 */ 858 fileid_t fx; 859 char pn[MAXPATHLEN]; 860 fp->fi_count = DEV_BSIZE; 861 fp->fi_blocknum = fsbtodb(&fp->fi_devp->un_fs.di_fs, 862 fp->fi_inode->i_db[0]); 863 864 /* 865 * Return failure if: 866 * (a) we get an I/O error reading the path name. 867 * (b) the path name points to a non-existant file, 868 * (c) we get an I/O error reading the target inode. 869 */ 870 if ((fp->fi_memp = get_bcache(fp)) == NULL) { 871 if (set_bcache(fp)) 872 return (-1); 873 lufs_merge_deltas(fp); 874 } 875 if (!(n = find(&fx, strcpy(pn, fp->fi_memp))) || 876 openi(fp = &fx, n)) { 877 return (-1); 878 } 879 } 880 881 if ((fp->fi_inode->i_smode & IFMT) == IFDIR) { 882 /* 883 * If target file is a directory, go ahead 884 * and read it. This consists of making 885 * repeated calls to readdir() until we reach 886 * end-of-file or run out of buffer space. 887 */ 888 int cnt = 0; 889 struct direct *dp; 890 struct dirinfo dir; 891 892 dir.fi = fp; 893 oldblok = fp->fi_blocknum; 894 dir.loc = oldoff = fp->fi_offset; 895 896 for (dp = readdir(&dir); dp; dp = readdir(&dir)) { 897 /* 898 * Read all directory entries in the file ... 899 */ 900 901 if (dp->d_ino) { 902 /* 903 * Next entry is valid. 904 * Compute name length and 905 * break loop if there's not 906 * enough space in the output 907 * buffer for the next entry. 908 * 909 * NOTE: "SLOP" is the number 910 * of bytes inserted into the 911 * dirent struct's "d_name" 912 * field by the compiler to 913 * preserve alignment. 914 */ 915 dep->d_ino = dp->d_ino; 916 n = strlen(dp->d_name); 917 n = roundup((sizeof (struct dirent) + 918 ((n > SLOP) ? n : 0)), 919 sizeof (off_t)); 920 921 if (n > size) 922 break; /* user buffer is full */ 923 924 oldblok = fp->fi_blocknum; 925 oldoff = dir.loc; 926 size -= n; 927 cnt += 1; 928 929 (void) strcpy(dep->d_name, dp->d_name); 930 dep->d_off = dir.loc; 931 dep->d_reclen = (ushort_t)n; 932 933 dep = (struct dirent *) 934 ((char *)dep + n); 935 } 936 } 937 /* 938 * Remember where we left off for next time 939 */ 940 fp->fi_blocknum = oldblok; 941 fp->fi_offset = oldoff; 942 943 return (cnt); 944 } 945 } 946 947 #undef SLOP 948 949 return (-1); 950 } 951