xref: /minix3/sys/lib/libsa/ext2fs.c (revision d19d7d58aa5cd1165eefe1a0d807c8afe282db62)
1 /*	$NetBSD: ext2fs.c,v 1.11 2011/12/25 06:09:08 tsutsui 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@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) 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 #if defined(LIBSA_ENABLE_LS_OP)
150 
151 #define NELEM(x) (sizeof (x) / sizeof(*x))
152 
153 typedef struct entry_t entry_t;
154 struct entry_t {
155 	entry_t	*e_next;
156 	ino32_t	e_ino;
157 	uint8_t	e_type;
158 	char	e_name[1];
159 };
160 
161 static const char    *const typestr[] = {
162 	"unknown",
163 	"REG",
164 	"DIR",
165 	"CHR",
166 	"BLK",
167 	"FIFO",
168 	"SOCK",
169 	"LNK"
170 };
171 
172 static int
173 fn_match(const char *fname, const char *pattern)
174 {
175 	char fc, pc;
176 
177 	do {
178 		fc = *fname++;
179 		pc = *pattern++;
180 		if (!fc && !pc)
181 			return 1;
182 		if (pc == '?' && fc)
183 			pc = fc;
184 	} while (fc == pc);
185 
186 	if (pc != '*')
187 		return 0;
188 	/*
189 	 * Too hard (and unnecessary really) too check for "*?name" etc....
190 	 * "**" will look for a '*' and "*?" a '?'
191 	 */
192 	pc = *pattern++;
193 	if (!pc)
194 		return 1;
195 	while ((fname = strchr(fname, pc)))
196 		if (fn_match(++fname, pattern))
197 			return 1;
198 	return 0;
199 }
200 #endif /* LIBSA_ENABLE_LS_OP */
201 
202 static int read_inode(ino32_t, struct open_file *);
203 static int block_map(struct open_file *, indp_t, indp_t *);
204 static int buf_read_file(struct open_file *, char **, size_t *);
205 static int search_directory(const char *, int, struct open_file *, ino32_t *);
206 static int read_sblock(struct open_file *, struct m_ext2fs *);
207 static int read_gdblock(struct open_file *, struct m_ext2fs *);
208 #ifdef EXT2FS_DEBUG
209 static void dump_sblock(struct m_ext2fs *);
210 #endif
211 
212 /*
213  * Read a new inode into a file structure.
214  */
215 static int
216 read_inode(ino32_t inumber, struct open_file *f)
217 {
218 	struct file *fp = (struct file *)f->f_fsdata;
219 	struct m_ext2fs *fs = fp->f_fs;
220 	char *buf;
221 	size_t rsize;
222 	int rc;
223 	daddr_t inode_sector;
224 	struct ext2fs_dinode *dip;
225 
226 	inode_sector = FSBTODB(fs, ino_to_fsba(fs, inumber));
227 
228 	/*
229 	 * Read inode and save it.
230 	 */
231 	buf = fp->f_buf;
232 	twiddle();
233 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
234 	    inode_sector, fs->e2fs_bsize, buf, &rsize);
235 	if (rc)
236 		return rc;
237 	if (rsize != fs->e2fs_bsize)
238 		return EIO;
239 
240 	dip = (struct ext2fs_dinode *)(buf +
241 	    EXT2_DINODE_SIZE(fs) * ino_to_fsbo(fs, inumber));
242 	e2fs_iload(dip, &fp->f_di);
243 
244 	/*
245 	 * Clear out the old buffers
246 	 */
247 	fp->f_ind_cache_block = ~0;
248 	fp->f_buf_blkno = -1;
249 	return rc;
250 }
251 
252 /*
253  * Given an offset in a file, find the disk block number that
254  * contains that block.
255  */
256 static int
257 block_map(struct open_file *f, indp_t file_block, indp_t *disk_block_p)
258 {
259 	struct file *fp = (struct file *)f->f_fsdata;
260 	struct m_ext2fs *fs = fp->f_fs;
261 	uint level;
262 	indp_t ind_cache;
263 	indp_t ind_block_num;
264 	size_t rsize;
265 	int rc;
266 	indp_t *buf = (void *)fp->f_buf;
267 
268 	/*
269 	 * Index structure of an inode:
270 	 *
271 	 * e2di_blocks[0..NDADDR-1]
272 	 *			hold block numbers for blocks
273 	 *			0..NDADDR-1
274 	 *
275 	 * e2di_blocks[NDADDR+0]
276 	 *			block NDADDR+0 is the single indirect block
277 	 *			holds block numbers for blocks
278 	 *			NDADDR .. NDADDR + NINDIR(fs)-1
279 	 *
280 	 * e2di_blocks[NDADDR+1]
281 	 *			block NDADDR+1 is the double indirect block
282 	 *			holds block numbers for INDEX blocks for blocks
283 	 *			NDADDR + NINDIR(fs) ..
284 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
285 	 *
286 	 * e2di_blocks[NDADDR+2]
287 	 *			block NDADDR+2 is the triple indirect block
288 	 *			holds block numbers for	double-indirect
289 	 *			blocks for blocks
290 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
291 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2
292 	 *				+ NINDIR(fs)**3 - 1
293 	 */
294 
295 	if (file_block < NDADDR) {
296 		/* Direct block. */
297 		*disk_block_p = fs2h32(fp->f_di.e2di_blocks[file_block]);
298 		return 0;
299 	}
300 
301 	file_block -= NDADDR;
302 
303 	ind_cache = file_block >> LN2_IND_CACHE_SZ;
304 	if (ind_cache == fp->f_ind_cache_block) {
305 		*disk_block_p =
306 		    fs2h32(fp->f_ind_cache[file_block & IND_CACHE_MASK]);
307 		return 0;
308 	}
309 
310 	for (level = 0;;) {
311 		level += fp->f_nishift;
312 		if (file_block < (indp_t)1 << level)
313 			break;
314 		if (level > NIADDR * fp->f_nishift)
315 			/* Block number too high */
316 			return EFBIG;
317 		file_block -= (indp_t)1 << level;
318 	}
319 
320 	ind_block_num =
321 	    fs2h32(fp->f_di.e2di_blocks[NDADDR + (level / fp->f_nishift - 1)]);
322 
323 	for (;;) {
324 		level -= fp->f_nishift;
325 		if (ind_block_num == 0) {
326 			*disk_block_p = 0;	/* missing */
327 			return 0;
328 		}
329 
330 		twiddle();
331 		/*
332 		 * If we were feeling brave, we could work out the number
333 		 * of the disk sector and read a single disk sector instead
334 		 * of a filesystem block.
335 		 * However we don't do this very often anyway...
336 		 */
337 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
338 			FSBTODB(fp->f_fs, ind_block_num), fs->e2fs_bsize,
339 			buf, &rsize);
340 		if (rc)
341 			return rc;
342 		if (rsize != fs->e2fs_bsize)
343 			return EIO;
344 		ind_block_num = fs2h32(buf[file_block >> level]);
345 		if (level == 0)
346 			break;
347 		file_block &= (1 << level) - 1;
348 	}
349 
350 	/* Save the part of the block that contains this sector */
351 	memcpy(fp->f_ind_cache, &buf[file_block & ~IND_CACHE_MASK],
352 	    IND_CACHE_SZ * sizeof fp->f_ind_cache[0]);
353 	fp->f_ind_cache_block = ind_cache;
354 
355 	*disk_block_p = ind_block_num;
356 
357 	return 0;
358 }
359 
360 /*
361  * Read a portion of a file into an internal buffer.
362  * Return the location in the buffer and the amount in the buffer.
363  */
364 static int
365 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
366 {
367 	struct file *fp = (struct file *)f->f_fsdata;
368 	struct m_ext2fs *fs = fp->f_fs;
369 	long off;
370 	indp_t file_block;
371 	indp_t disk_block;
372 	size_t block_size;
373 	int rc;
374 
375 	off = blkoff(fs, fp->f_seekp);
376 	file_block = lblkno(fs, fp->f_seekp);
377 	block_size = fs->e2fs_bsize;	/* no fragment */
378 
379 	if (file_block != fp->f_buf_blkno) {
380 		rc = block_map(f, file_block, &disk_block);
381 		if (rc)
382 			return rc;
383 
384 		if (disk_block == 0) {
385 			memset(fp->f_buf, 0, block_size);
386 			fp->f_buf_size = block_size;
387 		} else {
388 			twiddle();
389 			rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
390 				FSBTODB(fs, disk_block),
391 				block_size, fp->f_buf, &fp->f_buf_size);
392 			if (rc)
393 				return rc;
394 		}
395 
396 		fp->f_buf_blkno = file_block;
397 	}
398 
399 	/*
400 	 * Return address of byte in buffer corresponding to
401 	 * offset, and size of remainder of buffer after that
402 	 * byte.
403 	 */
404 	*buf_p = fp->f_buf + off;
405 	*size_p = block_size - off;
406 
407 	/*
408 	 * But truncate buffer at end of file.
409 	 */
410 	/* XXX should handle LARGEFILE */
411 	if (*size_p > fp->f_di.e2di_size - fp->f_seekp)
412 		*size_p = fp->f_di.e2di_size - fp->f_seekp;
413 
414 	return 0;
415 }
416 
417 /*
418  * Search a directory for a name and return its
419  * inode number.
420  */
421 static int
422 search_directory(const char *name, int length, struct open_file *f,
423 	ino32_t *inumber_p)
424 {
425 	struct file *fp = (struct file *)f->f_fsdata;
426 	struct ext2fs_direct *dp;
427 	struct ext2fs_direct *edp;
428 	char *buf;
429 	size_t buf_size;
430 	int namlen;
431 	int rc;
432 
433 	fp->f_seekp = 0;
434 	/* XXX should handle LARGEFILE */
435 	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
436 		rc = buf_read_file(f, &buf, &buf_size);
437 		if (rc)
438 			return rc;
439 
440 		dp = (struct ext2fs_direct *)buf;
441 		edp = (struct ext2fs_direct *)(buf + buf_size);
442 		for (; dp < edp;
443 		    dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
444 			if (fs2h16(dp->e2d_reclen) <= 0)
445 				break;
446 			if (fs2h32(dp->e2d_ino) == (ino32_t)0)
447 				continue;
448 			namlen = dp->e2d_namlen;
449 			if (namlen == length &&
450 			    !memcmp(name, dp->e2d_name, length)) {
451 				/* found entry */
452 				*inumber_p = fs2h32(dp->e2d_ino);
453 				return 0;
454 			}
455 		}
456 		fp->f_seekp += buf_size;
457 	}
458 	return ENOENT;
459 }
460 
461 int
462 read_sblock(struct open_file *f, struct m_ext2fs *fs)
463 {
464 	static uint8_t sbbuf[SBSIZE];
465 	struct ext2fs ext2fs;
466 	size_t buf_size;
467 	int rc;
468 
469 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
470 	    SBOFF / DEV_BSIZE, SBSIZE, sbbuf, &buf_size);
471 	if (rc)
472 		return rc;
473 
474 	if (buf_size != SBSIZE)
475 		return EIO;
476 
477 	e2fs_sbload((void *)sbbuf, &ext2fs);
478 	if (ext2fs.e2fs_magic != E2FS_MAGIC)
479 		return EINVAL;
480 	if (ext2fs.e2fs_rev > E2FS_REV1 ||
481 	    (ext2fs.e2fs_rev == E2FS_REV1 &&
482 	     (ext2fs.e2fs_first_ino != EXT2_FIRSTINO ||
483 	     (ext2fs.e2fs_inode_size != 128 && ext2fs.e2fs_inode_size != 256) ||
484 	      ext2fs.e2fs_features_incompat & ~EXT2F_INCOMPAT_SUPP))) {
485 		return ENODEV;
486 	}
487 
488 	e2fs_sbload((void *)sbbuf, &fs->e2fs);
489 	/* compute in-memory m_ext2fs values */
490 	fs->e2fs_ncg =
491 	    howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock,
492 	    fs->e2fs.e2fs_bpg);
493 	/* XXX assume hw bsize = 512 */
494 	fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1;
495 	fs->e2fs_bsize = MINBSIZE << fs->e2fs.e2fs_log_bsize;
496 	fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize;
497 	fs->e2fs_qbmask = fs->e2fs_bsize - 1;
498 	fs->e2fs_bmask = ~fs->e2fs_qbmask;
499 	fs->e2fs_ngdb =
500 	    howmany(fs->e2fs_ncg, fs->e2fs_bsize / sizeof(struct ext2_gd));
501 	fs->e2fs_ipb = fs->e2fs_bsize / ext2fs.e2fs_inode_size;
502 	fs->e2fs_itpg = fs->e2fs.e2fs_ipg / fs->e2fs_ipb;
503 
504 	return 0;
505 }
506 
507 int
508 read_gdblock(struct open_file *f, struct m_ext2fs *fs)
509 {
510 	struct file *fp = (struct file *)f->f_fsdata;
511 	size_t rsize;
512 	uint gdpb;
513 	int i, rc;
514 
515 	gdpb = fs->e2fs_bsize / sizeof(struct ext2_gd);
516 
517 	for (i = 0; i < fs->e2fs_ngdb; i++) {
518 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
519 		    FSBTODB(fs, fs->e2fs.e2fs_first_dblock +
520 		    1 /* superblock */ + i),
521 		    fs->e2fs_bsize, fp->f_buf, &rsize);
522 		if (rc)
523 			return rc;
524 		if (rsize != fs->e2fs_bsize)
525 			return EIO;
526 
527 		e2fs_cgload((struct ext2_gd *)fp->f_buf,
528 		    &fs->e2fs_gd[i * gdpb],
529 		    (i == (fs->e2fs_ngdb - 1)) ?
530 		    (fs->e2fs_ncg - gdpb * i) * sizeof(struct ext2_gd):
531 		    fs->e2fs_bsize);
532 	}
533 
534 	return 0;
535 }
536 
537 
538 /*
539  * Open a file.
540  */
541 __compactcall int
542 ext2fs_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 m_ext2fs *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 #ifdef EXT2FS_DEBUG
575 	dump_sblock(fs);
576 #endif
577 
578 	/* alloc a block sized buffer used for all fs transfers */
579 	fp->f_buf = alloc(fs->e2fs_bsize);
580 
581 	/* read group descriptor blocks */
582 	fs->e2fs_gd = alloc(sizeof(struct ext2_gd) * fs->e2fs_ncg);
583 	rc = read_gdblock(f, fs);
584 	if (rc)
585 		goto out;
586 
587 	/*
588 	 * Calculate indirect block levels.
589 	 */
590 	{
591 		indp_t mult;
592 		int ln2;
593 
594 		/*
595 		 * We note that the number of indirect blocks is always
596 		 * a power of 2.  This lets us use shifts and masks instead
597 		 * of divide and remainder and avoinds pulling in the
598 		 * 64bit division routine into the boot code.
599 		 */
600 		mult = NINDIR(fs);
601 #ifdef DEBUG
602 		if (!powerof2(mult)) {
603 			/* Hummm was't a power of 2 */
604 			rc = EINVAL;
605 			goto out;
606 		}
607 #endif
608 		for (ln2 = 0; mult != 1; ln2++)
609 			mult >>= 1;
610 
611 		fp->f_nishift = ln2;
612 	}
613 
614 	inumber = EXT2_ROOTINO;
615 	if ((rc = read_inode(inumber, f)) != 0)
616 		goto out;
617 
618 #ifndef LIBSA_FS_SINGLECOMPONENT
619 	cp = path;
620 	while (*cp) {
621 
622 		/*
623 		 * Remove extra separators
624 		 */
625 		while (*cp == '/')
626 			cp++;
627 		if (*cp == '\0')
628 			break;
629 
630 		/*
631 		 * Check that current node is a directory.
632 		 */
633 		if ((fp->f_di.e2di_mode & EXT2_IFMT) != EXT2_IFDIR) {
634 			rc = ENOTDIR;
635 			goto out;
636 		}
637 
638 		/*
639 		 * Get next component of path name.
640 		 */
641 		ncp = cp;
642 		while ((c = *cp) != '\0' && c != '/')
643 			cp++;
644 
645 		/*
646 		 * Look up component in current directory.
647 		 * Save directory inumber in case we find a
648 		 * symbolic link.
649 		 */
650 #ifndef LIBSA_NO_FS_SYMLINK
651 		parent_inumber = inumber;
652 #endif
653 		rc = search_directory(ncp, cp - ncp, f, &inumber);
654 		if (rc)
655 			goto out;
656 
657 		/*
658 		 * Open next component.
659 		 */
660 		if ((rc = read_inode(inumber, f)) != 0)
661 			goto out;
662 
663 #ifndef LIBSA_NO_FS_SYMLINK
664 		/*
665 		 * Check for symbolic link.
666 		 */
667 		if ((fp->f_di.e2di_mode & EXT2_IFMT) == EXT2_IFLNK) {
668 			/* XXX should handle LARGEFILE */
669 			int link_len = fp->f_di.e2di_size;
670 			int len;
671 
672 			len = strlen(cp);
673 
674 			if (link_len + len > MAXPATHLEN ||
675 			    ++nlinks > MAXSYMLINKS) {
676 				rc = ENOENT;
677 				goto out;
678 			}
679 
680 			memmove(&namebuf[link_len], cp, len + 1);
681 
682 			if (link_len < EXT2_MAXSYMLINKLEN) {
683 				memcpy(namebuf, fp->f_di.e2di_blocks, link_len);
684 			} else {
685 				/*
686 				 * Read file for symbolic link
687 				 */
688 				size_t buf_size;
689 				indp_t	disk_block;
690 
691 				buf = fp->f_buf;
692 				rc = block_map(f, (indp_t)0, &disk_block);
693 				if (rc)
694 					goto out;
695 
696 				twiddle();
697 				rc = DEV_STRATEGY(f->f_dev)(f->f_devdata,
698 					F_READ, FSBTODB(fs, disk_block),
699 					fs->e2fs_bsize, buf, &buf_size);
700 				if (rc)
701 					goto out;
702 
703 				memcpy(namebuf, buf, link_len);
704 			}
705 
706 			/*
707 			 * If relative pathname, restart at parent directory.
708 			 * If absolute pathname, restart at root.
709 			 */
710 			cp = namebuf;
711 			if (*cp != '/')
712 				inumber = parent_inumber;
713 			else
714 				inumber = (ino32_t)EXT2_ROOTINO;
715 
716 			if ((rc = read_inode(inumber, f)) != 0)
717 				goto out;
718 		}
719 #endif	/* !LIBSA_NO_FS_SYMLINK */
720 	}
721 
722 	/*
723 	 * Found terminal component.
724 	 */
725 	rc = 0;
726 
727 #else /* !LIBSA_FS_SINGLECOMPONENT */
728 
729 	/* look up component in the current (root) directory */
730 	rc = search_directory(path, strlen(path), f, &inumber);
731 	if (rc)
732 		goto out;
733 
734 	/* open it */
735 	rc = read_inode(inumber, f);
736 
737 #endif /* !LIBSA_FS_SINGLECOMPONENT */
738 
739 	fp->f_seekp = 0;		/* reset seek pointer */
740 
741 out:
742 	if (rc)
743 		ext2fs_close(f);
744 	else {
745 		fsmod = "ext2fs";
746 		fsmod2 = "ffs";
747 	}
748 	return rc;
749 }
750 
751 __compactcall int
752 ext2fs_close(struct open_file *f)
753 {
754 	struct file *fp = (struct file *)f->f_fsdata;
755 
756 	f->f_fsdata = NULL;
757 	if (fp == NULL)
758 		return 0;
759 
760 	if (fp->f_fs->e2fs_gd)
761 		dealloc(fp->f_fs->e2fs_gd,
762 		    sizeof(struct ext2_gd) * fp->f_fs->e2fs_ncg);
763 	if (fp->f_buf)
764 		dealloc(fp->f_buf, fp->f_fs->e2fs_bsize);
765 	dealloc(fp->f_fs, sizeof(*fp->f_fs));
766 	dealloc(fp, sizeof(struct file));
767 	return 0;
768 }
769 
770 /*
771  * Copy a portion of a file into kernel memory.
772  * Cross block boundaries when necessary.
773  */
774 __compactcall int
775 ext2fs_read(struct open_file *f, void *start, size_t size, size_t *resid)
776 {
777 	struct file *fp = (struct file *)f->f_fsdata;
778 	size_t csize;
779 	char *buf;
780 	size_t buf_size;
781 	int rc = 0;
782 	char *addr = start;
783 
784 	while (size != 0) {
785 		/* XXX should handle LARGEFILE */
786 		if (fp->f_seekp >= (off_t)fp->f_di.e2di_size)
787 			break;
788 
789 		rc = buf_read_file(f, &buf, &buf_size);
790 		if (rc)
791 			break;
792 
793 		csize = size;
794 		if (csize > buf_size)
795 			csize = buf_size;
796 
797 		memcpy(addr, buf, csize);
798 
799 		fp->f_seekp += csize;
800 		addr += csize;
801 		size -= csize;
802 	}
803 	if (resid)
804 		*resid = size;
805 	return rc;
806 }
807 
808 /*
809  * Not implemented.
810  */
811 #ifndef LIBSA_NO_FS_WRITE
812 __compactcall int
813 ext2fs_write(struct open_file *f, void *start, size_t size, size_t *resid)
814 {
815 
816 	return EROFS;
817 }
818 #endif /* !LIBSA_NO_FS_WRITE */
819 
820 #ifndef LIBSA_NO_FS_SEEK
821 __compactcall off_t
822 ext2fs_seek(struct open_file *f, off_t offset, int where)
823 {
824 	struct file *fp = (struct file *)f->f_fsdata;
825 
826 	switch (where) {
827 	case SEEK_SET:
828 		fp->f_seekp = offset;
829 		break;
830 	case SEEK_CUR:
831 		fp->f_seekp += offset;
832 		break;
833 	case SEEK_END:
834 		/* XXX should handle LARGEFILE */
835 		fp->f_seekp = fp->f_di.e2di_size - offset;
836 		break;
837 	default:
838 		return -1;
839 	}
840 	return fp->f_seekp;
841 }
842 #endif /* !LIBSA_NO_FS_SEEK */
843 
844 __compactcall int
845 ext2fs_stat(struct open_file *f, struct stat *sb)
846 {
847 	struct file *fp = (struct file *)f->f_fsdata;
848 
849 	/* only important stuff */
850 	memset(sb, 0, sizeof *sb);
851 	sb->st_mode = fp->f_di.e2di_mode;
852 	sb->st_uid = fp->f_di.e2di_uid;
853 	sb->st_gid = fp->f_di.e2di_gid;
854 	/* XXX should handle LARGEFILE */
855 	sb->st_size = fp->f_di.e2di_size;
856 	return 0;
857 }
858 
859 #if defined(LIBSA_ENABLE_LS_OP)
860 __compactcall void
861 ext2fs_ls(struct open_file *f, const char *pattern,
862 		void (*funcp)(char* arg), char* path)
863 {
864 	struct file *fp = (struct file *)f->f_fsdata;
865 	size_t block_size = fp->f_fs->e2fs_bsize;
866 	char *buf;
867 	size_t buf_size;
868 	entry_t	*names = 0, *n, **np;
869 
870 	fp->f_seekp = 0;
871 	while (fp->f_seekp < (off_t)fp->f_di.e2di_size) {
872 		struct ext2fs_direct  *dp, *edp;
873 		int rc = buf_read_file(f, &buf, &buf_size);
874 		if (rc)
875 			goto out;
876 		if (buf_size != block_size || buf_size == 0)
877 			goto out;
878 
879 		dp = (struct ext2fs_direct *)buf;
880 		edp = (struct ext2fs_direct *)(buf + buf_size);
881 
882 		for (; dp < edp;
883 		     dp = (void *)((char *)dp + fs2h16(dp->e2d_reclen))) {
884 			const char *t;
885 
886 			if (fs2h16(dp->e2d_reclen) <= 0)
887 				goto out;
888 
889 			if (fs2h32(dp->e2d_ino) == 0)
890 				continue;
891 
892 			if (dp->e2d_type >= NELEM(typestr) ||
893 			    !(t = typestr[dp->e2d_type])) {
894 				/*
895 				 * This does not handle "old"
896 				 * filesystems properly. On little
897 				 * endian machines, we get a bogus
898 				 * type name if the namlen matches a
899 				 * valid type identifier. We could
900 				 * check if we read namlen "0" and
901 				 * handle this case specially, if
902 				 * there were a pressing need...
903 				 */
904 				printf("bad dir entry\n");
905 				goto out;
906 			}
907 			if (pattern && !fn_match(dp->e2d_name, pattern))
908 				continue;
909 			n = alloc(sizeof *n + strlen(dp->e2d_name));
910 			if (!n) {
911 				printf("%d: %s (%s)\n",
912 					fs2h32(dp->e2d_ino), dp->e2d_name, t);
913 				continue;
914 			}
915 			n->e_ino = fs2h32(dp->e2d_ino);
916 			n->e_type = dp->e2d_type;
917 			strcpy(n->e_name, dp->e2d_name);
918 			for (np = &names; *np; np = &(*np)->e_next) {
919 				if (strcmp(n->e_name, (*np)->e_name) < 0)
920 					break;
921 			}
922 			n->e_next = *np;
923 			*np = n;
924 		}
925 		fp->f_seekp += buf_size;
926 	}
927 
928 	if (names) {
929 		entry_t *p_names = names;
930 		do {
931 			n = p_names;
932 			printf("%d: %s (%s)\n",
933 				n->e_ino, n->e_name, typestr[n->e_type]);
934 			p_names = n->e_next;
935 		} while (p_names);
936 	} else {
937 		printf("not found\n");
938 	}
939 out:
940 	if (names) {
941 		do {
942 			n = names;
943 			names = n->e_next;
944 			dealloc(n, 0);
945 		} while (names);
946 	}
947 	return;
948 }
949 #endif
950 
951 /*
952  * byte swap functions for big endian machines
953  * (ext2fs is always little endian)
954  *
955  * XXX: We should use src/sys/ufs/ext2fs/ext2fs_bswap.c
956  */
957 
958 /* These functions are only needed if native byte order is not big endian */
959 #if BYTE_ORDER == BIG_ENDIAN
960 void
961 e2fs_sb_bswap(struct ext2fs *old, struct ext2fs *new)
962 {
963 
964 	/* preserve unused fields */
965 	memcpy(new, old, sizeof(struct ext2fs));
966 	new->e2fs_icount	=	bswap32(old->e2fs_icount);
967 	new->e2fs_bcount	=	bswap32(old->e2fs_bcount);
968 	new->e2fs_rbcount	=	bswap32(old->e2fs_rbcount);
969 	new->e2fs_fbcount	=	bswap32(old->e2fs_fbcount);
970 	new->e2fs_ficount	=	bswap32(old->e2fs_ficount);
971 	new->e2fs_first_dblock	=	bswap32(old->e2fs_first_dblock);
972 	new->e2fs_log_bsize	=	bswap32(old->e2fs_log_bsize);
973 	new->e2fs_fsize		=	bswap32(old->e2fs_fsize);
974 	new->e2fs_bpg		=	bswap32(old->e2fs_bpg);
975 	new->e2fs_fpg		=	bswap32(old->e2fs_fpg);
976 	new->e2fs_ipg		=	bswap32(old->e2fs_ipg);
977 	new->e2fs_mtime		=	bswap32(old->e2fs_mtime);
978 	new->e2fs_wtime		=	bswap32(old->e2fs_wtime);
979 	new->e2fs_mnt_count	=	bswap16(old->e2fs_mnt_count);
980 	new->e2fs_max_mnt_count	=	bswap16(old->e2fs_max_mnt_count);
981 	new->e2fs_magic		=	bswap16(old->e2fs_magic);
982 	new->e2fs_state		=	bswap16(old->e2fs_state);
983 	new->e2fs_beh		=	bswap16(old->e2fs_beh);
984 	new->e2fs_minrev	=	bswap16(old->e2fs_minrev);
985 	new->e2fs_lastfsck	=	bswap32(old->e2fs_lastfsck);
986 	new->e2fs_fsckintv	=	bswap32(old->e2fs_fsckintv);
987 	new->e2fs_creator	=	bswap32(old->e2fs_creator);
988 	new->e2fs_rev		=	bswap32(old->e2fs_rev);
989 	new->e2fs_ruid		=	bswap16(old->e2fs_ruid);
990 	new->e2fs_rgid		=	bswap16(old->e2fs_rgid);
991 	new->e2fs_first_ino	=	bswap32(old->e2fs_first_ino);
992 	new->e2fs_inode_size	=	bswap16(old->e2fs_inode_size);
993 	new->e2fs_block_group_nr =	bswap16(old->e2fs_block_group_nr);
994 	new->e2fs_features_compat =	bswap32(old->e2fs_features_compat);
995 	new->e2fs_features_incompat =	bswap32(old->e2fs_features_incompat);
996 	new->e2fs_features_rocompat =	bswap32(old->e2fs_features_rocompat);
997 	new->e2fs_algo		=	bswap32(old->e2fs_algo);
998 	new->e2fs_reserved_ngdb	=	bswap16(old->e2fs_reserved_ngdb);
999 }
1000 
1001 void e2fs_cg_bswap(struct ext2_gd *old, struct ext2_gd *new, int size)
1002 {
1003 	int i;
1004 
1005 	for (i = 0; i < (size / sizeof(struct ext2_gd)); i++) {
1006 		new[i].ext2bgd_b_bitmap	= bswap32(old[i].ext2bgd_b_bitmap);
1007 		new[i].ext2bgd_i_bitmap	= bswap32(old[i].ext2bgd_i_bitmap);
1008 		new[i].ext2bgd_i_tables	= bswap32(old[i].ext2bgd_i_tables);
1009 		new[i].ext2bgd_nbfree	= bswap16(old[i].ext2bgd_nbfree);
1010 		new[i].ext2bgd_nifree	= bswap16(old[i].ext2bgd_nifree);
1011 		new[i].ext2bgd_ndirs	= bswap16(old[i].ext2bgd_ndirs);
1012 	}
1013 }
1014 
1015 void e2fs_i_bswap(struct ext2fs_dinode *old, struct ext2fs_dinode *new)
1016 {
1017 
1018 	new->e2di_mode		=	bswap16(old->e2di_mode);
1019 	new->e2di_uid		=	bswap16(old->e2di_uid);
1020 	new->e2di_gid		=	bswap16(old->e2di_gid);
1021 	new->e2di_nlink		=	bswap16(old->e2di_nlink);
1022 	new->e2di_size		=	bswap32(old->e2di_size);
1023 	new->e2di_atime		=	bswap32(old->e2di_atime);
1024 	new->e2di_ctime		=	bswap32(old->e2di_ctime);
1025 	new->e2di_mtime		=	bswap32(old->e2di_mtime);
1026 	new->e2di_dtime		=	bswap32(old->e2di_dtime);
1027 	new->e2di_nblock	=	bswap32(old->e2di_nblock);
1028 	new->e2di_flags		=	bswap32(old->e2di_flags);
1029 	new->e2di_gen		=	bswap32(old->e2di_gen);
1030 	new->e2di_facl		=	bswap32(old->e2di_facl);
1031 	new->e2di_dacl		=	bswap32(old->e2di_dacl);
1032 	new->e2di_faddr		=	bswap32(old->e2di_faddr);
1033 	memcpy(&new->e2di_blocks[0], &old->e2di_blocks[0],
1034 	    (NDADDR + NIADDR) * sizeof(uint32_t));
1035 }
1036 #endif
1037 
1038 #ifdef EXT2FS_DEBUG
1039 void
1040 dump_sblock(struct m_ext2fs *fs)
1041 {
1042 
1043 	printf("fs->e2fs.e2fs_bcount = %u\n", fs->e2fs.e2fs_bcount);
1044 	printf("fs->e2fs.e2fs_first_dblock = %u\n", fs->e2fs.e2fs_first_dblock);
1045 	printf("fs->e2fs.e2fs_log_bsize = %u\n", fs->e2fs.e2fs_log_bsize);
1046 	printf("fs->e2fs.e2fs_bpg = %u\n", fs->e2fs.e2fs_bpg);
1047 	printf("fs->e2fs.e2fs_ipg = %u\n", fs->e2fs.e2fs_ipg);
1048 	printf("fs->e2fs.e2fs_magic = 0x%x\n", fs->e2fs.e2fs_magic);
1049 	printf("fs->e2fs.e2fs_rev = %u\n", fs->e2fs.e2fs_rev);
1050 
1051 	if (fs->e2fs.e2fs_rev == E2FS_REV1) {
1052 		printf("fs->e2fs.e2fs_first_ino = %u\n",
1053 		    fs->e2fs.e2fs_first_ino);
1054 		printf("fs->e2fs.e2fs_inode_size = %u\n",
1055 		    fs->e2fs.e2fs_inode_size);
1056 		printf("fs->e2fs.e2fs_features_compat = %u\n",
1057 		    fs->e2fs.e2fs_features_compat);
1058 		printf("fs->e2fs.e2fs_features_incompat = %u\n",
1059 		    fs->e2fs.e2fs_features_incompat);
1060 		printf("fs->e2fs.e2fs_features_rocompat = %u\n",
1061 		    fs->e2fs.e2fs_features_rocompat);
1062 		printf("fs->e2fs.e2fs_reserved_ngdb = %u\n",
1063 		    fs->e2fs.e2fs_reserved_ngdb);
1064 	}
1065 
1066 	printf("fs->e2fs_bsize = %u\n", fs->e2fs_bsize);
1067 	printf("fs->e2fs_fsbtodb = %u\n", fs->e2fs_fsbtodb);
1068 	printf("fs->e2fs_ncg = %u\n", fs->e2fs_ncg);
1069 	printf("fs->e2fs_ngdb = %u\n", fs->e2fs_ngdb);
1070 	printf("fs->e2fs_ipb = %u\n", fs->e2fs_ipb);
1071 	printf("fs->e2fs_itpg = %u\n", fs->e2fs_itpg);
1072 }
1073 #endif
1074