Home | History | Annotate | Line # | Download | only in libsa
      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