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