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