1 /* $FreeBSD: src/lib/libstand/ufs.c,v 1.5.6.1 2000/05/04 13:47:53 ps Exp $ */ 2 /* $NetBSD: ufs.c,v 1.20 1998/03/01 07:15:39 ross Exp $ */ 3 4 /*- 5 * Copyright (c) 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * The Mach Operating System project at Carnegie-Mellon University. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 * 36 * Copyright (c) 1990, 1991 Carnegie Mellon University 37 * All Rights Reserved. 38 * 39 * Author: David Golub 40 * 41 * Permission to use, copy, modify and distribute this software and its 42 * documentation is hereby granted, provided that both the copyright 43 * notice and this permission notice appear in all copies of the 44 * software, derivative works or modified versions, and any portions 45 * thereof, and that both notices appear in supporting documentation. 46 * 47 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 48 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 49 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 50 * 51 * Carnegie Mellon requests users of this software to return to 52 * 53 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 54 * School of Computer Science 55 * Carnegie Mellon University 56 * Pittsburgh PA 15213-3890 57 * 58 * any improvements or extensions that they make and grant Carnegie the 59 * rights to redistribute these changes. 60 */ 61 62 /* 63 * Stand-alone file reading package. 64 */ 65 66 #include <sys/param.h> 67 #include <sys/time.h> 68 69 #include "stand.h" 70 71 #include <vfs/ufs/dinode.h> 72 #include <vfs/ufs/dir.h> 73 #include <vfs/ufs/fs.h> 74 #include "string.h" 75 76 static int ufs_open(const char *path, struct open_file *f); 77 static int ufs_close(struct open_file *f); 78 static int ufs_read(struct open_file *f, void *buf, size_t size, size_t *resid); 79 static off_t ufs_seek(struct open_file *f, off_t offset, int where); 80 static int ufs_stat(struct open_file *f, struct stat *sb); 81 static int ufs_readdir(struct open_file *f, struct dirent *d); 82 83 struct fs_ops ufs_fsops = { 84 "ufs", 85 ufs_open, 86 ufs_close, 87 ufs_read, 88 null_write, 89 ufs_seek, 90 ufs_stat, 91 ufs_readdir 92 }; 93 94 /* 95 * In-core open file. 96 */ 97 struct file { 98 off_t f_seekp; /* seek pointer */ 99 struct fs *f_fs; /* pointer to super-block */ 100 struct ufs1_dinode f_di; /* copy of on-disk inode */ 101 int f_nindir[UFS_NIADDR]; 102 /* number of blocks mapped by 103 indirect block at level i */ 104 char *f_blk[UFS_NIADDR];/* buffer for indirect block at 105 level i */ 106 size_t f_blksize[UFS_NIADDR]; 107 /* size of buffer */ 108 daddr_t f_blkno[UFS_NIADDR];/* disk address of block in buffer */ 109 char *f_buf; /* buffer for data block */ 110 size_t f_buf_size; /* size of data block */ 111 daddr_t f_buf_blkno; /* block number of data block */ 112 }; 113 114 static int read_inode(ino_t, struct open_file *); 115 static int block_map(struct open_file *, daddr_t, daddr_t *); 116 static int buf_read_file(struct open_file *, char **, size_t *); 117 static int search_directory(char *, struct open_file *, ino_t *); 118 #ifdef COMPAT_UFS 119 static void ffs_oldfscompat(struct fs *); 120 #endif 121 122 /* 123 * Read a new inode into a file structure. 124 */ 125 static int 126 read_inode(ino_t inumber, struct open_file *f) 127 { 128 struct file *fp = (struct file *)f->f_fsdata; 129 struct fs *fs = fp->f_fs; 130 char *buf; 131 size_t rsize; 132 int rc; 133 134 if (fs == NULL) 135 panic("fs == NULL"); 136 137 /* 138 * Read inode and save it. 139 */ 140 buf = malloc(fs->fs_bsize); 141 twiddle(); 142 rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 143 fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize, 144 buf, &rsize); 145 if (rc) 146 goto out; 147 if (rsize != fs->fs_bsize) { 148 rc = EIO; 149 goto out; 150 } 151 152 { 153 struct ufs1_dinode *dp; 154 155 dp = (struct ufs1_dinode *)buf; 156 fp->f_di = dp[ino_to_fsbo(fs, inumber)]; 157 } 158 159 /* 160 * Clear out the old buffers 161 */ 162 { 163 int level; 164 165 for (level = 0; level < UFS_NIADDR; level++) 166 fp->f_blkno[level] = -1; 167 fp->f_buf_blkno = -1; 168 } 169 out: 170 free(buf); 171 return (rc); 172 } 173 174 /* 175 * Given an offset in a file, find the disk block number that 176 * contains that block. 177 * 178 * Parameters: 179 * disk_block_p: out 180 */ 181 static int 182 block_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p) 183 { 184 struct file *fp = (struct file *)f->f_fsdata; 185 struct fs *fs = fp->f_fs; 186 int level; 187 int idx; 188 daddr_t ind_block_num; 189 daddr_t *ind_p; 190 int rc; 191 192 /* 193 * Index structure of an inode: 194 * 195 * di_db[0..UFS_NDADDR-1] hold block numbers for blocks 196 * 0..UFS_NDADDR-1 197 * 198 * di_ib[0] index block 0 is the single indirect block 199 * holds block numbers for blocks 200 * UFS_NDADDR .. UFS_NDADDR + NINDIR(fs)-1 201 * 202 * di_ib[1] index block 1 is the double indirect block 203 * holds block numbers for INDEX blocks for blocks 204 * UFS_NDADDR + NINDIR(fs) .. 205 * UFS_NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1 206 * 207 * di_ib[2] index block 2 is the triple indirect block 208 * holds block numbers for double-indirect 209 * blocks for blocks 210 * UFS_NDADDR + NINDIR(fs) + NINDIR(fs)**2 .. 211 * UFS_NDADDR + NINDIR(fs) + NINDIR(fs)**2 212 * + NINDIR(fs)**3 - 1 213 */ 214 215 if (file_block < UFS_NDADDR) { 216 /* Direct block. */ 217 *disk_block_p = fp->f_di.di_db[file_block]; 218 return (0); 219 } 220 221 file_block -= UFS_NDADDR; 222 223 /* 224 * nindir[0] = NINDIR 225 * nindir[1] = NINDIR**2 226 * nindir[2] = NINDIR**3 227 * etc 228 */ 229 for (level = 0; level < UFS_NIADDR; level++) { 230 if (file_block < fp->f_nindir[level]) 231 break; 232 file_block -= fp->f_nindir[level]; 233 } 234 if (level == UFS_NIADDR) { 235 /* Block number too high */ 236 return (EFBIG); 237 } 238 239 ind_block_num = fp->f_di.di_ib[level]; 240 241 for (; level >= 0; level--) { 242 if (ind_block_num == 0) { 243 *disk_block_p = 0; /* missing */ 244 return (0); 245 } 246 247 if (fp->f_blkno[level] != ind_block_num) { 248 if (fp->f_blk[level] == NULL) 249 fp->f_blk[level] = 250 malloc(fs->fs_bsize); 251 twiddle(); 252 rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 253 fsbtodb(fp->f_fs, ind_block_num), 254 fs->fs_bsize, 255 fp->f_blk[level], 256 &fp->f_blksize[level]); 257 if (rc) 258 return (rc); 259 if (fp->f_blksize[level] != fs->fs_bsize) 260 return (EIO); 261 fp->f_blkno[level] = ind_block_num; 262 } 263 264 ind_p = (daddr_t *)fp->f_blk[level]; 265 266 if (level > 0) { 267 idx = file_block / fp->f_nindir[level - 1]; 268 file_block %= fp->f_nindir[level - 1]; 269 } else 270 idx = file_block; 271 272 ind_block_num = ind_p[idx]; 273 } 274 275 *disk_block_p = ind_block_num; 276 277 return (0); 278 } 279 280 /* 281 * Read a portion of a file into an internal buffer. Return 282 * the location in the buffer and the amount in the buffer. 283 * 284 * Parameters: 285 * buf_p: out 286 * size_p: out 287 */ 288 static int 289 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p) 290 { 291 struct file *fp = (struct file *)f->f_fsdata; 292 struct fs *fs = fp->f_fs; 293 long off; 294 daddr_t file_block; 295 daddr_t disk_block; 296 size_t block_size; 297 int rc; 298 299 off = blkoff(fs, fp->f_seekp); 300 file_block = lblkno(fs, fp->f_seekp); 301 block_size = dblksize(fs, &fp->f_di, file_block); 302 303 if (file_block != fp->f_buf_blkno) { 304 rc = block_map(f, file_block, &disk_block); 305 if (rc) 306 return (rc); 307 308 if (fp->f_buf == NULL) 309 fp->f_buf = malloc(fs->fs_bsize); 310 311 if (disk_block == 0) { 312 bzero(fp->f_buf, block_size); 313 fp->f_buf_size = block_size; 314 } else { 315 twiddle(); 316 rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 317 fsbtodb(fs, disk_block), 318 block_size, fp->f_buf, &fp->f_buf_size); 319 if (rc) 320 return (rc); 321 } 322 323 fp->f_buf_blkno = file_block; 324 } 325 326 /* 327 * Return address of byte in buffer corresponding to 328 * offset, and size of remainder of buffer after that 329 * byte. 330 */ 331 *buf_p = fp->f_buf + off; 332 *size_p = block_size - off; 333 334 /* 335 * But truncate buffer at end of file. 336 */ 337 if (*size_p > fp->f_di.di_size - fp->f_seekp) 338 *size_p = fp->f_di.di_size - fp->f_seekp; 339 340 return (0); 341 } 342 343 /* 344 * Search a directory for a name and return its 345 * i_number. 346 * 347 * Parameters: 348 * inumber_p: out 349 */ 350 static int 351 search_directory(char *name, struct open_file *f, ino_t *inumber_p) 352 { 353 struct file *fp = (struct file *)f->f_fsdata; 354 struct direct *dp; 355 struct direct *edp; 356 char *buf; 357 size_t buf_size; 358 int namlen, length; 359 int rc; 360 361 length = strlen(name); 362 363 fp->f_seekp = 0; 364 while (fp->f_seekp < fp->f_di.di_size) { 365 rc = buf_read_file(f, &buf, &buf_size); 366 if (rc) 367 return (rc); 368 369 dp = (struct direct *)buf; 370 edp = (struct direct *)(buf + buf_size); 371 while (dp < edp) { 372 if (dp->d_ino == (ino_t)0) 373 goto next; 374 if (dp->d_type == DT_WHT) 375 goto next; 376 #if BYTE_ORDER == LITTLE_ENDIAN 377 if (fp->f_fs->fs_maxsymlinklen <= 0) 378 namlen = dp->d_type; 379 else 380 #endif 381 namlen = dp->d_namlen; 382 if (namlen == length && 383 !strcmp(name, dp->d_name)) { 384 /* found entry */ 385 *inumber_p = dp->d_ino; 386 return (0); 387 } 388 next: 389 dp = (struct direct *)((char *)dp + dp->d_reclen); 390 } 391 fp->f_seekp += buf_size; 392 } 393 return (ENOENT); 394 } 395 396 /* 397 * Open a file. 398 */ 399 static int 400 ufs_open(const char *upath, struct open_file *f) 401 { 402 char *cp, *ncp; 403 int c; 404 ino_t inumber, parent_inumber; 405 struct file *fp; 406 struct fs *fs; 407 int rc; 408 size_t buf_size; 409 int nlinks = 0; 410 char namebuf[MAXPATHLEN+1]; 411 char *buf = NULL; 412 char *path = NULL; 413 414 /* allocate file system specific data structure */ 415 fp = malloc(sizeof(struct file)); 416 bzero(fp, sizeof(struct file)); 417 f->f_fsdata = (void *)fp; 418 419 /* allocate space and read super block */ 420 fs = malloc(SBSIZE); 421 fp->f_fs = fs; 422 twiddle(); 423 rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 424 SBOFF / DEV_BSIZE, SBSIZE, (char *)fs, &buf_size); 425 if (rc) 426 goto out; 427 428 if (buf_size != SBSIZE || fs->fs_magic != FS_MAGIC || 429 fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) { 430 rc = EINVAL; 431 goto out; 432 } 433 #ifdef COMPAT_UFS 434 ffs_oldfscompat(fs); 435 #endif 436 437 /* 438 * Calculate indirect block levels. 439 */ 440 { 441 int omult; 442 int mult; 443 int level; 444 445 omult = 0; 446 mult = 1; 447 for (level = 0; level < UFS_NIADDR; level++) { 448 mult *= NINDIR(fs); 449 if (mult < omult) 450 mult = 0x7FFFFFFF; 451 fp->f_nindir[level] = mult; 452 omult = mult; 453 } 454 } 455 456 inumber = UFS_ROOTINO; 457 if ((rc = read_inode(inumber, f)) != 0) 458 goto out; 459 460 cp = path = strdup(upath); 461 if (path == NULL) { 462 rc = ENOMEM; 463 goto out; 464 } 465 while (*cp) { 466 467 /* 468 * Remove extra separators 469 */ 470 while (*cp == '/') 471 cp++; 472 if (*cp == '\0') 473 break; 474 475 /* 476 * Check that current node is a directory. 477 */ 478 if ((fp->f_di.di_mode & IFMT) != IFDIR) { 479 rc = ENOTDIR; 480 goto out; 481 } 482 483 /* 484 * Get next component of path name. 485 */ 486 { 487 int len = 0; 488 489 ncp = cp; 490 while ((c = *cp) != '\0' && c != '/') { 491 if (++len > MAXNAMLEN) { 492 rc = ENOENT; 493 goto out; 494 } 495 cp++; 496 } 497 *cp = '\0'; 498 } 499 500 /* 501 * Look up component in current directory. 502 * Save directory inumber in case we find a 503 * symbolic link. 504 */ 505 parent_inumber = inumber; 506 rc = search_directory(ncp, f, &inumber); 507 *cp = c; 508 if (rc) 509 goto out; 510 511 /* 512 * Open next component. 513 */ 514 if ((rc = read_inode(inumber, f)) != 0) 515 goto out; 516 517 /* 518 * Check for symbolic link. 519 */ 520 if ((fp->f_di.di_mode & IFMT) == IFLNK) { 521 int link_len = fp->f_di.di_size; 522 int len; 523 524 len = strlen(cp); 525 526 if (link_len + len > MAXPATHLEN || 527 ++nlinks > MAXSYMLINKS) { 528 rc = ENOENT; 529 goto out; 530 } 531 532 bcopy(cp, &namebuf[link_len], len + 1); 533 534 if (link_len < fs->fs_maxsymlinklen) { 535 bcopy(fp->f_di.di_shortlink, namebuf, 536 (unsigned) link_len); 537 } else { 538 /* 539 * Read file for symbolic link 540 */ 541 size_t buf_size; 542 daddr_t disk_block; 543 struct fs *fs = fp->f_fs; 544 545 if (!buf) 546 buf = malloc(fs->fs_bsize); 547 rc = block_map(f, (daddr_t)0, &disk_block); 548 if (rc) 549 goto out; 550 551 twiddle(); 552 rc = (f->f_dev->dv_strategy)(f->f_devdata, 553 F_READ, fsbtodb(fs, disk_block), 554 fs->fs_bsize, buf, &buf_size); 555 if (rc) 556 goto out; 557 558 bcopy((char *)buf, namebuf, (unsigned)link_len); 559 } 560 561 /* 562 * If relative pathname, restart at parent directory. 563 * If absolute pathname, restart at root. 564 */ 565 cp = namebuf; 566 if (*cp != '/') 567 inumber = parent_inumber; 568 else 569 inumber = (ino_t)UFS_ROOTINO; 570 571 if ((rc = read_inode(inumber, f)) != 0) 572 goto out; 573 } 574 } 575 576 /* 577 * Found terminal component. 578 */ 579 fp->f_seekp = 0; 580 rc = 0; 581 out: 582 if (buf) 583 free(buf); 584 if (path) 585 free(path); 586 if (rc) { 587 f->f_fsdata = NULL; 588 if (fp->f_buf) 589 free(fp->f_buf); 590 free(fp->f_fs); 591 free(fp); 592 } 593 return (rc); 594 } 595 596 static int 597 ufs_close(struct open_file *f) 598 { 599 struct file *fp = (struct file *)f->f_fsdata; 600 int level; 601 602 f->f_fsdata = NULL; 603 if (fp == NULL) 604 return (0); 605 606 for (level = 0; level < UFS_NIADDR; level++) { 607 if (fp->f_blk[level]) 608 free(fp->f_blk[level]); 609 } 610 if (fp->f_buf) 611 free(fp->f_buf); 612 free(fp->f_fs); 613 free(fp); 614 return (0); 615 } 616 617 /* 618 * Copy a portion of a file into kernel memory. 619 * Cross block boundaries when necessary. 620 * 621 * Parameters: 622 * resid: out 623 */ 624 static int 625 ufs_read(struct open_file *f, void *start, size_t size, size_t *resid) 626 { 627 struct file *fp = (struct file *)f->f_fsdata; 628 size_t csize; 629 char *buf; 630 size_t buf_size; 631 int rc = 0; 632 char *addr = start; 633 634 while (size != 0) { 635 if (fp->f_seekp >= fp->f_di.di_size) 636 break; 637 638 rc = buf_read_file(f, &buf, &buf_size); 639 if (rc) 640 break; 641 642 csize = size; 643 if (csize > buf_size) 644 csize = buf_size; 645 646 bcopy(buf, addr, csize); 647 648 fp->f_seekp += csize; 649 addr += csize; 650 size -= csize; 651 } 652 if (resid) 653 *resid = size; 654 return (rc); 655 } 656 657 static off_t 658 ufs_seek(struct open_file *f, off_t offset, int where) 659 { 660 struct file *fp = (struct file *)f->f_fsdata; 661 662 switch (where) { 663 case SEEK_SET: 664 fp->f_seekp = offset; 665 break; 666 case SEEK_CUR: 667 fp->f_seekp += offset; 668 break; 669 case SEEK_END: 670 fp->f_seekp = fp->f_di.di_size - offset; 671 break; 672 default: 673 return (-1); 674 } 675 return (fp->f_seekp); 676 } 677 678 static int 679 ufs_stat(struct open_file *f, struct stat *sb) 680 { 681 struct file *fp = (struct file *)f->f_fsdata; 682 683 /* only important stuff */ 684 sb->st_mode = fp->f_di.di_mode; 685 sb->st_uid = fp->f_di.di_uid; 686 sb->st_gid = fp->f_di.di_gid; 687 sb->st_size = fp->f_di.di_size; 688 return (0); 689 } 690 691 static int 692 ufs_readdir(struct open_file *f, struct dirent *d) 693 { 694 struct file *fp = (struct file *)f->f_fsdata; 695 struct direct *dp; 696 char *buf; 697 size_t buf_size; 698 int error; 699 700 /* 701 * assume that a directory entry will not be split across blocks 702 */ 703 again: 704 if (fp->f_seekp >= fp->f_di.di_size) 705 return (ENOENT); 706 error = buf_read_file(f, &buf, &buf_size); 707 if (error) 708 return (error); 709 dp = (struct direct *)buf; 710 fp->f_seekp += dp->d_reclen; 711 if (dp->d_ino == (ino_t)0) 712 goto again; 713 d->d_type = dp->d_type; 714 strcpy(d->d_name, dp->d_name); 715 return (0); 716 } 717 718 #ifdef COMPAT_UFS 719 /* 720 * Sanity checks for old file systems. 721 * 722 * XXX - goes away some day. 723 */ 724 static void 725 ffs_oldfscompat(struct fs *fs) 726 { 727 int i; 728 729 fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */ 730 fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */ 731 if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */ 732 fs->fs_nrpos = 8; /* XXX */ 733 if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ 734 quad_t sizepb = fs->fs_bsize; /* XXX */ 735 /* XXX */ 736 fs->fs_maxfilesize = fs->fs_bsize * UFS_NDADDR - 1; /* XXX */ 737 for (i = 0; i < UFS_NIADDR; i++) { /* XXX */ 738 sizepb *= NINDIR(fs); /* XXX */ 739 fs->fs_maxfilesize += sizepb; /* XXX */ 740 } /* XXX */ 741 fs->fs_qbmask = ~fs->fs_bmask; /* XXX */ 742 fs->fs_qfmask = ~fs->fs_fmask; /* XXX */ 743 } /* XXX */ 744 } 745 #endif 746