Home | History | Annotate | Line # | Download | only in libsa
minixfs3.c revision 1.10
      1 /*	$NetBSD: minixfs3.c,v 1.10 2022/04/24 06:48:15 mlelstv 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 	u_int secsize;
    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 	secsize = 0;
    461 	rc = DEV_IOCTL(f->f_dev)(f, SAIOSECSIZE, &secsize);
    462 	if (rc != 0 || secsize == 0)
    463 		secsize = DEV_BSIZE;
    464 
    465 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    466 	    SUPER_BLOCK_OFF / secsize, MINBSIZE, sbbuf, &buf_size);
    467 	if (rc)
    468 		return rc;
    469 
    470 	if (buf_size != MINBSIZE)
    471 		return EIO;
    472 
    473 	mfs_sbload((void *)sbbuf, fs);
    474 
    475 	if (fs->mfs_magic != SUPER_MAGIC)
    476 		return EINVAL;
    477 	if (fs->mfs_block_size < MINBSIZE)
    478 		return EINVAL;
    479 	if ((fs->mfs_block_size % 512) != 0)
    480 		return EINVAL;
    481 	if (SBSIZE > fs->mfs_block_size)
    482 		return EINVAL;
    483 	if ((fs->mfs_block_size % INODE_SIZE) != 0)
    484 		return EINVAL;
    485 
    486 	/* For even larger disks, a similar problem occurs with s_firstdatazone.
    487 	 * If the on-disk field contains zero, we assume that the value was too
    488 	 * large to fit, and compute it on the fly.
    489 	 */
    490 	if (fs->mfs_firstdatazone_old == 0) {
    491 		block_t offset;
    492 		offset = START_BLOCK + fs->mfs_imap_blocks + fs->mfs_zmap_blocks;
    493 		offset += (fs->mfs_ninodes + fs->mfs_inodes_per_block - 1) /
    494 				fs->mfs_inodes_per_block;
    495 
    496 		fs->mfs_firstdatazone =
    497 			(offset + (1 << fs->mfs_log_zone_size) - 1) >>
    498 				fs->mfs_log_zone_size;
    499 	} else {
    500 		fs->mfs_firstdatazone = (zone_t) fs->mfs_firstdatazone_old;
    501 	}
    502 
    503 	if (fs->mfs_imap_blocks < 1 || fs->mfs_zmap_blocks < 1
    504 			|| fs->mfs_ninodes < 1 || fs->mfs_zones < 1
    505 			|| fs->mfs_firstdatazone <= 4
    506 			|| fs->mfs_firstdatazone >= fs->mfs_zones
    507 			|| (unsigned) fs->mfs_log_zone_size > 4)
    508 		return EINVAL;
    509 
    510 	/* compute in-memory mfs_sblock values */
    511 	fs->mfs_inodes_per_block = fs->mfs_block_size / INODE_SIZE;
    512 
    513 
    514 	{
    515 		int32_t mult = fs->mfs_block_size >> LOG_MINBSIZE;
    516 		int ln2 = LOG_MINBSIZE;
    517 
    518 		for (; mult != 1; ln2++)
    519 			mult >>= 1;
    520 
    521 		fs->mfs_bshift = ln2;
    522 		/* XXX assume hw bsize = 512 */
    523 		fs->mfs_fsbtodb = ln2 - LOG_MINBSIZE + 1;
    524 	}
    525 
    526 	fs->mfs_qbmask = fs->mfs_block_size - 1;
    527 	fs->mfs_bmask = ~fs->mfs_qbmask;
    528 
    529 	return 0;
    530 }
    531 
    532 /*
    533  * Open a file.
    534  */
    535 __compactcall int
    536 minixfs3_open(const char *path, struct open_file *f)
    537 {
    538 #ifndef LIBSA_FS_SINGLECOMPONENT
    539 	const char *cp, *ncp;
    540 	int c;
    541 #endif
    542 	ino32_t inumber;
    543 	struct file *fp;
    544 	struct mfs_sblock *fs;
    545 	int rc;
    546 #ifndef LIBSA_NO_FS_SYMLINK
    547 	ino32_t parent_inumber;
    548 	int nlinks = 0;
    549 	char namebuf[MAXPATHLEN+1];
    550 	char *buf;
    551 #endif
    552 
    553 	/* allocate file system specific data structure */
    554 	fp = alloc(sizeof(struct file));
    555 	memset(fp, 0, sizeof(struct file));
    556 	f->f_fsdata = (void *)fp;
    557 
    558 	/* allocate space and read super block */
    559 	fs = alloc(sizeof(*fs));
    560 	memset(fs, 0, sizeof(*fs));
    561 	fp->f_fs = fs;
    562 	twiddle();
    563 
    564 	rc = read_sblock(f, fs);
    565 	if (rc)
    566 		goto out;
    567 
    568 	/* alloc a block sized buffer used for all fs transfers */
    569 	fp->f_buf = alloc(fs->mfs_block_size);
    570 
    571 	/*
    572 	 * Calculate indirect block levels.
    573 	 */
    574 	{
    575 		int32_t mult;
    576 		int ln2;
    577 
    578 		/*
    579 		 * We note that the number of indirect blocks is always
    580 		 * a power of 2.  This lets us use shifts and masks instead
    581 		 * of divide and remainder and avoids pulling in the
    582 		 * 64bit division routine into the boot code.
    583 		 */
    584 		mult = MFS_NINDIR(fs);
    585 #ifdef DEBUG
    586 		if (!powerof2(mult)) {
    587 			/* Hummm was't a power of 2 */
    588 			rc = EINVAL;
    589 			goto out;
    590 		}
    591 #endif
    592 		for (ln2 = 0; mult != 1; ln2++)
    593 			mult >>= 1;
    594 
    595 		fp->f_nishift = ln2;
    596 	}
    597 
    598 	inumber = ROOT_INODE;
    599 	if ((rc = read_inode(inumber, f)) != 0)
    600 		goto out;
    601 
    602 #ifndef LIBSA_FS_SINGLECOMPONENT
    603 	cp = path;
    604 	while (*cp) {
    605 
    606 		/*
    607 		 * Remove extra separators
    608 		 */
    609 		while (*cp == '/')
    610 			cp++;
    611 		if (*cp == '\0')
    612 			break;
    613 
    614 		/*
    615 		 * Check that current node is a directory.
    616 		 */
    617 		if ((fp->f_di.mdi_mode & I_TYPE) != I_DIRECTORY) {
    618 			rc = ENOTDIR;
    619 			goto out;
    620 		}
    621 
    622 		/*
    623 		 * Get next component of path name.
    624 		 */
    625 		ncp = cp;
    626 		while ((c = *cp) != '\0' && c != '/')
    627 			cp++;
    628 
    629 		/*
    630 		 * Look up component in current directory.
    631 		 * Save directory inumber in case we find a
    632 		 * symbolic link.
    633 		 */
    634 #ifndef LIBSA_NO_FS_SYMLINK
    635 		parent_inumber = inumber;
    636 #endif
    637 		rc = search_directory(ncp, cp - ncp, f, &inumber);
    638 		if (rc)
    639 			goto out;
    640 
    641 		/*
    642 		 * Open next component.
    643 		 */
    644 		if ((rc = read_inode(inumber, f)) != 0)
    645 			goto out;
    646 
    647 #ifndef LIBSA_NO_FS_SYMLINK
    648 		/*
    649 		 * Check for symbolic link.
    650 		 */
    651 		if ((fp->f_di.mdi_mode & I_TYPE) == I_SYMBOLIC_LINK) {
    652 			int link_len = fp->f_di.mdi_size;
    653 			int len;
    654 			size_t buf_size;
    655 			block_t	disk_block;
    656 
    657 			len = strlen(cp);
    658 
    659 			if (link_len + len > MAXPATHLEN ||
    660 			    ++nlinks > MAXSYMLINKS) {
    661 				rc = ENOENT;
    662 				goto out;
    663 			}
    664 
    665 			memmove(&namebuf[link_len], cp, len + 1);
    666 
    667 			/*
    668 			 * Read file for symbolic link
    669 			 */
    670 			buf = fp->f_buf;
    671 			rc = block_map(f, (block_t)0, &disk_block);
    672 			if (rc)
    673 				goto out;
    674 
    675 			twiddle();
    676 			rc = DEV_STRATEGY(f->f_dev)(f->f_devdata,
    677 					F_READ, FSBTODB(fs, disk_block),
    678 					fs->mfs_block_size, buf, &buf_size);
    679 			if (rc)
    680 				goto out;
    681 
    682 			memcpy(namebuf, buf, link_len);
    683 
    684 			/*
    685 			 * If relative pathname, restart at parent directory.
    686 			 * If absolute pathname, restart at root.
    687 			 */
    688 			cp = namebuf;
    689 			if (*cp != '/')
    690 				inumber = parent_inumber;
    691 			else
    692 				inumber = (ino32_t) ROOT_INODE;
    693 
    694 			if ((rc = read_inode(inumber, f)) != 0)
    695 				goto out;
    696 		}
    697 #endif	/* !LIBSA_NO_FS_SYMLINK */
    698 	}
    699 
    700 	/*
    701 	 * Found terminal component.
    702 	 */
    703 	rc = 0;
    704 
    705 #else /* !LIBSA_FS_SINGLECOMPONENT */
    706 
    707 	/* look up component in the current (root) directory */
    708 	rc = search_directory(path, strlen(path), f, &inumber);
    709 	if (rc)
    710 		goto out;
    711 
    712 	/* open it */
    713 	rc = read_inode(inumber, f);
    714 
    715 #endif /* !LIBSA_FS_SINGLECOMPONENT */
    716 
    717 	fp->f_seekp = 0;		/* reset seek pointer */
    718 
    719 out:
    720 	if (rc)
    721 		minixfs3_close(f);
    722 
    723 	return rc;
    724 }
    725 
    726 __compactcall int
    727 minixfs3_close(struct open_file *f)
    728 {
    729 	struct file *fp = (struct file *)f->f_fsdata;
    730 
    731 	f->f_fsdata = NULL;
    732 	if (fp == NULL)
    733 		return 0;
    734 
    735 	if (fp->f_buf)
    736 		dealloc(fp->f_buf, fp->f_fs->mfs_block_size);
    737 	dealloc(fp->f_fs, sizeof(*fp->f_fs));
    738 	dealloc(fp, sizeof(struct file));
    739 	return 0;
    740 }
    741 
    742 /*
    743  * Copy a portion of a file into kernel memory.
    744  * Cross block boundaries when necessary.
    745  */
    746 __compactcall int
    747 minixfs3_read(struct open_file *f, void *start, size_t size, size_t *resid)
    748 {
    749 	struct file *fp = (struct file *)f->f_fsdata;
    750 	size_t csize;
    751 	char *buf;
    752 	size_t buf_size;
    753 	int rc = 0;
    754 	char *addr = start;
    755 
    756 	while (size != 0) {
    757 		if (fp->f_seekp >= (off_t)fp->f_di.mdi_size)
    758 			break;
    759 
    760 		rc = buf_read_file(f, &buf, &buf_size);
    761 		if (rc)
    762 			break;
    763 
    764 		csize = size;
    765 		if (csize > buf_size)
    766 			csize = buf_size;
    767 
    768 		memcpy(addr, buf, csize);
    769 
    770 		fp->f_seekp += csize;
    771 		addr += csize;
    772 		size -= csize;
    773 	}
    774 
    775 	if (resid)
    776 		*resid = size;
    777 	return rc;
    778 }
    779 
    780 /*
    781  * Not implemented.
    782  */
    783 #ifndef LIBSA_NO_FS_WRITE
    784 __compactcall int
    785 minixfs3_write(struct open_file *f, void *start, size_t size, size_t *resid)
    786 {
    787 
    788 	return EROFS;
    789 }
    790 #endif /* !LIBSA_NO_FS_WRITE */
    791 
    792 #ifndef LIBSA_NO_FS_SEEK
    793 __compactcall off_t
    794 minixfs3_seek(struct open_file *f, off_t offset, int where)
    795 {
    796 	struct file *fp = (struct file *)f->f_fsdata;
    797 
    798 	switch (where) {
    799 	case SEEK_SET:
    800 		fp->f_seekp = offset;
    801 		break;
    802 	case SEEK_CUR:
    803 		fp->f_seekp += offset;
    804 		break;
    805 	case SEEK_END:
    806 		fp->f_seekp = fp->f_di.mdi_size - offset;
    807 		break;
    808 	default:
    809 		return -1;
    810 	}
    811 	return fp->f_seekp;
    812 }
    813 #endif /* !LIBSA_NO_FS_SEEK */
    814 
    815 __compactcall int
    816 minixfs3_stat(struct open_file *f, struct stat *sb)
    817 {
    818 	struct file *fp = (struct file *)f->f_fsdata;
    819 
    820 	/* only important stuff */
    821 	memset(sb, 0, sizeof *sb);
    822 	sb->st_mode = fp->f_di.mdi_mode;
    823 	sb->st_uid = fp->f_di.mdi_uid;
    824 	sb->st_gid = fp->f_di.mdi_gid;
    825 	sb->st_size = fp->f_di.mdi_size;
    826 	return 0;
    827 }
    828 
    829 #if defined(LIBSA_ENABLE_LS_OP)
    830 #include "ls.h"
    831 __compactcall void
    832 minixfs3_ls(struct open_file *f, const char *pattern)
    833 {
    834 	struct file *fp = (struct file *)f->f_fsdata;
    835 	struct mfs_sblock *fs = fp->f_fs;
    836 	struct mfs_direct *dp;
    837 	struct mfs_direct *dbuf;
    838 	size_t buf_size;
    839 	lsentry_t *names = 0;
    840 
    841 	fp->f_seekp = 0;
    842 	while (fp->f_seekp < (off_t)fp->f_di.mdi_size) {
    843 		int rc = buf_read_file(f, &dbuf, &buf_size);
    844 		if (rc)
    845 			goto out;
    846 
    847 		/* XXX we assume, that buf_read_file reads an fs block and
    848 		 * doesn't truncate buffer. Currently i_size in MFS doesn't
    849 		 * the same as size of allocated blocks, it makes buf_read_file
    850 		 * to truncate buf_size.
    851 		 */
    852 		if (buf_size < fs->mfs_block_size)
    853 			buf_size = fs->mfs_block_size;
    854 
    855 		for (dp = dbuf; dp < &dbuf[NR_DIR_ENTRIES(fs)]; dp++) {
    856 			char *cp;
    857 			int namlen;
    858 
    859 			if (fs2h32(dp->mfsd_ino) == 0)
    860 				continue;
    861 
    862 			/* Compute the length of the name,
    863 			 * We don't use strlen and strcpy, because original MFS
    864 			 * code doesn't.
    865 			 */
    866 			cp = memchr(dp->mfsd_name, '\0', sizeof(dp->mfsd_name));
    867 			if (cp == NULL)
    868 				namlen = sizeof(dp->mfsd_name);
    869 			else
    870 				namlen = cp - (dp->mfsd_name);
    871 
    872 			lsadd(&names, pattern, dp->mfsd_name, namlen,
    873 			    fs2h32(dp->mfsd_ino), "?");
    874 		}
    875 		fp->f_seekp += buf_size;
    876 	}
    877 	lsprint(names);
    878 out:	lsfree(names);
    879 }
    880 #endif
    881 
    882 /*
    883  * byte swap functions for big endian machines
    884  * (mfs is always little endian)
    885  */
    886 
    887 /* These functions are only needed if native byte order is not big endian */
    888 #if BYTE_ORDER == BIG_ENDIAN
    889 void
    890 minixfs3_sb_bswap(struct mfs_sblock *old, struct mfs_sblock *new)
    891 {
    892 	new->mfs_ninodes	=	bswap32(old->mfs_ninodes);
    893 	new->mfs_nzones		=	bswap16(old->mfs_nzones);
    894 	new->mfs_imap_blocks	=	bswap16(old->mfs_imap_blocks);
    895 	new->mfs_zmap_blocks	=	bswap16(old->mfs_zmap_blocks);
    896 	new->mfs_firstdatazone_old =	bswap16(old->mfs_firstdatazone_old);
    897 	new->mfs_log_zone_size	=	bswap16(old->mfs_log_zone_size);
    898 	new->mfs_max_size	=	bswap32(old->mfs_max_size);
    899 	new->mfs_zones		=	bswap32(old->mfs_zones);
    900 	new->mfs_magic		=	bswap16(old->mfs_magic);
    901 	new->mfs_block_size	=	bswap16(old->mfs_block_size);
    902 	new->mfs_disk_version	=	old->mfs_disk_version;
    903 }
    904 
    905 void minixfs3_i_bswap(struct mfs_dinode *old, struct mfs_dinode *new)
    906 {
    907 	int i;
    908 
    909 	new->mdi_mode		=	bswap16(old->mdi_mode);
    910 	new->mdi_nlinks		=	bswap16(old->mdi_nlinks);
    911 	new->mdi_uid		=	bswap16(old->mdi_uid);
    912 	new->mdi_gid		=	bswap16(old->mdi_gid);
    913 	new->mdi_size		=	bswap32(old->mdi_size);
    914 	new->mdi_atime		=	bswap32(old->mdi_atime);
    915 	new->mdi_mtime		=	bswap32(old->mdi_mtime);
    916 	new->mdi_ctime		=	bswap32(old->mdi_ctime);
    917 
    918 	/* We don't swap here, because indirects must be swapped later
    919 	 * anyway, hence everything is done by block_map().
    920 	 */
    921 	for (i = 0; i < NR_TZONES; i++)
    922 		new->mdi_zone[i] = old->mdi_zone[i];
    923 }
    924 #endif
    925