xref: /netbsd-src/sys/lib/libsa/ufs.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: ufs.c,v 1.80 2021/05/27 06:54:44 mrg 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 /* Do not (yet) support FFS_EI on LFS. */
104 #undef LIBSA_FFS_EI
105 /*
106  * In-core LFS superblock - just the on-disk one.
107  */
108 struct salfs {
109 	union {
110 		struct dlfs u_32;
111 		struct dlfs64 u_64;
112 	} lfs_dlfs_u;
113 	unsigned lfs_is64 : 1,
114 		lfs_dobyteswap : 1,
115 		lfs_hasolddirfmt : 1;
116 };
117 /* Get lfs accessors that use struct salfs. */
118 #define STRUCT_LFS struct salfs
119 #include <ufs/lfs/lfs_accessors.h>
120 
121 /* override this to avoid a mess with the dinode accessors */
122 #define lfs_dino_getsize(fs, dp) ((dp)->di_size)
123 
124 typedef struct salfs FS;
125 #define fs_magic	lfs_dlfs_u.u_32.dlfs_magic
126 #define fs_maxsymlinklen lfs_dlfs_u.u_32.dlfs_maxsymlinklen
127 #define lfs_version	lfs_dlfs_u.u_32.dlfs_version
128 
129 #define SBLOCKSIZE	LFS_SBPAD
130 #define SBLOCKOFFSET	LFS_LABELPAD
131 #else
132 /* NB ufs2 doesn't use the common superblock code... */
133 typedef struct fs FS;
134 #define SBLOCKOFFSET	SBLOCK_UFS1
135 #endif
136 
137 #if defined(LIBSA_NO_TWIDDLE)
138 #define twiddle()
139 #endif
140 
141 #undef cgstart
142 #if defined(LIBSA_FFSv2)
143 #define cgstart(fc, c) cgstart_ufs2((fs), (c))
144 #else
145 #define cgstart(fc, c) cgstart_ufs1((fs), (c))
146 #endif
147 
148 #ifndef ufs_dinode
149 #define ufs_dinode	ufs1_dinode
150 #endif
151 #ifndef indp_t
152 #define indp_t		int32_t
153 #endif
154 typedef uint32_t	ino32_t;
155 
156 #ifndef FSBTODB
157 #define FSBTODB(fs, indp) FFS_FSBTODB(fs, indp)
158 #endif
159 #ifndef FS_MAGIC
160 #define FS_MAGIC FS_UFS1_MAGIC
161 #endif
162 #ifndef UFS_NINDIR
163 #define UFS_NINDIR FFS_NINDIR
164 #endif
165 #ifndef ufs_blkoff
166 #define ufs_blkoff ffs_blkoff
167 #endif
168 #ifndef ufs_lblkno
169 #define ufs_lblkno ffs_lblkno
170 #endif
171 #ifndef ufs_dinode_swap
172 #define ufs_dinode_swap ffs_dinode1_swap
173 #endif
174 #ifndef ufs_indp_swap
175 #define ufs_indp_swap bswap32
176 #endif
177 
178 /*
179  * To avoid having a lot of filesystem-block sized buffers lurking (which
180  * could be 32k) we only keep a few entries of the indirect block map.
181  * With 8k blocks, 2^8 blocks is ~500k so we reread the indirect block
182  * ~13 times pulling in a 6M kernel.
183  * The cache size must be smaller than the smallest filesystem block,
184  * so LN2_IND_CACHE_SZ <= 9 (UFS2 and 4k blocks).
185  */
186 #define LN2_IND_CACHE_SZ	6
187 #define IND_CACHE_SZ		(1 << LN2_IND_CACHE_SZ)
188 #define IND_CACHE_MASK		(IND_CACHE_SZ - 1)
189 
190 /*
191  * In-core open file.
192  */
193 struct file {
194 	off_t		f_seekp;	/* seek pointer */
195 	FS		*f_fs;		/* pointer to super-block */
196 	struct ufs_dinode	f_di;		/* copy of on-disk inode */
197 	uint		f_nishift;	/* for blocks in indirect block */
198 	indp_t		f_ind_cache_block;
199 	indp_t		f_ind_cache[IND_CACHE_SZ];
200 
201 	char		*f_buf;		/* buffer for data block */
202 	size_t		f_buf_size;	/* size of data block */
203 	daddr_t		f_buf_blkno;	/* block number of data block */
204 #if defined(LIBSA_FFS_EI)
205 	bool		f_swapped;	/* FFS is other endian */
206 #endif
207 };
208 
209 static int read_inode(ino32_t, struct open_file *);
210 static int block_map(struct open_file *, indp_t, indp_t *);
211 static int buf_read_file(struct open_file *, char **, size_t *);
212 static int search_directory(const char *, int, struct open_file *, ino32_t *);
213 #ifdef LIBSA_FFSv1
214 static void ffs_oldfscompat(FS *);
215 #endif
216 
217 static __inline__ bool
218 ffs_is_magic(FS *fs)
219 {
220 	return fs->fs_magic == FS_MAGIC;
221 }
222 
223 static __inline__ void
224 ffs_fix_magic_swapped(struct file *fp, FS *fs)
225 {
226 #ifdef LIBSA_FFS_EI
227 	fp->f_swapped = fs->fs_magic == bswap32(FS_MAGIC);
228 	if (fp->f_swapped)
229 {
230 		ffs_sb_swap(fs, fs);
231 }
232 #endif
233 }
234 
235 #ifdef LIBSA_FFS_EI
236 static __inline__ bool
237 ffs_swapped(struct file *fp)
238 {
239 	return fp->f_swapped;
240 }
241 #endif
242 
243 static __inline__ uint16_t
244 ffs_get_reclen(struct file *fp, struct direct *dp)
245 {
246 #ifdef LIBSA_FFS_EI
247 	if (ffs_swapped(fp))
248 		return bswap16(dp->d_reclen);
249 #endif
250 	return dp->d_reclen;
251 }
252 
253 static __inline__ uint32_t
254 ffs_get_ino(struct file *fp, struct direct *dp)
255 {
256 #ifdef LIBSA_FFS_EI
257 	if (ffs_swapped(fp))
258 		return bswap32(dp->d_ino);
259 #endif
260 	return dp->d_ino;
261 }
262 
263 
264 #ifdef LIBSA_LFS
265 /*
266  * Find an inode's block.  Look it up in the ifile.  Whee!
267  */
268 static int
269 find_inode_sector(ino32_t inumber, struct open_file *f, daddr_t *isp)
270 {
271 	struct file *fp = (struct file *)f->f_fsdata;
272 	FS *fs = fp->f_fs;
273 	daddr_t ifileent_blkno;
274 	char *ent_in_buf;
275 	size_t buf_after_ent;
276 	size_t entsize;
277 	int rc;
278 
279 	rc = read_inode(LFS_IFILE_INUM, f);
280 	if (rc)
281 		return rc;
282 
283 	entsize = fs->lfs_is64 ? sizeof(IFILE64) :
284 		(lfs_sb_getversion(fs) > 1 ? sizeof(IFILE32) : sizeof(IFILE_V1));
285 	ifileent_blkno =
286 	    (inumber / lfs_sb_getifpb(fs)) + lfs_sb_getcleansz(fs) + lfs_sb_getsegtabsz(fs);
287 	fp->f_seekp = (off_t)ifileent_blkno * lfs_sb_getbsize(fs) +
288 	    (inumber % lfs_sb_getifpb(fs)) * entsize;
289 	rc = buf_read_file(f, &ent_in_buf, &buf_after_ent);
290 	if (rc)
291 		return rc;
292 	/* make sure something's not badly wrong, but don't panic. */
293 	if (buf_after_ent < entsize)
294 		return EINVAL;
295 
296 	*isp = FSBTODB(fs, lfs_if_getdaddr(fs, (IFILE *)ent_in_buf));
297 	if (*isp == LFS_UNUSED_DADDR)	/* again, something badly wrong */
298 		return EINVAL;
299 	return 0;
300 }
301 #endif
302 
303 /*
304  * Read a new inode into a file structure.
305  */
306 static int
307 read_inode(ino32_t inumber, struct open_file *f)
308 {
309 	struct file *fp = (struct file *)f->f_fsdata;
310 	FS *fs = fp->f_fs;
311 	char *buf;
312 	size_t rsize;
313 	int rc;
314 	daddr_t inode_sector = 0; /* XXX: gcc */
315 #ifdef LIBSA_LFS
316 	struct ufs_dinode *dip;
317 	int cnt;
318 #endif
319 
320 #ifdef LIBSA_LFS
321 	if (inumber == LFS_IFILE_INUM)
322 		inode_sector = FSBTODB(fs, lfs_sb_getidaddr(fs));
323 	else if ((rc = find_inode_sector(inumber, f, &inode_sector)) != 0)
324 		return rc;
325 #else
326 	inode_sector = FSBTODB(fs, ino_to_fsba(fs, inumber));
327 #endif
328 
329 	/*
330 	 * Read inode and save it.
331 	 */
332 	buf = fp->f_buf;
333 	twiddle();
334 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
335 	    inode_sector, fs->fs_bsize, buf, &rsize);
336 	if (rc)
337 		return rc;
338 	if (rsize != (size_t)fs->fs_bsize)
339 		return EIO;
340 
341 #ifdef LIBSA_LFS
342 	cnt = INOPBx(fs);
343 	dip = (struct ufs_dinode *)buf + (cnt - 1);
344 	for (; dip->di_inumber != inumber; --dip) {
345 		/* kernel code panics, but boot blocks which panic are Bad. */
346 		if (--cnt == 0)
347 			return EINVAL;
348 	}
349 	fp->f_di = *dip;
350 #else
351 	fp->f_di = ((struct ufs_dinode *)buf)[ino_to_fsbo(fs, inumber)];
352 #ifdef LIBSA_FFS_EI
353 	if (ffs_swapped(fp))
354 		ufs_dinode_swap(&fp->f_di, &fp->f_di);
355 #endif
356 #endif
357 
358 	/*
359 	 * Clear out the old buffers
360 	 */
361 	fp->f_ind_cache_block = ~0;
362 	fp->f_buf_blkno = -1;
363 	return rc;
364 }
365 
366 /*
367  * Given an offset in a file, find the disk block number that
368  * contains that block.
369  */
370 static int
371 block_map(struct open_file *f, indp_t file_block, indp_t *disk_block_p)
372 {
373 	struct file *fp = (struct file *)f->f_fsdata;
374 	FS *fs = fp->f_fs;
375 	uint level;
376 	indp_t ind_cache;
377 	indp_t ind_block_num;
378 	size_t rsize;
379 	int rc;
380 	indp_t *buf = (void *)fp->f_buf;
381 
382 	/*
383 	 * Index structure of an inode:
384 	 *
385 	 * di_db[0..UFS_NDADDR-1]	hold block numbers for blocks
386 	 *			0..UFS_NDADDR-1
387 	 *
388 	 * di_ib[0]		index block 0 is the single indirect block
389 	 *			holds block numbers for blocks
390 	 *			UFS_NDADDR .. UFS_NDADDR + UFS_NINDIR(fs)-1
391 	 *
392 	 * di_ib[1]		index block 1 is the double indirect block
393 	 *			holds block numbers for INDEX blocks for blocks
394 	 *			UFS_NDADDR + UFS_NINDIR(fs) ..
395 	 *			UFS_NDADDR + UFS_NINDIR(fs) + UFS_NINDIR(fs)**2 - 1
396 	 *
397 	 * di_ib[2]		index block 2 is the triple indirect block
398 	 *			holds block numbers for double-indirect
399 	 *			blocks for blocks
400 	 *			UFS_NDADDR + UFS_NINDIR(fs) + UFS_NINDIR(fs)**2 ..
401 	 *			UFS_NDADDR + UFS_NINDIR(fs) + UFS_NINDIR(fs)**2
402 	 *				+ UFS_NINDIR(fs)**3 - 1
403 	 */
404 
405 	if (file_block < UFS_NDADDR) {
406 		/* Direct block. */
407 		*disk_block_p = fp->f_di.di_db[file_block];
408 		return 0;
409 	}
410 
411 	file_block -= UFS_NDADDR;
412 
413 	ind_cache = file_block >> LN2_IND_CACHE_SZ;
414 	if (ind_cache == fp->f_ind_cache_block) {
415 		*disk_block_p = fp->f_ind_cache[file_block & IND_CACHE_MASK];
416 		return 0;
417 	}
418 
419 	for (level = 0;;) {
420 		level += fp->f_nishift;
421 		if (file_block < (indp_t)1 << level)
422 			break;
423 		if (level > UFS_NIADDR * fp->f_nishift)
424 			/* Block number too high */
425 			return EFBIG;
426 		file_block -= (indp_t)1 << level;
427 	}
428 
429 	ind_block_num = fp->f_di.di_ib[level / fp->f_nishift - 1];
430 
431 	for (;;) {
432 		level -= fp->f_nishift;
433 		if (ind_block_num == 0) {
434 			*disk_block_p = 0;	/* missing */
435 			return 0;
436 		}
437 
438 		twiddle();
439 		/*
440 		 * If we were feeling brave, we could work out the number
441 		 * of the disk sector and read a single disk sector instead
442 		 * of a filesystem block.
443 		 * However we don't do this very often anyway...
444 		 */
445 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
446 			FSBTODB(fp->f_fs, ind_block_num), fs->fs_bsize,
447 			buf, &rsize);
448 		if (rc)
449 			return rc;
450 		if (rsize != (size_t)fs->fs_bsize)
451 			return EIO;
452 #ifdef LIBSA_FFS_EI
453 		if (ffs_swapped(fp))
454 			ind_block_num = ufs_indp_swap(buf[file_block >> level]);
455 		else
456 #endif
457 			ind_block_num = buf[file_block >> level];
458 		if (level == 0)
459 			break;
460 		file_block &= (1 << level) - 1;
461 	}
462 
463 	/* Save the part of the block that contains this sector */
464 #if defined(LIBSA_FFS_EI)
465 	if (ffs_swapped(fp)) {
466 		size_t i;
467 
468 		for (i = 0; i < IND_CACHE_SZ; i++) {
469 			fp->f_ind_cache[i] = ufs_indp_swap(
470 			    buf[(file_block & ~IND_CACHE_MASK) + i]);
471 		}
472 	} else
473 #endif
474 		memcpy(fp->f_ind_cache, &buf[file_block & ~IND_CACHE_MASK],
475 		    IND_CACHE_SZ * sizeof fp->f_ind_cache[0]);
476 	fp->f_ind_cache_block = ind_cache;
477 
478 	*disk_block_p = ind_block_num;
479 
480 	return 0;
481 }
482 
483 /*
484  * Read a portion of a file into an internal buffer.
485  * Return the location in the buffer and the amount in the buffer.
486  */
487 static int
488 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
489 {
490 	struct file *fp = (struct file *)f->f_fsdata;
491 	FS *fs = fp->f_fs;
492 	long off;
493 	indp_t file_block;
494 	size_t block_size;
495 	int rc;
496 
497 	off = ufs_blkoff(fs, fp->f_seekp);
498 	file_block = ufs_lblkno(fs, fp->f_seekp);
499 #ifdef LIBSA_LFS
500 	block_size = (size_t)dblksize(fs, &fp->f_di, (uint64_t)file_block);
501 #else
502 	block_size = (size_t)ffs_sblksize(fs, (int64_t)fp->f_di.di_size, file_block);
503 #endif
504 
505 	if (file_block != fp->f_buf_blkno) {
506 		indp_t disk_block = 0; /* XXX: gcc */
507 		rc = block_map(f, file_block, &disk_block);
508 		if (rc)
509 			return rc;
510 
511 		if (disk_block == 0) {
512 			memset(fp->f_buf, 0, block_size);
513 			fp->f_buf_size = block_size;
514 		} else {
515 			twiddle();
516 			rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
517 				FSBTODB(fs, disk_block),
518 				block_size, fp->f_buf, &fp->f_buf_size);
519 			if (rc)
520 				return rc;
521 		}
522 
523 		fp->f_buf_blkno = file_block;
524 	}
525 
526 	/*
527 	 * Return address of byte in buffer corresponding to
528 	 * offset, and size of remainder of buffer after that
529 	 * byte.
530 	 */
531 	*buf_p = fp->f_buf + off;
532 	*size_p = block_size - off;
533 
534 	/*
535 	 * But truncate buffer at end of file.
536 	 */
537 	if (*size_p > fp->f_di.di_size - fp->f_seekp)
538 		*size_p = fp->f_di.di_size - fp->f_seekp;
539 
540 	return 0;
541 }
542 
543 /*
544  * Search a directory for a name and return its
545  * inode number.
546  */
547 static int
548 search_directory(const char *name, int length, struct open_file *f,
549 	ino32_t *inumber_p)
550 {
551 	struct file *fp = (struct file *)f->f_fsdata;
552 	struct direct *dp;
553 	struct direct *edp;
554 	char *buf;
555 	size_t buf_size;
556 	int namlen;
557 	int rc;
558 
559 	fp->f_seekp = 0;
560 	while (fp->f_seekp < (off_t)fp->f_di.di_size) {
561 		rc = buf_read_file(f, &buf, &buf_size);
562 		if (rc)
563 			return rc;
564 
565 		dp = (struct direct *)buf;
566 		edp = (struct direct *)(buf + buf_size);
567 		for (; dp < edp;
568 		     dp = (void *)((char *)dp + ffs_get_reclen(fp, dp))) {
569 			if (ffs_get_reclen(fp, dp) <= 0)
570 				break;
571 			if (ffs_get_ino(fp, dp) == (ino32_t)0)
572 				continue;
573 #if BYTE_ORDER == LITTLE_ENDIAN
574 			if (fp->f_fs->fs_maxsymlinklen <= 0)
575 				namlen = dp->d_type;
576 			else
577 #endif
578 				namlen = dp->d_namlen;
579 			if (namlen == length &&
580 			    !memcmp(name, dp->d_name, length)) {
581 				/* found entry */
582 				*inumber_p = ffs_get_ino(fp, dp);
583 				return 0;
584 			}
585 		}
586 		fp->f_seekp += buf_size;
587 	}
588 	return ENOENT;
589 }
590 
591 static __inline__ int
592 ffs_find_superblock(struct open_file *f, FS *fs)
593 {
594 	struct file *fp = (struct file *)f->f_fsdata;
595 	int rc;
596 	size_t buf_size;
597 #ifdef LIBSA_FFSv2
598 	static daddr_t sblock_try[] = SBLOCKSEARCH;
599 	int i;
600 
601 	for (i = 0; sblock_try[i] != -1; i++) {
602 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
603 		    sblock_try[i] / DEV_BSIZE, SBLOCKSIZE, fs, &buf_size);
604 		if (rc)
605 			return rc;
606 		if (buf_size != SBLOCKSIZE)
607 			return EINVAL;
608 		ffs_fix_magic_swapped(fp, fs);
609 		if (fs->fs_sblockloc != sblock_try[i])
610 			/* an alternate superblock - try again */
611 			continue;
612 		if (ffs_is_magic(fs))
613 			return 0;
614 	}
615 	return EINVAL;
616 #else /* LIBSA_FFSv2 */
617 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
618 		SBLOCKOFFSET / DEV_BSIZE, SBLOCKSIZE, fs, &buf_size);
619 	if (rc)
620 		return rc;
621 	if (buf_size != SBLOCKSIZE)
622 		return EINVAL;
623 	ffs_fix_magic_swapped(fp, fs);
624 
625 #ifdef LIBSA_LFS
626 	if (fs->lfs_version != REQUIRED_LFS_VERSION)
627 		return EINVAL;
628 #endif
629 	if (!ffs_is_magic(fs))
630 		return EINVAL;
631 
632 	return 0;
633 #endif /* !LIBSA_FFSv2 */
634 }
635 
636 /*
637  * Open a file.
638  */
639 __compactcall int
640 ufs_open(const char *path, struct open_file *f)
641 {
642 #ifndef LIBSA_FS_SINGLECOMPONENT
643 	const char *cp, *ncp;
644 	int c;
645 #endif
646 	ino32_t inumber;
647 	struct file *fp;
648 	FS *fs;
649 	int rc;
650 #ifndef LIBSA_NO_FS_SYMLINK
651 	ino32_t parent_inumber;
652 	int nlinks = 0;
653 	char namebuf[MAXPATHLEN+1];
654 	char *buf;
655 #endif
656 
657 	/* allocate file system specific data structure */
658 	fp = alloc(sizeof(struct file));
659 	memset(fp, 0, sizeof(struct file));
660 	f->f_fsdata = (void *)fp;
661 
662 	/* allocate space and read super block */
663 	fs = alloc(SBLOCKSIZE);
664 	fp->f_fs = fs;
665 	twiddle();
666 
667 	rc = ffs_find_superblock(f, fs);
668 	if (rc)
669 		goto out;
670 
671 #if defined(LIBSA_LFS) && REQUIRED_LFS_VERSION == 2
672 	/*
673 	 * XXX	We should check the second superblock and use the eldest
674 	 *	of the two.  See comments near the top of lfs_mountfs()
675 	 *	in sys/ufs/lfs/lfs_vfsops.c.
676 	 *      This may need a LIBSA_LFS_SMALL check as well.
677 	 */
678 #endif
679 #if defined(LIBSA_LFS)
680 	fs->lfs_is64 = 0;
681 	fs->lfs_dobyteswap = 0;
682 	fs->lfs_hasolddirfmt = (fs->fs_maxsymlinklen <= 0);
683 #endif
684 #ifdef LIBSA_FFSv1
685 	ffs_oldfscompat(fs);
686 #endif
687 
688 	if (fs->fs_bsize > MAXBSIZE ||
689 	    (size_t)fs->fs_bsize < sizeof(FS)) {
690 		rc = EINVAL;
691 		goto out;
692 	}
693 
694 	/*
695 	 * Calculate indirect block levels.
696 	 */
697 	{
698 		indp_t mult;
699 		int ln2;
700 
701 		/*
702 		 * We note that the number of indirect blocks is always
703 		 * a power of 2.  This lets us use shifts and masks instead
704 		 * of divide and remainder and avoinds pulling in the
705 		 * 64bit division routine into the boot code.
706 		 */
707 		mult = UFS_NINDIR(fs);
708 #ifdef DEBUG
709 		if (mult & (mult - 1)) {
710 			/* Hummm was't a power of 2 */
711 			rc = EINVAL;
712 			goto out;
713 		}
714 #endif
715 		for (ln2 = 0; mult != 1; ln2++)
716 			mult >>= 1;
717 
718 		fp->f_nishift = ln2;
719 	}
720 
721 	/* alloc a block sized buffer used for all fs transfers */
722 	fp->f_buf = alloc(fs->fs_bsize);
723 	inumber = UFS_ROOTINO;
724 	if ((rc = read_inode(inumber, f)) != 0)
725 		goto out;
726 
727 #ifndef LIBSA_FS_SINGLECOMPONENT
728 	cp = path;
729 	while (*cp) {
730 
731 		/*
732 		 * Remove extra separators
733 		 */
734 		while (*cp == '/')
735 			cp++;
736 		if (*cp == '\0')
737 			break;
738 
739 		/*
740 		 * Check that current node is a directory.
741 		 */
742 		if ((fp->f_di.di_mode & IFMT) != IFDIR) {
743 			rc = ENOTDIR;
744 			goto out;
745 		}
746 
747 		/*
748 		 * Get next component of path name.
749 		 */
750 		ncp = cp;
751 		while ((c = *cp) != '\0' && c != '/')
752 			cp++;
753 
754 		/*
755 		 * Look up component in current directory.
756 		 * Save directory inumber in case we find a
757 		 * symbolic link.
758 		 */
759 #ifndef LIBSA_NO_FS_SYMLINK
760 		parent_inumber = inumber;
761 #endif
762 		rc = search_directory(ncp, cp - ncp, f, &inumber);
763 		if (rc)
764 			goto out;
765 
766 		/*
767 		 * Open next component.
768 		 */
769 		if ((rc = read_inode(inumber, f)) != 0)
770 			goto out;
771 
772 #ifndef LIBSA_NO_FS_SYMLINK
773 		/*
774 		 * Check for symbolic link.
775 		 */
776 		if ((fp->f_di.di_mode & IFMT) == IFLNK) {
777 			int link_len = fp->f_di.di_size;
778 			int len;
779 
780 			len = strlen(cp);
781 
782 			if (link_len + len > MAXPATHLEN ||
783 			    ++nlinks > MAXSYMLINKS) {
784 				rc = ENOENT;
785 				goto out;
786 			}
787 
788 			memmove(&namebuf[link_len], cp, len + 1);
789 
790 			if (link_len < fs->fs_maxsymlinklen) {
791 				memcpy(namebuf, fp->f_di.di_db, link_len);
792 			} else {
793 				/*
794 				 * Read file for symbolic link
795 				 */
796 				size_t buf_size;
797 				indp_t	disk_block;
798 
799 				buf = fp->f_buf;
800 				rc = block_map(f, (indp_t)0, &disk_block);
801 				if (rc)
802 					goto out;
803 
804 				twiddle();
805 				rc = DEV_STRATEGY(f->f_dev)(f->f_devdata,
806 					F_READ, FSBTODB(fs, disk_block),
807 					fs->fs_bsize, buf, &buf_size);
808 				if (rc)
809 					goto out;
810 
811 				memcpy(namebuf, buf, link_len);
812 			}
813 
814 			/*
815 			 * If relative pathname, restart at parent directory.
816 			 * If absolute pathname, restart at root.
817 			 */
818 			cp = namebuf;
819 			if (*cp != '/')
820 				inumber = parent_inumber;
821 			else
822 				inumber = (ino32_t)UFS_ROOTINO;
823 
824 			if ((rc = read_inode(inumber, f)) != 0)
825 				goto out;
826 		}
827 #endif	/* !LIBSA_NO_FS_SYMLINK */
828 	}
829 
830 	/*
831 	 * Found terminal component.
832 	 */
833 	rc = 0;
834 
835 #else /* !LIBSA_FS_SINGLECOMPONENT */
836 
837 	/* look up component in the current (root) directory */
838 	rc = search_directory(path, strlen(path), f, &inumber);
839 	if (rc)
840 		goto out;
841 
842 	/* open it */
843 	rc = read_inode(inumber, f);
844 
845 #endif /* !LIBSA_FS_SINGLECOMPONENT */
846 
847 	fp->f_seekp = 0;		/* reset seek pointer */
848 
849 out:
850 	if (rc)
851 		ufs_close(f);
852 #ifdef FSMOD		/* Only defined for lfs */
853 	else
854 		fsmod = FSMOD;
855 #endif
856 	return rc;
857 }
858 
859 __compactcall int
860 ufs_close(struct open_file *f)
861 {
862 	struct file *fp = (struct file *)f->f_fsdata;
863 
864 	f->f_fsdata = NULL;
865 	if (fp == NULL)
866 		return 0;
867 
868 	if (fp->f_buf)
869 		dealloc(fp->f_buf, fp->f_fs->fs_bsize);
870 	dealloc(fp->f_fs, SBLOCKSIZE);
871 	dealloc(fp, sizeof(struct file));
872 	return 0;
873 }
874 
875 /*
876  * Copy a portion of a file into kernel memory.
877  * Cross block boundaries when necessary.
878  */
879 __compactcall int
880 ufs_read(struct open_file *f, void *start, size_t size, size_t *resid)
881 {
882 	struct file *fp = (struct file *)f->f_fsdata;
883 	size_t csize;
884 	char *buf;
885 	size_t buf_size;
886 	int rc = 0;
887 	char *addr = start;
888 
889 	while (size != 0) {
890 		if (fp->f_seekp >= (off_t)fp->f_di.di_size)
891 			break;
892 
893 		rc = buf_read_file(f, &buf, &buf_size);
894 		if (rc)
895 			break;
896 
897 		csize = size;
898 		if (csize > buf_size)
899 			csize = buf_size;
900 
901 		memcpy(addr, buf, csize);
902 
903 		fp->f_seekp += csize;
904 		addr += csize;
905 		size -= csize;
906 	}
907 	if (resid)
908 		*resid = size;
909 	return rc;
910 }
911 
912 /*
913  * Not implemented.
914  */
915 #ifndef LIBSA_NO_FS_WRITE
916 __compactcall int
917 ufs_write(struct open_file *f, void *start, size_t size, size_t *resid)
918 {
919 
920 	return EROFS;
921 }
922 #endif /* !LIBSA_NO_FS_WRITE */
923 
924 #ifndef LIBSA_NO_FS_SEEK
925 __compactcall off_t
926 ufs_seek(struct open_file *f, off_t offset, int where)
927 {
928 	struct file *fp = (struct file *)f->f_fsdata;
929 
930 	switch (where) {
931 	case SEEK_SET:
932 		fp->f_seekp = offset;
933 		break;
934 	case SEEK_CUR:
935 		fp->f_seekp += offset;
936 		break;
937 	case SEEK_END:
938 		fp->f_seekp = fp->f_di.di_size - offset;
939 		break;
940 	default:
941 		return -1;
942 	}
943 	return fp->f_seekp;
944 }
945 #endif /* !LIBSA_NO_FS_SEEK */
946 
947 __compactcall int
948 ufs_stat(struct open_file *f, struct stat *sb)
949 {
950 	struct file *fp = (struct file *)f->f_fsdata;
951 
952 	/* only important stuff */
953 	memset(sb, 0, sizeof *sb);
954 	sb->st_mode = fp->f_di.di_mode;
955 	sb->st_uid = fp->f_di.di_uid;
956 	sb->st_gid = fp->f_di.di_gid;
957 	sb->st_size = fp->f_di.di_size;
958 	return 0;
959 }
960 
961 #if defined(LIBSA_ENABLE_LS_OP)
962 
963 #include "ls.h"
964 
965 static const char    *const typestr[] = {
966 	"unknown",
967 	"FIFO",
968 	"CHR",
969 	0,
970 	"DIR",
971 	0,
972 	"BLK",
973 	0,
974 	"REG",
975 	0,
976 	"LNK",
977 	0,
978 	"SOCK",
979 	0,
980 	"WHT"
981 };
982 
983 __compactcall void
984 ufs_ls(struct open_file *f, const char *pattern)
985 {
986 	struct file *fp = (struct file *)f->f_fsdata;
987 	char *buf;
988 	size_t buf_size;
989 	lsentry_t *names = NULL;
990 
991 	fp->f_seekp = 0;
992 	while (fp->f_seekp < (off_t)fp->f_di.di_size) {
993 		struct direct  *dp, *edp;
994 		int rc = buf_read_file(f, &buf, &buf_size);
995 		if (rc)
996 			goto out;
997 		/* some firmware might use block size larger than DEV_BSIZE */
998 		if (buf_size < UFS_DIRBLKSIZ)
999 			goto out;
1000 
1001 		dp = (struct direct *)buf;
1002 		edp = (struct direct *)(buf + buf_size);
1003 
1004 		for (; dp < edp;
1005 		     dp = (void *)((char *)dp + ffs_get_reclen(fp, dp))) {
1006 			const char *t;
1007 			if (ffs_get_ino(fp, dp) == 0)
1008 				continue;
1009 
1010 			if (dp->d_type >= NELEM(typestr) ||
1011 			    !(t = typestr[dp->d_type])) {
1012 				/*
1013 				 * This does not handle "old"
1014 				 * filesystems properly. On little
1015 				 * endian machines, we get a bogus
1016 				 * type name if the namlen matches a
1017 				 * valid type identifier. We could
1018 				 * check if we read namlen "0" and
1019 				 * handle this case specially, if
1020 				 * there were a pressing need...
1021 				 */
1022 				printf("bad dir entry\n");
1023 				goto out;
1024 			}
1025 			lsadd(&names, pattern, dp->d_name, strlen(dp->d_name),
1026 			    ffs_get_ino(fp, dp), t);
1027 		}
1028 		fp->f_seekp += buf_size;
1029 	}
1030 	lsprint(names);
1031 out:	lsfree(names);
1032 }
1033 #endif /* LIBSA_ENABLE_LS_OP */
1034 
1035 #ifdef LIBSA_FFSv1
1036 /*
1037  * Sanity checks for old file systems.
1038  *
1039  * XXX - goes away some day.
1040  * Stripped of stuff libsa doesn't need.....
1041  */
1042 static void
1043 ffs_oldfscompat(FS *fs)
1044 {
1045 
1046 #ifdef COMPAT_UFS
1047 	/*
1048 	 * Newer Solaris versions have a slightly incompatible
1049 	 * superblock - so always calculate this values on the fly, which
1050 	 * is good enough for libsa purposes
1051 	 */
1052 	if (fs->fs_magic == FS_UFS1_MAGIC
1053 #ifndef COMPAT_SOLARIS_UFS
1054 	    && fs->fs_old_inodefmt < FS_44INODEFMT
1055 #endif
1056 	    ) {
1057 		fs->fs_qbmask = ~fs->fs_bmask;
1058 		fs->fs_qfmask = ~fs->fs_fmask;
1059 	}
1060 #endif
1061 }
1062 #endif
1063