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