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