159765Sjlemon/*-
259765Sjlemon * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@freebsd.org>
359765Sjlemon * All rights reserved.
459765Sjlemon *
559765Sjlemon * Redistribution and use in source and binary forms, with or without
659765Sjlemon * modification, are permitted provided that the following conditions
759765Sjlemon * are met:
859765Sjlemon * 1. Redistributions of source code must retain the above copyright
959765Sjlemon *    notice, this list of conditions and the following disclaimer.
1059765Sjlemon * 2. Redistributions in binary form must reproduce the above copyright
1159765Sjlemon *    notice, this list of conditions and the following disclaimer in the
1259765Sjlemon *    documentation and/or other materials provided with the distribution.
1359765Sjlemon *
1459765Sjlemon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1559765Sjlemon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1659765Sjlemon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1759765Sjlemon * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1859765Sjlemon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1959765Sjlemon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2059765Sjlemon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2159765Sjlemon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2259765Sjlemon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2359765Sjlemon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2459765Sjlemon * SUCH DAMAGE.
2559765Sjlemon */
2684221Sdillon
2784221Sdillon#include <sys/cdefs.h>
2884221Sdillon__FBSDID("$FreeBSD: stable/11/stand/libsa/ext2fs.c 332154 2018-04-06 21:37:25Z kevans $");
2984221Sdillon
3059765Sjlemon/*-
3159765Sjlemon * Copyright (c) 1993
3259765Sjlemon *	The Regents of the University of California.  All rights reserved.
3359765Sjlemon *
3459765Sjlemon * This code is derived from software contributed to Berkeley by
3559765Sjlemon * The Mach Operating System project at Carnegie-Mellon University.
3659765Sjlemon *
3759765Sjlemon * Redistribution and use in source and binary forms, with or without
3859765Sjlemon * modification, are permitted provided that the following conditions
3959765Sjlemon * are met:
4059765Sjlemon * 1. Redistributions of source code must retain the above copyright
4159765Sjlemon *    notice, this list of conditions and the following disclaimer.
4259765Sjlemon * 2. Redistributions in binary form must reproduce the above copyright
4359765Sjlemon *    notice, this list of conditions and the following disclaimer in the
4459765Sjlemon *    documentation and/or other materials provided with the distribution.
4559765Sjlemon * 3. All advertising materials mentioning features or use of this software
4659765Sjlemon *    must display the following acknowledgement:
4759765Sjlemon *	This product includes software developed by the University of
4859765Sjlemon *	California, Berkeley and its contributors.
4959765Sjlemon * 4. Neither the name of the University nor the names of its contributors
5059765Sjlemon *    may be used to endorse or promote products derived from this software
5159765Sjlemon *    without specific prior written permission.
5259765Sjlemon *
5359765Sjlemon * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5459765Sjlemon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5559765Sjlemon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5659765Sjlemon * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
5759765Sjlemon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
5859765Sjlemon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
5959765Sjlemon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6059765Sjlemon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6159765Sjlemon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6259765Sjlemon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6359765Sjlemon * SUCH DAMAGE.
6459765Sjlemon *
6559765Sjlemon *
6659765Sjlemon * Copyright (c) 1990, 1991 Carnegie Mellon University
6759765Sjlemon * All Rights Reserved.
6859765Sjlemon *
6959765Sjlemon * Author: David Golub
7059765Sjlemon *
7159765Sjlemon * Permission to use, copy, modify and distribute this software and its
7259765Sjlemon * documentation is hereby granted, provided that both the copyright
7359765Sjlemon * notice and this permission notice appear in all copies of the
7459765Sjlemon * software, derivative works or modified versions, and any portions
7559765Sjlemon * thereof, and that both notices appear in supporting documentation.
7659765Sjlemon *
7759765Sjlemon * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
7859765Sjlemon * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
7959765Sjlemon * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
8059765Sjlemon *
8159765Sjlemon * Carnegie Mellon requests users of this software to return to
8259765Sjlemon *
8359765Sjlemon *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
8459765Sjlemon *  School of Computer Science
8559765Sjlemon *  Carnegie Mellon University
8659765Sjlemon *  Pittsburgh PA 15213-3890
8759765Sjlemon *
8859765Sjlemon * any improvements or extensions that they make and grant Carnegie the
8959765Sjlemon * rights to redistribute these changes.
9059765Sjlemon */
9159765Sjlemon
9259765Sjlemon#include <sys/param.h>
9359765Sjlemon#include <sys/time.h>
9459765Sjlemon#include "stand.h"
9559765Sjlemon#include "string.h"
9659765Sjlemon
9759765Sjlemonstatic int	ext2fs_open(const char *path, struct open_file *f);
9859765Sjlemonstatic int	ext2fs_close(struct open_file *f);
9959765Sjlemonstatic int	ext2fs_read(struct open_file *f, void *buf,
10059765Sjlemon			 size_t size, size_t *resid);
10159765Sjlemonstatic off_t	ext2fs_seek(struct open_file *f, off_t offset, int where);
10259765Sjlemonstatic int	ext2fs_stat(struct open_file *f, struct stat *sb);
10359765Sjlemonstatic int	ext2fs_readdir(struct open_file *f, struct dirent *d);
10459765Sjlemon
10559765Sjlemonstatic int dtmap[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR,
10659765Sjlemon			 DT_BLK, DT_FIFO, DT_SOCK, DT_LNK };
10759765Sjlemon#define EXTFTODT(x)	(x) > sizeof(dtmap) / sizeof(dtmap[0]) ? \
10859765Sjlemon			DT_UNKNOWN : dtmap[x]
10959765Sjlemon
11059765Sjlemonstruct fs_ops ext2fs_fsops = {
11159765Sjlemon	"ext2fs",
11259765Sjlemon	ext2fs_open,
11359765Sjlemon	ext2fs_close,
11459765Sjlemon	ext2fs_read,
11559765Sjlemon	null_write,
11659765Sjlemon	ext2fs_seek,
11759765Sjlemon	ext2fs_stat,
11859765Sjlemon	ext2fs_readdir
11959765Sjlemon};
12059765Sjlemon
12159765Sjlemon#define	EXT2_SBSIZE	1024
12259765Sjlemon#define	EXT2_SBLOCK	(1024 / DEV_BSIZE)	/* block offset of superblock */
12359765Sjlemon#define EXT2_MAGIC	0xef53
12459765Sjlemon#define EXT2_ROOTINO	2
12559765Sjlemon
12659765Sjlemon#define EXT2_REV0		0	/* original revision of ext2 */
12759765Sjlemon#define EXT2_R0_ISIZE		128	/* inode size */
12859765Sjlemon#define EXT2_R0_FIRSTINO	11	/* first inode */
12959765Sjlemon
13059765Sjlemon#define EXT2_MINBSHIFT		10	/* mininum block shift */
13159765Sjlemon#define EXT2_MINFSHIFT		10	/* mininum frag shift */
13259765Sjlemon
13359765Sjlemon#define NDADDR		12		/* # of direct blocks */
13459765Sjlemon#define NIADDR		3		/* # of indirect blocks */
13559765Sjlemon
13659765Sjlemon/*
13759765Sjlemon * file system block to disk address
13859765Sjlemon */
13959765Sjlemon#define fsb_to_db(fs, blk)	((blk) << (fs)->fs_fsbtodb)
14059765Sjlemon
14159765Sjlemon/*
14259765Sjlemon * inode to block group offset
14359765Sjlemon * inode to block group
14459765Sjlemon * inode to disk address
14559765Sjlemon * inode to block offset
14659765Sjlemon */
14759765Sjlemon#define ino_to_bgo(fs, ino)	(((ino) - 1) % (fs)->fs_ipg)
14859765Sjlemon#define ino_to_bg(fs, ino)	(((ino) - 1) / (fs)->fs_ipg)
14959765Sjlemon#define ino_to_db(fs, bg, ino) \
15059765Sjlemon	fsb_to_db(fs, ((bg)[ino_to_bg(fs, ino)].bg_inotbl + \
15159765Sjlemon	    ino_to_bgo(fs, ino) / (fs)->fs_ipb))
15259765Sjlemon#define ino_to_bo(fs, ino)	(ino_to_bgo(fs, ino) % (fs)->fs_ipb)
15359765Sjlemon
15459765Sjlemon#define nindir(fs) \
155332154Skevans	((fs)->fs_bsize / sizeof(uint32_t))
15659765Sjlemon#define lblkno(fs, loc)				/* loc / bsize */ \
15759765Sjlemon	((loc) >> (fs)->fs_bshift)
15859765Sjlemon#define smalllblktosize(fs, blk)		/* blk * bsize */ \
15959765Sjlemon	((blk) << (fs)->fs_bshift)
16059765Sjlemon#define blkoff(fs, loc)				/* loc % bsize */ \
16159765Sjlemon	((loc) & (fs)->fs_bmask)
16259765Sjlemon#define fragroundup(fs, size)			/* roundup(size, fsize) */ \
16359765Sjlemon	(((size) + (fs)->fs_fmask) & ~(fs)->fs_fmask)
16459765Sjlemon#define dblksize(fs, dip, lbn) \
16559765Sjlemon	(((lbn) >= NDADDR || (dip)->di_size >= smalllblktosize(fs, (lbn) + 1)) \
16659765Sjlemon	    ? (fs)->fs_bsize \
16759765Sjlemon	    : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
16859765Sjlemon
16959765Sjlemon/*
17059765Sjlemon * superblock describing ext2fs
17159765Sjlemon */
17259765Sjlemonstruct ext2fs_disk {
173332154Skevans	uint32_t	fd_inodes;	/* # of inodes */
174332154Skevans	uint32_t	fd_blocks;	/* # of blocks */
175332154Skevans	uint32_t	fd_resblk;	/* # of reserved blocks */
176332154Skevans	uint32_t	fd_freeblk;	/* # of free blocks */
177332154Skevans	uint32_t	fd_freeino;	/* # of free inodes */
178332154Skevans	uint32_t	fd_firstblk;	/* first data block */
179332154Skevans	uint32_t	fd_bsize;	/* block size */
180332154Skevans	uint32_t	fd_fsize;	/* frag size */
181332154Skevans	uint32_t	fd_bpg;		/* blocks per group */
182332154Skevans	uint32_t	fd_fpg;		/* frags per group */
183332154Skevans	uint32_t	fd_ipg;		/* inodes per group */
184332154Skevans	uint32_t	fd_mtime;	/* mount time */
185332154Skevans	uint32_t	fd_wtime;	/* write time */
186332154Skevans	uint16_t	fd_mount;	/* # of mounts */
18759765Sjlemon	int16_t		fd_maxmount;	/* max # of mounts */
188332154Skevans	uint16_t	fd_magic;	/* magic number */
189332154Skevans	uint16_t	fd_state;	/* state */
190332154Skevans	uint16_t	fd_eflag;	/* error flags */
191332154Skevans	uint16_t	fd_mnrrev;	/* minor revision */
192332154Skevans	uint32_t	fd_lastchk;	/* last check */
193332154Skevans	uint32_t	fd_chkintvl;	/* maximum check interval */
194332154Skevans	uint32_t	fd_os;		/* os */
195332154Skevans	uint32_t	fd_revision;	/* revision */
196332154Skevans	uint16_t	fd_uid;		/* uid for reserved blocks */
197332154Skevans	uint16_t	fd_gid;		/* gid for reserved blocks */
19859765Sjlemon
199332154Skevans	uint32_t	fd_firstino;	/* first non-reserved inode */
200332154Skevans	uint16_t	fd_isize;	/* inode size */
201332154Skevans	uint16_t	fd_nblkgrp;	/* block group # of superblock */
202332154Skevans	uint32_t	fd_fcompat;	/* compatible features */
203332154Skevans	uint32_t	fd_fincompat;	/* incompatible features */
204332154Skevans	uint32_t	fd_frocompat;	/* read-only compatibilties */
205332154Skevans	uint8_t		fd_uuid[16];	/* volume uuid */
20659765Sjlemon	char 		fd_volname[16];	/* volume name */
20759765Sjlemon	char 		fd_fsmnt[64];	/* name last mounted on */
208332154Skevans	uint32_t	fd_bitmap;	/* compression bitmap */
20959765Sjlemon
210332154Skevans	uint8_t		fd_nblkpa;	/* # of blocks to preallocate */
211332154Skevans	uint8_t		fd_ndblkpa;	/* # of dir blocks to preallocate */
21259765Sjlemon};
21359765Sjlemon
21459765Sjlemonstruct ext2fs_core {
21559765Sjlemon	int		fc_bsize;	/* block size */
21659765Sjlemon	int		fc_bshift;	/* block shift amount */
21759765Sjlemon	int		fc_bmask;	/* block mask */
21859765Sjlemon	int		fc_fsize;	/* frag size */
21959765Sjlemon	int		fc_fshift;	/* frag shift amount */
22059765Sjlemon	int		fc_fmask;	/* frag mask */
22159765Sjlemon	int		fc_isize;	/* inode size */
22259765Sjlemon	int		fc_imask;	/* inode mask */
22359765Sjlemon	int		fc_firstino;	/* first non-reserved inode */
22459765Sjlemon	int		fc_ipb;		/* inodes per block */
22559765Sjlemon	int		fc_fsbtodb;	/* fsb to ds shift */
22659765Sjlemon};
22759765Sjlemon
22859765Sjlemonstruct ext2fs {
22959765Sjlemon	struct		ext2fs_disk fs_fd;
23059765Sjlemon	char		fs_pad[EXT2_SBSIZE - sizeof(struct ext2fs_disk)];
23159765Sjlemon	struct		ext2fs_core fs_fc;
23259765Sjlemon
23359765Sjlemon#define fs_magic	fs_fd.fd_magic
23459765Sjlemon#define fs_revision	fs_fd.fd_revision
23559765Sjlemon#define fs_blocks	fs_fd.fd_blocks
23659765Sjlemon#define fs_firstblk	fs_fd.fd_firstblk
23759765Sjlemon#define fs_bpg		fs_fd.fd_bpg
23859765Sjlemon#define fs_ipg		fs_fd.fd_ipg
23959765Sjlemon
24059765Sjlemon#define fs_bsize	fs_fc.fc_bsize
24159765Sjlemon#define fs_bshift	fs_fc.fc_bshift
24259765Sjlemon#define fs_bmask	fs_fc.fc_bmask
24359765Sjlemon#define fs_fsize	fs_fc.fc_fsize
24459765Sjlemon#define fs_fshift	fs_fc.fc_fshift
24559765Sjlemon#define fs_fmask	fs_fc.fc_fmask
24659765Sjlemon#define fs_isize	fs_fc.fc_isize
24759765Sjlemon#define fs_imask	fs_fc.fc_imask
24859765Sjlemon#define fs_firstino	fs_fc.fc_firstino
24959765Sjlemon#define fs_ipb		fs_fc.fc_ipb
25059765Sjlemon#define fs_fsbtodb	fs_fc.fc_fsbtodb
25159765Sjlemon};
25259765Sjlemon
25359765Sjlemonstruct ext2blkgrp {
254332154Skevans	uint32_t	bg_blkmap;	/* block bitmap */
255332154Skevans	uint32_t	bg_inomap;	/* inode bitmap */
256332154Skevans	uint32_t	bg_inotbl;	/* inode table */
257332154Skevans	uint16_t	bg_nfblk;	/* # of free blocks */
258332154Skevans	uint16_t	bg_nfino;	/* # of free inodes */
259332154Skevans	uint16_t	bg_ndirs;	/* # of dirs */
26059765Sjlemon	char		bg_pad[14];
26159765Sjlemon};
26259765Sjlemon
26359765Sjlemonstruct ext2dinode {
264332154Skevans	uint16_t	di_mode;	/* mode */
265332154Skevans	uint16_t	di_uid;		/* uid */
266332154Skevans	uint32_t	di_size;	/* byte size */
267332154Skevans	uint32_t	di_atime;	/* access time */
268332154Skevans	uint32_t	di_ctime;	/* creation time */
269332154Skevans	uint32_t	di_mtime;	/* modification time */
270332154Skevans	uint32_t	di_dtime;	/* deletion time */
271332154Skevans	uint16_t	di_gid;		/* gid */
272332154Skevans	uint16_t	di_nlink;	/* link count */
273332154Skevans	uint32_t	di_nblk;	/* block count */
274332154Skevans	uint32_t	di_flags;	/* file flags */
27559765Sjlemon
276332154Skevans	uint32_t	di_osdep1;	/* os dependent stuff */
27759765Sjlemon
278332154Skevans	uint32_t	di_db[NDADDR];	/* direct blocks */
279332154Skevans	uint32_t	di_ib[NIADDR];	/* indirect blocks */
280332154Skevans	uint32_t	di_version;	/* version */
281332154Skevans	uint32_t	di_facl;	/* file acl */
282332154Skevans	uint32_t	di_dacl;	/* dir acl */
283332154Skevans	uint32_t	di_faddr;	/* fragment addr */
28459765Sjlemon
285332154Skevans	uint8_t		di_frag;	/* fragment number */
286332154Skevans	uint8_t		di_fsize;	/* fragment size */
28759765Sjlemon
28859765Sjlemon	char		di_pad[10];
28959765Sjlemon
29059765Sjlemon#define di_shortlink	di_db
29159765Sjlemon};
29259765Sjlemon
29359765Sjlemon#define EXT2_MAXNAMLEN       255
29459765Sjlemon
29559765Sjlemonstruct ext2dirent {
296332154Skevans	uint32_t	d_ino;		/* inode */
297332154Skevans	uint16_t	d_reclen;	/* directory entry length */
298332154Skevans	uint8_t		d_namlen;	/* name length */
299332154Skevans	uint8_t		d_type;		/* file type */
30059765Sjlemon	char		d_name[EXT2_MAXNAMLEN];
30159765Sjlemon};
30259765Sjlemon
30359765Sjlemonstruct file {
30459765Sjlemon	off_t		f_seekp;		/* seek pointer */
30559765Sjlemon	struct 		ext2fs *f_fs;		/* pointer to super-block */
30659765Sjlemon	struct 		ext2blkgrp *f_bg;	/* pointer to blkgrp map */
30759765Sjlemon	struct 		ext2dinode f_di;	/* copy of on-disk inode */
30859765Sjlemon	int		f_nindir[NIADDR];	/* number of blocks mapped by
30959765Sjlemon						   indirect block at level i */
31059765Sjlemon	char		*f_blk[NIADDR];		/* buffer for indirect block
31159765Sjlemon						   at level i */
31259765Sjlemon	size_t		f_blksize[NIADDR];	/* size of buffer */
31359765Sjlemon	daddr_t		f_blkno[NIADDR];	/* disk address of block in
31459765Sjlemon						   buffer */
31559765Sjlemon	char		*f_buf;			/* buffer for data block */
31659765Sjlemon	size_t		f_buf_size;		/* size of data block */
31759765Sjlemon	daddr_t		f_buf_blkno;		/* block number of data block */
31859765Sjlemon};
31959765Sjlemon
32059765Sjlemon/* forward decls */
32159765Sjlemonstatic int 	read_inode(ino_t inumber, struct open_file *f);
32259765Sjlemonstatic int	block_map(struct open_file *f, daddr_t file_block,
32359765Sjlemon		    daddr_t *disk_block_p);
32459765Sjlemonstatic int	buf_read_file(struct open_file *f, char **buf_p,
32559765Sjlemon		    size_t *size_p);
32659765Sjlemonstatic int	search_directory(char *name, struct open_file *f,
32759765Sjlemon		    ino_t *inumber_p);
32859765Sjlemon
32959765Sjlemon/*
33059765Sjlemon * Open a file.
33159765Sjlemon */
33259765Sjlemonstatic int
33359765Sjlemonext2fs_open(const char *upath, struct open_file *f)
33459765Sjlemon{
33559765Sjlemon	struct file *fp;
33659765Sjlemon	struct ext2fs *fs;
33759765Sjlemon	size_t buf_size;
33859765Sjlemon	ino_t inumber, parent_inumber;
33959765Sjlemon	int i, len, groups, bg_per_blk, blkgrps, mult;
34059765Sjlemon	int nlinks = 0;
34159765Sjlemon	int error = 0;
34259765Sjlemon	char *cp, *ncp, *path = NULL, *buf = NULL;
34359765Sjlemon	char namebuf[MAXPATHLEN+1];
34459765Sjlemon	char c;
34559765Sjlemon
34659765Sjlemon	/* allocate file system specific data structure */
34759765Sjlemon	fp = malloc(sizeof(struct file));
34859765Sjlemon	if (fp == NULL)
34959765Sjlemon		return (ENOMEM);
35059765Sjlemon	bzero(fp, sizeof(struct file));
35159765Sjlemon	f->f_fsdata = (void *)fp;
35259765Sjlemon
35359765Sjlemon	/* allocate space and read super block */
35459765Sjlemon	fs = (struct ext2fs *)malloc(sizeof(*fs));
35559765Sjlemon	fp->f_fs = fs;
356276079Sian	twiddle(1);
35759765Sjlemon	error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
358313355Stsoome	    EXT2_SBLOCK, EXT2_SBSIZE, (char *)fs, &buf_size);
35959765Sjlemon	if (error)
36059765Sjlemon		goto out;
36159765Sjlemon
36259765Sjlemon	if (buf_size != EXT2_SBSIZE || fs->fs_magic != EXT2_MAGIC) {
36359765Sjlemon		error = EINVAL;
36459765Sjlemon		goto out;
36559765Sjlemon	}
36659765Sjlemon
36759765Sjlemon	/*
36859765Sjlemon	 * compute in-core values for the superblock
36959765Sjlemon	 */
37059765Sjlemon	fs->fs_bshift = EXT2_MINBSHIFT + fs->fs_fd.fd_bsize;
37159765Sjlemon	fs->fs_bsize = 1 << fs->fs_bshift;
37259765Sjlemon	fs->fs_bmask = fs->fs_bsize - 1;
37359765Sjlemon
37459765Sjlemon	fs->fs_fshift = EXT2_MINFSHIFT + fs->fs_fd.fd_fsize;
37559765Sjlemon	fs->fs_fsize = 1 << fs->fs_fshift;
37659765Sjlemon	fs->fs_fmask = fs->fs_fsize - 1;
37759765Sjlemon
37859765Sjlemon	if (fs->fs_revision == EXT2_REV0) {
37959765Sjlemon		fs->fs_isize = EXT2_R0_ISIZE;
38059765Sjlemon		fs->fs_firstino = EXT2_R0_FIRSTINO;
38159765Sjlemon	} else {
38259765Sjlemon		fs->fs_isize = fs->fs_fd.fd_isize;
38359765Sjlemon		fs->fs_firstino = fs->fs_fd.fd_firstino;
38459765Sjlemon	}
38559765Sjlemon	fs->fs_imask = fs->fs_isize - 1;
38659765Sjlemon	fs->fs_ipb = fs->fs_bsize / fs->fs_isize;
38759765Sjlemon	fs->fs_fsbtodb = (fs->fs_bsize / DEV_BSIZE) - 1;
38859765Sjlemon
38959765Sjlemon	/*
39059765Sjlemon	 * we have to load in the "group descriptors" here
39159765Sjlemon	 */
39259765Sjlemon	groups = howmany(fs->fs_blocks - fs->fs_firstblk, fs->fs_bpg);
39359765Sjlemon	bg_per_blk = fs->fs_bsize / sizeof(struct ext2blkgrp);
39459765Sjlemon	blkgrps = howmany(groups, bg_per_blk);
39559765Sjlemon	len = blkgrps * fs->fs_bsize;
39659765Sjlemon
39759765Sjlemon	fp->f_bg = malloc(len);
398276079Sian	twiddle(1);
39959765Sjlemon	error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
400313355Stsoome	    EXT2_SBLOCK + EXT2_SBSIZE / DEV_BSIZE, len,
40159765Sjlemon	    (char *)fp->f_bg, &buf_size);
40259765Sjlemon	if (error)
40359765Sjlemon		goto out;
40459765Sjlemon
40559765Sjlemon	/*
40659765Sjlemon	 * XXX
40759765Sjlemon	 * validation of values?  (blocksize, descriptors, etc?)
40859765Sjlemon	 */
40959765Sjlemon
41059765Sjlemon	/*
41159765Sjlemon	 * Calculate indirect block levels.
41259765Sjlemon	 */
41359765Sjlemon	mult = 1;
41459765Sjlemon	for (i = 0; i < NIADDR; i++) {
41559765Sjlemon		mult *= nindir(fs);
41659765Sjlemon		fp->f_nindir[i] = mult;
41759765Sjlemon	}
41859765Sjlemon
41959765Sjlemon	inumber = EXT2_ROOTINO;
42059765Sjlemon	if ((error = read_inode(inumber, f)) != 0)
42159765Sjlemon		goto out;
42259765Sjlemon
42359765Sjlemon	path = strdup(upath);
42459765Sjlemon	if (path == NULL) {
42559765Sjlemon		error = ENOMEM;
42659765Sjlemon		goto out;
42759765Sjlemon	}
42859765Sjlemon	cp = path;
42959765Sjlemon	while (*cp) {
43059765Sjlemon		/*
43159765Sjlemon		 * Remove extra separators
43259765Sjlemon		 */
43359765Sjlemon		while (*cp == '/')
43459765Sjlemon			cp++;
43559765Sjlemon		if (*cp == '\0')
43659765Sjlemon			break;
43759765Sjlemon
43859765Sjlemon		/*
43959765Sjlemon		 * Check that current node is a directory.
44059765Sjlemon		 */
44159765Sjlemon		if (! S_ISDIR(fp->f_di.di_mode)) {
44259765Sjlemon			error = ENOTDIR;
44359765Sjlemon			goto out;
44459765Sjlemon		}
44559765Sjlemon
44659765Sjlemon		/*
44759765Sjlemon		 * Get next component of path name.
44859765Sjlemon		 */
44959765Sjlemon		len = 0;
45059765Sjlemon
45159765Sjlemon		ncp = cp;
45259765Sjlemon		while ((c = *cp) != '\0' && c != '/') {
45359765Sjlemon			if (++len > EXT2_MAXNAMLEN) {
45459765Sjlemon				error = ENOENT;
45559765Sjlemon				goto out;
45659765Sjlemon			}
45759765Sjlemon			cp++;
45859765Sjlemon		}
45959765Sjlemon		*cp = '\0';
46059765Sjlemon
46159765Sjlemon		/*
46259765Sjlemon		 * Look up component in current directory.
46359765Sjlemon		 * Save directory inumber in case we find a
46459765Sjlemon		 * symbolic link.
46559765Sjlemon		 */
46659765Sjlemon		parent_inumber = inumber;
46759765Sjlemon		error = search_directory(ncp, f, &inumber);
46859765Sjlemon		*cp = c;
46959765Sjlemon		if (error)
47059765Sjlemon			goto out;
47159765Sjlemon
47259765Sjlemon		/*
47359765Sjlemon		 * Open next component.
47459765Sjlemon		 */
47559765Sjlemon		if ((error = read_inode(inumber, f)) != 0)
47659765Sjlemon			goto out;
47759765Sjlemon
47859765Sjlemon		/*
47959765Sjlemon		 * Check for symbolic link.
48059765Sjlemon		 */
48159765Sjlemon		if (S_ISLNK(fp->f_di.di_mode)) {
48259765Sjlemon			int link_len = fp->f_di.di_size;
48359765Sjlemon			int len;
48459765Sjlemon
48559765Sjlemon			len = strlen(cp);
48659765Sjlemon			if (link_len + len > MAXPATHLEN ||
48759765Sjlemon			    ++nlinks > MAXSYMLINKS) {
48859765Sjlemon				error = ENOENT;
48959765Sjlemon				goto out;
49059765Sjlemon			}
49159765Sjlemon
49259765Sjlemon			bcopy(cp, &namebuf[link_len], len + 1);
49359765Sjlemon			if (fp->f_di.di_nblk == 0) {
49459765Sjlemon				bcopy(fp->f_di.di_shortlink,
49559765Sjlemon				    namebuf, link_len);
49659765Sjlemon			} else {
49759765Sjlemon				/*
49859765Sjlemon				 * Read file for symbolic link
49959765Sjlemon				 */
50059765Sjlemon				struct ext2fs *fs = fp->f_fs;
50159765Sjlemon				daddr_t	disk_block;
50259765Sjlemon				size_t buf_size;
50359765Sjlemon
50459765Sjlemon				if (! buf)
50559765Sjlemon					buf = malloc(fs->fs_bsize);
50659765Sjlemon				error = block_map(f, (daddr_t)0, &disk_block);
50759765Sjlemon				if (error)
50859765Sjlemon					goto out;
50959765Sjlemon
510276079Sian				twiddle(1);
51159765Sjlemon				error = (f->f_dev->dv_strategy)(f->f_devdata,
512313355Stsoome				    F_READ, fsb_to_db(fs, disk_block),
51359765Sjlemon				    fs->fs_bsize, buf, &buf_size);
51459765Sjlemon				if (error)
51559765Sjlemon					goto out;
51659765Sjlemon
51759765Sjlemon				bcopy((char *)buf, namebuf, link_len);
51859765Sjlemon			}
51959765Sjlemon
52059765Sjlemon			/*
52159765Sjlemon			 * If relative pathname, restart at parent directory.
52259765Sjlemon			 * If absolute pathname, restart at root.
52359765Sjlemon			 */
52459765Sjlemon			cp = namebuf;
52559765Sjlemon			if (*cp != '/')
52659765Sjlemon				inumber = parent_inumber;
52759765Sjlemon			else
52859765Sjlemon				inumber = (ino_t)EXT2_ROOTINO;
52959765Sjlemon
53059765Sjlemon			if ((error = read_inode(inumber, f)) != 0)
53159765Sjlemon				goto out;
53259765Sjlemon		}
53359765Sjlemon	}
53459765Sjlemon
53559765Sjlemon	/*
53659765Sjlemon	 * Found terminal component.
53759765Sjlemon	 */
53859765Sjlemon	error = 0;
539251561Spfg	fp->f_seekp = 0;
54059765Sjlemonout:
54159765Sjlemon	if (buf)
54259765Sjlemon		free(buf);
54359765Sjlemon	if (path)
54459765Sjlemon		free(path);
54559765Sjlemon	if (error) {
54659765Sjlemon		if (fp->f_buf)
54759765Sjlemon			free(fp->f_buf);
54859765Sjlemon		free(fp->f_fs);
54959765Sjlemon		free(fp);
55059765Sjlemon	}
55159765Sjlemon	return (error);
55259765Sjlemon}
55359765Sjlemon
55459765Sjlemon/*
55559765Sjlemon * Read a new inode into a file structure.
55659765Sjlemon */
55759765Sjlemonstatic int
55859765Sjlemonread_inode(ino_t inumber, struct open_file *f)
55959765Sjlemon{
56059765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
56159765Sjlemon	struct ext2fs *fs = fp->f_fs;
56259765Sjlemon	struct ext2dinode *dp;
56359765Sjlemon	char *buf;
56459765Sjlemon	size_t rsize;
56559765Sjlemon	int level, error = 0;
56659765Sjlemon
56759765Sjlemon	/*
56859765Sjlemon	 * Read inode and save it.
56959765Sjlemon	 */
57059765Sjlemon	buf = malloc(fs->fs_bsize);
571276079Sian	twiddle(1);
57259765Sjlemon	error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
573313355Stsoome	    ino_to_db(fs, fp->f_bg, inumber), fs->fs_bsize, buf, &rsize);
57459765Sjlemon	if (error)
57559765Sjlemon		goto out;
57659765Sjlemon	if (rsize != fs->fs_bsize) {
57759765Sjlemon		error = EIO;
57859765Sjlemon		goto out;
57959765Sjlemon	}
58059765Sjlemon
58159765Sjlemon	dp = (struct ext2dinode *)buf;
58259765Sjlemon	fp->f_di = dp[ino_to_bo(fs, inumber)];
58359765Sjlemon
58459765Sjlemon	/* clear out old buffers */
58559765Sjlemon	for (level = 0; level < NIADDR; level++)
58659765Sjlemon		fp->f_blkno[level] = -1;
58759765Sjlemon	fp->f_buf_blkno = -1;
588251561Spfg	fp->f_seekp = 0;
58959765Sjlemon
59059765Sjlemonout:
59159765Sjlemon	free(buf);
59259765Sjlemon	return (error);
59359765Sjlemon}
59459765Sjlemon
59559765Sjlemon/*
59659765Sjlemon * Given an offset in a file, find the disk block number that
59759765Sjlemon * contains that block.
59859765Sjlemon */
59959765Sjlemonstatic int
60059765Sjlemonblock_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p)
60159765Sjlemon{
60259765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
60359765Sjlemon	struct ext2fs *fs = fp->f_fs;
60459765Sjlemon	daddr_t ind_block_num;
60596941Siedowse	int32_t *ind_p;
60659765Sjlemon	int idx, level;
60759765Sjlemon	int error;
60859765Sjlemon
60959765Sjlemon	/*
61059765Sjlemon	 * Index structure of an inode:
61159765Sjlemon	 *
61259765Sjlemon	 * di_db[0..NDADDR-1]	hold block numbers for blocks
61359765Sjlemon	 *			0..NDADDR-1
61459765Sjlemon	 *
61559765Sjlemon	 * di_ib[0]		index block 0 is the single indirect block
61659765Sjlemon	 *			holds block numbers for blocks
61759765Sjlemon	 *			NDADDR .. NDADDR + NINDIR(fs)-1
61859765Sjlemon	 *
61959765Sjlemon	 * di_ib[1]		index block 1 is the double indirect block
62059765Sjlemon	 *			holds block numbers for INDEX blocks for blocks
62159765Sjlemon	 *			NDADDR + NINDIR(fs) ..
62259765Sjlemon	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
62359765Sjlemon	 *
62459765Sjlemon	 * di_ib[2]		index block 2 is the triple indirect block
62559765Sjlemon	 *			holds block numbers for double-indirect
62659765Sjlemon	 *			blocks for blocks
62759765Sjlemon	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
62859765Sjlemon	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2
62959765Sjlemon	 *				+ NINDIR(fs)**3 - 1
63059765Sjlemon	 */
63159765Sjlemon
63259765Sjlemon	if (file_block < NDADDR) {
63359765Sjlemon		/* Direct block. */
63459765Sjlemon		*disk_block_p = fp->f_di.di_db[file_block];
63559765Sjlemon		return (0);
63659765Sjlemon	}
63759765Sjlemon
63859765Sjlemon	file_block -= NDADDR;
63959765Sjlemon
64059765Sjlemon	/*
64159765Sjlemon	 * nindir[0] = NINDIR
64259765Sjlemon	 * nindir[1] = NINDIR**2
64359765Sjlemon	 * nindir[2] = NINDIR**3
64459765Sjlemon	 *	etc
64559765Sjlemon	 */
64659765Sjlemon	for (level = 0; level < NIADDR; level++) {
64759765Sjlemon		if (file_block < fp->f_nindir[level])
64859765Sjlemon			break;
64959765Sjlemon		file_block -= fp->f_nindir[level];
65059765Sjlemon	}
65159765Sjlemon	if (level == NIADDR) {
65259765Sjlemon		/* Block number too high */
65359765Sjlemon		return (EFBIG);
65459765Sjlemon	}
65559765Sjlemon
65659765Sjlemon	ind_block_num = fp->f_di.di_ib[level];
65759765Sjlemon
65859765Sjlemon	for (; level >= 0; level--) {
65959765Sjlemon		if (ind_block_num == 0) {
66059765Sjlemon			*disk_block_p = 0;	/* missing */
66159765Sjlemon			return (0);
66259765Sjlemon		}
66359765Sjlemon
66459765Sjlemon		if (fp->f_blkno[level] != ind_block_num) {
66559765Sjlemon			if (fp->f_blk[level] == (char *)0)
66659765Sjlemon				fp->f_blk[level] =
66759765Sjlemon					malloc(fs->fs_bsize);
668276079Sian			twiddle(1);
66959765Sjlemon			error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
670313355Stsoome			    fsb_to_db(fp->f_fs, ind_block_num), fs->fs_bsize,
67159765Sjlemon			    fp->f_blk[level], &fp->f_blksize[level]);
67259765Sjlemon			if (error)
67359765Sjlemon				return (error);
67459765Sjlemon			if (fp->f_blksize[level] != fs->fs_bsize)
67559765Sjlemon				return (EIO);
67659765Sjlemon			fp->f_blkno[level] = ind_block_num;
67759765Sjlemon		}
67859765Sjlemon
67996941Siedowse		ind_p = (int32_t *)fp->f_blk[level];
68059765Sjlemon
68159765Sjlemon		if (level > 0) {
68259765Sjlemon			idx = file_block / fp->f_nindir[level - 1];
68359765Sjlemon			file_block %= fp->f_nindir[level - 1];
68459765Sjlemon		} else {
68559765Sjlemon			idx = file_block;
68659765Sjlemon		}
68759765Sjlemon		ind_block_num = ind_p[idx];
68859765Sjlemon	}
68959765Sjlemon
69059765Sjlemon	*disk_block_p = ind_block_num;
69159765Sjlemon
69259765Sjlemon	return (0);
69359765Sjlemon}
69459765Sjlemon
69559765Sjlemon/*
69659765Sjlemon * Read a portion of a file into an internal buffer.  Return
69759765Sjlemon * the location in the buffer and the amount in the buffer.
69859765Sjlemon */
69959765Sjlemonstatic int
70059765Sjlemonbuf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
70159765Sjlemon{
70259765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
70359765Sjlemon	struct ext2fs *fs = fp->f_fs;
70459765Sjlemon	long off;
70559765Sjlemon	daddr_t file_block;
70659765Sjlemon	daddr_t	disk_block;
70759765Sjlemon	size_t block_size;
70859765Sjlemon	int error = 0;
70959765Sjlemon
71059765Sjlemon	off = blkoff(fs, fp->f_seekp);
71159765Sjlemon	file_block = lblkno(fs, fp->f_seekp);
71259765Sjlemon	block_size = dblksize(fs, &fp->f_di, file_block);
71359765Sjlemon
71459765Sjlemon	if (file_block != fp->f_buf_blkno) {
71559765Sjlemon		error = block_map(f, file_block, &disk_block);
71659765Sjlemon		if (error)
71759765Sjlemon			goto done;
71859765Sjlemon
71959765Sjlemon		if (fp->f_buf == (char *)0)
72059765Sjlemon			fp->f_buf = malloc(fs->fs_bsize);
72159765Sjlemon
72259765Sjlemon		if (disk_block == 0) {
72359765Sjlemon			bzero(fp->f_buf, block_size);
72459765Sjlemon			fp->f_buf_size = block_size;
72559765Sjlemon		} else {
726276079Sian			twiddle(4);
72759765Sjlemon			error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
728313355Stsoome			    fsb_to_db(fs, disk_block), block_size,
72959765Sjlemon			    fp->f_buf, &fp->f_buf_size);
73059765Sjlemon			if (error)
73159765Sjlemon				goto done;
73259765Sjlemon		}
73359765Sjlemon		fp->f_buf_blkno = file_block;
73459765Sjlemon	}
73559765Sjlemon
73659765Sjlemon	/*
73759765Sjlemon	 * Return address of byte in buffer corresponding to
73859765Sjlemon	 * offset, and size of remainder of buffer after that
73959765Sjlemon	 * byte.
74059765Sjlemon	 */
74159765Sjlemon	*buf_p = fp->f_buf + off;
74259765Sjlemon	*size_p = block_size - off;
74359765Sjlemon
74459765Sjlemon	/*
74559765Sjlemon	 * But truncate buffer at end of file.
74659765Sjlemon	 */
74759765Sjlemon	if (*size_p > fp->f_di.di_size - fp->f_seekp)
74859765Sjlemon		*size_p = fp->f_di.di_size - fp->f_seekp;
74959765Sjlemondone:
75059765Sjlemon	return (error);
75159765Sjlemon}
75259765Sjlemon
75359765Sjlemon/*
75459765Sjlemon * Search a directory for a name and return its
75559765Sjlemon * i_number.
75659765Sjlemon */
75759765Sjlemonstatic int
75859765Sjlemonsearch_directory(char *name, struct open_file *f, ino_t *inumber_p)
75959765Sjlemon{
76059765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
76159765Sjlemon	struct ext2dirent *dp, *edp;
76259765Sjlemon	char *buf;
76359765Sjlemon	size_t buf_size;
76459765Sjlemon	int namlen, length;
76559765Sjlemon	int error;
76659765Sjlemon
76759765Sjlemon	length = strlen(name);
76859765Sjlemon	fp->f_seekp = 0;
76959765Sjlemon	while (fp->f_seekp < fp->f_di.di_size) {
77059765Sjlemon		error = buf_read_file(f, &buf, &buf_size);
77159765Sjlemon		if (error)
77259765Sjlemon			return (error);
77359765Sjlemon		dp = (struct ext2dirent *)buf;
77459765Sjlemon		edp = (struct ext2dirent *)(buf + buf_size);
77559765Sjlemon		while (dp < edp) {
77659765Sjlemon			if (dp->d_ino == (ino_t)0)
77759765Sjlemon				goto next;
77859765Sjlemon			namlen = dp->d_namlen;
77959765Sjlemon			if (namlen == length &&
78059765Sjlemon			    strncmp(name, dp->d_name, length) == 0) {
78159765Sjlemon				/* found entry */
78259765Sjlemon				*inumber_p = dp->d_ino;
78359765Sjlemon				return (0);
78459765Sjlemon			}
78559765Sjlemon		next:
78659765Sjlemon			dp = (struct ext2dirent *)((char *)dp + dp->d_reclen);
78759765Sjlemon		}
78859765Sjlemon		fp->f_seekp += buf_size;
78959765Sjlemon	}
79059765Sjlemon	return (ENOENT);
79159765Sjlemon}
79259765Sjlemon
79359765Sjlemonstatic int
79459765Sjlemonext2fs_close(struct open_file *f)
79559765Sjlemon{
79659765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
79759765Sjlemon	int level;
79859765Sjlemon
79959765Sjlemon	f->f_fsdata = (void *)0;
80059765Sjlemon	if (fp == (struct file *)0)
80159765Sjlemon		return (0);
80259765Sjlemon
80359765Sjlemon	for (level = 0; level < NIADDR; level++) {
80459765Sjlemon		if (fp->f_blk[level])
80559765Sjlemon			free(fp->f_blk[level]);
80659765Sjlemon	}
80759765Sjlemon	if (fp->f_buf)
80859765Sjlemon		free(fp->f_buf);
80959765Sjlemon	if (fp->f_bg)
81059765Sjlemon		free(fp->f_bg);
81159765Sjlemon	free(fp->f_fs);
81259765Sjlemon	free(fp);
81359765Sjlemon	return (0);
81459765Sjlemon}
81559765Sjlemon
81659765Sjlemonstatic int
81759765Sjlemonext2fs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
81859765Sjlemon{
81959765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
82059765Sjlemon	size_t csize, buf_size;
82159765Sjlemon	char *buf;
82259765Sjlemon	int error = 0;
82359765Sjlemon
82459765Sjlemon	while (size != 0) {
82559765Sjlemon		if (fp->f_seekp >= fp->f_di.di_size)
82659765Sjlemon			break;
82759765Sjlemon
82859765Sjlemon		error = buf_read_file(f, &buf, &buf_size);
82959765Sjlemon		if (error)
83059765Sjlemon			break;
83159765Sjlemon
83259765Sjlemon		csize = size;
83359765Sjlemon		if (csize > buf_size)
83459765Sjlemon			csize = buf_size;
83559765Sjlemon
83659765Sjlemon		bcopy(buf, addr, csize);
83759765Sjlemon
83859765Sjlemon		fp->f_seekp += csize;
839136093Sstefanf		addr = (char *)addr + csize;
84059765Sjlemon		size -= csize;
84159765Sjlemon	}
84259765Sjlemon	if (resid)
84359765Sjlemon		*resid = size;
84459765Sjlemon	return (error);
84559765Sjlemon}
84659765Sjlemon
84759765Sjlemonstatic off_t
84859765Sjlemonext2fs_seek(struct open_file *f, off_t offset, int where)
84959765Sjlemon{
85059765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
85159765Sjlemon
85259765Sjlemon	switch (where) {
85359765Sjlemon	case SEEK_SET:
85459765Sjlemon		fp->f_seekp = offset;
85559765Sjlemon		break;
85659765Sjlemon	case SEEK_CUR:
85759765Sjlemon		fp->f_seekp += offset;
85859765Sjlemon		break;
85959765Sjlemon	case SEEK_END:
86059765Sjlemon		fp->f_seekp = fp->f_di.di_size - offset;
86159765Sjlemon		break;
86259765Sjlemon	default:
863124811Sjhb		errno = EINVAL;
86459765Sjlemon		return (-1);
86559765Sjlemon	}
86659765Sjlemon	return (fp->f_seekp);
86759765Sjlemon}
86859765Sjlemon
86959765Sjlemonstatic int
87059765Sjlemonext2fs_stat(struct open_file *f, struct stat *sb)
87159765Sjlemon{
87259765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
87359765Sjlemon
87459765Sjlemon	/* only important stuff */
87559765Sjlemon	sb->st_mode = fp->f_di.di_mode;
87659765Sjlemon	sb->st_uid = fp->f_di.di_uid;
87759765Sjlemon	sb->st_gid = fp->f_di.di_gid;
87859765Sjlemon	sb->st_size = fp->f_di.di_size;
87959765Sjlemon	return (0);
88059765Sjlemon}
88159765Sjlemon
88259765Sjlemonstatic int
88359765Sjlemonext2fs_readdir(struct open_file *f, struct dirent *d)
88459765Sjlemon{
88559765Sjlemon	struct file *fp = (struct file *)f->f_fsdata;
88659765Sjlemon	struct ext2dirent *ed;
88759765Sjlemon	char *buf;
88859765Sjlemon	size_t buf_size;
88959765Sjlemon	int error;
89059765Sjlemon
89159765Sjlemon	/*
89259765Sjlemon	 * assume that a directory entry will not be split across blocks
89359765Sjlemon	 */
89459765Sjlemonagain:
89559765Sjlemon	if (fp->f_seekp >= fp->f_di.di_size)
89659765Sjlemon		return (ENOENT);
89759765Sjlemon	error = buf_read_file(f, &buf, &buf_size);
89859765Sjlemon	if (error)
89959765Sjlemon		return (error);
90059765Sjlemon	ed = (struct ext2dirent *)buf;
90159765Sjlemon	fp->f_seekp += ed->d_reclen;
90259765Sjlemon	if (ed->d_ino == (ino_t)0)
90359765Sjlemon		goto again;
90459765Sjlemon	d->d_type = EXTFTODT(ed->d_type);
90559765Sjlemon	strncpy(d->d_name, ed->d_name, ed->d_namlen);
90659765Sjlemon	d->d_name[ed->d_namlen] = '\0';
90759765Sjlemon	return (0);
90859765Sjlemon}
909