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