Home | History | Annotate | Line # | Download | only in libsa
      1 /*	$NetBSD: minixfs3.c,v 1.13 2022/04/29 07:42:07 rin 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/time.h>
    122 #ifdef _STANDALONE
    123 #include <lib/libkern/libkern.h>
    124 #else
    125 #include <stddef.h>
    126 #include <string.h>
    127 #endif
    128 
    129 #include "stand.h"
    130 #include "minixfs3.h"
    131 
    132 #if defined(LIBSA_FS_SINGLECOMPONENT) && !defined(LIBSA_NO_FS_SYMLINK)
    133 #define LIBSA_NO_FS_SYMLINK
    134 #endif
    135 
    136 #if defined(LIBSA_NO_TWIDDLE)
    137 #define twiddle()
    138 #endif
    139 
    140 typedef uint32_t	ino32_t;
    141 #ifndef FSBTODB
    142 #define FSBTODB(fs, indp) MFS_FSBTODB(fs, indp)
    143 #endif
    144 
    145 /*
    146  * To avoid having a lot of filesystem-block sized buffers lurking (which
    147  * could be 32k) we only keep a few entries of the indirect block map.
    148  * With 8k blocks, 2^8 blocks is ~500k so we reread the indirect block
    149  * ~13 times pulling in a 6M kernel.
    150  * The cache size must be smaller than the smallest filesystem block,
    151  * so LN2_IND_CACHE_SZ <= 9 (UFS2 and 4k blocks).
    152  */
    153 #define LN2_IND_CACHE_SZ	6
    154 #define IND_CACHE_SZ		(1 << LN2_IND_CACHE_SZ)
    155 #define IND_CACHE_MASK		(IND_CACHE_SZ - 1)
    156 
    157 /*
    158  * In-core open file.
    159  */
    160 struct file {
    161 	off_t		f_seekp;	/* seek pointer */
    162 	struct mfs_sblock  *f_fs;	/* pointer to super-block */
    163 	struct mfs_dinode  f_di;	/* copy of on-disk inode */
    164 	uint		f_nishift;	/* for blocks in indirect block */
    165 	block_t		f_ind_cache_block;
    166 	block_t		f_ind_cache[IND_CACHE_SZ];
    167 
    168 	char		*f_buf;		/* buffer for data block */
    169 	size_t		f_buf_size;	/* size of data block */
    170 	daddr_t		f_buf_blkno;	/* block number of data block */
    171 };
    172 
    173 static int read_inode(ino32_t, struct open_file *);
    174 static int block_map(struct open_file *, block_t, block_t *);
    175 static int buf_read_file(struct open_file *, void *, size_t *);
    176 static int search_directory(const char *, int, struct open_file *, ino32_t *);
    177 static int read_sblock(struct open_file *, struct mfs_sblock *);
    178 
    179 /*
    180  * Read a new inode into a file structure.
    181  */
    182 static int
    183 read_inode(ino32_t inumber, struct open_file *f)
    184 {
    185 	struct file *fp = (struct file *)f->f_fsdata;
    186 	struct mfs_sblock *fs = fp->f_fs;
    187 	char *buf;
    188 	size_t rsize;
    189 	int rc;
    190 	daddr_t inode_sector;
    191 	struct mfs_dinode *dip;
    192 
    193 	inode_sector = FSBTODB(fs, ino_to_fsba(fs, inumber));
    194 
    195 	/*
    196 	 * Read inode and save it.
    197 	 */
    198 	buf = fp->f_buf;
    199 	twiddle();
    200 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    201 	    inode_sector, fs->mfs_block_size, buf, &rsize);
    202 	if (rc)
    203 		return rc;
    204 	if (rsize != fs->mfs_block_size)
    205 		return EIO;
    206 
    207 	dip = (struct mfs_dinode *)(buf +
    208 	    INODE_SIZE * ino_to_fsbo(fs, inumber));
    209 	mfs_iload(dip, &fp->f_di);
    210 
    211 	/*
    212 	 * Clear out the old buffers
    213 	 */
    214 	fp->f_ind_cache_block = ~0;
    215 	fp->f_buf_blkno = -1;
    216 	return rc;
    217 }
    218 
    219 /*
    220  * Given an offset in a file, find the disk block number (not zone!)
    221  * that contains that block.
    222  */
    223 static int
    224 block_map(struct open_file *f, block_t file_block, block_t *disk_block_p)
    225 {
    226 	struct file *fp = (struct file *)f->f_fsdata;
    227 	struct mfs_sblock *fs = fp->f_fs;
    228 	uint level;
    229 	block_t ind_cache;
    230 	block_t ind_block_num;
    231 	zone_t zone;
    232 	size_t rsize;
    233 	int rc;
    234 	int boff;
    235 	int scale = fs->mfs_log_zone_size; /* for block-zone conversion */
    236 	block_t *buf = (void *)fp->f_buf;
    237 
    238 	/*
    239 	 * Index structure of an inode:
    240 	 *
    241 	 * mdi_blocks[0..NR_DZONES-1]
    242 	 *			hold zone numbers for zones
    243 	 *			0..NR_DZONES-1
    244 	 *
    245 	 * mdi_blocks[NR_DZONES+0]
    246 	 *			block NDADDR+0 is the single indirect block
    247 	 *			holds zone numbers for zones
    248 	 *			NR_DZONES .. NR_DZONES + MFS_NINDIR(fs)-1
    249 	 *
    250 	 * mdi_blocks[NR_DZONES+1]
    251 	 *			block NDADDR+1 is the double indirect block
    252 	 *			holds zone numbers for INDEX blocks for zones
    253 	 *			NR_DZONES + MFS_NINDIR(fs) ..
    254 	 *			NR_TZONES + MFS_NINDIR(fs) + MFS_NINDIR(fs)**2 - 1
    255 	 */
    256 
    257 	zone = file_block >> scale;
    258 	boff = (int) (file_block - (zone << scale) ); /* relative blk in zone */
    259 
    260 	if (zone < NR_DZONES) {
    261 		/* Direct zone */
    262 		zone_t z = fs2h32(fp->f_di.mdi_zone[zone]);
    263 		if (z == NO_ZONE) {
    264 			*disk_block_p = NO_BLOCK;
    265 			return 0;
    266 		}
    267 		*disk_block_p = (block_t) ((z << scale) + boff);
    268 		return 0;
    269 	}
    270 
    271 	zone -= NR_DZONES;
    272 
    273 	ind_cache = zone >> LN2_IND_CACHE_SZ;
    274 	if (ind_cache == fp->f_ind_cache_block) {
    275 		*disk_block_p =
    276 		    fs2h32(fp->f_ind_cache[zone & IND_CACHE_MASK]);
    277 		return 0;
    278 	}
    279 
    280 	for (level = 0;;) {
    281 		level += fp->f_nishift;
    282 
    283 		if (zone < (block_t)1 << level)
    284 			break;
    285 		if (level > NIADDR * fp->f_nishift)
    286 			/* Zone number too high */
    287 			return EFBIG;
    288 		zone -= (block_t)1 << level;
    289 	}
    290 
    291 	ind_block_num =
    292 	    fs2h32(fp->f_di.mdi_zone[NR_DZONES + (level / fp->f_nishift - 1)]);
    293 
    294 	for (;;) {
    295 		level -= fp->f_nishift;
    296 		if (ind_block_num == 0) {
    297 			*disk_block_p = NO_BLOCK;	/* missing */
    298 			return 0;
    299 		}
    300 
    301 		twiddle();
    302 		/*
    303 		 * If we were feeling brave, we could work out the number
    304 		 * of the disk sector and read a single disk sector instead
    305 		 * of a filesystem block.
    306 		 * However we don't do this very often anyway...
    307 		 */
    308 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    309 			FSBTODB(fs, ind_block_num), fs->mfs_block_size,
    310 			buf, &rsize);
    311 		if (rc)
    312 			return rc;
    313 		if (rsize != fs->mfs_block_size)
    314 			return EIO;
    315 
    316 		ind_block_num = fs2h32(buf[zone >> level]);
    317 		if (level == 0)
    318 			break;
    319 		zone &= (1 << level) - 1;
    320 	}
    321 
    322 	/* Save the part of the block that contains this sector */
    323 	memcpy(fp->f_ind_cache, &buf[zone & ~IND_CACHE_MASK],
    324 	    IND_CACHE_SZ * sizeof fp->f_ind_cache[0]);
    325 	fp->f_ind_cache_block = ind_cache;
    326 
    327 	zone = (zone_t)ind_block_num;
    328 	*disk_block_p = (block_t)((zone << scale) + boff);
    329 	return 0;
    330 }
    331 
    332 /*
    333  * Read a portion of a file into an internal buffer.
    334  * Return the location in the buffer and the amount in the buffer.
    335  */
    336 static int
    337 buf_read_file(struct open_file *f, void *v, size_t *size_p)
    338 {
    339 	char **buf_p = v;
    340 	struct file *fp = (struct file *)f->f_fsdata;
    341 	struct mfs_sblock *fs = fp->f_fs;
    342 	long off;
    343 	block_t file_block;
    344 	block_t disk_block = 0;	/* XXX: gcc */
    345 	size_t block_size, nsz;
    346 	int rc;
    347 
    348 	off = mfs_blkoff(fs, fp->f_seekp);
    349 	file_block = mfs_lblkno(fs, fp->f_seekp);
    350 	block_size = fs->mfs_block_size;
    351 
    352 	if (file_block != fp->f_buf_blkno) {
    353 		rc = block_map(f, file_block, &disk_block);
    354 		if (rc)
    355 			return rc;
    356 
    357 		if (disk_block == 0) {
    358 			memset(fp->f_buf, 0, block_size);
    359 			fp->f_buf_size = block_size;
    360 		} else {
    361 			twiddle();
    362 			rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    363 				FSBTODB(fs, disk_block),
    364 				block_size, fp->f_buf, &fp->f_buf_size);
    365 			if (rc)
    366 				return rc;
    367 		}
    368 
    369 		fp->f_buf_blkno = file_block;
    370 	}
    371 
    372 	/*
    373 	 * Return address of byte in buffer corresponding to
    374 	 * offset, and size of remainder of buffer after that
    375 	 * byte.
    376 	 */
    377 	*buf_p = fp->f_buf + off;
    378 	*size_p = block_size - off;
    379 
    380 	/*
    381 	 * But truncate buffer at end of file.
    382 	 */
    383 	nsz = (size_t)(fp->f_di.mdi_size - fp->f_seekp);
    384 	if (*size_p > nsz)
    385 		*size_p = nsz;
    386 
    387 	return 0;
    388 }
    389 
    390 /*
    391  * Search a directory for a name and return its
    392  * inode number.
    393  */
    394 static int
    395 search_directory(const char *name, int length, struct open_file *f,
    396 	ino32_t *inumber_p)
    397 {
    398 	struct file *fp = (struct file *)f->f_fsdata;
    399 	struct mfs_sblock *fs = fp->f_fs;
    400 	struct mfs_direct *dp;
    401 	struct mfs_direct *dbuf;
    402 	size_t buf_size;
    403 	int namlen;
    404 	int rc;
    405 
    406 	fp->f_seekp = 0;
    407 
    408 	while (fp->f_seekp < (off_t)fp->f_di.mdi_size) {
    409 		rc = buf_read_file(f, (void *)&dbuf, &buf_size);
    410 		if (rc)
    411 			return rc;
    412 		if (buf_size == 0)
    413 			return EIO;
    414 
    415 		/* XXX we assume, that buf_read_file reads an fs block and
    416 		 * doesn't truncate buffer. Currently i_size in MFS doesn't
    417 		 * the same as size of allocated blocks, it makes buf_read_file
    418 		 * to truncate buf_size.
    419 		 */
    420 		if (buf_size < fs->mfs_block_size)
    421 			buf_size = fs->mfs_block_size;
    422 
    423 		for (dp = dbuf; dp < &dbuf[NR_DIR_ENTRIES(fs)]; dp++) {
    424 			char *cp;
    425 			if (fs2h32(dp->mfsd_ino) == (ino32_t) 0)
    426 				continue;
    427 			/* Compute the length of the name */
    428 			cp = memchr(dp->mfsd_name, '\0', sizeof(dp->mfsd_name));
    429 			if (cp == NULL)
    430 				namlen = sizeof(dp->mfsd_name);
    431 			else
    432 				namlen = cp - (dp->mfsd_name);
    433 
    434 			if (namlen == length &&
    435 			    !memcmp(name, dp->mfsd_name, length)) {
    436 				/* found entry */
    437 				*inumber_p = fs2h32(dp->mfsd_ino);
    438 				return 0;
    439 			}
    440 		}
    441 		fp->f_seekp += buf_size;
    442 	}
    443 	return ENOENT;
    444 }
    445 
    446 int
    447 read_sblock(struct open_file *f, struct mfs_sblock *fs)
    448 {
    449 	static uint8_t sbbuf[MINBSIZE];
    450 	size_t buf_size;
    451 	int rc;
    452 
    453 	/* We must read amount multiple of sector size, hence we can't
    454 	 * read SBSIZE and read MINBSIZE.
    455 	 */
    456 	if (SBSIZE > MINBSIZE)
    457 		return EINVAL;
    458 
    459 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    460 	    SUPER_BLOCK_OFF / GETSECSIZE(f), MINBSIZE, sbbuf, &buf_size);
    461 	if (rc)
    462 		return rc;
    463 
    464 	if (buf_size != MINBSIZE)
    465 		return EIO;
    466 
    467 	mfs_sbload((void *)sbbuf, fs);
    468 
    469 	if (fs->mfs_magic != SUPER_MAGIC)
    470 		return EINVAL;
    471 	if (fs->mfs_block_size < MINBSIZE)
    472 		return EINVAL;
    473 	if ((fs->mfs_block_size % 512) != 0)
    474 		return EINVAL;
    475 	if (SBSIZE > fs->mfs_block_size)
    476 		return EINVAL;
    477 	if ((fs->mfs_block_size % INODE_SIZE) != 0)
    478 		return EINVAL;
    479 
    480 	/* For even larger disks, a similar problem occurs with s_firstdatazone.
    481 	 * If the on-disk field contains zero, we assume that the value was too
    482 	 * large to fit, and compute it on the fly.
    483 	 */
    484 	if (fs->mfs_firstdatazone_old == 0) {
    485 		block_t offset;
    486 		offset = START_BLOCK + fs->mfs_imap_blocks + fs->mfs_zmap_blocks;
    487 		offset += (fs->mfs_ninodes + fs->mfs_inodes_per_block - 1) /
    488 				fs->mfs_inodes_per_block;
    489 
    490 		fs->mfs_firstdatazone =
    491 			(offset + (1 << fs->mfs_log_zone_size) - 1) >>
    492 				fs->mfs_log_zone_size;
    493 	} else {
    494 		fs->mfs_firstdatazone = (zone_t) fs->mfs_firstdatazone_old;
    495 	}
    496 
    497 	if (fs->mfs_imap_blocks < 1 || fs->mfs_zmap_blocks < 1
    498 			|| fs->mfs_ninodes < 1 || fs->mfs_zones < 1
    499 			|| fs->mfs_firstdatazone <= 4
    500 			|| fs->mfs_firstdatazone >= fs->mfs_zones
    501 			|| (unsigned) fs->mfs_log_zone_size > 4)
    502 		return EINVAL;
    503 
    504 	/* compute in-memory mfs_sblock values */
    505 	fs->mfs_inodes_per_block = fs->mfs_block_size / INODE_SIZE;
    506 
    507 
    508 	{
    509 		int32_t mult = fs->mfs_block_size >> LOG_MINBSIZE;
    510 		int ln2 = LOG_MINBSIZE;
    511 
    512 		for (; mult != 1; ln2++)
    513 			mult >>= 1;
    514 
    515 		fs->mfs_bshift = ln2;
    516 		/* XXX assume hw bsize = 512 */
    517 		fs->mfs_fsbtodb = ln2 - LOG_MINBSIZE + 1;
    518 	}
    519 
    520 	fs->mfs_qbmask = fs->mfs_block_size - 1;
    521 	fs->mfs_bmask = ~fs->mfs_qbmask;
    522 
    523 	return 0;
    524 }
    525 
    526 /*
    527  * Open a file.
    528  */
    529 __compactcall int
    530 minixfs3_open(const char *path, struct open_file *f)
    531 {
    532 #ifndef LIBSA_FS_SINGLECOMPONENT
    533 	const char *cp, *ncp;
    534 	int c;
    535 #endif
    536 	ino32_t inumber;
    537 	struct file *fp;
    538 	struct mfs_sblock *fs;
    539 	int rc;
    540 #ifndef LIBSA_NO_FS_SYMLINK
    541 	ino32_t parent_inumber;
    542 	int nlinks = 0;
    543 	char namebuf[MAXPATHLEN+1];
    544 	char *buf;
    545 #endif
    546 
    547 	/* allocate file system specific data structure */
    548 	fp = alloc(sizeof(struct file));
    549 	memset(fp, 0, sizeof(struct file));
    550 	f->f_fsdata = (void *)fp;
    551 
    552 	/* allocate space and read super block */
    553 	fs = alloc(sizeof(*fs));
    554 	memset(fs, 0, sizeof(*fs));
    555 	fp->f_fs = fs;
    556 	twiddle();
    557 
    558 	rc = read_sblock(f, fs);
    559 	if (rc)
    560 		goto out;
    561 
    562 	/* alloc a block sized buffer used for all fs transfers */
    563 	fp->f_buf = alloc(fs->mfs_block_size);
    564 
    565 	/*
    566 	 * Calculate indirect block levels.
    567 	 */
    568 	{
    569 		int32_t mult;
    570 		int ln2;
    571 
    572 		/*
    573 		 * We note that the number of indirect blocks is always
    574 		 * a power of 2.  This lets us use shifts and masks instead
    575 		 * of divide and remainder and avoids pulling in the
    576 		 * 64bit division routine into the boot code.
    577 		 */
    578 		mult = MFS_NINDIR(fs);
    579 #ifdef DEBUG
    580 		if (!powerof2(mult)) {
    581 			/* Hummm was't a power of 2 */
    582 			rc = EINVAL;
    583 			goto out;
    584 		}
    585 #endif
    586 		for (ln2 = 0; mult != 1; ln2++)
    587 			mult >>= 1;
    588 
    589 		fp->f_nishift = ln2;
    590 	}
    591 
    592 	inumber = ROOT_INODE;
    593 	if ((rc = read_inode(inumber, f)) != 0)
    594 		goto out;
    595 
    596 #ifndef LIBSA_FS_SINGLECOMPONENT
    597 	cp = path;
    598 	while (*cp) {
    599 
    600 		/*
    601 		 * Remove extra separators
    602 		 */
    603 		while (*cp == '/')
    604 			cp++;
    605 		if (*cp == '\0')
    606 			break;
    607 
    608 		/*
    609 		 * Check that current node is a directory.
    610 		 */
    611 		if ((fp->f_di.mdi_mode & I_TYPE) != I_DIRECTORY) {
    612 			rc = ENOTDIR;
    613 			goto out;
    614 		}
    615 
    616 		/*
    617 		 * Get next component of path name.
    618 		 */
    619 		ncp = cp;
    620 		while ((c = *cp) != '\0' && c != '/')
    621 			cp++;
    622 
    623 		/*
    624 		 * Look up component in current directory.
    625 		 * Save directory inumber in case we find a
    626 		 * symbolic link.
    627 		 */
    628 #ifndef LIBSA_NO_FS_SYMLINK
    629 		parent_inumber = inumber;
    630 #endif
    631 		rc = search_directory(ncp, cp - ncp, f, &inumber);
    632 		if (rc)
    633 			goto out;
    634 
    635 		/*
    636 		 * Open next component.
    637 		 */
    638 		if ((rc = read_inode(inumber, f)) != 0)
    639 			goto out;
    640 
    641 #ifndef LIBSA_NO_FS_SYMLINK
    642 		/*
    643 		 * Check for symbolic link.
    644 		 */
    645 		if ((fp->f_di.mdi_mode & I_TYPE) == I_SYMBOLIC_LINK) {
    646 			int link_len = fp->f_di.mdi_size;
    647 			int len;
    648 			size_t buf_size;
    649 			block_t	disk_block;
    650 
    651 			len = strlen(cp);
    652 
    653 			if (link_len + len > MAXPATHLEN ||
    654 			    ++nlinks > MAXSYMLINKS) {
    655 				rc = ENOENT;
    656 				goto out;
    657 			}
    658 
    659 			memmove(&namebuf[link_len], cp, len + 1);
    660 
    661 			/*
    662 			 * Read file for symbolic link
    663 			 */
    664 			buf = fp->f_buf;
    665 			rc = block_map(f, (block_t)0, &disk_block);
    666 			if (rc)
    667 				goto out;
    668 
    669 			twiddle();
    670 			rc = DEV_STRATEGY(f->f_dev)(f->f_devdata,
    671 					F_READ, FSBTODB(fs, disk_block),
    672 					fs->mfs_block_size, buf, &buf_size);
    673 			if (rc)
    674 				goto out;
    675 
    676 			memcpy(namebuf, buf, link_len);
    677 
    678 			/*
    679 			 * If relative pathname, restart at parent directory.
    680 			 * If absolute pathname, restart at root.
    681 			 */
    682 			cp = namebuf;
    683 			if (*cp != '/')
    684 				inumber = parent_inumber;
    685 			else
    686 				inumber = (ino32_t) ROOT_INODE;
    687 
    688 			if ((rc = read_inode(inumber, f)) != 0)
    689 				goto out;
    690 		}
    691 #endif	/* !LIBSA_NO_FS_SYMLINK */
    692 	}
    693 
    694 	/*
    695 	 * Found terminal component.
    696 	 */
    697 	rc = 0;
    698 
    699 #else /* !LIBSA_FS_SINGLECOMPONENT */
    700 
    701 	/* look up component in the current (root) directory */
    702 	rc = search_directory(path, strlen(path), f, &inumber);
    703 	if (rc)
    704 		goto out;
    705 
    706 	/* open it */
    707 	rc = read_inode(inumber, f);
    708 
    709 #endif /* !LIBSA_FS_SINGLECOMPONENT */
    710 
    711 	fp->f_seekp = 0;		/* reset seek pointer */
    712 
    713 out:
    714 	if (rc)
    715 		minixfs3_close(f);
    716 
    717 	return rc;
    718 }
    719 
    720 __compactcall int
    721 minixfs3_close(struct open_file *f)
    722 {
    723 	struct file *fp = (struct file *)f->f_fsdata;
    724 
    725 	f->f_fsdata = NULL;
    726 	if (fp == NULL)
    727 		return 0;
    728 
    729 	if (fp->f_buf)
    730 		dealloc(fp->f_buf, fp->f_fs->mfs_block_size);
    731 	dealloc(fp->f_fs, sizeof(*fp->f_fs));
    732 	dealloc(fp, sizeof(struct file));
    733 	return 0;
    734 }
    735 
    736 /*
    737  * Copy a portion of a file into kernel memory.
    738  * Cross block boundaries when necessary.
    739  */
    740 __compactcall int
    741 minixfs3_read(struct open_file *f, void *start, size_t size, size_t *resid)
    742 {
    743 	struct file *fp = (struct file *)f->f_fsdata;
    744 	size_t csize;
    745 	char *buf;
    746 	size_t buf_size;
    747 	int rc = 0;
    748 	char *addr = start;
    749 
    750 	while (size != 0) {
    751 		if (fp->f_seekp >= (off_t)fp->f_di.mdi_size)
    752 			break;
    753 
    754 		rc = buf_read_file(f, &buf, &buf_size);
    755 		if (rc)
    756 			break;
    757 
    758 		csize = size;
    759 		if (csize > buf_size)
    760 			csize = buf_size;
    761 
    762 		memcpy(addr, buf, csize);
    763 
    764 		fp->f_seekp += csize;
    765 		addr += csize;
    766 		size -= csize;
    767 	}
    768 
    769 	if (resid)
    770 		*resid = size;
    771 	return rc;
    772 }
    773 
    774 /*
    775  * Not implemented.
    776  */
    777 #ifndef LIBSA_NO_FS_WRITE
    778 __compactcall int
    779 minixfs3_write(struct open_file *f, void *start, size_t size, size_t *resid)
    780 {
    781 
    782 	return EROFS;
    783 }
    784 #endif /* !LIBSA_NO_FS_WRITE */
    785 
    786 #ifndef LIBSA_NO_FS_SEEK
    787 __compactcall off_t
    788 minixfs3_seek(struct open_file *f, off_t offset, int where)
    789 {
    790 	struct file *fp = (struct file *)f->f_fsdata;
    791 
    792 	switch (where) {
    793 	case SEEK_SET:
    794 		fp->f_seekp = offset;
    795 		break;
    796 	case SEEK_CUR:
    797 		fp->f_seekp += offset;
    798 		break;
    799 	case SEEK_END:
    800 		fp->f_seekp = fp->f_di.mdi_size - offset;
    801 		break;
    802 	default:
    803 		return -1;
    804 	}
    805 	return fp->f_seekp;
    806 }
    807 #endif /* !LIBSA_NO_FS_SEEK */
    808 
    809 __compactcall int
    810 minixfs3_stat(struct open_file *f, struct stat *sb)
    811 {
    812 	struct file *fp = (struct file *)f->f_fsdata;
    813 
    814 	/* only important stuff */
    815 	memset(sb, 0, sizeof *sb);
    816 	sb->st_mode = fp->f_di.mdi_mode;
    817 	sb->st_uid = fp->f_di.mdi_uid;
    818 	sb->st_gid = fp->f_di.mdi_gid;
    819 	sb->st_size = fp->f_di.mdi_size;
    820 	return 0;
    821 }
    822 
    823 #if defined(LIBSA_ENABLE_LS_OP)
    824 #include "ls.h"
    825 __compactcall void
    826 minixfs3_ls(struct open_file *f, const char *pattern)
    827 {
    828 	struct file *fp = (struct file *)f->f_fsdata;
    829 	struct mfs_sblock *fs = fp->f_fs;
    830 	struct mfs_direct *dp;
    831 	struct mfs_direct *dbuf;
    832 	size_t buf_size;
    833 	lsentry_t *names = 0;
    834 
    835 	fp->f_seekp = 0;
    836 	while (fp->f_seekp < (off_t)fp->f_di.mdi_size) {
    837 		int rc = buf_read_file(f, &dbuf, &buf_size);
    838 		if (rc)
    839 			goto out;
    840 
    841 		/* XXX we assume, that buf_read_file reads an fs block and
    842 		 * doesn't truncate buffer. Currently i_size in MFS doesn't
    843 		 * the same as size of allocated blocks, it makes buf_read_file
    844 		 * to truncate buf_size.
    845 		 */
    846 		if (buf_size < fs->mfs_block_size)
    847 			buf_size = fs->mfs_block_size;
    848 
    849 		for (dp = dbuf; dp < &dbuf[NR_DIR_ENTRIES(fs)]; dp++) {
    850 			char *cp;
    851 			int namlen;
    852 
    853 			if (fs2h32(dp->mfsd_ino) == 0)
    854 				continue;
    855 
    856 			/* Compute the length of the name,
    857 			 * We don't use strlen and strcpy, because original MFS
    858 			 * code doesn't.
    859 			 */
    860 			cp = memchr(dp->mfsd_name, '\0', sizeof(dp->mfsd_name));
    861 			if (cp == NULL)
    862 				namlen = sizeof(dp->mfsd_name);
    863 			else
    864 				namlen = cp - (dp->mfsd_name);
    865 
    866 			lsadd(&names, pattern, dp->mfsd_name, namlen,
    867 			    fs2h32(dp->mfsd_ino), "?");
    868 		}
    869 		fp->f_seekp += buf_size;
    870 	}
    871 	lsprint(names);
    872 out:	lsfree(names);
    873 }
    874 #endif
    875 
    876 /*
    877  * byte swap functions for big endian machines
    878  * (mfs is always little endian)
    879  */
    880 
    881 /* These functions are only needed if native byte order is not big endian */
    882 #if BYTE_ORDER == BIG_ENDIAN
    883 void
    884 minixfs3_sb_bswap(struct mfs_sblock *old, struct mfs_sblock *new)
    885 {
    886 	new->mfs_ninodes	=	bswap32(old->mfs_ninodes);
    887 	new->mfs_nzones		=	bswap16(old->mfs_nzones);
    888 	new->mfs_imap_blocks	=	bswap16(old->mfs_imap_blocks);
    889 	new->mfs_zmap_blocks	=	bswap16(old->mfs_zmap_blocks);
    890 	new->mfs_firstdatazone_old =	bswap16(old->mfs_firstdatazone_old);
    891 	new->mfs_log_zone_size	=	bswap16(old->mfs_log_zone_size);
    892 	new->mfs_max_size	=	bswap32(old->mfs_max_size);
    893 	new->mfs_zones		=	bswap32(old->mfs_zones);
    894 	new->mfs_magic		=	bswap16(old->mfs_magic);
    895 	new->mfs_block_size	=	bswap16(old->mfs_block_size);
    896 	new->mfs_disk_version	=	old->mfs_disk_version;
    897 }
    898 
    899 void minixfs3_i_bswap(struct mfs_dinode *old, struct mfs_dinode *new)
    900 {
    901 	int i;
    902 
    903 	new->mdi_mode		=	bswap16(old->mdi_mode);
    904 	new->mdi_nlinks		=	bswap16(old->mdi_nlinks);
    905 	new->mdi_uid		=	bswap16(old->mdi_uid);
    906 	new->mdi_gid		=	bswap16(old->mdi_gid);
    907 	new->mdi_size		=	bswap32(old->mdi_size);
    908 	new->mdi_atime		=	bswap32(old->mdi_atime);
    909 	new->mdi_mtime		=	bswap32(old->mdi_mtime);
    910 	new->mdi_ctime		=	bswap32(old->mdi_ctime);
    911 
    912 	/* We don't swap here, because indirects must be swapped later
    913 	 * anyway, hence everything is done by block_map().
    914 	 */
    915 	for (i = 0; i < NR_TZONES; i++)
    916 		new->mdi_zone[i] = old->mdi_zone[i];
    917 }
    918 #endif
    919