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 * 4. 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__FBSDID("$FreeBSD$");
72
73/*
74 *	Stand-alone file reading package.
75 */
76
77#include <sys/param.h>
78#include <sys/disklabel.h>
79#include <sys/time.h>
80#include <ufs/ufs/dinode.h>
81#include <ufs/ufs/dir.h>
82#include <ufs/ffs/fs.h>
83#include "stand.h"
84#include "string.h"
85
86static int	ufs_open(const char *path, struct open_file *f);
87static int	ufs_write(struct open_file *f, void *buf, size_t size, size_t *resid);
88static int	ufs_close(struct open_file *f);
89static int	ufs_read(struct open_file *f, void *buf, size_t size, size_t *resid);
90static off_t	ufs_seek(struct open_file *f, off_t offset, int where);
91static int	ufs_stat(struct open_file *f, struct stat *sb);
92static int	ufs_readdir(struct open_file *f, struct dirent *d);
93
94struct fs_ops ufs_fsops = {
95	"ufs",
96	ufs_open,
97	ufs_close,
98	ufs_read,
99	ufs_write,
100	ufs_seek,
101	ufs_stat,
102	ufs_readdir
103};
104
105/*
106 * In-core open file.
107 */
108struct file {
109	off_t		f_seekp;	/* seek pointer */
110	struct fs	*f_fs;		/* pointer to super-block */
111	union dinode {
112		struct ufs1_dinode di1;
113		struct ufs2_dinode di2;
114	}		f_di;		/* copy of on-disk inode */
115	int		f_nindir[NIADDR];
116					/* number of blocks mapped by
117					   indirect block at level i */
118	char		*f_blk[NIADDR];	/* buffer for indirect block at
119					   level i */
120	size_t		f_blksize[NIADDR];
121					/* size of buffer */
122	ufs2_daddr_t	f_blkno[NIADDR];/* disk address of block in buffer */
123	ufs2_daddr_t	f_buf_blkno;	/* block number of data block */
124	char		*f_buf;		/* buffer for data block */
125	size_t		f_buf_size;	/* size of data block */
126};
127#define DIP(fp, field) \
128	((fp)->f_fs->fs_magic == FS_UFS1_MAGIC ? \
129	(fp)->f_di.di1.field : (fp)->f_di.di2.field)
130
131static int	read_inode(ino_t, struct open_file *);
132static int	block_map(struct open_file *, ufs2_daddr_t, ufs2_daddr_t *);
133static int	buf_read_file(struct open_file *, char **, size_t *);
134static int	buf_write_file(struct open_file *, char *, size_t *);
135static int	search_directory(char *, struct open_file *, ino_t *);
136
137/*
138 * Read a new inode into a file structure.
139 */
140static int
141read_inode(inumber, f)
142	ino_t inumber;
143	struct open_file *f;
144{
145	struct file *fp = (struct file *)f->f_fsdata;
146	struct fs *fs = fp->f_fs;
147	char *buf;
148	size_t rsize;
149	int rc;
150
151	if (fs == NULL)
152	    panic("fs == NULL");
153
154	/*
155	 * Read inode and save it.
156	 */
157	buf = malloc(fs->fs_bsize);
158	twiddle();
159	rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
160		fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize,
161		buf, &rsize);
162	if (rc)
163		goto out;
164	if (rsize != fs->fs_bsize) {
165		rc = EIO;
166		goto out;
167	}
168
169	if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
170		fp->f_di.di1 = ((struct ufs1_dinode *)buf)
171		    [ino_to_fsbo(fs, inumber)];
172	else
173		fp->f_di.di2 = ((struct ufs2_dinode *)buf)
174		    [ino_to_fsbo(fs, inumber)];
175
176	/*
177	 * Clear out the old buffers
178	 */
179	{
180		int level;
181
182		for (level = 0; level < NIADDR; level++)
183			fp->f_blkno[level] = -1;
184		fp->f_buf_blkno = -1;
185	}
186	fp->f_seekp = 0;
187out:
188	free(buf);
189	return (rc);
190}
191
192/*
193 * Given an offset in a file, find the disk block number that
194 * contains that block.
195 */
196static int
197block_map(f, file_block, disk_block_p)
198	struct open_file *f;
199	ufs2_daddr_t file_block;
200	ufs2_daddr_t *disk_block_p;	/* out */
201{
202	struct file *fp = (struct file *)f->f_fsdata;
203	struct fs *fs = fp->f_fs;
204	int level;
205	int idx;
206	ufs2_daddr_t ind_block_num;
207	int rc;
208
209	/*
210	 * Index structure of an inode:
211	 *
212	 * di_db[0..NDADDR-1]	hold block numbers for blocks
213	 *			0..NDADDR-1
214	 *
215	 * di_ib[0]		index block 0 is the single indirect block
216	 *			holds block numbers for blocks
217	 *			NDADDR .. NDADDR + NINDIR(fs)-1
218	 *
219	 * di_ib[1]		index block 1 is the double indirect block
220	 *			holds block numbers for INDEX blocks for blocks
221	 *			NDADDR + NINDIR(fs) ..
222	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
223	 *
224	 * di_ib[2]		index block 2 is the triple indirect block
225	 *			holds block numbers for double-indirect
226	 *			blocks for blocks
227	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
228	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2
229	 *				+ NINDIR(fs)**3 - 1
230	 */
231
232	if (file_block < NDADDR) {
233		/* Direct block. */
234		*disk_block_p = DIP(fp, di_db[file_block]);
235		return (0);
236	}
237
238	file_block -= NDADDR;
239
240	/*
241	 * nindir[0] = NINDIR
242	 * nindir[1] = NINDIR**2
243	 * nindir[2] = NINDIR**3
244	 *	etc
245	 */
246	for (level = 0; level < NIADDR; level++) {
247		if (file_block < fp->f_nindir[level])
248			break;
249		file_block -= fp->f_nindir[level];
250	}
251	if (level == NIADDR) {
252		/* Block number too high */
253		return (EFBIG);
254	}
255
256	ind_block_num = DIP(fp, di_ib[level]);
257
258	for (; level >= 0; level--) {
259		if (ind_block_num == 0) {
260			*disk_block_p = 0;	/* missing */
261			return (0);
262		}
263
264		if (fp->f_blkno[level] != ind_block_num) {
265			if (fp->f_blk[level] == (char *)0)
266				fp->f_blk[level] =
267					malloc(fs->fs_bsize);
268			twiddle();
269			rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
270				fsbtodb(fp->f_fs, ind_block_num),
271				fs->fs_bsize,
272				fp->f_blk[level],
273				&fp->f_blksize[level]);
274			if (rc)
275				return (rc);
276			if (fp->f_blksize[level] != fs->fs_bsize)
277				return (EIO);
278			fp->f_blkno[level] = ind_block_num;
279		}
280
281		if (level > 0) {
282			idx = file_block / fp->f_nindir[level - 1];
283			file_block %= fp->f_nindir[level - 1];
284		} else
285			idx = file_block;
286
287		if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
288			ind_block_num = ((ufs1_daddr_t *)fp->f_blk[level])[idx];
289		else
290			ind_block_num = ((ufs2_daddr_t *)fp->f_blk[level])[idx];
291	}
292
293	*disk_block_p = ind_block_num;
294
295	return (0);
296}
297
298/*
299 * Write a portion of a file from an internal buffer.
300 */
301static int
302buf_write_file(f, buf_p, size_p)
303	struct open_file *f;
304	char *buf_p;
305	size_t *size_p;		/* out */
306{
307	struct file *fp = (struct file *)f->f_fsdata;
308	struct fs *fs = fp->f_fs;
309	long off;
310	ufs_lbn_t file_block;
311	ufs2_daddr_t disk_block;
312	size_t block_size;
313	int rc;
314
315	/*
316	 * Calculate the starting block address and offset.
317	 */
318	off = blkoff(fs, fp->f_seekp);
319	file_block = lblkno(fs, fp->f_seekp);
320	block_size = sblksize(fs, DIP(fp, di_size), file_block);
321
322	rc = block_map(f, file_block, &disk_block);
323	if (rc)
324		return (rc);
325
326 	if (disk_block == 0)
327		/* Because we can't allocate space on the drive */
328		return (EFBIG);
329
330	/*
331	 * Truncate buffer at end of file, and at the end of
332	 * this block.
333	 */
334	if (*size_p > DIP(fp, di_size) - fp->f_seekp)
335		*size_p = DIP(fp, di_size) - fp->f_seekp;
336	if (*size_p > block_size - off)
337		*size_p = block_size - off;
338
339	/*
340	 * If we don't entirely occlude the block and it's not
341	 * in memory already, read it in first.
342	 */
343	if (((off > 0) || (*size_p + off < block_size)) &&
344	    (file_block != fp->f_buf_blkno)) {
345
346		if (fp->f_buf == (char *)0)
347			fp->f_buf = malloc(fs->fs_bsize);
348
349		twiddle();
350		rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
351			fsbtodb(fs, disk_block),
352			block_size, fp->f_buf, &fp->f_buf_size);
353		if (rc)
354			return (rc);
355
356		fp->f_buf_blkno = file_block;
357	}
358
359	/*
360	 *	Copy the user data into the cached block.
361	 */
362	bcopy(buf_p, fp->f_buf + off, *size_p);
363
364	/*
365	 *	Write the block out to storage.
366	 */
367
368	twiddle();
369	rc = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE,
370		fsbtodb(fs, disk_block),
371		block_size, fp->f_buf, &fp->f_buf_size);
372	return (rc);
373}
374
375/*
376 * Read a portion of a file into an internal buffer.  Return
377 * the location in the buffer and the amount in the buffer.
378 */
379static int
380buf_read_file(f, buf_p, size_p)
381	struct open_file *f;
382	char **buf_p;		/* out */
383	size_t *size_p;		/* out */
384{
385	struct file *fp = (struct file *)f->f_fsdata;
386	struct fs *fs = fp->f_fs;
387	long off;
388	ufs_lbn_t file_block;
389	ufs2_daddr_t disk_block;
390	size_t block_size;
391	int rc;
392
393	off = blkoff(fs, fp->f_seekp);
394	file_block = lblkno(fs, fp->f_seekp);
395	block_size = sblksize(fs, DIP(fp, di_size), file_block);
396
397	if (file_block != fp->f_buf_blkno) {
398		if (fp->f_buf == (char *)0)
399			fp->f_buf = malloc(fs->fs_bsize);
400
401		rc = block_map(f, file_block, &disk_block);
402		if (rc)
403			return (rc);
404
405		if (disk_block == 0) {
406			bzero(fp->f_buf, block_size);
407			fp->f_buf_size = block_size;
408		} else {
409			twiddle();
410			rc = (f->f_dev->dv_strategy)(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 > DIP(fp, di_size) - fp->f_seekp)
432		*size_p = DIP(fp, di_size) - fp->f_seekp;
433
434	return (0);
435}
436
437/*
438 * Search a directory for a name and return its
439 * i_number.
440 */
441static int
442search_directory(name, f, inumber_p)
443	char *name;
444	struct open_file *f;
445	ino_t *inumber_p;		/* out */
446{
447	struct file *fp = (struct file *)f->f_fsdata;
448	struct direct *dp;
449	struct direct *edp;
450	char *buf;
451	size_t buf_size;
452	int namlen, length;
453	int rc;
454
455	length = strlen(name);
456
457	fp->f_seekp = 0;
458	while (fp->f_seekp < DIP(fp, di_size)) {
459		rc = buf_read_file(f, &buf, &buf_size);
460		if (rc)
461			return (rc);
462
463		dp = (struct direct *)buf;
464		edp = (struct direct *)(buf + buf_size);
465		while (dp < edp) {
466			if (dp->d_ino == (ino_t)0)
467				goto next;
468#if BYTE_ORDER == LITTLE_ENDIAN
469			if (fp->f_fs->fs_maxsymlinklen <= 0)
470				namlen = dp->d_type;
471			else
472#endif
473				namlen = dp->d_namlen;
474			if (namlen == length &&
475			    !strcmp(name, dp->d_name)) {
476				/* found entry */
477				*inumber_p = dp->d_ino;
478				return (0);
479			}
480		next:
481			dp = (struct direct *)((char *)dp + dp->d_reclen);
482		}
483		fp->f_seekp += buf_size;
484	}
485	return (ENOENT);
486}
487
488static int sblock_try[] = SBLOCKSEARCH;
489
490/*
491 * Open a file.
492 */
493static int
494ufs_open(upath, f)
495	const char *upath;
496	struct open_file *f;
497{
498	char *cp, *ncp;
499	int c;
500	ino_t inumber, parent_inumber;
501	struct file *fp;
502	struct fs *fs;
503	int i, rc;
504	size_t buf_size;
505	int nlinks = 0;
506	char namebuf[MAXPATHLEN+1];
507	char *buf = NULL;
508	char *path = NULL;
509
510	/* allocate file system specific data structure */
511	fp = malloc(sizeof(struct file));
512	bzero(fp, sizeof(struct file));
513	f->f_fsdata = (void *)fp;
514
515	/* allocate space and read super block */
516	fs = malloc(SBLOCKSIZE);
517	fp->f_fs = fs;
518	twiddle();
519	/*
520	 * Try reading the superblock in each of its possible locations.
521	 */
522	for (i = 0; sblock_try[i] != -1; i++) {
523		rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
524		    sblock_try[i] / DEV_BSIZE, SBLOCKSIZE,
525		    (char *)fs, &buf_size);
526		if (rc)
527			goto out;
528		if ((fs->fs_magic == FS_UFS1_MAGIC ||
529		     (fs->fs_magic == FS_UFS2_MAGIC &&
530		      fs->fs_sblockloc == sblock_try[i])) &&
531		    buf_size == SBLOCKSIZE &&
532		    fs->fs_bsize <= MAXBSIZE &&
533		    fs->fs_bsize >= sizeof(struct fs))
534			break;
535	}
536	if (sblock_try[i] == -1) {
537		rc = EINVAL;
538		goto out;
539	}
540	/*
541	 * Calculate indirect block levels.
542	 */
543	{
544		ufs2_daddr_t mult;
545		int level;
546
547		mult = 1;
548		for (level = 0; level < NIADDR; level++) {
549			mult *= NINDIR(fs);
550			fp->f_nindir[level] = mult;
551		}
552	}
553
554	inumber = ROOTINO;
555	if ((rc = read_inode(inumber, f)) != 0)
556		goto out;
557
558	cp = path = strdup(upath);
559	if (path == NULL) {
560	    rc = ENOMEM;
561	    goto out;
562	}
563	while (*cp) {
564
565		/*
566		 * Remove extra separators
567		 */
568		while (*cp == '/')
569			cp++;
570		if (*cp == '\0')
571			break;
572
573		/*
574		 * Check that current node is a directory.
575		 */
576		if ((DIP(fp, di_mode) & IFMT) != IFDIR) {
577			rc = ENOTDIR;
578			goto out;
579		}
580
581		/*
582		 * Get next component of path name.
583		 */
584		{
585			int len = 0;
586
587			ncp = cp;
588			while ((c = *cp) != '\0' && c != '/') {
589				if (++len > MAXNAMLEN) {
590					rc = ENOENT;
591					goto out;
592				}
593				cp++;
594			}
595			*cp = '\0';
596		}
597
598		/*
599		 * Look up component in current directory.
600		 * Save directory inumber in case we find a
601		 * symbolic link.
602		 */
603		parent_inumber = inumber;
604		rc = search_directory(ncp, f, &inumber);
605		*cp = c;
606		if (rc)
607			goto out;
608
609		/*
610		 * Open next component.
611		 */
612		if ((rc = read_inode(inumber, f)) != 0)
613			goto out;
614
615		/*
616		 * Check for symbolic link.
617		 */
618		if ((DIP(fp, di_mode) & IFMT) == IFLNK) {
619			int link_len = DIP(fp, di_size);
620			int len;
621
622			len = strlen(cp);
623
624			if (link_len + len > MAXPATHLEN ||
625			    ++nlinks > MAXSYMLINKS) {
626				rc = ENOENT;
627				goto out;
628			}
629
630			bcopy(cp, &namebuf[link_len], len + 1);
631
632			if (link_len < fs->fs_maxsymlinklen) {
633				if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
634					cp = (caddr_t)(fp->f_di.di1.di_db);
635				else
636					cp = (caddr_t)(fp->f_di.di2.di_db);
637				bcopy(cp, namebuf, (unsigned) link_len);
638			} else {
639				/*
640				 * Read file for symbolic link
641				 */
642				size_t buf_size;
643				ufs2_daddr_t disk_block;
644				struct fs *fs = fp->f_fs;
645
646				if (!buf)
647					buf = malloc(fs->fs_bsize);
648				rc = block_map(f, (ufs2_daddr_t)0, &disk_block);
649				if (rc)
650					goto out;
651
652				twiddle();
653				rc = (f->f_dev->dv_strategy)(f->f_devdata,
654					F_READ, fsbtodb(fs, disk_block),
655					fs->fs_bsize, buf, &buf_size);
656				if (rc)
657					goto out;
658
659				bcopy((char *)buf, namebuf, (unsigned)link_len);
660			}
661
662			/*
663			 * If relative pathname, restart at parent directory.
664			 * If absolute pathname, restart at root.
665			 */
666			cp = namebuf;
667			if (*cp != '/')
668				inumber = parent_inumber;
669			else
670				inumber = (ino_t)ROOTINO;
671
672			if ((rc = read_inode(inumber, f)) != 0)
673				goto out;
674		}
675	}
676
677	/*
678	 * Found terminal component.
679	 */
680	rc = 0;
681	fp->f_seekp = 0;
682out:
683	if (buf)
684		free(buf);
685	if (path)
686		free(path);
687	if (rc) {
688		if (fp->f_buf)
689			free(fp->f_buf);
690		free(fp->f_fs);
691		free(fp);
692	}
693	return (rc);
694}
695
696static int
697ufs_close(f)
698	struct open_file *f;
699{
700	struct file *fp = (struct file *)f->f_fsdata;
701	int level;
702
703	f->f_fsdata = (void *)0;
704	if (fp == (struct file *)0)
705		return (0);
706
707	for (level = 0; level < NIADDR; level++) {
708		if (fp->f_blk[level])
709			free(fp->f_blk[level]);
710	}
711	if (fp->f_buf)
712		free(fp->f_buf);
713	free(fp->f_fs);
714	free(fp);
715	return (0);
716}
717
718/*
719 * Copy a portion of a file into kernel memory.
720 * Cross block boundaries when necessary.
721 */
722static int
723ufs_read(f, start, size, resid)
724	struct open_file *f;
725	void *start;
726	size_t size;
727	size_t *resid;	/* out */
728{
729	struct file *fp = (struct file *)f->f_fsdata;
730	size_t csize;
731	char *buf;
732	size_t buf_size;
733	int rc = 0;
734	char *addr = start;
735
736	while (size != 0) {
737		if (fp->f_seekp >= DIP(fp, di_size))
738			break;
739
740		rc = buf_read_file(f, &buf, &buf_size);
741		if (rc)
742			break;
743
744		csize = size;
745		if (csize > buf_size)
746			csize = buf_size;
747
748		bcopy(buf, addr, csize);
749
750		fp->f_seekp += csize;
751		addr += csize;
752		size -= csize;
753	}
754	if (resid)
755		*resid = size;
756	return (rc);
757}
758
759/*
760 * Write to a portion of an already allocated file.
761 * Cross block boundaries when necessary. Can not
762 * extend the file.
763 */
764static int
765ufs_write(f, start, size, resid)
766	struct open_file *f;
767	void *start;
768	size_t size;
769	size_t *resid;	/* out */
770{
771	struct file *fp = (struct file *)f->f_fsdata;
772	size_t csize;
773	int rc = 0;
774	char *addr = start;
775
776	csize = size;
777	while ((size != 0) && (csize != 0)) {
778		if (fp->f_seekp >= DIP(fp, di_size))
779			break;
780
781		if (csize >= 512) csize = 512; /* XXX */
782
783		rc = buf_write_file(f, addr, &csize);
784		if (rc)
785			break;
786
787		fp->f_seekp += csize;
788		addr += csize;
789		size -= csize;
790	}
791	if (resid)
792		*resid = size;
793	return (rc);
794}
795
796static off_t
797ufs_seek(f, offset, where)
798	struct open_file *f;
799	off_t offset;
800	int where;
801{
802	struct file *fp = (struct file *)f->f_fsdata;
803
804	switch (where) {
805	case SEEK_SET:
806		fp->f_seekp = offset;
807		break;
808	case SEEK_CUR:
809		fp->f_seekp += offset;
810		break;
811	case SEEK_END:
812		fp->f_seekp = DIP(fp, di_size) - offset;
813		break;
814	default:
815		errno = EINVAL;
816		return (-1);
817	}
818	return (fp->f_seekp);
819}
820
821static int
822ufs_stat(f, sb)
823	struct open_file *f;
824	struct stat *sb;
825{
826	struct file *fp = (struct file *)f->f_fsdata;
827
828	/* only important stuff */
829	sb->st_mode = DIP(fp, di_mode);
830	sb->st_uid = DIP(fp, di_uid);
831	sb->st_gid = DIP(fp, di_gid);
832	sb->st_size = DIP(fp, di_size);
833	return (0);
834}
835
836static int
837ufs_readdir(struct open_file *f, struct dirent *d)
838{
839	struct file *fp = (struct file *)f->f_fsdata;
840	struct direct *dp;
841	char *buf;
842	size_t buf_size;
843	int error;
844
845	/*
846	 * assume that a directory entry will not be split across blocks
847	 */
848again:
849	if (fp->f_seekp >= DIP(fp, di_size))
850		return (ENOENT);
851	error = buf_read_file(f, &buf, &buf_size);
852	if (error)
853		return (error);
854	dp = (struct direct *)buf;
855	fp->f_seekp += dp->d_reclen;
856	if (dp->d_ino == (ino_t)0)
857		goto again;
858	d->d_type = dp->d_type;
859	strcpy(d->d_name, dp->d_name);
860	return (0);
861}
862