Home | History | Annotate | Line # | Download | only in libsa
      1 /*	$NetBSD: ext2fs.c,v 1.34 2022/04/29 07:42:07 rin Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997 Manuel Bouyer.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 /*-
     28  * Copyright (c) 1993
     29  *	The Regents of the University of California.  All rights reserved.
     30  *
     31  * This code is derived from software contributed to Berkeley by
     32  * The Mach Operating System project at Carnegie-Mellon University.
     33  *
     34  * Redistribution and use in source and binary forms, with or without
     35  * modification, are permitted provided that the following conditions
     36  * are met:
     37  * 1. Redistributions of source code must retain the above copyright
     38  *    notice, this list of conditions and the following disclaimer.
     39  * 2. Redistributions in binary form must reproduce the above copyright
     40  *    notice, this list of conditions and the following disclaimer in the
     41  *    documentation and/or other materials provided with the distribution.
     42  * 3. Neither the name of the University nor the names of its contributors
     43  *    may be used to endorse or promote products derived from this software
     44  *    without specific prior written permission.
     45  *
     46  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     47  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     48  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     49  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     50  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     51  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     52  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     53  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     54  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     55  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     56  * SUCH DAMAGE.
     57  *
     58  *
     59  * Copyright (c) 1990, 1991 Carnegie Mellon University
     60  * All Rights Reserved.
     61  *
     62  * Author: David Golub
     63  *
     64  * Permission to use, copy, modify and distribute this software and its
     65  * documentation is hereby granted, provided that both the copyright
     66  * notice and this permission notice appear in all copies of the
     67  * software, derivative works or modified versions, and any portions
     68  * thereof, and that both notices appear in supporting documentation.
     69  *
     70  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
     71  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
     72  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
     73  *
     74  * Carnegie Mellon requests users of this software to return to
     75  *
     76  *  Software Distribution Coordinator  or  Software.Distribution (at) CS.CMU.EDU
     77  *  School of Computer Science
     78  *  Carnegie Mellon University
     79  *  Pittsburgh PA 15213-3890
     80  *
     81  * any improvements or extensions that they make and grant Carnegie the
     82  * rights to redistribute these changes.
     83  */
     84 
     85 /*
     86  *	Stand-alone file reading package for Ext2 file system.
     87  */
     88 
     89 /* #define EXT2FS_DEBUG */
     90 
     91 #include <sys/param.h>
     92 #include <sys/time.h>
     93 #include <ufs/ext2fs/ext2fs_dinode.h>
     94 #include <ufs/ext2fs/ext2fs_dir.h>
     95 #include <ufs/ext2fs/ext2fs.h>
     96 #ifdef _STANDALONE
     97 #include <lib/libkern/libkern.h>
     98 #else
     99 #include <string.h>
    100 #endif
    101 
    102 #include "stand.h"
    103 #include "ext2fs.h"
    104 
    105 #if defined(LIBSA_FS_SINGLECOMPONENT) && !defined(LIBSA_NO_FS_SYMLINK)
    106 #define LIBSA_NO_FS_SYMLINK
    107 #endif
    108 
    109 #if defined(LIBSA_NO_TWIDDLE)
    110 #define twiddle()
    111 #endif
    112 
    113 #ifndef indp_t
    114 #define indp_t		int32_t
    115 #endif
    116 typedef uint32_t	ino32_t;
    117 #ifndef FSBTODB
    118 #define FSBTODB(fs, indp) EXT2_FSBTODB(fs, indp)
    119 #endif
    120 
    121 /*
    122  * To avoid having a lot of filesystem-block sized buffers lurking (which
    123  * could be 32k) we only keep a few entries of the indirect block map.
    124  * With 8k blocks, 2^8 blocks is ~500k so we reread the indirect block
    125  * ~13 times pulling in a 6M kernel.
    126  * The cache size must be smaller than the smallest filesystem block,
    127  * so LN2_IND_CACHE_SZ <= 9 (UFS2 and 4k blocks).
    128  */
    129 #define LN2_IND_CACHE_SZ	6
    130 #define IND_CACHE_SZ		(1 << LN2_IND_CACHE_SZ)
    131 #define IND_CACHE_MASK		(IND_CACHE_SZ - 1)
    132 
    133 /*
    134  * In-core open file.
    135  */
    136 struct file {
    137 	off_t		f_seekp;	/* seek pointer */
    138 	struct m_ext2fs	*f_fs;		/* pointer to super-block */
    139 	struct ext2fs_dinode	f_di;		/* copy of on-disk inode */
    140 	uint		f_nishift;	/* for blocks in indirect block */
    141 	indp_t		f_ind_cache_block;
    142 	indp_t		f_ind_cache[IND_CACHE_SZ];
    143 
    144 	char		*f_buf;		/* buffer for data block */
    145 	size_t		f_buf_size;	/* size of data block */
    146 	daddr_t		f_buf_blkno;	/* block number of data block */
    147 };
    148 
    149 
    150 static int read_inode(ino32_t, struct open_file *);
    151 static int block_map(struct open_file *, indp_t, indp_t *);
    152 static int buf_read_file(struct open_file *, char **, size_t *);
    153 static int search_directory(const char *, int, struct open_file *, ino32_t *);
    154 static int read_sblock(struct open_file *, struct m_ext2fs *);
    155 static int read_gdblock(struct open_file *, struct m_ext2fs *);
    156 #ifdef EXT2FS_DEBUG
    157 static void dump_sblock(struct m_ext2fs *);
    158 #endif
    159 
    160 /*
    161  * Read a new inode into a file structure.
    162  */
    163 static int
    164 read_inode(ino32_t inumber, struct open_file *f)
    165 {
    166 	struct file *fp = (struct file *)f->f_fsdata;
    167 	struct m_ext2fs *fs = fp->f_fs;
    168 	char *buf;
    169 	size_t rsize;
    170 	int rc;
    171 	daddr_t inode_sector;
    172 	struct ext2fs_dinode *dip;
    173 
    174 	inode_sector = FSBTODB(fs, ino_to_fsba(fs, inumber));
    175 
    176 	/*
    177 	 * Read inode and save it.
    178 	 */
    179 	buf = fp->f_buf;
    180 	twiddle();
    181 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    182 	    inode_sector, fs->e2fs_bsize, buf, &rsize);
    183 	if (rc)
    184 		return rc;
    185 	if (rsize != (size_t)fs->e2fs_bsize)
    186 		return EIO;
    187 
    188 	dip = (struct ext2fs_dinode *)(buf +
    189 	    EXT2_DINODE_SIZE(fs) * ino_to_fsbo(fs, inumber));
    190 	e2fs_iload(dip, &fp->f_di, EXT2_DINODE_SIZE(fs));
    191 
    192 	/*
    193 	 * Clear out the old buffers
    194 	 */
    195 	fp->f_ind_cache_block = ~0;
    196 	fp->f_buf_blkno = -1;
    197 	return rc;
    198 }
    199 
    200 /*
    201  * Given an offset in a file, find the disk block number that
    202  * contains that block.
    203  */
    204 static int
    205 block_map(struct open_file *f, indp_t file_block, indp_t *disk_block_p)
    206 {
    207 	struct file *fp = (struct file *)f->f_fsdata;
    208 	struct m_ext2fs *fs = fp->f_fs;
    209 	uint level;
    210 	indp_t ind_cache;
    211 	indp_t ind_block_num;
    212 	size_t rsize;
    213 	int rc;
    214 	indp_t *buf = (void *)fp->f_buf;
    215 
    216 	/*
    217 	 * Index structure of an inode:
    218 	 *
    219 	 * e2di_blocks[0..EXT2FS_NDADDR-1]
    220 	 *		hold block numbers for blocks
    221 	 *		0..EXT2FS_NDADDR-1
    222 	 *
    223 	 * e2di_blocks[EXT2FS_NDADDR+0]
    224 	 *		block EXT2FS_NDADDR+0 is the single indirect block
    225 	 *		holds block numbers for blocks
    226 	 *		EXT2FS_NDADDR .. EXT2FS_NDADDR + EXT2_NINDIR(fs)-1
    227 	 *
    228 	 * e2di_blocks[EXT2FS_NDADDR+1]
    229 	 *		block EXT2FS_NDADDR+1 is the double indirect block
    230 	 *		holds block numbers for INDEX blocks for blocks
    231 	 *		EXT2FS_NDADDR + EXT2_NINDIR(fs) ..
    232 	 *		EXT2FS_NDADDR + EXT2_NINDIR(fs) + EXT2_NINDIR(fs)**2 - 1
    233 	 *
    234 	 * e2di_blocks[EXT2FS_NDADDR+2]
    235 	 *		block EXT2FS_NDADDR+2 is the triple indirect block
    236 	 *		holds block numbers for	double-indirect
    237 	 *		blocks for blocks
    238 	 *		EXT2FS_NDADDR + EXT2_NINDIR(fs) + EXT2_NINDIR(fs)**2 ..
    239 	 *		EXT2FS_NDADDR + EXT2_NINDIR(fs) + EXT2_NINDIR(fs)**2
    240 	 *			+ EXT2_NINDIR(fs)**3 - 1
    241 	 */
    242 
    243 	if (file_block < EXT2FS_NDADDR) {
    244 		/* Direct block. */
    245 		*disk_block_p = fs2h32(fp->f_di.e2di_blocks[file_block]);
    246 		return 0;
    247 	}
    248 
    249 	file_block -= EXT2FS_NDADDR;
    250 
    251 	ind_cache = file_block >> LN2_IND_CACHE_SZ;
    252 	if (ind_cache == fp->f_ind_cache_block) {
    253 		*disk_block_p =
    254 		    fs2h32(fp->f_ind_cache[file_block & IND_CACHE_MASK]);
    255 		return 0;
    256 	}
    257 
    258 	for (level = 0;;) {
    259 		level += fp->f_nishift;
    260 		if (file_block < (indp_t)1 << level)
    261 			break;
    262 		if (level > EXT2FS_NIADDR * fp->f_nishift)
    263 			/* Block number too high */
    264 			return EFBIG;
    265 		file_block -= (indp_t)1 << level;
    266 	}
    267 
    268 	ind_block_num =
    269 	    fs2h32(fp->f_di.e2di_blocks[EXT2FS_NDADDR +
    270 	    (level / fp->f_nishift - 1)]);
    271 
    272 	for (;;) {
    273 		level -= fp->f_nishift;
    274 		if (ind_block_num == 0) {
    275 			*disk_block_p = 0;	/* missing */
    276 			return 0;
    277 		}
    278 
    279 		twiddle();
    280 		/*
    281 		 * If we were feeling brave, we could work out the number
    282 		 * of the disk sector and read a single disk sector instead
    283 		 * of a filesystem block.
    284 		 * However we don't do this very often anyway...
    285 		 */
    286 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    287 			FSBTODB(fp->f_fs, ind_block_num), fs->e2fs_bsize,
    288 			buf, &rsize);
    289 		if (rc)
    290 			return rc;
    291 		if (rsize != (size_t)fs->e2fs_bsize)
    292 			return EIO;
    293 		ind_block_num = fs2h32(buf[file_block >> level]);
    294 		if (level == 0)
    295 			break;
    296 		file_block &= (1 << level) - 1;
    297 	}
    298 
    299 	/* Save the part of the block that contains this sector */
    300 	memcpy(fp->f_ind_cache, &buf[file_block & ~IND_CACHE_MASK],
    301 	    IND_CACHE_SZ * sizeof fp->f_ind_cache[0]);
    302 	fp->f_ind_cache_block = ind_cache;
    303 
    304 	*disk_block_p = ind_block_num;
    305 
    306 	return 0;
    307 }
    308 
    309 /*
    310  * Read a portion of a file into an internal buffer.
    311  * Return the location in the buffer and the amount in the buffer.
    312  */
    313 static int
    314 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
    315 {
    316 	struct file *fp = (struct file *)f->f_fsdata;
    317 	struct m_ext2fs *fs = fp->f_fs;
    318 	long off;
    319 	indp_t file_block;
    320 	indp_t disk_block = 0;	/* XXX: gcc */
    321 	size_t block_size, tsz;
    322 	int rc;
    323 
    324 	off = ext2_blkoff(fs, fp->f_seekp);
    325 	file_block = ext2_lblkno(fs, fp->f_seekp);
    326 	block_size = fs->e2fs_bsize;	/* no fragment */
    327 
    328 	if (file_block != fp->f_buf_blkno) {
    329 		rc = block_map(f, file_block, &disk_block);
    330 		if (rc)
    331 			return rc;
    332 
    333 		if (disk_block == 0) {
    334 			memset(fp->f_buf, 0, block_size);
    335 			fp->f_buf_size = block_size;
    336 		} else {
    337 			twiddle();
    338 			rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    339 				FSBTODB(fs, disk_block),
    340 				block_size, fp->f_buf, &fp->f_buf_size);
    341 			if (rc)
    342 				return rc;
    343 		}
    344 
    345 		fp->f_buf_blkno = file_block;
    346 	}
    347 
    348 	/*
    349 	 * Return address of byte in buffer corresponding to
    350 	 * offset, and size of remainder of buffer after that
    351 	 * byte.
    352 	 */
    353 	*buf_p = fp->f_buf + off;
    354 	*size_p = block_size - off;
    355 
    356 	/*
    357 	 * But truncate buffer at end of file.
    358 	 */
    359 	/* XXX should handle LARGEFILE */
    360 	tsz = (size_t)(fp->f_di.e2di_size - fp->f_seekp);
    361 	if (*size_p > tsz)
    362 		*size_p = tsz;
    363 
    364 	return 0;
    365 }
    366 
    367 /*
    368  * Search a directory for a name and return its
    369  * inode number.
    370  */
    371 static int
    372 search_directory(const char *name, int length, struct open_file *f,
    373 	ino32_t *inumber_p)
    374 {
    375 	struct file *fp = (struct file *)f->f_fsdata;
    376 	struct ext2fs_direct *dp;
    377 	struct ext2fs_direct *edp;
    378 	char *buf;
    379 	size_t buf_size;
    380 	int namlen;
    381 	int rc;
    382 
    383 	fp->f_seekp = 0;
    384 	/* XXX should handle LARGEFILE */
    385 	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
    386 		rc = buf_read_file(f, &buf, &buf_size);
    387 		if (rc)
    388 			return rc;
    389 
    390 		dp = (struct ext2fs_direct *)buf;
    391 		edp = (struct ext2fs_direct *)(buf + buf_size);
    392 		for (; dp < edp;
    393 		    dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
    394 			if (fs2h16(dp->e2d_reclen) <= 0)
    395 				break;
    396 			if (fs2h32(dp->e2d_ino) == (ino32_t)0)
    397 				continue;
    398 			namlen = dp->e2d_namlen;
    399 			if (namlen == length &&
    400 			    !memcmp(name, dp->e2d_name, length)) {
    401 				/* found entry */
    402 				*inumber_p = fs2h32(dp->e2d_ino);
    403 				return 0;
    404 			}
    405 		}
    406 		fp->f_seekp += buf_size;
    407 	}
    408 	return ENOENT;
    409 }
    410 
    411 int
    412 read_sblock(struct open_file *f, struct m_ext2fs *fs)
    413 {
    414 	static uint8_t sbbuf[SBSIZE];
    415 	struct ext2fs ext2fs;
    416 	size_t buf_size;
    417 	int rc;
    418 
    419 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    420 	    SBOFF / GETSECSIZE(f), SBSIZE, sbbuf, &buf_size);
    421 	if (rc)
    422 		return rc;
    423 
    424 	if (buf_size != SBSIZE)
    425 		return EIO;
    426 
    427 	e2fs_sbload((void *)sbbuf, &ext2fs);
    428 	if (ext2fs.e2fs_magic != E2FS_MAGIC)
    429 		return EINVAL;
    430 	if (ext2fs.e2fs_rev > E2FS_REV1 ||
    431 	    (ext2fs.e2fs_rev == E2FS_REV1 &&
    432 	     (ext2fs.e2fs_first_ino != EXT2_FIRSTINO ||
    433 	     (ext2fs.e2fs_inode_size != 128 && ext2fs.e2fs_inode_size != 256) ||
    434 	      ext2fs.e2fs_features_incompat & ~EXT2F_INCOMPAT_SUPP))) {
    435 		return ENODEV;
    436 	}
    437 
    438 	e2fs_sbload((void *)sbbuf, &fs->e2fs);
    439 	/* compute in-memory m_ext2fs values */
    440 	fs->e2fs_ncg =
    441 	    howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock,
    442 	    fs->e2fs.e2fs_bpg);
    443 	/* XXX assume hw bsize = 512 */
    444 	fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1;
    445 	fs->e2fs_bsize = MINBSIZE << fs->e2fs.e2fs_log_bsize;
    446 	fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize;
    447 	fs->e2fs_qbmask = fs->e2fs_bsize - 1;
    448 	fs->e2fs_bmask = ~fs->e2fs_qbmask;
    449 	fs->e2fs_ngdb =
    450 	    howmany(fs->e2fs_ncg, fs->e2fs_bsize / sizeof(struct ext2_gd));
    451 	fs->e2fs_ipb = fs->e2fs_bsize / ext2fs.e2fs_inode_size;
    452 	fs->e2fs_itpg = fs->e2fs.e2fs_ipg / fs->e2fs_ipb;
    453 
    454 	return 0;
    455 }
    456 
    457 int
    458 read_gdblock(struct open_file *f, struct m_ext2fs *fs)
    459 {
    460 	struct file *fp = (struct file *)f->f_fsdata;
    461 	size_t rsize;
    462 	uint gdpb;
    463 	int i, rc;
    464 
    465 	gdpb = fs->e2fs_bsize / sizeof(struct ext2_gd);
    466 
    467 	for (i = 0; i < fs->e2fs_ngdb; i++) {
    468 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
    469 		    FSBTODB(fs, fs->e2fs.e2fs_first_dblock +
    470 		    1 /* superblock */ + i),
    471 		    fs->e2fs_bsize, fp->f_buf, &rsize);
    472 		if (rc)
    473 			return rc;
    474 		if (rsize != (size_t)fs->e2fs_bsize)
    475 			return EIO;
    476 
    477 		e2fs_cgload((struct ext2_gd *)fp->f_buf,
    478 		    &fs->e2fs_gd[i * gdpb],
    479 		    (i == (fs->e2fs_ngdb - 1)) ?
    480 		    (fs->e2fs_ncg - gdpb * i) * sizeof(struct ext2_gd):
    481 		    (size_t)fs->e2fs_bsize);
    482 	}
    483 
    484 	return 0;
    485 }
    486 
    487 
    488 /*
    489  * Open a file.
    490  */
    491 __compactcall int
    492 ext2fs_open(const char *path, struct open_file *f)
    493 {
    494 #ifndef LIBSA_FS_SINGLECOMPONENT
    495 	const char *cp, *ncp;
    496 	int c;
    497 #endif
    498 	ino32_t inumber;
    499 	struct file *fp;
    500 	struct m_ext2fs *fs;
    501 	int rc;
    502 #ifndef LIBSA_NO_FS_SYMLINK
    503 	ino32_t parent_inumber;
    504 	int nlinks = 0;
    505 	char namebuf[MAXPATHLEN+1];
    506 	char *buf;
    507 #endif
    508 
    509 	/* allocate file system specific data structure */
    510 	fp = alloc(sizeof(struct file));
    511 	memset(fp, 0, sizeof(struct file));
    512 	f->f_fsdata = (void *)fp;
    513 
    514 	/* allocate space and read super block */
    515 	fs = alloc(sizeof(*fs));
    516 	memset(fs, 0, sizeof(*fs));
    517 	fp->f_fs = fs;
    518 	twiddle();
    519 
    520 	rc = read_sblock(f, fs);
    521 	if (rc)
    522 		goto out;
    523 
    524 #ifdef EXT2FS_DEBUG
    525 	dump_sblock(fs);
    526 #endif
    527 
    528 	/* alloc a block sized buffer used for all fs transfers */
    529 	fp->f_buf = alloc(fs->e2fs_bsize);
    530 
    531 	/* read group descriptor blocks */
    532 	fs->e2fs_gd = alloc(sizeof(struct ext2_gd) * fs->e2fs_ncg);
    533 	rc = read_gdblock(f, fs);
    534 	if (rc)
    535 		goto out;
    536 
    537 	/*
    538 	 * Calculate indirect block levels.
    539 	 */
    540 	{
    541 		indp_t mult;
    542 		int ln2;
    543 
    544 		/*
    545 		 * We note that the number of indirect blocks is always
    546 		 * a power of 2.  This lets us use shifts and masks instead
    547 		 * of divide and remainder and avoids pulling in the
    548 		 * 64bit division routine into the boot code.
    549 		 */
    550 		mult = EXT2_NINDIR(fs);
    551 #ifdef DEBUG
    552 		if (!powerof2(mult)) {
    553 			/* Hummm was't a power of 2 */
    554 			rc = EINVAL;
    555 			goto out;
    556 		}
    557 #endif
    558 		for (ln2 = 0; mult != 1; ln2++)
    559 			mult >>= 1;
    560 
    561 		fp->f_nishift = ln2;
    562 	}
    563 
    564 	inumber = EXT2_ROOTINO;
    565 	if ((rc = read_inode(inumber, f)) != 0)
    566 		goto out;
    567 
    568 #ifndef LIBSA_FS_SINGLECOMPONENT
    569 	cp = path;
    570 	while (*cp) {
    571 
    572 		/*
    573 		 * Remove extra separators
    574 		 */
    575 		while (*cp == '/')
    576 			cp++;
    577 		if (*cp == '\0')
    578 			break;
    579 
    580 		/*
    581 		 * Check that current node is a directory.
    582 		 */
    583 		if ((fp->f_di.e2di_mode & EXT2_IFMT) != EXT2_IFDIR) {
    584 			rc = ENOTDIR;
    585 			goto out;
    586 		}
    587 
    588 		/*
    589 		 * Get next component of path name.
    590 		 */
    591 		ncp = cp;
    592 		while ((c = *cp) != '\0' && c != '/')
    593 			cp++;
    594 
    595 		/*
    596 		 * Look up component in current directory.
    597 		 * Save directory inumber in case we find a
    598 		 * symbolic link.
    599 		 */
    600 #ifndef LIBSA_NO_FS_SYMLINK
    601 		parent_inumber = inumber;
    602 #endif
    603 		rc = search_directory(ncp, cp - ncp, f, &inumber);
    604 		if (rc)
    605 			goto out;
    606 
    607 		/*
    608 		 * Open next component.
    609 		 */
    610 		if ((rc = read_inode(inumber, f)) != 0)
    611 			goto out;
    612 
    613 #ifndef LIBSA_NO_FS_SYMLINK
    614 		/*
    615 		 * Check for symbolic link.
    616 		 */
    617 		if ((fp->f_di.e2di_mode & EXT2_IFMT) == EXT2_IFLNK) {
    618 			/* XXX should handle LARGEFILE */
    619 			size_t link_len = fp->f_di.e2di_size;
    620 			size_t len;
    621 
    622 			len = strlen(cp);
    623 
    624 			if (link_len + len > MAXPATHLEN ||
    625 			    ++nlinks > MAXSYMLINKS) {
    626 				rc = ENOENT;
    627 				goto out;
    628 			}
    629 
    630 			memmove(&namebuf[link_len], cp, len + 1);
    631 
    632 			if (link_len < EXT2_MAXSYMLINKLEN) {
    633 				memcpy(namebuf, fp->f_di.e2di_blocks, link_len);
    634 			} else {
    635 				/*
    636 				 * Read file for symbolic link
    637 				 */
    638 				size_t buf_size;
    639 				indp_t	disk_block;
    640 
    641 				buf = fp->f_buf;
    642 				rc = block_map(f, (indp_t)0, &disk_block);
    643 				if (rc)
    644 					goto out;
    645 
    646 				twiddle();
    647 				rc = DEV_STRATEGY(f->f_dev)(f->f_devdata,
    648 					F_READ, FSBTODB(fs, disk_block),
    649 					fs->e2fs_bsize, buf, &buf_size);
    650 				if (rc)
    651 					goto out;
    652 
    653 				memcpy(namebuf, buf, link_len);
    654 			}
    655 
    656 			/*
    657 			 * If relative pathname, restart at parent directory.
    658 			 * If absolute pathname, restart at root.
    659 			 */
    660 			cp = namebuf;
    661 			if (*cp != '/')
    662 				inumber = parent_inumber;
    663 			else
    664 				inumber = (ino32_t)EXT2_ROOTINO;
    665 
    666 			if ((rc = read_inode(inumber, f)) != 0)
    667 				goto out;
    668 		}
    669 #endif	/* !LIBSA_NO_FS_SYMLINK */
    670 	}
    671 
    672 	/*
    673 	 * Found terminal component.
    674 	 */
    675 	rc = 0;
    676 
    677 #else /* !LIBSA_FS_SINGLECOMPONENT */
    678 
    679 	/* look up component in the current (root) directory */
    680 	rc = search_directory(path, strlen(path), f, &inumber);
    681 	if (rc)
    682 		goto out;
    683 
    684 	/* open it */
    685 	rc = read_inode(inumber, f);
    686 
    687 #endif /* !LIBSA_FS_SINGLECOMPONENT */
    688 
    689 	fp->f_seekp = 0;		/* reset seek pointer */
    690 
    691 out:
    692 	if (rc)
    693 		ext2fs_close(f);
    694 	else
    695 		fsmod = "ufs/ext2fs";
    696 	return rc;
    697 }
    698 
    699 __compactcall int
    700 ext2fs_close(struct open_file *f)
    701 {
    702 	struct file *fp = (struct file *)f->f_fsdata;
    703 
    704 	f->f_fsdata = NULL;
    705 	if (fp == NULL)
    706 		return 0;
    707 
    708 	if (fp->f_fs->e2fs_gd)
    709 		dealloc(fp->f_fs->e2fs_gd,
    710 		    sizeof(struct ext2_gd) * fp->f_fs->e2fs_ncg);
    711 	if (fp->f_buf)
    712 		dealloc(fp->f_buf, fp->f_fs->e2fs_bsize);
    713 	dealloc(fp->f_fs, sizeof(*fp->f_fs));
    714 	dealloc(fp, sizeof(struct file));
    715 	return 0;
    716 }
    717 
    718 /*
    719  * Copy a portion of a file into kernel memory.
    720  * Cross block boundaries when necessary.
    721  */
    722 __compactcall int
    723 ext2fs_read(struct open_file *f, void *start, size_t size, size_t *resid)
    724 {
    725 	struct file *fp = (struct file *)f->f_fsdata;
    726 	size_t csize;
    727 	char *buf;
    728 	size_t buf_size;
    729 	int rc = 0;
    730 	char *addr = start;
    731 
    732 	while (size != 0) {
    733 		/* XXX should handle LARGEFILE */
    734 		if (fp->f_seekp >= (off_t)fp->f_di.e2di_size)
    735 			break;
    736 
    737 		rc = buf_read_file(f, &buf, &buf_size);
    738 		if (rc)
    739 			break;
    740 
    741 		csize = size;
    742 		if (csize > buf_size)
    743 			csize = buf_size;
    744 
    745 		memcpy(addr, buf, csize);
    746 
    747 		fp->f_seekp += csize;
    748 		addr += csize;
    749 		size -= csize;
    750 	}
    751 	if (resid)
    752 		*resid = size;
    753 	return rc;
    754 }
    755 
    756 /*
    757  * Not implemented.
    758  */
    759 #ifndef LIBSA_NO_FS_WRITE
    760 __compactcall int
    761 ext2fs_write(struct open_file *f, void *start, size_t size, size_t *resid)
    762 {
    763 
    764 	return EROFS;
    765 }
    766 #endif /* !LIBSA_NO_FS_WRITE */
    767 
    768 #ifndef LIBSA_NO_FS_SEEK
    769 __compactcall off_t
    770 ext2fs_seek(struct open_file *f, off_t offset, int where)
    771 {
    772 	struct file *fp = (struct file *)f->f_fsdata;
    773 
    774 	switch (where) {
    775 	case SEEK_SET:
    776 		fp->f_seekp = offset;
    777 		break;
    778 	case SEEK_CUR:
    779 		fp->f_seekp += offset;
    780 		break;
    781 	case SEEK_END:
    782 		/* XXX should handle LARGEFILE */
    783 		fp->f_seekp = fp->f_di.e2di_size - offset;
    784 		break;
    785 	default:
    786 		return -1;
    787 	}
    788 	return fp->f_seekp;
    789 }
    790 #endif /* !LIBSA_NO_FS_SEEK */
    791 
    792 __compactcall int
    793 ext2fs_stat(struct open_file *f, struct stat *sb)
    794 {
    795 	struct file *fp = (struct file *)f->f_fsdata;
    796 
    797 	/* only important stuff */
    798 	memset(sb, 0, sizeof *sb);
    799 	sb->st_mode = fp->f_di.e2di_mode;
    800 	sb->st_uid = fp->f_di.e2di_uid;
    801 	sb->st_gid = fp->f_di.e2di_gid;
    802 	/* XXX should handle LARGEFILE */
    803 	sb->st_size = fp->f_di.e2di_size;
    804 	return 0;
    805 }
    806 
    807 #if defined(LIBSA_ENABLE_LS_OP)
    808 
    809 #include "ls.h"
    810 
    811 static const char    *const typestr[] = {
    812 	"unknown",
    813 	"REG",
    814 	"DIR",
    815 	"CHR",
    816 	"BLK",
    817 	"FIFO",
    818 	"SOCK",
    819 	"LNK"
    820 };
    821 
    822 __compactcall void
    823 ext2fs_ls(struct open_file *f, const char *pattern)
    824 {
    825 	struct file *fp = (struct file *)f->f_fsdata;
    826 	size_t block_size = fp->f_fs->e2fs_bsize;
    827 	char *buf;
    828 	size_t buf_size;
    829 	lsentry_t *names = NULL;
    830 
    831 	fp->f_seekp = 0;
    832 	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
    833 		struct ext2fs_direct  *dp, *edp;
    834 		int rc = buf_read_file(f, &buf, &buf_size);
    835 		if (rc)
    836 			goto out;
    837 		if (buf_size != block_size || buf_size == 0)
    838 			goto out;
    839 
    840 		dp = (struct ext2fs_direct *)buf;
    841 		edp = (struct ext2fs_direct *)(buf + buf_size);
    842 
    843 		for (; dp < edp;
    844 		     dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
    845 			const char *t;
    846 
    847 			if (fs2h16(dp->e2d_reclen) <= 0)
    848 				goto out;
    849 
    850 			if (fs2h32(dp->e2d_ino) == 0)
    851 				continue;
    852 
    853 			if (dp->e2d_type >= NELEM(typestr) ||
    854 			    !(t = typestr[dp->e2d_type])) {
    855 				/*
    856 				 * This does not handle "old"
    857 				 * filesystems properly. On little
    858 				 * endian machines, we get a bogus
    859 				 * type name if the namlen matches a
    860 				 * valid type identifier. We could
    861 				 * check if we read namlen "0" and
    862 				 * handle this case specially, if
    863 				 * there were a pressing need...
    864 				 */
    865 				printf("bad dir entry\n");
    866 				goto out;
    867 			}
    868 			lsadd(&names, pattern, dp->e2d_name,
    869 			    dp->e2d_namlen, fs2h32(dp->e2d_ino), t);
    870 		}
    871 		fp->f_seekp += buf_size;
    872 	}
    873 
    874 	lsprint(names);
    875 out:	lsfree(names);
    876 }
    877 #endif
    878 
    879 /*
    880  * byte swap functions for big endian machines
    881  * (ext2fs is always little endian)
    882  *
    883  * XXX: We should use src/sys/ufs/ext2fs/ext2fs_bswap.c
    884  */
    885 
    886 /* These functions are only needed if native byte order is not big endian */
    887 #if BYTE_ORDER == BIG_ENDIAN
    888 void
    889 e2fs_sb_bswap(struct ext2fs *old, struct ext2fs *new)
    890 {
    891 
    892 	/* preserve unused fields */
    893 	memcpy(new, old, sizeof(struct ext2fs));
    894 	new->e2fs_icount	=	bswap32(old->e2fs_icount);
    895 	new->e2fs_bcount	=	bswap32(old->e2fs_bcount);
    896 	new->e2fs_rbcount	=	bswap32(old->e2fs_rbcount);
    897 	new->e2fs_fbcount	=	bswap32(old->e2fs_fbcount);
    898 	new->e2fs_ficount	=	bswap32(old->e2fs_ficount);
    899 	new->e2fs_first_dblock	=	bswap32(old->e2fs_first_dblock);
    900 	new->e2fs_log_bsize	=	bswap32(old->e2fs_log_bsize);
    901 	new->e2fs_fsize		=	bswap32(old->e2fs_fsize);
    902 	new->e2fs_bpg		=	bswap32(old->e2fs_bpg);
    903 	new->e2fs_fpg		=	bswap32(old->e2fs_fpg);
    904 	new->e2fs_ipg		=	bswap32(old->e2fs_ipg);
    905 	new->e2fs_mtime		=	bswap32(old->e2fs_mtime);
    906 	new->e2fs_wtime		=	bswap32(old->e2fs_wtime);
    907 	new->e2fs_mnt_count	=	bswap16(old->e2fs_mnt_count);
    908 	new->e2fs_max_mnt_count	=	bswap16(old->e2fs_max_mnt_count);
    909 	new->e2fs_magic		=	bswap16(old->e2fs_magic);
    910 	new->e2fs_state		=	bswap16(old->e2fs_state);
    911 	new->e2fs_beh		=	bswap16(old->e2fs_beh);
    912 	new->e2fs_minrev	=	bswap16(old->e2fs_minrev);
    913 	new->e2fs_lastfsck	=	bswap32(old->e2fs_lastfsck);
    914 	new->e2fs_fsckintv	=	bswap32(old->e2fs_fsckintv);
    915 	new->e2fs_creator	=	bswap32(old->e2fs_creator);
    916 	new->e2fs_rev		=	bswap32(old->e2fs_rev);
    917 	new->e2fs_ruid		=	bswap16(old->e2fs_ruid);
    918 	new->e2fs_rgid		=	bswap16(old->e2fs_rgid);
    919 	new->e2fs_first_ino	=	bswap32(old->e2fs_first_ino);
    920 	new->e2fs_inode_size	=	bswap16(old->e2fs_inode_size);
    921 	new->e2fs_block_group_nr =	bswap16(old->e2fs_block_group_nr);
    922 	new->e2fs_features_compat =	bswap32(old->e2fs_features_compat);
    923 	new->e2fs_features_incompat =	bswap32(old->e2fs_features_incompat);
    924 	new->e2fs_features_rocompat =	bswap32(old->e2fs_features_rocompat);
    925 	new->e2fs_algo		=	bswap32(old->e2fs_algo);
    926 	new->e2fs_reserved_ngdb	=	bswap16(old->e2fs_reserved_ngdb);
    927 }
    928 
    929 void e2fs_i_bswap(struct ext2fs_dinode *old, struct ext2fs_dinode *new,
    930     size_t isize)
    931 {
    932 	/* preserve non-swapped and unused fields */
    933 	memcpy(new, old, isize);
    934 
    935 	/* swap what needs to be swapped */
    936 	new->e2di_mode		=	bswap16(old->e2di_mode);
    937 	new->e2di_uid		=	bswap16(old->e2di_uid);
    938 	new->e2di_gid		=	bswap16(old->e2di_gid);
    939 	new->e2di_nlink		=	bswap16(old->e2di_nlink);
    940 	new->e2di_size		=	bswap32(old->e2di_size);
    941 	new->e2di_atime		=	bswap32(old->e2di_atime);
    942 	new->e2di_ctime		=	bswap32(old->e2di_ctime);
    943 	new->e2di_mtime		=	bswap32(old->e2di_mtime);
    944 	new->e2di_dtime		=	bswap32(old->e2di_dtime);
    945 	new->e2di_nblock	=	bswap32(old->e2di_nblock);
    946 	new->e2di_flags		=	bswap32(old->e2di_flags);
    947 	new->e2di_version	=	bswap32(old->e2di_version);
    948 	new->e2di_gen		=	bswap32(old->e2di_gen);
    949 	new->e2di_facl		=	bswap32(old->e2di_facl);
    950 	new->e2di_size_high	=	bswap32(old->e2di_size_high);
    951 	new->e2di_nblock_high	=	bswap16(old->e2di_nblock_high);
    952 	new->e2di_facl_high	=	bswap16(old->e2di_facl_high);
    953 	new->e2di_uid_high	=	bswap16(old->e2di_uid_high);
    954 	new->e2di_gid_high	=	bswap16(old->e2di_gid_high);
    955 	new->e2di_checksum_low  = 	bswap16(old->e2di_checksum_low);
    956 
    957 	/*
    958 	 * Following fields are only supported for inode sizes bigger
    959 	 * than the old ext2 one
    960 	 */
    961 	if (isize == EXT2_REV0_DINODE_SIZE)
    962 		return;
    963 
    964 	new->e2di_extra_isize   = bswap16(old->e2di_extra_isize);
    965 	new->e2di_checksum_high = bswap16(old->e2di_checksum_high);
    966 
    967 	/* Following fields are ext4, might not be actually present */
    968 	if (EXT2_DINODE_FITS(new, e2di_ctime_extra, isize))
    969 		new->e2di_ctime_extra   = bswap32(old->e2di_ctime_extra);
    970 	if (EXT2_DINODE_FITS(new, e2di_mtime_extra, isize))
    971 		new->e2di_mtime_extra	= bswap32(old->e2di_mtime_extra);
    972 	if (EXT2_DINODE_FITS(new, e2di_atime_extra, isize))
    973 		new->e2di_atime_extra	= bswap32(old->e2di_atime_extra);
    974 	if (EXT2_DINODE_FITS(new, e2di_crtime, isize))
    975 		new->e2di_crtime	= bswap32(old->e2di_crtime);
    976 	if (EXT2_DINODE_FITS(new, e2di_crtime_extra, isize))
    977 		new->e2di_crtime_extra	= bswap32(old->e2di_crtime_extra);
    978 	if (EXT2_DINODE_FITS(new, e2di_version_high, isize))
    979 		new->e2di_version_high	= bswap32(old->e2di_version_high);
    980 	if (EXT2_DINODE_FITS(new, e2di_projid, isize))
    981 		new->e2di_projid	= bswap32(old->e2di_projid);
    982 }
    983 #endif
    984 
    985 #ifdef EXT2FS_DEBUG
    986 void
    987 dump_sblock(struct m_ext2fs *fs)
    988 {
    989 
    990 	printf("fs->e2fs.e2fs_bcount = %u\n", fs->e2fs.e2fs_bcount);
    991 	printf("fs->e2fs.e2fs_first_dblock = %u\n", fs->e2fs.e2fs_first_dblock);
    992 	printf("fs->e2fs.e2fs_log_bsize = %u\n", fs->e2fs.e2fs_log_bsize);
    993 	printf("fs->e2fs.e2fs_bpg = %u\n", fs->e2fs.e2fs_bpg);
    994 	printf("fs->e2fs.e2fs_ipg = %u\n", fs->e2fs.e2fs_ipg);
    995 	printf("fs->e2fs.e2fs_magic = 0x%x\n", fs->e2fs.e2fs_magic);
    996 	printf("fs->e2fs.e2fs_rev = %u\n", fs->e2fs.e2fs_rev);
    997 
    998 	if (fs->e2fs.e2fs_rev == E2FS_REV1) {
    999 		printf("fs->e2fs.e2fs_first_ino = %u\n",
   1000 		    fs->e2fs.e2fs_first_ino);
   1001 		printf("fs->e2fs.e2fs_inode_size = %u\n",
   1002 		    fs->e2fs.e2fs_inode_size);
   1003 		printf("fs->e2fs.e2fs_features_compat = %u\n",
   1004 		    fs->e2fs.e2fs_features_compat);
   1005 		printf("fs->e2fs.e2fs_features_incompat = %u\n",
   1006 		    fs->e2fs.e2fs_features_incompat);
   1007 		printf("fs->e2fs.e2fs_features_rocompat = %u\n",
   1008 		    fs->e2fs.e2fs_features_rocompat);
   1009 		printf("fs->e2fs.e2fs_reserved_ngdb = %u\n",
   1010 		    fs->e2fs.e2fs_reserved_ngdb);
   1011 	}
   1012 
   1013 	printf("fs->e2fs_bsize = %u\n", fs->e2fs_bsize);
   1014 	printf("fs->e2fs_fsbtodb = %u\n", fs->e2fs_fsbtodb);
   1015 	printf("fs->e2fs_ncg = %u\n", fs->e2fs_ncg);
   1016 	printf("fs->e2fs_ngdb = %u\n", fs->e2fs_ngdb);
   1017 	printf("fs->e2fs_ipb = %u\n", fs->e2fs_ipb);
   1018 	printf("fs->e2fs_itpg = %u\n", fs->e2fs_itpg);
   1019 }
   1020 #endif
   1021