xref: /netbsd-src/sys/lib/libsa/ufs.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: ufs.c,v 1.43 2004/03/20 14:24:59 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. 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/mount.h>			/* XXX for MNAMELEN */
72 #include <ufs/lfs/lfs.h>
73 #else
74 #include <ufs/ffs/fs.h>
75 #endif
76 #ifdef _STANDALONE
77 #include <lib/libkern/libkern.h>
78 #else
79 #include <string.h>
80 #endif
81 
82 #include "stand.h"
83 #ifdef LIBSA_LFS
84 #include "lfs.h"
85 #else
86 #include "ufs.h"
87 #endif
88 
89 /* If this file is compiled by itself, build ufs (aka ffsv1) support */
90 #if !defined(LIBSA_FFSv2) && !defined(LIBSA_LFS)
91 #define LIBSA_FFSv1
92 #endif
93 
94 #if defined(LIBSA_FS_SINGLECOMPONENT) && !defined(LIBSA_NO_FS_SYMLINK)
95 #define LIBSA_NO_FS_SYMLINK
96 #endif
97 #if defined(COMPAT_UFS) && defined(LIBSA_NO_COMPAT_UFS)
98 #undef COMPAT_UFS
99 #endif
100 
101 #ifdef LIBSA_LFS
102 /*
103  * In-core LFS superblock.  This exists only to placate the macros in lfs.h,
104  */
105 struct fs {
106 	struct dlfs	lfs_dlfs;
107 };
108 #define fs_magic	lfs_magic
109 #define fs_maxsymlinklen lfs_maxsymlinklen
110 
111 #define FS_MAGIC	LFS_MAGIC
112 #define SBLOCKSIZE	LFS_SBPAD
113 #define SBLOCKOFFSET	LFS_LABELPAD
114 #else
115 /* NB ufs2 doesn't use the common suberblock code... */
116 #define FS_MAGIC	FS_UFS1_MAGIC
117 #define SBLOCKOFFSET	SBLOCK_UFS1
118 #endif
119 
120 #if defined(LIBSA_NO_TWIDDLE)
121 #define twiddle()
122 #endif
123 
124 #undef cgstart
125 #if defined(LIBSA_FFSv2)
126 #define cgstart(fc, c) cgstart_ufs2((fs), (c))
127 #else
128 #define cgstart(fc, c) cgstart_ufs1((fs), (c))
129 #endif
130 
131 #ifndef ufs_dinode
132 #define ufs_dinode	ufs1_dinode
133 #endif
134 #ifndef indp_t
135 #define indp_t		uint32_t
136 #endif
137 #ifndef FSBTODB
138 #define FSBTODB(fs, indp) fsbtodb(fs, indp)
139 #endif
140 
141 /*
142  * To avoid having a lot of filesystem-block sized buffers lurking (which
143  * could be 32k) we only keep a few entries of the indirect block map.
144  * With 8k blocks, 2^8 blocks is ~500k so we reread the indirect block
145  * ~13 times pulling in a 6M kernel.
146  * The cache size must be smaller than the smallest filesystem block,
147  * so LN2_IND_CACHE_SZ <= 9 (UFS2 and 4k blocks).
148  */
149 #define LN2_IND_CACHE_SZ	6
150 #define IND_CACHE_SZ		(1 << LN2_IND_CACHE_SZ)
151 #define IND_CACHE_MASK		(IND_CACHE_SZ - 1)
152 
153 /*
154  * In-core open file.
155  */
156 struct file {
157 	off_t		f_seekp;	/* seek pointer */
158 	struct fs	*f_fs;		/* pointer to super-block */
159 	struct ufs_dinode	f_di;		/* copy of on-disk inode */
160 	uint		f_nishift;	/* for blocks in indirect block */
161 	indp_t		f_ind_cache_block;
162 	indp_t		f_ind_cache[IND_CACHE_SZ];
163 
164 	char		*f_buf;		/* buffer for data block */
165 	size_t		f_buf_size;	/* size of data block */
166 	daddr_t		f_buf_blkno;	/* block number of data block */
167 };
168 
169 static int read_inode(ino_t, struct open_file *);
170 static int block_map(struct open_file *, indp_t, indp_t *);
171 static int buf_read_file(struct open_file *, char **, size_t *);
172 static int search_directory(const char *, int, struct open_file *, ino_t *);
173 #ifdef LIBSA_FFSv1
174 static void ffs_oldfscompat(struct fs *);
175 #endif
176 #ifdef LIBSA_FFSv2
177 static int ffs_find_superblock(struct open_file *, struct fs *);
178 #endif
179 
180 #ifdef LIBSA_LFS
181 /*
182  * Find an inode's block.  Look it up in the ifile.  Whee!
183  */
184 static int
185 find_inode_sector(ino_t inumber, struct open_file *f, daddr_t *isp)
186 {
187 	struct file *fp = (struct file *)f->f_fsdata;
188 	struct fs *fs = fp->f_fs;
189 	daddr_t ifileent_blkno;
190 	char *ent_in_buf;
191 	size_t buf_after_ent;
192 	int rc;
193 
194 	rc = read_inode(fs->lfs_ifile, f);
195 	if (rc)
196 		return (rc);
197 
198 	ifileent_blkno =
199 	    (inumber / fs->lfs_ifpb) + fs->lfs_cleansz + fs->lfs_segtabsz;
200 	fp->f_seekp = (off_t)ifileent_blkno * fs->fs_bsize +
201 	    (inumber % fs->lfs_ifpb) * sizeof (IFILE_Vx);
202 	rc = buf_read_file(f, &ent_in_buf, &buf_after_ent);
203 	if (rc)
204 		return (rc);
205 	/* make sure something's not badly wrong, but don't panic. */
206 	if (buf_after_ent < sizeof (IFILE_Vx))
207 		return (EINVAL);
208 
209 	*isp = FSBTODB(fs, ((IFILE_Vx *)ent_in_buf)->if_daddr);
210 	if (*isp == LFS_UNUSED_DADDR)	/* again, something badly wrong */
211 		return (EINVAL);
212 	return (0);
213 }
214 #endif
215 
216 /*
217  * Read a new inode into a file structure.
218  */
219 static int
220 read_inode(ino_t inumber, struct open_file *f)
221 {
222 	struct file *fp = (struct file *)f->f_fsdata;
223 	struct fs *fs = fp->f_fs;
224 	char *buf;
225 	ssize_t rsize;
226 	int rc;
227 	daddr_t inode_sector;
228 #ifdef LIBSA_LFS
229 	struct ufs_dinode *dip;
230 	int cnt;
231 #endif
232 
233 #ifdef LIBSA_LFS
234 	if (inumber == fs->lfs_ifile)
235 		inode_sector = FSBTODB(fs, fs->lfs_idaddr);
236 	else if ((rc = find_inode_sector(inumber, f, &inode_sector)) != 0)
237 		return (rc);
238 #else
239 	inode_sector = FSBTODB(fs, ino_to_fsba(fs, inumber));
240 #endif
241 
242 	/*
243 	 * Read inode and save it.
244 	 */
245 	buf = fp->f_buf;
246 	twiddle();
247 	rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
248 	    inode_sector, fs->fs_bsize, buf, &rsize);
249 	if (rc)
250 		return rc;
251 	if (rsize != (ssize_t)fs->fs_bsize)
252 		return EIO;
253 
254 #ifdef LIBSA_LFS
255 	cnt = INOPBx(fs);
256 	dip = (struct ufs_dinode *)buf + (cnt - 1);
257 	for (; dip->di_inumber != inumber; --dip) {
258 		/* kernel code panics, but boot blocks which panic are Bad. */
259 		if (--cnt == 0)
260 			return EINVAL;
261 	}
262 	fp->f_di = *dip;
263 #else
264 	fp->f_di = ((struct ufs_dinode *)buf)[ino_to_fsbo(fs, inumber)];
265 #endif
266 
267 	/*
268 	 * Clear out the old buffers
269 	 */
270 	fp->f_ind_cache_block = ~0;
271 	fp->f_buf_blkno = -1;
272 	return (rc);
273 }
274 
275 /*
276  * Given an offset in a file, find the disk block number that
277  * contains that block.
278  */
279 static int
280 block_map(struct open_file *f, indp_t file_block, indp_t *disk_block_p)
281 {
282 	struct file *fp = (struct file *)f->f_fsdata;
283 	struct fs *fs = fp->f_fs;
284 	unsigned level;
285 	indp_t ind_cache;
286 	indp_t ind_block_num;
287 	ssize_t rsize;
288 	int rc;
289 	indp_t *buf = (void *)fp->f_buf;
290 
291 	/*
292 	 * Index structure of an inode:
293 	 *
294 	 * di_db[0..NDADDR-1]	hold block numbers for blocks
295 	 *			0..NDADDR-1
296 	 *
297 	 * di_ib[0]		index block 0 is the single indirect block
298 	 *			holds block numbers for blocks
299 	 *			NDADDR .. NDADDR + NINDIR(fs)-1
300 	 *
301 	 * di_ib[1]		index block 1 is the double indirect block
302 	 *			holds block numbers for INDEX blocks for blocks
303 	 *			NDADDR + NINDIR(fs) ..
304 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
305 	 *
306 	 * di_ib[2]		index block 2 is the triple indirect block
307 	 *			holds block numbers for double-indirect
308 	 *			blocks for blocks
309 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
310 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2
311 	 *				+ NINDIR(fs)**3 - 1
312 	 */
313 
314 	if (file_block < NDADDR) {
315 		/* Direct block. */
316 		*disk_block_p = fp->f_di.di_db[file_block];
317 		return (0);
318 	}
319 
320 	file_block -= NDADDR;
321 
322 	ind_cache = file_block >> LN2_IND_CACHE_SZ;
323 	if (ind_cache == fp->f_ind_cache_block) {
324 		*disk_block_p = fp->f_ind_cache[file_block & IND_CACHE_MASK];
325 		return 0;
326 	}
327 
328 	for (level = 0;;) {
329 		level += fp->f_nishift;
330 		if (file_block < (indp_t)1 << level)
331 			break;
332 		if (level > NIADDR * fp->f_nishift)
333 			/* Block number too high */
334 			return (EFBIG);
335 		file_block -= (indp_t)1 << level;
336 	}
337 
338 	ind_block_num = fp->f_di.di_ib[level / fp->f_nishift - 1];
339 
340 	for (;;) {
341 		level -= fp->f_nishift;
342 		if (ind_block_num == 0) {
343 			*disk_block_p = 0;	/* missing */
344 			return (0);
345 		}
346 
347 		twiddle();
348 		/*
349 		 * If we were feeling brave, we could work out the number
350 		 * of the disk sector and read a single disk sector instead
351 		 * of a filesystem block.
352 		 * However we don't do this very often anyway...
353 		 */
354 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
355 			FSBTODB(fp->f_fs, ind_block_num), fs->fs_bsize,
356 			buf, &rsize);
357 		if (rc)
358 			return (rc);
359 		if (rsize != (ssize_t)fs->fs_bsize)
360 			return EIO;
361 		ind_block_num = buf[file_block >> level];
362 		if (level == 0)
363 			break;
364 		file_block &= (1 << level) - 1;
365 	}
366 
367 	/* Save the part of the block that contains this sector */
368 	memcpy(fp->f_ind_cache, &buf[file_block & ~IND_CACHE_MASK],
369 	    IND_CACHE_SZ * sizeof fp->f_ind_cache[0]);
370 	fp->f_ind_cache_block = ind_cache;
371 
372 	*disk_block_p = ind_block_num;
373 
374 	return (0);
375 }
376 
377 /*
378  * Read a portion of a file into an internal buffer.
379  * Return the location in the buffer and the amount in the buffer.
380  */
381 static int
382 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
383 {
384 	struct file *fp = (struct file *)f->f_fsdata;
385 	struct fs *fs = fp->f_fs;
386 	long off;
387 	indp_t file_block;
388 	indp_t disk_block;
389 	size_t block_size;
390 	int rc;
391 
392 	off = blkoff(fs, fp->f_seekp);
393 	file_block = lblkno(fs, fp->f_seekp);
394 #ifdef LIBSA_LFS
395 	block_size = dblksize(fs, &fp->f_di, file_block);
396 #else
397 	block_size = sblksize(fs, (int64_t)fp->f_di.di_size, file_block);
398 #endif
399 
400 	if (file_block != fp->f_buf_blkno) {
401 		rc = block_map(f, file_block, &disk_block);
402 		if (rc)
403 			return (rc);
404 
405 		if (disk_block == 0) {
406 			memset(fp->f_buf, 0, block_size);
407 			fp->f_buf_size = block_size;
408 		} else {
409 			twiddle();
410 			rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
411 				FSBTODB(fs, disk_block),
412 				block_size, fp->f_buf, &fp->f_buf_size);
413 			if (rc)
414 				return (rc);
415 		}
416 
417 		fp->f_buf_blkno = file_block;
418 	}
419 
420 	/*
421 	 * Return address of byte in buffer corresponding to
422 	 * offset, and size of remainder of buffer after that
423 	 * byte.
424 	 */
425 	*buf_p = fp->f_buf + off;
426 	*size_p = block_size - off;
427 
428 	/*
429 	 * But truncate buffer at end of file.
430 	 */
431 	if (*size_p > fp->f_di.di_size - fp->f_seekp)
432 		*size_p = fp->f_di.di_size - fp->f_seekp;
433 
434 	return (0);
435 }
436 
437 /*
438  * Search a directory for a name and return its
439  * inode number.
440  */
441 static int
442 search_directory(const char *name, int length, struct open_file *f,
443     ino_t *inumber_p)
444 {
445 	struct file *fp = (struct file *)f->f_fsdata;
446 	struct direct *dp;
447 	struct direct *edp;
448 	char *buf;
449 	size_t buf_size;
450 	int namlen;
451 	int rc;
452 
453 	fp->f_seekp = 0;
454 	while (fp->f_seekp < (off_t)fp->f_di.di_size) {
455 		rc = buf_read_file(f, &buf, &buf_size);
456 		if (rc)
457 			return (rc);
458 
459 		dp = (struct direct *)buf;
460 		edp = (struct direct *)(buf + buf_size);
461 		for (;dp < edp; dp = (void *)((char *)dp + dp->d_reclen)) {
462 			if (dp->d_reclen <= 0)
463 				break;
464 			if (dp->d_ino == (ino_t)0)
465 				continue;
466 #if BYTE_ORDER == LITTLE_ENDIAN
467 			if (fp->f_fs->fs_maxsymlinklen <= 0)
468 				namlen = dp->d_type;
469 			else
470 #endif
471 				namlen = dp->d_namlen;
472 			if (namlen == length &&
473 			    !memcmp(name, dp->d_name, length)) {
474 				/* found entry */
475 				*inumber_p = dp->d_ino;
476 				return (0);
477 			}
478 		}
479 		fp->f_seekp += buf_size;
480 	}
481 	return (ENOENT);
482 }
483 
484 #ifdef LIBSA_FFSv2
485 
486 daddr_t sblock_try[] = SBLOCKSEARCH;
487 
488 static int
489 ffs_find_superblock(struct open_file *f, struct fs *fs)
490 {
491 	int i, rc;
492 	size_t buf_size;
493 
494 	for (i = 0; sblock_try[i] != -1; i++) {
495 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
496 		    sblock_try[i] / DEV_BSIZE, SBLOCKSIZE, fs, &buf_size);
497 		if (rc != 0 || buf_size != SBLOCKSIZE)
498 			return rc;
499 		if (fs->fs_sblockloc != sblock_try[i])
500 			/* an alternate superblock - try again */
501 			continue;
502 		if (fs->fs_magic == FS_UFS2_MAGIC) {
503 			return 0;
504 		}
505 	}
506 	return EINVAL;
507 }
508 
509 #endif
510 
511 /*
512  * Open a file.
513  */
514 int
515 ufs_open(const char *path, struct open_file *f)
516 {
517 #ifndef LIBSA_FS_SINGLECOMPONENT
518 	const char *cp, *ncp;
519 	int c;
520 #endif
521 	ino_t inumber;
522 	struct file *fp;
523 	struct fs *fs;
524 	int rc;
525 #ifndef LIBSA_NO_FS_SYMLINK
526 	ino_t parent_inumber;
527 	int nlinks = 0;
528 	char namebuf[MAXPATHLEN+1];
529 	char *buf;
530 #endif
531 
532 	/* allocate file system specific data structure */
533 	fp = alloc(sizeof(struct file));
534 	memset(fp, 0, sizeof(struct file));
535 	f->f_fsdata = (void *)fp;
536 
537 	/* allocate space and read super block */
538 	fs = alloc(SBLOCKSIZE);
539 	fp->f_fs = fs;
540 	twiddle();
541 
542 #ifdef LIBSA_FFSv2
543 	rc = ffs_find_superblock(f, fs);
544 	if (rc)
545 		goto out;
546 #else
547 	{
548 		size_t buf_size;
549 		rc = DEV_STRATEGY(f->f_dev)(f->f_devdata, F_READ,
550 			SBLOCKOFFSET / DEV_BSIZE, SBLOCKSIZE, fs, &buf_size);
551 		if (rc)
552 			goto out;
553 		if (buf_size != SBLOCKSIZE ||
554 #ifdef LIBSA_FFS
555 		    fs->lfs_version != REQUIRED_LFS_VERSION ||
556 #endif
557 		    fs->fs_magic != FS_MAGIC) {
558 			rc = EINVAL;
559 			goto out;
560 		}
561 	}
562 #if defined(LIBSA_LFS) && REQUIRED_LFS_VERSION == 2
563 	/*
564 	 * XXX	We should check the second superblock and use the eldest
565 	 *	of the two.  See comments near the top of lfs_mountfs()
566 	 *	in sys/ufs/lfs/lfs_vfsops.c.
567 	 *      This may need a LIBSA_LFS_SMALL check as well.
568 	 */
569 #endif
570 #endif
571 
572 #ifdef LIBSA_FFSv1
573 	ffs_oldfscompat(fs);
574 #endif
575 
576 	if (fs->fs_bsize > MAXBSIZE ||
577 	    (size_t)fs->fs_bsize < sizeof(struct fs)) {
578 		rc = EINVAL;
579 		goto out;
580 	}
581 
582 	/*
583 	 * Calculate indirect block levels.
584 	 */
585 	{
586 		indp_t mult;
587 		int ln2;
588 
589 		/*
590 		 * We note that the number of indirect blocks is always
591 		 * a power of 2.  This lets us use shifts and masks instead
592 		 * of divide and remainder and avoinds pulling in the
593 		 * 64bit division routine into the boot code.
594 		 */
595 		mult = NINDIR(fs);
596 #ifdef DEBUG
597 		if (mult & (mult - 1)) {
598 			/* Hummm was't a power of 2 */
599 			rc = EINVAL;
600 			goto out;
601 		}
602 #endif
603 		for (ln2 = 0; mult != 1; ln2++)
604 			mult >>= 1;
605 
606 		fp->f_nishift = ln2;
607 	}
608 
609 	/* alloc a block sized buffer used for all fs transfers */
610 	fp->f_buf = alloc(fs->fs_bsize);
611 	inumber = ROOTINO;
612 	if ((rc = read_inode(inumber, f)) != 0)
613 		goto out;
614 
615 #ifndef LIBSA_FS_SINGLECOMPONENT
616 	cp = path;
617 	while (*cp) {
618 
619 		/*
620 		 * Remove extra separators
621 		 */
622 		while (*cp == '/')
623 			cp++;
624 		if (*cp == '\0')
625 			break;
626 
627 		/*
628 		 * Check that current node is a directory.
629 		 */
630 		if ((fp->f_di.di_mode & IFMT) != IFDIR) {
631 			rc = ENOTDIR;
632 			goto out;
633 		}
634 
635 		/*
636 		 * Get next component of path name.
637 		 */
638 		ncp = cp;
639 		while ((c = *cp) != '\0' && c != '/')
640 			cp++;
641 
642 		/*
643 		 * Look up component in current directory.
644 		 * Save directory inumber in case we find a
645 		 * symbolic link.
646 		 */
647 #ifndef LIBSA_NO_FS_SYMLINK
648 		parent_inumber = inumber;
649 #endif
650 		rc = search_directory(ncp, cp - ncp, f, &inumber);
651 		if (rc)
652 			goto out;
653 
654 		/*
655 		 * Open next component.
656 		 */
657 		if ((rc = read_inode(inumber, f)) != 0)
658 			goto out;
659 
660 #ifndef LIBSA_NO_FS_SYMLINK
661 		/*
662 		 * Check for symbolic link.
663 		 */
664 		if ((fp->f_di.di_mode & IFMT) == IFLNK) {
665 			int link_len = fp->f_di.di_size;
666 			int len;
667 
668 			len = strlen(cp);
669 
670 			if (link_len + len > MAXPATHLEN ||
671 			    ++nlinks > MAXSYMLINKS) {
672 				rc = ENOENT;
673 				goto out;
674 			}
675 
676 			memmove(&namebuf[link_len], cp, len + 1);
677 
678 			if (link_len < fs->fs_maxsymlinklen) {
679 				memcpy(namebuf, fp->f_di.di_db, link_len);
680 			} else {
681 				/*
682 				 * Read file for symbolic link
683 				 */
684 				size_t buf_size;
685 				indp_t	disk_block;
686 
687 				buf = fp->f_buf;
688 				rc = block_map(f, (indp_t)0, &disk_block);
689 				if (rc)
690 					goto out;
691 
692 				twiddle();
693 				rc = DEV_STRATEGY(f->f_dev)(f->f_devdata,
694 					F_READ, FSBTODB(fs, disk_block),
695 					fs->fs_bsize, buf, &buf_size);
696 				if (rc)
697 					goto out;
698 
699 				memcpy(namebuf, buf, link_len);
700 			}
701 
702 			/*
703 			 * If relative pathname, restart at parent directory.
704 			 * If absolute pathname, restart at root.
705 			 */
706 			cp = namebuf;
707 			if (*cp != '/')
708 				inumber = parent_inumber;
709 			else
710 				inumber = (ino_t)ROOTINO;
711 
712 			if ((rc = read_inode(inumber, f)) != 0)
713 				goto out;
714 		}
715 #endif	/* !LIBSA_NO_FS_SYMLINK */
716 	}
717 
718 	/*
719 	 * Found terminal component.
720 	 */
721 	rc = 0;
722 
723 #else /* !LIBSA_FS_SINGLECOMPONENT */
724 
725 	/* look up component in the current (root) directory */
726 	rc = search_directory(path, strlen(path), f, &inumber);
727 	if (rc)
728 		goto out;
729 
730 	/* open it */
731 	rc = read_inode(inumber, f);
732 
733 #endif /* !LIBSA_FS_SINGLECOMPONENT */
734 
735         fp->f_seekp = 0;		/* reset seek pointer */
736 
737 out:
738 	if (rc)
739 		ufs_close(f);
740 	return (rc);
741 }
742 
743 int
744 ufs_close(struct open_file *f)
745 {
746 	struct file *fp = (struct file *)f->f_fsdata;
747 
748 	f->f_fsdata = NULL;
749 	if (fp == NULL)
750 		return (0);
751 
752 	if (fp->f_buf)
753 		free(fp->f_buf, fp->f_fs->fs_bsize);
754 	free(fp->f_fs, SBLOCKSIZE);
755 	free(fp, sizeof(struct file));
756 	return (0);
757 }
758 
759 /*
760  * Copy a portion of a file into kernel memory.
761  * Cross block boundaries when necessary.
762  */
763 int
764 ufs_read(struct open_file *f, void *start, size_t size, size_t *resid)
765 {
766 	struct file *fp = (struct file *)f->f_fsdata;
767 	size_t csize;
768 	char *buf;
769 	size_t buf_size;
770 	int rc = 0;
771 	char *addr = start;
772 
773 	while (size != 0) {
774 		if (fp->f_seekp >= (off_t)fp->f_di.di_size)
775 			break;
776 
777 		rc = buf_read_file(f, &buf, &buf_size);
778 		if (rc)
779 			break;
780 
781 		csize = size;
782 		if (csize > buf_size)
783 			csize = buf_size;
784 
785 		memcpy(addr, buf, csize);
786 
787 		fp->f_seekp += csize;
788 		addr += csize;
789 		size -= csize;
790 	}
791 	if (resid)
792 		*resid = size;
793 	return (rc);
794 }
795 
796 /*
797  * Not implemented.
798  */
799 #ifndef LIBSA_NO_FS_WRITE
800 int
801 ufs_write(struct open_file *f, void *start, size_t size, size_t *resid)
802 {
803 
804 	return (EROFS);
805 }
806 #endif /* !LIBSA_NO_FS_WRITE */
807 
808 #ifndef LIBSA_NO_FS_SEEK
809 off_t
810 ufs_seek(struct open_file *f, off_t offset, int where)
811 {
812 	struct file *fp = (struct file *)f->f_fsdata;
813 
814 	switch (where) {
815 	case SEEK_SET:
816 		fp->f_seekp = offset;
817 		break;
818 	case SEEK_CUR:
819 		fp->f_seekp += offset;
820 		break;
821 	case SEEK_END:
822 		fp->f_seekp = fp->f_di.di_size - offset;
823 		break;
824 	default:
825 		return (-1);
826 	}
827 	return (fp->f_seekp);
828 }
829 #endif /* !LIBSA_NO_FS_SEEK */
830 
831 int
832 ufs_stat(struct open_file *f, struct stat *sb)
833 {
834 	struct file *fp = (struct file *)f->f_fsdata;
835 
836 	/* only important stuff */
837 	memset(sb, 0, sizeof *sb);
838 	sb->st_mode = fp->f_di.di_mode;
839 	sb->st_uid = fp->f_di.di_uid;
840 	sb->st_gid = fp->f_di.di_gid;
841 	sb->st_size = fp->f_di.di_size;
842 	return (0);
843 }
844 
845 #ifdef LIBSA_FFSv1
846 /*
847  * Sanity checks for old file systems.
848  *
849  * XXX - goes away some day.
850  * Stripped of stuff libsa doesn't need.....
851  */
852 static void
853 ffs_oldfscompat(struct fs *fs)
854 {
855 
856 #ifdef COMPAT_UFS
857 	if (fs->fs_magic == FS_UFS1_MAGIC &&
858 	    fs->fs_old_inodefmt < FS_44INODEFMT) {
859 		fs->fs_qbmask = ~fs->fs_bmask;
860 		fs->fs_qfmask = ~fs->fs_fmask;
861 	}
862 #endif
863 }
864 #endif
865