1 /* $OpenBSD: ufs.c,v 1.16 2003/08/11 06:23:09 deraadt Exp $ */ 2 /* $NetBSD: ufs.c,v 1.16 1996/09/30 16:01:22 ws 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 #include <sys/stat.h> 69 #include <ufs/ffs/fs.h> 70 #include <ufs/ufs/dinode.h> 71 #include <ufs/ufs/dir.h> 72 #include <lib/libkern/libkern.h> 73 74 #include "stand.h" 75 #include "ufs.h" 76 77 /* 78 * In-core open file. 79 */ 80 struct file { 81 off_t f_seekp; /* seek pointer */ 82 struct fs *f_fs; /* pointer to super-block */ 83 struct dinode f_di; /* copy of on-disk inode */ 84 int f_nindir[NIADDR]; 85 /* number of blocks mapped by 86 indirect block at level i */ 87 char *f_blk[NIADDR]; /* buffer for indirect block at 88 level i */ 89 size_t f_blksize[NIADDR]; 90 /* size of buffer */ 91 daddr_t f_blkno[NIADDR];/* disk address of block in buffer */ 92 char *f_buf; /* buffer for data block */ 93 size_t f_buf_size; /* size of data block */ 94 daddr_t f_buf_blkno; /* block number of data block */ 95 }; 96 97 static int read_inode(ino_t, struct open_file *); 98 static int block_map(struct open_file *, daddr_t, daddr_t *); 99 static int buf_read_file(struct open_file *, char **, size_t *); 100 static int search_directory(char *, struct open_file *, ino_t *); 101 #ifdef COMPAT_UFS 102 static void ffs_oldfscompat(struct fs *); 103 #endif 104 105 /* 106 * Read a new inode into a file structure. 107 */ 108 static int 109 read_inode(ino_t inumber, struct open_file *f) 110 { 111 struct file *fp = (struct file *)f->f_fsdata; 112 struct fs *fs = fp->f_fs; 113 char *buf; 114 size_t rsize; 115 int rc; 116 117 /* 118 * Read inode and save it. 119 */ 120 buf = alloc(fs->fs_bsize); 121 twiddle(); 122 rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 123 fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize, 124 buf, &rsize); 125 if (rc) 126 goto out; 127 if (rsize != (size_t)fs->fs_bsize) { 128 rc = EIO; 129 goto out; 130 } 131 132 { 133 struct dinode *dp; 134 135 dp = (struct dinode *)buf; 136 fp->f_di = dp[ino_to_fsbo(fs, inumber)]; 137 } 138 139 /* 140 * Clear out the old buffers 141 */ 142 { 143 int level; 144 145 for (level = 0; level < NIADDR; level++) 146 fp->f_blkno[level] = -1; 147 fp->f_buf_blkno = -1; 148 } 149 out: 150 free(buf, fs->fs_bsize); 151 return (rc); 152 } 153 154 /* 155 * Given an offset in a file, find the disk block number that 156 * contains that block. 157 */ 158 static int 159 block_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p) 160 { 161 struct file *fp = (struct file *)f->f_fsdata; 162 daddr_t ind_block_num, *ind_p; 163 struct fs *fs = fp->f_fs; 164 int level, idx, rc; 165 166 /* 167 * Index structure of an inode: 168 * 169 * di_db[0..NDADDR-1] hold block numbers for blocks 170 * 0..NDADDR-1 171 * 172 * di_ib[0] index block 0 is the single indirect block 173 * holds block numbers for blocks 174 * NDADDR .. NDADDR + NINDIR(fs)-1 175 * 176 * di_ib[1] index block 1 is the double indirect block 177 * holds block numbers for INDEX blocks for blocks 178 * NDADDR + NINDIR(fs) .. 179 * NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1 180 * 181 * di_ib[2] index block 2 is the triple indirect block 182 * holds block numbers for double-indirect 183 * blocks for blocks 184 * NDADDR + NINDIR(fs) + NINDIR(fs)**2 .. 185 * NDADDR + NINDIR(fs) + NINDIR(fs)**2 186 * + NINDIR(fs)**3 - 1 187 */ 188 189 if (file_block < NDADDR) { 190 /* Direct block. */ 191 *disk_block_p = fp->f_di.di_db[file_block]; 192 return (0); 193 } 194 195 file_block -= NDADDR; 196 197 /* 198 * nindir[0] = NINDIR 199 * nindir[1] = NINDIR**2 200 * nindir[2] = NINDIR**3 201 * etc 202 */ 203 for (level = 0; level < NIADDR; level++) { 204 if (file_block < fp->f_nindir[level]) 205 break; 206 file_block -= fp->f_nindir[level]; 207 } 208 if (level == NIADDR) { 209 /* Block number too high */ 210 return (EFBIG); 211 } 212 213 ind_block_num = fp->f_di.di_ib[level]; 214 215 for (; level >= 0; level--) { 216 if (ind_block_num == 0) { 217 *disk_block_p = 0; /* missing */ 218 return (0); 219 } 220 221 if (fp->f_blkno[level] != ind_block_num) { 222 if (fp->f_blk[level] == (char *)0) 223 fp->f_blk[level] = 224 alloc(fs->fs_bsize); 225 twiddle(); 226 rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 227 fsbtodb(fp->f_fs, ind_block_num), fs->fs_bsize, 228 fp->f_blk[level], &fp->f_blksize[level]); 229 if (rc) 230 return (rc); 231 if (fp->f_blksize[level] != (size_t)fs->fs_bsize) 232 return (EIO); 233 fp->f_blkno[level] = ind_block_num; 234 } 235 236 ind_p = (daddr_t *)fp->f_blk[level]; 237 238 if (level > 0) { 239 idx = file_block / fp->f_nindir[level - 1]; 240 file_block %= fp->f_nindir[level - 1]; 241 } else 242 idx = file_block; 243 244 ind_block_num = ind_p[idx]; 245 } 246 247 *disk_block_p = ind_block_num; 248 return (0); 249 } 250 251 /* 252 * Read a portion of a file into an internal buffer. Return 253 * the location in the buffer and the amount in the buffer. 254 */ 255 static int 256 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p) 257 { 258 struct file *fp = (struct file *)f->f_fsdata; 259 struct fs *fs = fp->f_fs; 260 daddr_t file_block, disk_block; 261 size_t block_size; 262 long off; 263 int rc; 264 265 off = blkoff(fs, fp->f_seekp); 266 file_block = lblkno(fs, fp->f_seekp); 267 block_size = dblksize(fs, &fp->f_di, file_block); 268 269 if (file_block != fp->f_buf_blkno) { 270 rc = block_map(f, file_block, &disk_block); 271 if (rc) 272 return (rc); 273 274 if (fp->f_buf == (char *)0) 275 fp->f_buf = alloc(fs->fs_bsize); 276 277 if (disk_block == 0) { 278 bzero(fp->f_buf, block_size); 279 fp->f_buf_size = block_size; 280 } else { 281 twiddle(); 282 rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 283 fsbtodb(fs, disk_block), 284 block_size, fp->f_buf, &fp->f_buf_size); 285 if (rc) 286 return (rc); 287 } 288 289 fp->f_buf_blkno = file_block; 290 } 291 292 /* 293 * Return address of byte in buffer corresponding to 294 * offset, and size of remainder of buffer after that 295 * byte. 296 */ 297 *buf_p = fp->f_buf + off; 298 *size_p = block_size - off; 299 300 /* 301 * But truncate buffer at end of file. 302 */ 303 if (*size_p > fp->f_di.di_size - fp->f_seekp) 304 *size_p = fp->f_di.di_size - fp->f_seekp; 305 306 return (0); 307 } 308 309 /* 310 * Search a directory for a name and return its 311 * i_number. 312 */ 313 static int 314 search_directory(char *name, struct open_file *f, ino_t *inumber_p) 315 { 316 struct file *fp = (struct file *)f->f_fsdata; 317 int namlen, length, rc; 318 struct direct *dp, *edp; 319 size_t buf_size; 320 char *buf; 321 322 length = strlen(name); 323 324 fp->f_seekp = 0; 325 while (fp->f_seekp < fp->f_di.di_size) { 326 rc = buf_read_file(f, &buf, &buf_size); 327 if (rc) 328 return (rc); 329 330 dp = (struct direct *)buf; 331 edp = (struct direct *)(buf + buf_size); 332 while (dp < edp) { 333 if (dp->d_ino == (ino_t)0) 334 goto next; 335 #if BYTE_ORDER == LITTLE_ENDIAN 336 if (fp->f_fs->fs_maxsymlinklen <= 0) 337 namlen = dp->d_type; 338 else 339 #endif 340 namlen = dp->d_namlen; 341 if (namlen == length && 342 !strcmp(name, dp->d_name)) { 343 /* found entry */ 344 *inumber_p = dp->d_ino; 345 return (0); 346 } 347 next: 348 dp = (struct direct *)((char *)dp + dp->d_reclen); 349 } 350 fp->f_seekp += buf_size; 351 } 352 return (ENOENT); 353 } 354 355 /* 356 * Open a file. 357 */ 358 int 359 ufs_open(char *path, struct open_file *f) 360 { 361 char namebuf[MAXPATHLEN+1], *cp, *ncp, *buf = NULL; 362 ino_t inumber, parent_inumber; 363 int rc, c, nlinks = 0; 364 struct file *fp; 365 size_t buf_size; 366 struct fs *fs; 367 368 /* allocate file system specific data structure */ 369 fp = alloc(sizeof(struct file)); 370 bzero(fp, sizeof(struct file)); 371 f->f_fsdata = (void *)fp; 372 373 /* allocate space and read super block */ 374 fs = alloc(SBSIZE); 375 fp->f_fs = fs; 376 twiddle(); 377 rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ, 378 SBLOCK, SBSIZE, (char *)fs, &buf_size); 379 if (rc) 380 goto out; 381 382 if (buf_size != SBSIZE || fs->fs_magic != FS_MAGIC || 383 fs->fs_bsize > MAXBSIZE || fs->fs_bsize < sizeof(struct fs)) { 384 rc = EINVAL; 385 goto out; 386 } 387 #ifdef COMPAT_UFS 388 ffs_oldfscompat(fs); 389 #endif 390 391 /* 392 * Calculate indirect block levels. 393 */ 394 { 395 int mult; 396 int level; 397 398 mult = 1; 399 for (level = 0; level < NIADDR; level++) { 400 mult *= NINDIR(fs); 401 fp->f_nindir[level] = mult; 402 } 403 } 404 405 inumber = ROOTINO; 406 if ((rc = read_inode(inumber, f)) != 0) 407 goto out; 408 409 cp = path; 410 while (*cp) { 411 412 /* 413 * Remove extra separators 414 */ 415 while (*cp == '/') 416 cp++; 417 if (*cp == '\0') 418 break; 419 420 /* 421 * Check that current node is a directory. 422 */ 423 if ((fp->f_di.di_mode & IFMT) != IFDIR) { 424 rc = ENOTDIR; 425 goto out; 426 } 427 428 /* 429 * Get next component of path name. 430 */ 431 { 432 int len = 0; 433 434 ncp = cp; 435 while ((c = *cp) != '\0' && c != '/') { 436 if (++len > MAXNAMLEN) { 437 rc = ENOENT; 438 goto out; 439 } 440 cp++; 441 } 442 *cp = '\0'; 443 } 444 445 /* 446 * Look up component in current directory. 447 * Save directory inumber in case we find a 448 * symbolic link. 449 */ 450 parent_inumber = inumber; 451 rc = search_directory(ncp, f, &inumber); 452 *cp = c; 453 if (rc) 454 goto out; 455 456 /* 457 * Open next component. 458 */ 459 if ((rc = read_inode(inumber, f)) != 0) 460 goto out; 461 462 /* 463 * Check for symbolic link. 464 */ 465 if ((fp->f_di.di_mode & IFMT) == IFLNK) { 466 int link_len = fp->f_di.di_size; 467 int len; 468 469 len = strlen(cp); 470 471 if (link_len + len > MAXPATHLEN || 472 ++nlinks > MAXSYMLINKS) { 473 rc = ENOENT; 474 goto out; 475 } 476 477 bcopy(cp, &namebuf[link_len], len + 1); 478 479 if (link_len < fs->fs_maxsymlinklen) { 480 bcopy(fp->f_di.di_shortlink, namebuf, 481 (unsigned) link_len); 482 } else { 483 /* 484 * Read file for symbolic link 485 */ 486 size_t buf_size; 487 daddr_t disk_block; 488 struct fs *fs = fp->f_fs; 489 490 if (!buf) 491 buf = alloc(fs->fs_bsize); 492 rc = block_map(f, (daddr_t)0, &disk_block); 493 if (rc) 494 goto out; 495 496 twiddle(); 497 rc = (f->f_dev->dv_strategy)(f->f_devdata, 498 F_READ, fsbtodb(fs, disk_block), 499 fs->fs_bsize, buf, &buf_size); 500 if (rc) 501 goto out; 502 503 bcopy((char *)buf, namebuf, (unsigned)link_len); 504 } 505 506 /* 507 * If relative pathname, restart at parent directory. 508 * If absolute pathname, restart at root. 509 */ 510 cp = namebuf; 511 if (*cp != '/') 512 inumber = parent_inumber; 513 else 514 inumber = (ino_t)ROOTINO; 515 516 if ((rc = read_inode(inumber, f)) != 0) 517 goto out; 518 } 519 } 520 521 /* 522 * Found terminal component. 523 */ 524 rc = 0; 525 out: 526 if (buf) 527 free(buf, fs->fs_bsize); 528 if (rc) { 529 free(fp->f_fs, SBSIZE); 530 free(fp, sizeof(struct file)); 531 } 532 return (rc); 533 } 534 535 int 536 ufs_close(struct open_file *f) 537 { 538 struct file *fp = (struct file *)f->f_fsdata; 539 int level; 540 541 f->f_fsdata = (void *)0; 542 if (fp == (struct file *)0) 543 return (0); 544 545 for (level = 0; level < NIADDR; level++) { 546 if (fp->f_blk[level]) 547 free(fp->f_blk[level], fp->f_fs->fs_bsize); 548 } 549 if (fp->f_buf) 550 free(fp->f_buf, fp->f_fs->fs_bsize); 551 free(fp->f_fs, SBSIZE); 552 free(fp, sizeof(struct file)); 553 return (0); 554 } 555 556 /* 557 * Copy a portion of a file into kernel memory. 558 * Cross block boundaries when necessary. 559 */ 560 int 561 ufs_read(struct open_file *f, void *start, size_t size, size_t *resid) 562 { 563 struct file *fp = (struct file *)f->f_fsdata; 564 char *buf, *addr = start; 565 size_t csize, buf_size; 566 int rc = 0; 567 568 while (size != 0) { 569 if (fp->f_seekp >= fp->f_di.di_size) 570 break; 571 572 rc = buf_read_file(f, &buf, &buf_size); 573 if (rc) 574 break; 575 576 csize = size; 577 if (csize > buf_size) 578 csize = buf_size; 579 580 bcopy(buf, addr, csize); 581 582 fp->f_seekp += csize; 583 addr += csize; 584 size -= csize; 585 } 586 if (resid) 587 *resid = size; 588 return (rc); 589 } 590 591 /* 592 * Not implemented. 593 */ 594 int 595 ufs_write(struct open_file *f, void *start, size_t size, size_t *resid) 596 { 597 598 return (EROFS); 599 } 600 601 off_t 602 ufs_seek(struct open_file *f, off_t offset, int where) 603 { 604 struct file *fp = (struct file *)f->f_fsdata; 605 606 switch (where) { 607 case SEEK_SET: 608 fp->f_seekp = offset; 609 break; 610 case SEEK_CUR: 611 fp->f_seekp += offset; 612 break; 613 case SEEK_END: 614 fp->f_seekp = fp->f_di.di_size - offset; 615 break; 616 default: 617 return (-1); 618 } 619 return (fp->f_seekp); 620 } 621 622 int 623 ufs_stat(struct open_file *f, struct stat *sb) 624 { 625 struct file *fp = (struct file *)f->f_fsdata; 626 627 /* only important stuff */ 628 sb->st_mode = fp->f_di.di_mode; 629 sb->st_uid = fp->f_di.di_uid; 630 sb->st_gid = fp->f_di.di_gid; 631 sb->st_size = fp->f_di.di_size; 632 return (0); 633 } 634 635 #ifndef NO_READDIR 636 int 637 ufs_readdir(struct open_file *f, char *name) 638 { 639 struct file *fp = (struct file *)f->f_fsdata; 640 struct direct *dp, *edp; 641 size_t buf_size; 642 int rc, namlen; 643 char *buf; 644 645 if (name == NULL) 646 fp->f_seekp = 0; 647 else { 648 /* end of dir */ 649 if (fp->f_seekp >= fp->f_di.di_size) { 650 *name = '\0'; 651 return -1; 652 } 653 654 do { 655 if ((rc = buf_read_file(f, &buf, &buf_size)) != 0) 656 return rc; 657 658 dp = (struct direct *)buf; 659 edp = (struct direct *)(buf + buf_size); 660 while (dp < edp && dp->d_ino == (ino_t)0) 661 dp = (struct direct *)((char *)dp + dp->d_reclen); 662 fp->f_seekp += buf_size - 663 ((u_int8_t *)edp - (u_int8_t *)dp); 664 } while (dp >= edp); 665 666 #if BYTE_ORDER == LITTLE_ENDIAN 667 if (fp->f_fs->fs_maxsymlinklen <= 0) 668 namlen = dp->d_type; 669 else 670 #endif 671 namlen = dp->d_namlen; 672 strncpy(name, dp->d_name, namlen + 1); 673 674 fp->f_seekp += dp->d_reclen; 675 } 676 677 return 0; 678 } 679 #endif 680 681 #ifdef COMPAT_UFS 682 /* 683 * Sanity checks for old file systems. 684 * 685 * XXX - goes away some day. 686 */ 687 static void 688 ffs_oldfscompat(struct fs *fs) 689 { 690 int i; 691 692 fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect); /* XXX */ 693 fs->fs_interleave = max(fs->fs_interleave, 1); /* XXX */ 694 if (fs->fs_postblformat == FS_42POSTBLFMT) /* XXX */ 695 fs->fs_nrpos = 8; /* XXX */ 696 if (fs->fs_inodefmt < FS_44INODEFMT) { /* XXX */ 697 quad_t sizepb = fs->fs_bsize; /* XXX */ 698 /* XXX */ 699 fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1; /* XXX */ 700 for (i = 0; i < NIADDR; i++) { /* XXX */ 701 sizepb *= NINDIR(fs); /* XXX */ 702 fs->fs_maxfilesize += sizepb; /* XXX */ 703 } /* XXX */ 704 fs->fs_qbmask = ~fs->fs_bmask; /* XXX */ 705 fs->fs_qfmask = ~fs->fs_fmask; /* XXX */ 706 } /* XXX */ 707 } 708 #endif 709