1 /* $NetBSD: minixfs3.c,v 1.14 2025/12/19 09:31:16 nia Exp $ */ 2 3 /*- 4 * Copyright (c) 2012 5 * Vrije Universiteit, Amsterdam, The Netherlands. All rights reserved. 6 * 7 * Author: Evgeniy Ivanov (based on libsa/ext2fs.c). 8 * 9 * This code is derived from src/sys/lib/libsa/ext2fs.c contributed to 10 * The NetBSD Foundation, see copyrights below. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS 22 * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 23 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE 25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* 35 * Copyright (c) 1997 Manuel Bouyer. 36 * 37 * Redistribution and use in source and binary forms, with or without 38 * modification, are permitted provided that the following conditions 39 * are met: 40 * 1. Redistributions of source code must retain the above copyright 41 * notice, this list of conditions and the following disclaimer. 42 * 2. Redistributions in binary form must reproduce the above copyright 43 * notice, this list of conditions and the following disclaimer in the 44 * documentation and/or other materials provided with the distribution. 45 * 46 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 48 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 49 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 50 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 51 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 52 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 53 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 54 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 55 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 */ 57 58 /*- 59 * Copyright (c) 1993 60 * The Regents of the University of California. All rights reserved. 61 * 62 * This code is derived from software contributed to Berkeley by 63 * The Mach Operating System project at Carnegie-Mellon University. 64 * 65 * Redistribution and use in source and binary forms, with or without 66 * modification, are permitted provided that the following conditions 67 * are met: 68 * 1. Redistributions of source code must retain the above copyright 69 * notice, this list of conditions and the following disclaimer. 70 * 2. Redistributions in binary form must reproduce the above copyright 71 * notice, this list of conditions and the following disclaimer in the 72 * documentation and/or other materials provided with the distribution. 73 * 3. Neither the name of the University nor the names of its contributors 74 * may be used to endorse or promote products derived from this software 75 * without specific prior written permission. 76 * 77 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 78 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 79 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 80 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 81 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 82 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 83 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 84 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 85 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 86 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 87 * SUCH DAMAGE. 88 * 89 * 90 * Copyright (c) 1990, 1991 Carnegie Mellon University 91 * All Rights Reserved. 92 * 93 * Author: David Golub 94 * 95 * Permission to use, copy, modify and distribute this software and its 96 * documentation is hereby granted, provided that both the copyright 97 * notice and this permission notice appear in all copies of the 98 * software, derivative works or modified versions, and any portions 99 * thereof, and that both notices appear in supporting documentation. 100 * 101 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 102 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 103 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 104 * 105 * Carnegie Mellon requests users of this software to return to 106 * 107 * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU 108 * School of Computer Science 109 * Carnegie Mellon University 110 * Pittsburgh PA 15213-3890 111 * 112 * any improvements or extensions that they make and grant Carnegie the 113 * rights to redistribute these changes. 114 */ 115 116 /* 117 * Stand-alone file reading package for MFS file system. 118 */ 119 120 #include <sys/param.h> 121 #include <sys/endian.h> 122 #include <sys/time.h> 123 #ifdef _STANDALONE 124 #include <lib/libkern/libkern.h> 125 #else 126 #include <stddef.h> 127 #include <string.h> 128 #endif 129 130 #include "stand.h" 131 #include "minixfs3.h" 132 133 #if defined(LIBSA_FS_SINGLECOMPONENT) && !defined(LIBSA_NO_FS_SYMLINK) 134 #define LIBSA_NO_FS_SYMLINK 135 #endif 136 137 #if defined(LIBSA_NO_TWIDDLE) 138 #define twiddle() 139 #endif 140 141 typedef uint32_t ino32_t; 142 #ifndef FSBTODB 143 #define FSBTODB(fs, indp) MFS_FSBTODB(fs, indp) 144 #endif 145 146 /* 147 * To avoid having a lot of filesystem-block sized buffers lurking (which 148 * could be 32k) we only keep a few entries of the indirect block map. 149 * With 8k blocks, 2^8 blocks is ~500k so we reread the indirect block 150 * ~13 times pulling in a 6M kernel. 151 * The cache size must be smaller than the smallest filesystem block, 152 * so LN2_IND_CACHE_SZ <= 9 (UFS2 and 4k blocks). 153 */ 154 #define LN2_IND_CACHE_SZ 6 155 #define IND_CACHE_SZ (1 << LN2_IND_CACHE_SZ) 156 #define IND_CACHE_MASK (IND_CACHE_SZ - 1) 157 158 /* 159 * In-core open file. 160 */ 161 struct file { 162 off_t f_seekp; /* seek pointer */ 163 struct mfs_sblock *f_fs; /* pointer to super-block */ 164 struct mfs_dinode f_di; /* copy of on-disk inode */ 165 uint f_nishift; /* for blocks in indirect block */ 166 block_t f_ind_cache_block; 167 block_t f_ind_cache[IND_CACHE_SZ]; 168 169 char *f_buf; /* buffer for data block */ 170 size_t f_buf_size; /* size of data block */ 171 daddr_t f_buf_blkno; /* block number of data block */ 172 }; 173 174 static int read_inode(ino32_t, struct open_file *); 175 static int block_map(struct open_file *, block_t, block_t *); 176 static int buf_read_file(struct open_file *, void *, size_t *); 177 static int search_directory(const char *, int, struct open_file *, ino32_t *); 178 static int read_sblock(struct open_file *, struct mfs_sblock *); 179 180 /* 181 * Read a new inode into a file structure. 182 */ 183 static int 184 read_inode(ino32_t inumber, struct open_file *f) 185 { 186 struct file *fp = (struct file *)f->f_fsdata; 187 struct mfs_sblock *fs = fp->f_fs; 188 char *buf; 189 size_t rsize; 190 int rc; 191 daddr_t inode_sector; 192 struct mfs_dinode *dip; 193 194 inode_sector = FSBTODB(fs, ino_to_fsba(fs, inumber)); 195 196 /* 197 * Read inode and save it. 198 */ 199 buf = fp->f_buf; 200 twiddle(); 201 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, 202 inode_sector, fs->mfs_block_size, buf, &rsize); 203 if (rc) 204 return rc; 205 if (rsize != fs->mfs_block_size) 206 return EIO; 207 208 dip = (struct mfs_dinode *)(buf + 209 INODE_SIZE * ino_to_fsbo(fs, inumber)); 210 mfs_iload(dip, &fp->f_di); 211 212 /* 213 * Clear out the old buffers 214 */ 215 fp->f_ind_cache_block = ~0; 216 fp->f_buf_blkno = -1; 217 return rc; 218 } 219 220 /* 221 * Given an offset in a file, find the disk block number (not zone!) 222 * that contains that block. 223 */ 224 static int 225 block_map(struct open_file *f, block_t file_block, block_t *disk_block_p) 226 { 227 struct file *fp = (struct file *)f->f_fsdata; 228 struct mfs_sblock *fs = fp->f_fs; 229 uint level; 230 block_t ind_cache; 231 block_t ind_block_num; 232 zone_t zone; 233 size_t rsize; 234 int rc; 235 int boff; 236 int scale = fs->mfs_log_zone_size; /* for block-zone conversion */ 237 block_t *buf = (void *)fp->f_buf; 238 239 /* 240 * Index structure of an inode: 241 * 242 * mdi_blocks[0..NR_DZONES-1] 243 * hold zone numbers for zones 244 * 0..NR_DZONES-1 245 * 246 * mdi_blocks[NR_DZONES+0] 247 * block NDADDR+0 is the single indirect block 248 * holds zone numbers for zones 249 * NR_DZONES .. NR_DZONES + MFS_NINDIR(fs)-1 250 * 251 * mdi_blocks[NR_DZONES+1] 252 * block NDADDR+1 is the double indirect block 253 * holds zone numbers for INDEX blocks for zones 254 * NR_DZONES + MFS_NINDIR(fs) .. 255 * NR_TZONES + MFS_NINDIR(fs) + MFS_NINDIR(fs)**2 - 1 256 */ 257 258 zone = file_block >> scale; 259 boff = (int) (file_block - (zone << scale) ); /* relative blk in zone */ 260 261 if (zone < NR_DZONES) { 262 /* Direct zone */ 263 zone_t z = fs2h32(fp->f_di.mdi_zone[zone]); 264 if (z == NO_ZONE) { 265 *disk_block_p = NO_BLOCK; 266 return 0; 267 } 268 *disk_block_p = (block_t) ((z << scale) + boff); 269 return 0; 270 } 271 272 zone -= NR_DZONES; 273 274 ind_cache = zone >> LN2_IND_CACHE_SZ; 275 if (ind_cache == fp->f_ind_cache_block) { 276 *disk_block_p = 277 fs2h32(fp->f_ind_cache[zone & IND_CACHE_MASK]); 278 return 0; 279 } 280 281 for (level = 0;;) { 282 level += fp->f_nishift; 283 284 if (zone < (block_t)1 << level) 285 break; 286 if (level > NIADDR * fp->f_nishift) 287 /* Zone number too high */ 288 return EFBIG; 289 zone -= (block_t)1 << level; 290 } 291 292 ind_block_num = 293 fs2h32(fp->f_di.mdi_zone[NR_DZONES + (level / fp->f_nishift - 1)]); 294 295 for (;;) { 296 level -= fp->f_nishift; 297 if (ind_block_num == 0) { 298 *disk_block_p = NO_BLOCK; /* missing */ 299 return 0; 300 } 301 302 twiddle(); 303 /* 304 * If we were feeling brave, we could work out the number 305 * of the disk sector and read a single disk sector instead 306 * of a filesystem block. 307 * However we don't do this very often anyway... 308 */ 309 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, 310 FSBTODB(fs, ind_block_num), fs->mfs_block_size, 311 buf, &rsize); 312 if (rc) 313 return rc; 314 if (rsize != fs->mfs_block_size) 315 return EIO; 316 317 ind_block_num = fs2h32(buf[zone >> level]); 318 if (level == 0) 319 break; 320 zone &= (1 << level) - 1; 321 } 322 323 /* Save the part of the block that contains this sector */ 324 memcpy(fp->f_ind_cache, &buf[zone & ~IND_CACHE_MASK], 325 IND_CACHE_SZ * sizeof fp->f_ind_cache[0]); 326 fp->f_ind_cache_block = ind_cache; 327 328 zone = (zone_t)ind_block_num; 329 *disk_block_p = (block_t)((zone << scale) + boff); 330 return 0; 331 } 332 333 /* 334 * Read a portion of a file into an internal buffer. 335 * Return the location in the buffer and the amount in the buffer. 336 */ 337 static int 338 buf_read_file(struct open_file *f, void *v, size_t *size_p) 339 { 340 char **buf_p = v; 341 struct file *fp = (struct file *)f->f_fsdata; 342 struct mfs_sblock *fs = fp->f_fs; 343 long off; 344 block_t file_block; 345 block_t disk_block = 0; /* XXX: gcc */ 346 size_t block_size, nsz; 347 int rc; 348 349 off = mfs_blkoff(fs, fp->f_seekp); 350 file_block = mfs_lblkno(fs, fp->f_seekp); 351 block_size = fs->mfs_block_size; 352 353 if (file_block != fp->f_buf_blkno) { 354 rc = block_map(f, file_block, &disk_block); 355 if (rc) 356 return rc; 357 358 if (disk_block == 0) { 359 memset(fp->f_buf, 0, block_size); 360 fp->f_buf_size = block_size; 361 } else { 362 twiddle(); 363 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, 364 FSBTODB(fs, disk_block), 365 block_size, fp->f_buf, &fp->f_buf_size); 366 if (rc) 367 return rc; 368 } 369 370 fp->f_buf_blkno = file_block; 371 } 372 373 /* 374 * Return address of byte in buffer corresponding to 375 * offset, and size of remainder of buffer after that 376 * byte. 377 */ 378 *buf_p = fp->f_buf + off; 379 *size_p = block_size - off; 380 381 /* 382 * But truncate buffer at end of file. 383 */ 384 nsz = (size_t)(fp->f_di.mdi_size - fp->f_seekp); 385 if (*size_p > nsz) 386 *size_p = nsz; 387 388 return 0; 389 } 390 391 /* 392 * Search a directory for a name and return its 393 * inode number. 394 */ 395 static int 396 search_directory(const char *name, int length, struct open_file *f, 397 ino32_t *inumber_p) 398 { 399 struct file *fp = (struct file *)f->f_fsdata; 400 struct mfs_sblock *fs = fp->f_fs; 401 struct mfs_direct *dp; 402 struct mfs_direct *dbuf; 403 size_t buf_size; 404 int namlen; 405 int rc; 406 407 fp->f_seekp = 0; 408 409 while (fp->f_seekp < (off_t)fp->f_di.mdi_size) { 410 rc = buf_read_file(f, (void *)&dbuf, &buf_size); 411 if (rc) 412 return rc; 413 if (buf_size == 0) 414 return EIO; 415 416 /* XXX we assume, that buf_read_file reads an fs block and 417 * doesn't truncate buffer. Currently i_size in MFS doesn't 418 * the same as size of allocated blocks, it makes buf_read_file 419 * to truncate buf_size. 420 */ 421 if (buf_size < fs->mfs_block_size) 422 buf_size = fs->mfs_block_size; 423 424 for (dp = dbuf; dp < &dbuf[NR_DIR_ENTRIES(fs)]; dp++) { 425 char *cp; 426 if (fs2h32(dp->mfsd_ino) == (ino32_t) 0) 427 continue; 428 /* Compute the length of the name */ 429 cp = memchr(dp->mfsd_name, '\0', sizeof(dp->mfsd_name)); 430 if (cp == NULL) 431 namlen = sizeof(dp->mfsd_name); 432 else 433 namlen = cp - (dp->mfsd_name); 434 435 if (namlen == length && 436 !memcmp(name, dp->mfsd_name, length)) { 437 /* found entry */ 438 *inumber_p = fs2h32(dp->mfsd_ino); 439 return 0; 440 } 441 } 442 fp->f_seekp += buf_size; 443 } 444 return ENOENT; 445 } 446 447 int 448 read_sblock(struct open_file *f, struct mfs_sblock *fs) 449 { 450 static uint8_t sbbuf[MINBSIZE]; 451 size_t buf_size; 452 int rc; 453 454 /* We must read amount multiple of sector size, hence we can't 455 * read SBSIZE and read MINBSIZE. 456 */ 457 if (SBSIZE > MINBSIZE) 458 return EINVAL; 459 460 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ, 461 SUPER_BLOCK_OFF / GETSECSIZE(f), MINBSIZE, sbbuf, &buf_size); 462 if (rc) 463 return rc; 464 465 if (buf_size != MINBSIZE) 466 return EIO; 467 468 mfs_sbload((void *)sbbuf, fs); 469 470 if (fs->mfs_magic != SUPER_MAGIC) 471 return EINVAL; 472 if (fs->mfs_block_size < MINBSIZE) 473 return EINVAL; 474 if ((fs->mfs_block_size % 512) != 0) 475 return EINVAL; 476 if (SBSIZE > fs->mfs_block_size) 477 return EINVAL; 478 if ((fs->mfs_block_size % INODE_SIZE) != 0) 479 return EINVAL; 480 481 /* For even larger disks, a similar problem occurs with s_firstdatazone. 482 * If the on-disk field contains zero, we assume that the value was too 483 * large to fit, and compute it on the fly. 484 */ 485 if (fs->mfs_firstdatazone_old == 0) { 486 block_t offset; 487 offset = START_BLOCK + fs->mfs_imap_blocks + fs->mfs_zmap_blocks; 488 offset += (fs->mfs_ninodes + fs->mfs_inodes_per_block - 1) / 489 fs->mfs_inodes_per_block; 490 491 fs->mfs_firstdatazone = 492 (offset + (1 << fs->mfs_log_zone_size) - 1) >> 493 fs->mfs_log_zone_size; 494 } else { 495 fs->mfs_firstdatazone = (zone_t) fs->mfs_firstdatazone_old; 496 } 497 498 if (fs->mfs_imap_blocks < 1 || fs->mfs_zmap_blocks < 1 499 || fs->mfs_ninodes < 1 || fs->mfs_zones < 1 500 || fs->mfs_firstdatazone <= 4 501 || fs->mfs_firstdatazone >= fs->mfs_zones 502 || (unsigned) fs->mfs_log_zone_size > 4) 503 return EINVAL; 504 505 /* compute in-memory mfs_sblock values */ 506 fs->mfs_inodes_per_block = fs->mfs_block_size / INODE_SIZE; 507 508 509 { 510 int32_t mult = fs->mfs_block_size >> LOG_MINBSIZE; 511 int ln2 = LOG_MINBSIZE; 512 513 for (; mult != 1; ln2++) 514 mult >>= 1; 515 516 fs->mfs_bshift = ln2; 517 /* XXX assume hw bsize = 512 */ 518 fs->mfs_fsbtodb = ln2 - LOG_MINBSIZE + 1; 519 } 520 521 fs->mfs_qbmask = fs->mfs_block_size - 1; 522 fs->mfs_bmask = ~fs->mfs_qbmask; 523 524 return 0; 525 } 526 527 /* 528 * Open a file. 529 */ 530 __compactcall int 531 minixfs3_open(const char *path, struct open_file *f) 532 { 533 #ifndef LIBSA_FS_SINGLECOMPONENT 534 const char *cp, *ncp; 535 int c; 536 #endif 537 ino32_t inumber; 538 struct file *fp; 539 struct mfs_sblock *fs; 540 int rc; 541 #ifndef LIBSA_NO_FS_SYMLINK 542 ino32_t parent_inumber; 543 int nlinks = 0; 544 char namebuf[MAXPATHLEN+1]; 545 char *buf; 546 #endif 547 548 /* allocate file system specific data structure */ 549 fp = alloc(sizeof(struct file)); 550 memset(fp, 0, sizeof(struct file)); 551 f->f_fsdata = (void *)fp; 552 553 /* allocate space and read super block */ 554 fs = alloc(sizeof(*fs)); 555 memset(fs, 0, sizeof(*fs)); 556 fp->f_fs = fs; 557 twiddle(); 558 559 rc = read_sblock(f, fs); 560 if (rc) 561 goto out; 562 563 /* alloc a block sized buffer used for all fs transfers */ 564 fp->f_buf = alloc(fs->mfs_block_size); 565 566 /* 567 * Calculate indirect block levels. 568 */ 569 { 570 int32_t mult; 571 int ln2; 572 573 /* 574 * We note that the number of indirect blocks is always 575 * a power of 2. This lets us use shifts and masks instead 576 * of divide and remainder and avoids pulling in the 577 * 64bit division routine into the boot code. 578 */ 579 mult = MFS_NINDIR(fs); 580 #ifdef DEBUG 581 if (!powerof2(mult)) { 582 /* Hummm was't a power of 2 */ 583 rc = EINVAL; 584 goto out; 585 } 586 #endif 587 for (ln2 = 0; mult != 1; ln2++) 588 mult >>= 1; 589 590 fp->f_nishift = ln2; 591 } 592 593 inumber = ROOT_INODE; 594 if ((rc = read_inode(inumber, f)) != 0) 595 goto out; 596 597 #ifndef LIBSA_FS_SINGLECOMPONENT 598 cp = path; 599 while (*cp) { 600 601 /* 602 * Remove extra separators 603 */ 604 while (*cp == '/') 605 cp++; 606 if (*cp == '\0') 607 break; 608 609 /* 610 * Check that current node is a directory. 611 */ 612 if ((fp->f_di.mdi_mode & I_TYPE) != I_DIRECTORY) { 613 rc = ENOTDIR; 614 goto out; 615 } 616 617 /* 618 * Get next component of path name. 619 */ 620 ncp = cp; 621 while ((c = *cp) != '\0' && c != '/') 622 cp++; 623 624 /* 625 * Look up component in current directory. 626 * Save directory inumber in case we find a 627 * symbolic link. 628 */ 629 #ifndef LIBSA_NO_FS_SYMLINK 630 parent_inumber = inumber; 631 #endif 632 rc = search_directory(ncp, cp - ncp, f, &inumber); 633 if (rc) 634 goto out; 635 636 /* 637 * Open next component. 638 */ 639 if ((rc = read_inode(inumber, f)) != 0) 640 goto out; 641 642 #ifndef LIBSA_NO_FS_SYMLINK 643 /* 644 * Check for symbolic link. 645 */ 646 if ((fp->f_di.mdi_mode & I_TYPE) == I_SYMBOLIC_LINK) { 647 int link_len = fp->f_di.mdi_size; 648 int len; 649 size_t buf_size; 650 block_t disk_block; 651 652 len = strlen(cp); 653 654 if (link_len + len > MAXPATHLEN || 655 ++nlinks > MAXSYMLINKS) { 656 rc = ENOENT; 657 goto out; 658 } 659 660 memmove(&namebuf[link_len], cp, len + 1); 661 662 /* 663 * Read file for symbolic link 664 */ 665 buf = fp->f_buf; 666 rc = block_map(f, (block_t)0, &disk_block); 667 if (rc) 668 goto out; 669 670 twiddle(); 671 rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, 672 F_READ, FSBTODB(fs, disk_block), 673 fs->mfs_block_size, buf, &buf_size); 674 if (rc) 675 goto out; 676 677 memcpy(namebuf, buf, link_len); 678 679 /* 680 * If relative pathname, restart at parent directory. 681 * If absolute pathname, restart at root. 682 */ 683 cp = namebuf; 684 if (*cp != '/') 685 inumber = parent_inumber; 686 else 687 inumber = (ino32_t) ROOT_INODE; 688 689 if ((rc = read_inode(inumber, f)) != 0) 690 goto out; 691 } 692 #endif /* !LIBSA_NO_FS_SYMLINK */ 693 } 694 695 /* 696 * Found terminal component. 697 */ 698 rc = 0; 699 700 #else /* !LIBSA_FS_SINGLECOMPONENT */ 701 702 /* look up component in the current (root) directory */ 703 rc = search_directory(path, strlen(path), f, &inumber); 704 if (rc) 705 goto out; 706 707 /* open it */ 708 rc = read_inode(inumber, f); 709 710 #endif /* !LIBSA_FS_SINGLECOMPONENT */ 711 712 fp->f_seekp = 0; /* reset seek pointer */ 713 714 out: 715 if (rc) 716 minixfs3_close(f); 717 718 return rc; 719 } 720 721 __compactcall int 722 minixfs3_close(struct open_file *f) 723 { 724 struct file *fp = (struct file *)f->f_fsdata; 725 726 f->f_fsdata = NULL; 727 if (fp == NULL) 728 return 0; 729 730 if (fp->f_buf) 731 dealloc(fp->f_buf, fp->f_fs->mfs_block_size); 732 dealloc(fp->f_fs, sizeof(*fp->f_fs)); 733 dealloc(fp, sizeof(struct file)); 734 return 0; 735 } 736 737 /* 738 * Copy a portion of a file into kernel memory. 739 * Cross block boundaries when necessary. 740 */ 741 __compactcall int 742 minixfs3_read(struct open_file *f, void *start, size_t size, size_t *resid) 743 { 744 struct file *fp = (struct file *)f->f_fsdata; 745 size_t csize; 746 char *buf; 747 size_t buf_size; 748 int rc = 0; 749 char *addr = start; 750 751 while (size != 0) { 752 if (fp->f_seekp >= (off_t)fp->f_di.mdi_size) 753 break; 754 755 rc = buf_read_file(f, &buf, &buf_size); 756 if (rc) 757 break; 758 759 csize = size; 760 if (csize > buf_size) 761 csize = buf_size; 762 763 memcpy(addr, buf, csize); 764 765 fp->f_seekp += csize; 766 addr += csize; 767 size -= csize; 768 } 769 770 if (resid) 771 *resid = size; 772 return rc; 773 } 774 775 /* 776 * Not implemented. 777 */ 778 #ifndef LIBSA_NO_FS_WRITE 779 __compactcall int 780 minixfs3_write(struct open_file *f, void *start, size_t size, size_t *resid) 781 { 782 783 return EROFS; 784 } 785 #endif /* !LIBSA_NO_FS_WRITE */ 786 787 #ifndef LIBSA_NO_FS_SEEK 788 __compactcall off_t 789 minixfs3_seek(struct open_file *f, off_t offset, int where) 790 { 791 struct file *fp = (struct file *)f->f_fsdata; 792 793 switch (where) { 794 case SEEK_SET: 795 fp->f_seekp = offset; 796 break; 797 case SEEK_CUR: 798 fp->f_seekp += offset; 799 break; 800 case SEEK_END: 801 fp->f_seekp = fp->f_di.mdi_size - offset; 802 break; 803 default: 804 return -1; 805 } 806 return fp->f_seekp; 807 } 808 #endif /* !LIBSA_NO_FS_SEEK */ 809 810 __compactcall int 811 minixfs3_stat(struct open_file *f, struct stat *sb) 812 { 813 struct file *fp = (struct file *)f->f_fsdata; 814 815 /* only important stuff */ 816 memset(sb, 0, sizeof *sb); 817 sb->st_mode = fp->f_di.mdi_mode; 818 sb->st_uid = fp->f_di.mdi_uid; 819 sb->st_gid = fp->f_di.mdi_gid; 820 sb->st_size = fp->f_di.mdi_size; 821 return 0; 822 } 823 824 #if defined(LIBSA_ENABLE_LS_OP) 825 #include "ls.h" 826 __compactcall void 827 minixfs3_ls(struct open_file *f, const char *pattern) 828 { 829 struct file *fp = (struct file *)f->f_fsdata; 830 struct mfs_sblock *fs = fp->f_fs; 831 struct mfs_direct *dp; 832 struct mfs_direct *dbuf; 833 size_t buf_size; 834 lsentry_t *names = 0; 835 836 fp->f_seekp = 0; 837 while (fp->f_seekp < (off_t)fp->f_di.mdi_size) { 838 int rc = buf_read_file(f, &dbuf, &buf_size); 839 if (rc) 840 goto out; 841 842 /* XXX we assume, that buf_read_file reads an fs block and 843 * doesn't truncate buffer. Currently i_size in MFS doesn't 844 * the same as size of allocated blocks, it makes buf_read_file 845 * to truncate buf_size. 846 */ 847 if (buf_size < fs->mfs_block_size) 848 buf_size = fs->mfs_block_size; 849 850 for (dp = dbuf; dp < &dbuf[NR_DIR_ENTRIES(fs)]; dp++) { 851 char *cp; 852 int namlen; 853 854 if (fs2h32(dp->mfsd_ino) == 0) 855 continue; 856 857 /* Compute the length of the name, 858 * We don't use strlen and strcpy, because original MFS 859 * code doesn't. 860 */ 861 cp = memchr(dp->mfsd_name, '\0', sizeof(dp->mfsd_name)); 862 if (cp == NULL) 863 namlen = sizeof(dp->mfsd_name); 864 else 865 namlen = cp - (dp->mfsd_name); 866 867 lsadd(&names, pattern, dp->mfsd_name, namlen, 868 fs2h32(dp->mfsd_ino), "?"); 869 } 870 fp->f_seekp += buf_size; 871 } 872 lsprint(names); 873 out: lsfree(names); 874 } 875 #endif 876 877 /* 878 * byte swap functions for big endian machines 879 * (mfs is always little endian) 880 */ 881 882 /* These functions are only needed if native byte order is not big endian */ 883 #if BYTE_ORDER == BIG_ENDIAN 884 void 885 minixfs3_sb_bswap(struct mfs_sblock *old, struct mfs_sblock *new) 886 { 887 new->mfs_ninodes = bswap32(old->mfs_ninodes); 888 new->mfs_nzones = bswap16(old->mfs_nzones); 889 new->mfs_imap_blocks = bswap16(old->mfs_imap_blocks); 890 new->mfs_zmap_blocks = bswap16(old->mfs_zmap_blocks); 891 new->mfs_firstdatazone_old = bswap16(old->mfs_firstdatazone_old); 892 new->mfs_log_zone_size = bswap16(old->mfs_log_zone_size); 893 new->mfs_max_size = bswap32(old->mfs_max_size); 894 new->mfs_zones = bswap32(old->mfs_zones); 895 new->mfs_magic = bswap16(old->mfs_magic); 896 new->mfs_block_size = bswap16(old->mfs_block_size); 897 new->mfs_disk_version = old->mfs_disk_version; 898 } 899 900 void minixfs3_i_bswap(struct mfs_dinode *old, struct mfs_dinode *new) 901 { 902 int i; 903 904 new->mdi_mode = bswap16(old->mdi_mode); 905 new->mdi_nlinks = bswap16(old->mdi_nlinks); 906 new->mdi_uid = bswap16(old->mdi_uid); 907 new->mdi_gid = bswap16(old->mdi_gid); 908 new->mdi_size = bswap32(old->mdi_size); 909 new->mdi_atime = bswap32(old->mdi_atime); 910 new->mdi_mtime = bswap32(old->mdi_mtime); 911 new->mdi_ctime = bswap32(old->mdi_ctime); 912 913 /* We don't swap here, because indirects must be swapped later 914 * anyway, hence everything is done by block_map(). 915 */ 916 for (i = 0; i < NR_TZONES; i++) 917 new->mdi_zone[i] = old->mdi_zone[i]; 918 } 919 #endif 920