1/*	$NetBSD: ufs.c,v 1.20 1998/03/01 07:15:39 ross Exp $	*/
2
3/*
4 * Copyright (c) 2002 Networks Associates Technology, Inc.
5 * All rights reserved.
6 *
7 * This software was developed for the FreeBSD Project by Marshall
8 * Kirk McKusick and Network Associates Laboratories, the Security
9 * Research Division of Network Associates, Inc. under DARPA/SPAWAR
10 * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
11 * research program
12 *
13 * Copyright (c) 1982, 1989, 1993
14 *	The Regents of the University of California.  All rights reserved.
15 *
16 * This code is derived from software contributed to Berkeley by
17 * The Mach Operating System project at Carnegie-Mellon University.
18 *
19 * Redistribution and use in source and binary forms, with or without
20 * modification, are permitted provided that the following conditions
21 * are met:
22 * 1. Redistributions of source code must retain the above copyright
23 *    notice, this list of conditions and the following disclaimer.
24 * 2. Redistributions in binary form must reproduce the above copyright
25 *    notice, this list of conditions and the following disclaimer in the
26 *    documentation and/or other materials provided with the distribution.
27 * 3. Neither the name of the University nor the names of its contributors
28 *    may be used to endorse or promote products derived from this software
29 *    without specific prior written permission.
30 *
31 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
32 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
33 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
35 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
40 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41 * SUCH DAMAGE.
42 *
43 *
44 * Copyright (c) 1990, 1991 Carnegie Mellon University
45 * All Rights Reserved.
46 *
47 * Author: David Golub
48 *
49 * Permission to use, copy, modify and distribute this software and its
50 * documentation is hereby granted, provided that both the copyright
51 * notice and this permission notice appear in all copies of the
52 * software, derivative works or modified versions, and any portions
53 * thereof, and that both notices appear in supporting documentation.
54 *
55 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
56 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
57 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
58 *
59 * Carnegie Mellon requests users of this software to return to
60 *
61 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
62 *  School of Computer Science
63 *  Carnegie Mellon University
64 *  Pittsburgh PA 15213-3890
65 *
66 * any improvements or extensions that they make and grant Carnegie the
67 * rights to redistribute these changes.
68 */
69
70#include <sys/cdefs.h>
71
72/*
73 *	Stand-alone file reading package.
74 */
75
76#include <sys/param.h>
77#include <sys/disklabel.h>
78#include <sys/time.h>
79#include <ufs/ufs/dinode.h>
80#include <ufs/ufs/dir.h>
81#include <ufs/ffs/fs.h>
82#include "stand.h"
83#include "string.h"
84
85static int	ufs_open(const char *, struct open_file *);
86static int	ufs_write(struct open_file *, const void *, size_t, size_t *);
87static int	ufs_close(struct open_file *);
88static int	ufs_read(struct open_file *, void *, size_t, size_t *);
89static off_t	ufs_seek(struct open_file *, off_t, int);
90static int	ufs_stat(struct open_file *, struct stat *);
91static int	ufs_readdir(struct open_file *, struct dirent *);
92
93struct fs_ops ufs_fsops = {
94	"ufs",
95	ufs_open,
96	ufs_close,
97	ufs_read,
98	ufs_write,
99	ufs_seek,
100	ufs_stat,
101	ufs_readdir
102};
103
104/*
105 * In-core open file.
106 */
107struct file {
108	off_t		f_seekp;	/* seek pointer */
109	struct fs	*f_fs;		/* pointer to super-block */
110	union dinode {
111		struct ufs1_dinode di1;
112		struct ufs2_dinode di2;
113	}		f_di;		/* copy of on-disk inode */
114	int		f_nindir[NIADDR];
115					/*
116					 * number of blocks mapped by
117					 * indirect block at level i
118					 */
119	char		*f_blk[NIADDR];
120					/*
121					 * buffer for indirect block at
122					 * level i
123					 */
124	size_t		f_blksize[NIADDR]; /* size of buffer */
125	ufs2_daddr_t	f_blkno[NIADDR]; /* disk address of block in buffer */
126	ufs2_daddr_t	f_buf_blkno;	/* block number of data block */
127	char		*f_buf;		/* buffer for data block */
128	size_t		f_buf_size;	/* size of data block */
129};
130#define	DIP(fp, field) \
131	((fp)->f_fs->fs_magic == FS_UFS1_MAGIC ? \
132	(fp)->f_di.di1.field : (fp)->f_di.di2.field)
133
134static int	read_inode(ino_t, struct open_file *);
135static int	block_map(struct open_file *, ufs2_daddr_t, ufs2_daddr_t *);
136static int	buf_read_file(struct open_file *, char **, size_t *);
137static int	buf_write_file(struct open_file *, const char *, size_t *);
138static int	search_directory(char *, struct open_file *, ino_t *);
139
140/*
141 * Read a new inode into a file structure.
142 */
143static int
144read_inode(ino_t inumber, struct open_file *f)
145{
146	struct file *fp = (struct file *)f->f_fsdata;
147	struct fs *fs = fp->f_fs;
148	char *buf;
149	size_t rsize;
150	int rc;
151
152	if (fs == NULL)
153		panic("fs == NULL");
154
155	/*
156	 * Read inode and save it.
157	 */
158	buf = malloc(fs->fs_bsize);
159	twiddle(1);
160	rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
161	    fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize,
162	    buf, &rsize);
163	if (rc)
164		goto out;
165	if (rsize != fs->fs_bsize) {
166		rc = EIO;
167		goto out;
168	}
169
170	if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
171		fp->f_di.di1 = ((struct ufs1_dinode *)buf)
172		    [ino_to_fsbo(fs, inumber)];
173	else
174		fp->f_di.di2 = ((struct ufs2_dinode *)buf)
175		    [ino_to_fsbo(fs, inumber)];
176
177	/*
178	 * Clear out the old buffers
179	 */
180	{
181		int level;
182
183		for (level = 0; level < NIADDR; level++)
184			fp->f_blkno[level] = -1;
185		fp->f_buf_blkno = -1;
186	}
187	fp->f_seekp = 0;
188out:
189	free(buf);
190	return (rc);
191}
192
193/*
194 * Given an offset in a file, find the disk block number that
195 * contains that block.
196 */
197static int
198block_map(struct open_file *f, ufs2_daddr_t file_block,
199    ufs2_daddr_t *disk_block_p)
200{
201	struct file *fp = (struct file *)f->f_fsdata;
202	struct fs *fs = fp->f_fs;
203	int level;
204	int idx;
205	ufs2_daddr_t ind_block_num;
206	int rc;
207
208	/*
209	 * Index structure of an inode:
210	 *
211	 * di_db[0..NDADDR-1]	hold block numbers for blocks
212	 *			0..NDADDR-1
213	 *
214	 * di_ib[0]		index block 0 is the single indirect block
215	 *			holds block numbers for blocks
216	 *			NDADDR .. NDADDR + NINDIR(fs)-1
217	 *
218	 * di_ib[1]		index block 1 is the double indirect block
219	 *			holds block numbers for INDEX blocks for blocks
220	 *			NDADDR + NINDIR(fs) ..
221	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
222	 *
223	 * di_ib[2]		index block 2 is the triple indirect block
224	 *			holds block numbers for double-indirect
225	 *			blocks for blocks
226	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
227	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2
228	 *				+ NINDIR(fs)**3 - 1
229	 */
230
231	if (file_block < NDADDR) {
232		/* Direct block. */
233		*disk_block_p = DIP(fp, di_db[file_block]);
234		return (0);
235	}
236
237	file_block -= NDADDR;
238
239	/*
240	 * nindir[0] = NINDIR
241	 * nindir[1] = NINDIR**2
242	 * nindir[2] = NINDIR**3
243	 *	etc
244	 */
245	for (level = 0; level < NIADDR; level++) {
246		if (file_block < fp->f_nindir[level])
247			break;
248		file_block -= fp->f_nindir[level];
249	}
250	if (level == NIADDR) {
251		/* Block number too high */
252		return (EFBIG);
253	}
254
255	ind_block_num = DIP(fp, di_ib[level]);
256
257	for (; level >= 0; level--) {
258		if (ind_block_num == 0) {
259			*disk_block_p = 0;	/* missing */
260			return (0);
261		}
262
263		if (fp->f_blkno[level] != ind_block_num) {
264			if (fp->f_blk[level] == NULL)
265				fp->f_blk[level] =
266				    malloc(fs->fs_bsize);
267			twiddle(1);
268			rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
269			    fsbtodb(fp->f_fs, ind_block_num),
270			    fs->fs_bsize,
271			    fp->f_blk[level],
272			    &fp->f_blksize[level]);
273			if (rc)
274				return (rc);
275			if (fp->f_blksize[level] != fs->fs_bsize)
276				return (EIO);
277			fp->f_blkno[level] = ind_block_num;
278		}
279
280		if (level > 0) {
281			idx = file_block / fp->f_nindir[level - 1];
282			file_block %= fp->f_nindir[level - 1];
283		} else
284			idx = file_block;
285
286		if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
287			ind_block_num = ((ufs1_daddr_t *)fp->f_blk[level])[idx];
288		else
289			ind_block_num = ((ufs2_daddr_t *)fp->f_blk[level])[idx];
290	}
291
292	*disk_block_p = ind_block_num;
293
294	return (0);
295}
296
297/*
298 * Write a portion of a file from an internal buffer.
299 */
300static int
301buf_write_file(struct open_file *f, const char *buf_p, size_t *size_p)
302{
303	struct file *fp = (struct file *)f->f_fsdata;
304	struct fs *fs = fp->f_fs;
305	long off;
306	ufs_lbn_t file_block;
307	ufs2_daddr_t disk_block;
308	size_t block_size;
309	int rc;
310
311	/*
312	 * Calculate the starting block address and offset.
313	 */
314	off = blkoff(fs, fp->f_seekp);
315	file_block = lblkno(fs, fp->f_seekp);
316	block_size = sblksize(fs, DIP(fp, di_size), file_block);
317
318	rc = block_map(f, file_block, &disk_block);
319	if (rc)
320		return (rc);
321
322	if (disk_block == 0)
323		/* Because we can't allocate space on the drive */
324		return (EFBIG);
325
326	/*
327	 * Truncate buffer at end of file, and at the end of
328	 * this block.
329	 */
330	if (*size_p > DIP(fp, di_size) - fp->f_seekp)
331		*size_p = DIP(fp, di_size) - fp->f_seekp;
332	if (*size_p > block_size - off)
333		*size_p = block_size - off;
334
335	/*
336	 * If we don't entirely occlude the block and it's not
337	 * in memory already, read it in first.
338	 */
339	if (((off > 0) || (*size_p + off < block_size)) &&
340	    (file_block != fp->f_buf_blkno)) {
341
342		if (fp->f_buf == (char *)0)
343			fp->f_buf = malloc(fs->fs_bsize);
344
345		twiddle(8);
346		rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
347		    fsbtodb(fs, disk_block),
348		    block_size, fp->f_buf, &fp->f_buf_size);
349		if (rc)
350			return (rc);
351
352		fp->f_buf_blkno = file_block;
353	}
354
355	/*
356	 *	Copy the user data into the cached block.
357	 */
358	bcopy(buf_p, fp->f_buf + off, *size_p);
359
360	/*
361	 *	Write the block out to storage.
362	 */
363
364	twiddle(4);
365	rc = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE,
366	    fsbtodb(fs, disk_block),
367	    block_size, fp->f_buf, &fp->f_buf_size);
368	return (rc);
369}
370
371/*
372 * Read a portion of a file into an internal buffer.  Return
373 * the location in the buffer and the amount in the buffer.
374 */
375static int
376buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
377{
378	struct file *fp = (struct file *)f->f_fsdata;
379	struct fs *fs = fp->f_fs;
380	long off;
381	ufs_lbn_t file_block;
382	ufs2_daddr_t disk_block;
383	size_t block_size;
384	int rc;
385
386	off = blkoff(fs, fp->f_seekp);
387	file_block = lblkno(fs, fp->f_seekp);
388	block_size = sblksize(fs, DIP(fp, di_size), file_block);
389
390	if (file_block != fp->f_buf_blkno) {
391		if (fp->f_buf == (char *)0)
392			fp->f_buf = malloc(fs->fs_bsize);
393
394		rc = block_map(f, file_block, &disk_block);
395		if (rc)
396			return (rc);
397
398		if (disk_block == 0) {
399			bzero(fp->f_buf, block_size);
400			fp->f_buf_size = block_size;
401		} else {
402			twiddle(4);
403			rc = (f->f_dev->dv_strategy)(f->f_devdata,
404			    F_READ, fsbtodb(fs, disk_block),
405			    block_size, fp->f_buf, &fp->f_buf_size);
406			if (rc)
407				return (rc);
408		}
409
410		fp->f_buf_blkno = file_block;
411	}
412
413	/*
414	 * Return address of byte in buffer corresponding to
415	 * offset, and size of remainder of buffer after that
416	 * byte.
417	 */
418	*buf_p = fp->f_buf + off;
419	*size_p = block_size - off;
420
421	/*
422	 * But truncate buffer at end of file.
423	 */
424	if (*size_p > DIP(fp, di_size) - fp->f_seekp)
425		*size_p = DIP(fp, di_size) - fp->f_seekp;
426
427	return (0);
428}
429
430/*
431 * Search a directory for a name and return its
432 * i_number.
433 */
434static int
435search_directory(char *name, struct open_file *f, ino_t *inumber_p)
436{
437	struct file *fp = (struct file *)f->f_fsdata;
438	struct direct *dp;
439	struct direct *edp;
440	char *buf;
441	size_t buf_size;
442	int namlen, length;
443	int rc;
444
445	length = strlen(name);
446
447	fp->f_seekp = 0;
448	while (fp->f_seekp < DIP(fp, di_size)) {
449		rc = buf_read_file(f, &buf, &buf_size);
450		if (rc)
451			return (rc);
452
453		dp = (struct direct *)buf;
454		edp = (struct direct *)(buf + buf_size);
455		while (dp < edp) {
456			if (dp->d_ino == (ino_t)0)
457				goto next;
458			namlen = dp->d_namlen;
459			if (namlen == length &&
460			    strcmp(name, dp->d_name) == 0) {
461				/* found entry */
462				*inumber_p = dp->d_ino;
463				return (0);
464			}
465		next:
466			dp = (struct direct *)((char *)dp + dp->d_reclen);
467		}
468		fp->f_seekp += buf_size;
469	}
470	return (ENOENT);
471}
472
473static int sblock_try[] = SBLOCKSEARCH;
474
475/*
476 * Open a file.
477 */
478static int
479ufs_open(const char *upath, struct open_file *f)
480{
481	char *cp, *ncp;
482	int c;
483	ino_t inumber, parent_inumber;
484	struct file *fp;
485	struct fs *fs;
486	int i, rc;
487	size_t buf_size;
488	int nlinks = 0;
489	char namebuf[MAXPATHLEN+1];
490	char *buf = NULL;
491	char *path = NULL;
492
493	/* allocate file system specific data structure */
494	fp = malloc(sizeof (struct file));
495	bzero(fp, sizeof (struct file));
496	f->f_fsdata = (void *)fp;
497
498	/* allocate space and read super block */
499	fs = malloc(SBLOCKSIZE);
500	fp->f_fs = fs;
501	twiddle(1);
502	/*
503	 * Try reading the superblock in each of its possible locations.
504	 */
505	for (i = 0; sblock_try[i] != -1; i++) {
506		rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
507		    sblock_try[i] / DEV_BSIZE, SBLOCKSIZE,
508		    (char *)fs, &buf_size);
509		if (rc)
510			goto out;
511		if ((fs->fs_magic == FS_UFS1_MAGIC ||
512		    (fs->fs_magic == FS_UFS2_MAGIC &&
513		    fs->fs_sblockloc == sblock_try[i])) &&
514		    buf_size == SBLOCKSIZE &&
515		    fs->fs_bsize <= MAXBSIZE &&
516		    fs->fs_bsize >= sizeof (struct fs))
517			break;
518	}
519	if (sblock_try[i] == -1) {
520		rc = EINVAL;
521		goto out;
522	}
523	/*
524	 * Calculate indirect block levels.
525	 */
526	{
527		ufs2_daddr_t mult;
528		int level;
529
530		mult = 1;
531		for (level = 0; level < NIADDR; level++) {
532			mult *= NINDIR(fs);
533			fp->f_nindir[level] = mult;
534		}
535	}
536
537	inumber = ROOTINO;
538	if ((rc = read_inode(inumber, f)) != 0)
539		goto out;
540
541	cp = path = strdup(upath);
542	if (path == NULL) {
543		rc = ENOMEM;
544		goto out;
545	}
546	while (*cp) {
547
548		/*
549		 * Remove extra separators
550		 */
551		while (*cp == '/')
552			cp++;
553		if (*cp == '\0')
554			break;
555
556		/*
557		 * Check that current node is a directory.
558		 */
559		if ((DIP(fp, di_mode) & IFMT) != IFDIR) {
560			rc = ENOTDIR;
561			goto out;
562		}
563
564		/*
565		 * Get next component of path name.
566		 */
567		{
568			int len = 0;
569
570			ncp = cp;
571			while ((c = *cp) != '\0' && c != '/') {
572				if (++len > UFS_MAXNAMLEN) {
573					rc = ENOENT;
574					goto out;
575				}
576				cp++;
577			}
578			*cp = '\0';
579		}
580
581		/*
582		 * Look up component in current directory.
583		 * Save directory inumber in case we find a
584		 * symbolic link.
585		 */
586		parent_inumber = inumber;
587		rc = search_directory(ncp, f, &inumber);
588		*cp = c;
589		if (rc)
590			goto out;
591
592		/*
593		 * Open next component.
594		 */
595		if ((rc = read_inode(inumber, f)) != 0)
596			goto out;
597
598		/*
599		 * Check for symbolic link.
600		 */
601		if ((DIP(fp, di_mode) & IFMT) == IFLNK) {
602			int link_len = DIP(fp, di_size);
603			int len;
604
605			len = strlen(cp);
606
607			if (link_len + len > MAXPATHLEN ||
608			    ++nlinks > MAXSYMLINKS) {
609				rc = ENOENT;
610				goto out;
611			}
612
613			bcopy(cp, &namebuf[link_len], len + 1);
614
615			if (link_len < fs->fs_maxsymlinklen) {
616				if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
617					cp = (caddr_t)(fp->f_di.di1.di_db);
618				else
619					cp = (caddr_t)(fp->f_di.di2.di_db);
620				bcopy(cp, namebuf, (unsigned)link_len);
621			} else {
622				/*
623				 * Read file for symbolic link
624				 */
625				size_t buf_size;
626				ufs2_daddr_t disk_block;
627				struct fs *fs = fp->f_fs;
628
629				if (!buf)
630					buf = malloc(fs->fs_bsize);
631				rc = block_map(f, (ufs2_daddr_t)0, &disk_block);
632				if (rc)
633					goto out;
634
635				twiddle(1);
636				rc = (f->f_dev->dv_strategy)(f->f_devdata,
637				    F_READ, fsbtodb(fs, disk_block),
638				    fs->fs_bsize, buf, &buf_size);
639				if (rc)
640					goto out;
641
642				bcopy((char *)buf, namebuf, (unsigned)link_len);
643			}
644
645			/*
646			 * If relative pathname, restart at parent directory.
647			 * If absolute pathname, restart at root.
648			 */
649			cp = namebuf;
650			if (*cp != '/')
651				inumber = parent_inumber;
652			else
653				inumber = (ino_t)ROOTINO;
654
655			if ((rc = read_inode(inumber, f)) != 0)
656				goto out;
657		}
658	}
659
660	/*
661	 * Found terminal component.
662	 */
663	rc = 0;
664	fp->f_seekp = 0;
665out:
666	if (buf)
667		free(buf);
668	if (path)
669		free(path);
670	if (rc) {
671		if (fp->f_buf)
672			free(fp->f_buf);
673		free(fp->f_fs);
674		free(fp);
675	}
676	return (rc);
677}
678
679static int
680ufs_close(struct open_file *f)
681{
682	struct file *fp = (struct file *)f->f_fsdata;
683	int level;
684
685	f->f_fsdata = (void *)0;
686	if (fp == (struct file *)0)
687		return (0);
688
689	for (level = 0; level < NIADDR; level++) {
690		if (fp->f_blk[level])
691			free(fp->f_blk[level]);
692	}
693	if (fp->f_buf)
694		free(fp->f_buf);
695	free(fp->f_fs);
696	free(fp);
697	return (0);
698}
699
700/*
701 * Copy a portion of a file into kernel memory.
702 * Cross block boundaries when necessary.
703 */
704static int
705ufs_read(struct open_file *f, void *start, size_t size, size_t *resid)
706{
707	struct file *fp = (struct file *)f->f_fsdata;
708	size_t csize;
709	char *buf;
710	size_t buf_size;
711	int rc = 0;
712	char *addr = start;
713
714	while (size != 0) {
715		if (fp->f_seekp >= DIP(fp, di_size))
716			break;
717
718		rc = buf_read_file(f, &buf, &buf_size);
719		if (rc)
720			break;
721
722		csize = size;
723		if (csize > buf_size)
724			csize = buf_size;
725
726		bcopy(buf, addr, csize);
727
728		fp->f_seekp += csize;
729		addr += csize;
730		size -= csize;
731	}
732	if (resid)
733		*resid = size;
734	return (rc);
735}
736
737/*
738 * Write to a portion of an already allocated file.
739 * Cross block boundaries when necessary. Can not
740 * extend the file.
741 */
742static int
743ufs_write(struct open_file *f, const void *start, size_t size, size_t *resid)
744{
745	struct file *fp = (struct file *)f->f_fsdata;
746	size_t csize;
747	int rc = 0;
748	const char *addr = start;
749
750	csize = size;
751	while ((size != 0) && (csize != 0)) {
752		if (fp->f_seekp >= DIP(fp, di_size))
753			break;
754
755		if (csize >= 512) csize = 512; /* XXX */
756
757		rc = buf_write_file(f, addr, &csize);
758		if (rc)
759			break;
760
761		fp->f_seekp += csize;
762		addr += csize;
763		size -= csize;
764	}
765	if (resid)
766		*resid = size;
767	return (rc);
768}
769
770static off_t
771ufs_seek(struct open_file *f, off_t offset, int where)
772{
773	struct file *fp = (struct file *)f->f_fsdata;
774
775	switch (where) {
776	case SEEK_SET:
777		fp->f_seekp = offset;
778		break;
779	case SEEK_CUR:
780		fp->f_seekp += offset;
781		break;
782	case SEEK_END:
783		fp->f_seekp = DIP(fp, di_size) - offset;
784		break;
785	default:
786		errno = EINVAL;
787		return (-1);
788	}
789	return (fp->f_seekp);
790}
791
792static int
793ufs_stat(struct open_file *f, struct stat *sb)
794{
795	struct file *fp = (struct file *)f->f_fsdata;
796
797	/* only important stuff */
798	sb->st_mode = DIP(fp, di_mode);
799	sb->st_uid = DIP(fp, di_uid);
800	sb->st_gid = DIP(fp, di_gid);
801	sb->st_size = DIP(fp, di_size);
802	return (0);
803}
804
805static int
806ufs_readdir(struct open_file *f, struct dirent *d)
807{
808	struct file *fp = (struct file *)f->f_fsdata;
809	struct direct *dp;
810	char *buf;
811	size_t buf_size;
812	int error;
813
814	/*
815	 * assume that a directory entry will not be split across blocks
816	 */
817again:
818	if (fp->f_seekp >= DIP(fp, di_size))
819		return (ENOENT);
820	error = buf_read_file(f, &buf, &buf_size);
821	if (error)
822		return (error);
823	dp = (struct direct *)buf;
824	fp->f_seekp += dp->d_reclen;
825	if (dp->d_ino == (ino_t)0)
826		goto again;
827
828	d->d_type = 0;		/* illumos ufs does not have type in direct */
829	strcpy(d->d_name, dp->d_name);
830	return (0);
831}
832