xref: /dragonfly/stand/lib/ext2fs.c (revision 655933d6)
1 /*-
2  * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *	$FreeBSD: src/lib/libstand/ext2fs.c,v 1.1.2.2 2001/03/05 06:26:07 kris Exp $
27  */
28 /*-
29  * Copyright (c) 1993
30  *	The Regents of the University of California.  All rights reserved.
31  *
32  * This code is derived from software contributed to Berkeley by
33  * The Mach Operating System project at Carnegie-Mellon University.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  * 3. Neither the name of the University nor the names of its contributors
44  *    may be used to endorse or promote products derived from this software
45  *    without specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57  * SUCH DAMAGE.
58  *
59  *
60  * Copyright (c) 1990, 1991 Carnegie Mellon University
61  * All Rights Reserved.
62  *
63  * Author: David Golub
64  *
65  * Permission to use, copy, modify and distribute this software and its
66  * documentation is hereby granted, provided that both the copyright
67  * notice and this permission notice appear in all copies of the
68  * software, derivative works or modified versions, and any portions
69  * thereof, and that both notices appear in supporting documentation.
70  *
71  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
72  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
73  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
74  *
75  * Carnegie Mellon requests users of this software to return to
76  *
77  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
78  *  School of Computer Science
79  *  Carnegie Mellon University
80  *  Pittsburgh PA 15213-3890
81  *
82  * any improvements or extensions that they make and grant Carnegie the
83  * rights to redistribute these changes.
84  */
85 
86 #include <sys/param.h>
87 #include <sys/time.h>
88 #include "stand.h"
89 #include "string.h"
90 
91 static int	ext2fs_open(const char *path, struct open_file *f);
92 static int	ext2fs_close(struct open_file *f);
93 static int	ext2fs_read(struct open_file *f, void *buf,
94 			 size_t size, size_t *resid);
95 static off_t	ext2fs_seek(struct open_file *f, off_t offset, int where);
96 static int	ext2fs_stat(struct open_file *f, struct stat *sb);
97 static int	ext2fs_readdir(struct open_file *f, struct dirent *d);
98 
99 static int dtmap[] = { DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR,
100 			 DT_BLK, DT_FIFO, DT_SOCK, DT_LNK };
101 #define EXTFTODT(x)	(x) > NELEM(dtmap) ? DT_UNKNOWN : dtmap[(x)]
102 
103 struct fs_ops ext2fs_fsops = {
104 	"ext2fs",
105 	ext2fs_open,
106 	ext2fs_close,
107 	ext2fs_read,
108 	null_write,
109 	ext2fs_seek,
110 	ext2fs_stat,
111 	ext2fs_readdir
112 };
113 
114 #define	EXT2_SBSIZE	1024
115 #define	EXT2_SBLOCK	(1024 / DEV_BSIZE)	/* block offset of superblock */
116 #define EXT2_MAGIC	0xef53
117 #define EXT2_ROOTINO	2
118 
119 #define EXT2_REV0		0	/* original revision of ext2 */
120 #define EXT2_R0_ISIZE		128	/* inode size */
121 #define EXT2_R0_FIRSTINO	11	/* first inode */
122 
123 #define EXT2_MINBSHIFT		10	/* mininum block shift */
124 #define EXT2_MINFSHIFT		10	/* mininum frag shift */
125 
126 #define NDADDR		12		/* # of direct blocks */
127 #define NIADDR		3		/* # of indirect blocks */
128 
129 /*
130  * file system block to disk address
131  */
132 #define fsb_to_db(fs, blk)	((blk) << (fs)->fs_fsbtodb)
133 
134 /*
135  * inode to block group offset
136  * inode to block group
137  * inode to disk address
138  * inode to block offset
139  */
140 #define ino_to_bgo(fs, ino)	(((ino) - 1) % (fs)->fs_ipg)
141 #define ino_to_bg(fs, ino)	(((ino) - 1) / (fs)->fs_ipg)
142 #define ino_to_db(fs, bg, ino) \
143 	fsb_to_db(fs, ((bg)[ino_to_bg(fs, ino)].bg_inotbl + \
144 	    ino_to_bgo(fs, ino) / (fs)->fs_ipb))
145 #define ino_to_bo(fs, ino)	(ino_to_bgo(fs, ino) % (fs)->fs_ipb)
146 
147 #define nindir(fs) \
148 	((fs)->fs_bsize / sizeof(u_int32_t))
149 #define lblkno(fs, loc)				/* loc / bsize */ \
150 	((loc) >> (fs)->fs_bshift)
151 #define smalllblktosize(fs, blk)		/* blk * bsize */ \
152 	((blk) << (fs)->fs_bshift)
153 #define blkoff(fs, loc)				/* loc % bsize */ \
154 	((loc) & (fs)->fs_bmask)
155 #define fragroundup(fs, size)			/* roundup(size, fsize) */ \
156 	(((size) + (fs)->fs_fmask) & ~(fs)->fs_fmask)
157 #define dblksize(fs, dip, lbn) \
158 	(((lbn) >= NDADDR || (dip)->di_size >= smalllblktosize(fs, (lbn) + 1)) \
159 	    ? (fs)->fs_bsize \
160 	    : (fragroundup(fs, blkoff(fs, (dip)->di_size))))
161 
162 /*
163  * superblock describing ext2fs
164  */
165 struct ext2fs_disk {
166 	u_int32_t	fd_inodes;	/* # of inodes */
167 	u_int32_t	fd_blocks;	/* # of blocks */
168 	u_int32_t	fd_resblk;	/* # of reserved blocks */
169 	u_int32_t	fd_freeblk;	/* # of free blocks */
170 	u_int32_t	fd_freeino;	/* # of free inodes */
171 	u_int32_t	fd_firstblk;	/* first data block */
172 	u_int32_t	fd_bsize;	/* block size */
173 	u_int32_t	fd_fsize;	/* frag size */
174 	u_int32_t	fd_bpg;		/* blocks per group */
175 	u_int32_t	fd_fpg;		/* frags per group */
176 	u_int32_t	fd_ipg;		/* inodes per group */
177 	u_int32_t	fd_mtime;	/* mount time */
178 	u_int32_t	fd_wtime;	/* write time */
179 	u_int16_t	fd_mount;	/* # of mounts */
180 	int16_t		fd_maxmount;	/* max # of mounts */
181 	u_int16_t	fd_magic;	/* magic number */
182 	u_int16_t	fd_state;	/* state */
183 	u_int16_t	fd_eflag;	/* error flags */
184 	u_int16_t	fd_mnrrev;	/* minor revision */
185 	u_int32_t	fd_lastchk;	/* last check */
186 	u_int32_t	fd_chkintvl;	/* maximum check interval */
187 	u_int32_t	fd_os;		/* os */
188 	u_int32_t	fd_revision;	/* revision */
189 	u_int16_t	fd_uid;		/* uid for reserved blocks */
190 	u_int16_t	fd_gid;		/* gid for reserved blocks */
191 
192 	u_int32_t	fd_firstino;	/* first non-reserved inode */
193 	u_int16_t	fd_isize;	/* inode size */
194 	u_int16_t	fd_nblkgrp;	/* block group # of superblock */
195 	u_int32_t	fd_fcompat;	/* compatible features */
196 	u_int32_t	fd_fincompat;	/* incompatible features */
197 	u_int32_t	fd_frocompat;	/* read-only compatibilties */
198 	u_int8_t	fd_uuid[16];	/* volume uuid */
199 	char 		fd_volname[16];	/* volume name */
200 	char 		fd_fsmnt[64];	/* name last mounted on */
201 	u_int32_t	fd_bitmap;	/* compression bitmap */
202 
203 	u_int8_t	fd_nblkpa;	/* # of blocks to preallocate */
204 	u_int8_t	fd_ndblkpa;	/* # of dir blocks to preallocate */
205 };
206 
207 struct ext2fs_core {
208 	int		fc_bsize;	/* block size */
209 	int		fc_bshift;	/* block shift amount */
210 	int		fc_bmask;	/* block mask */
211 	int		fc_fsize;	/* frag size */
212 	int		fc_fshift;	/* frag shift amount */
213 	int		fc_fmask;	/* frag mask */
214 	int		fc_isize;	/* inode size */
215 	int		fc_imask;	/* inode mask */
216 	int		fc_firstino;	/* first non-reserved inode */
217 	int		fc_ipb;		/* inodes per block */
218 	int		fc_fsbtodb;	/* fsb to ds shift */
219 };
220 
221 struct ext2fs {
222 	struct		ext2fs_disk fs_fd;
223 	char		fs_pad[EXT2_SBSIZE - sizeof(struct ext2fs_disk)];
224 	struct		ext2fs_core fs_fc;
225 
226 #define fs_magic	fs_fd.fd_magic
227 #define fs_revision	fs_fd.fd_revision
228 #define fs_blocks	fs_fd.fd_blocks
229 #define fs_firstblk	fs_fd.fd_firstblk
230 #define fs_bpg		fs_fd.fd_bpg
231 #define fs_ipg		fs_fd.fd_ipg
232 
233 #define fs_bsize	fs_fc.fc_bsize
234 #define fs_bshift	fs_fc.fc_bshift
235 #define fs_bmask	fs_fc.fc_bmask
236 #define fs_fsize	fs_fc.fc_fsize
237 #define fs_fshift	fs_fc.fc_fshift
238 #define fs_fmask	fs_fc.fc_fmask
239 #define fs_isize	fs_fc.fc_isize
240 #define fs_imask	fs_fc.fc_imask
241 #define fs_firstino	fs_fc.fc_firstino
242 #define fs_ipb		fs_fc.fc_ipb
243 #define fs_fsbtodb	fs_fc.fc_fsbtodb
244 };
245 
246 struct ext2blkgrp {
247 	u_int32_t	bg_blkmap;	/* block bitmap */
248 	u_int32_t	bg_inomap;	/* inode bitmap */
249 	u_int32_t	bg_inotbl;	/* inode table */
250 	u_int16_t	bg_nfblk;	/* # of free blocks */
251 	u_int16_t	bg_nfino;	/* # of free inodes */
252 	u_int16_t	bg_ndirs;	/* # of dirs */
253 	char		bg_pad[14];
254 };
255 
256 struct ext2dinode {
257 	u_int16_t	di_mode;	/* mode */
258 	u_int16_t	di_uid;		/* uid */
259 	u_int32_t	di_size;	/* byte size */
260 	u_int32_t	di_atime;	/* access time */
261 	u_int32_t	di_ctime;	/* creation time */
262 	u_int32_t	di_mtime;	/* modification time */
263 	u_int32_t	di_dtime;	/* deletion time */
264 	u_int16_t	di_gid;		/* gid */
265 	u_int16_t	di_nlink;	/* link count */
266 	u_int32_t	di_nblk;	/* block count */
267 	u_int32_t	di_flags;	/* file flags */
268 
269 	u_int32_t	di_osdep1;	/* os dependent stuff */
270 
271 	u_int32_t	di_db[NDADDR];	/* direct blocks */
272 	u_int32_t	di_ib[NIADDR];	/* indirect blocks */
273 	u_int32_t	di_version;	/* version */
274 	u_int32_t	di_facl;	/* file acl */
275 	u_int32_t	di_dacl;	/* dir acl */
276 	u_int32_t	di_faddr;	/* fragment addr */
277 
278 	u_int8_t	di_frag;	/* fragment number */
279 	u_int8_t	di_fsize;	/* fragment size */
280 
281 	char		di_pad[10];
282 
283 #define di_shortlink	di_db
284 };
285 
286 #define EXT2_MAXNAMLEN       255
287 
288 struct ext2dirent {
289 	u_int32_t	d_ino;		/* inode */
290 	u_int16_t	d_reclen;	/* directory entry length */
291 	u_int8_t	d_namlen;	/* name length */
292 	u_int8_t	d_type;		/* file type */
293 	char		d_name[EXT2_MAXNAMLEN];
294 };
295 
296 struct file {
297 	off_t		f_seekp;		/* seek pointer */
298 	struct 		ext2fs *f_fs;		/* pointer to super-block */
299 	struct 		ext2blkgrp *f_bg;	/* pointer to blkgrp map */
300 	struct 		ext2dinode f_di;	/* copy of on-disk inode */
301 	int		f_nindir[NIADDR];	/* number of blocks mapped by
302 						   indirect block at level i */
303 	char		*f_blk[NIADDR];		/* buffer for indirect block
304 						   at level i */
305 	size_t		f_blksize[NIADDR];	/* size of buffer */
306 	daddr_t		f_blkno[NIADDR];	/* disk address of block in
307 						   buffer */
308 	char		*f_buf;			/* buffer for data block */
309 	size_t		f_buf_size;		/* size of data block */
310 	daddr_t		f_buf_blkno;		/* block number of data block */
311 };
312 
313 /* forward decls */
314 static int 	read_inode(ino_t inumber, struct open_file *f);
315 static int	block_map(struct open_file *f, daddr_t file_block,
316 		    daddr_t *disk_block_p);
317 static int	buf_read_file(struct open_file *f, char **buf_p,
318 		    size_t *size_p);
319 static int	search_directory(char *name, struct open_file *f,
320 		    ino_t *inumber_p);
321 
322 /*
323  * Open a file.
324  */
325 static int
326 ext2fs_open(const char *upath, struct open_file *f)
327 {
328 	struct file *fp;
329 	struct ext2fs *fs;
330 	size_t buf_size;
331 	ino_t inumber, parent_inumber;
332 	int i, len, groups, bg_per_blk, blkgrps, mult;
333 	int nlinks = 0;
334 	int error = 0;
335 	char *cp, *ncp, *path = NULL, *buf = NULL;
336 	char namebuf[MAXPATHLEN+1];
337 	char c;
338 
339 	/* allocate file system specific data structure */
340 	fp = malloc(sizeof(struct file));
341 	if (fp == NULL)
342 		return (ENOMEM);
343 	bzero(fp, sizeof(struct file));
344 	f->f_fsdata = (void *)fp;
345 
346 	/* allocate space and read super block */
347 	fs = (struct ext2fs *)malloc(sizeof(*fs));
348 	fp->f_fs = fs;
349 	twiddle();
350 	error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
351 	    EXT2_SBLOCK, EXT2_SBSIZE, (char *)fs, &buf_size);
352 	if (error)
353 		goto out;
354 
355 	if (buf_size != EXT2_SBSIZE || fs->fs_magic != EXT2_MAGIC) {
356 		error = EINVAL;
357 		goto out;
358 	}
359 
360 	/*
361 	 * compute in-core values for the superblock
362 	 */
363 	fs->fs_bshift = EXT2_MINBSHIFT + fs->fs_fd.fd_bsize;
364 	fs->fs_bsize = 1 << fs->fs_bshift;
365 	fs->fs_bmask = fs->fs_bsize - 1;
366 
367 	fs->fs_fshift = EXT2_MINFSHIFT + fs->fs_fd.fd_fsize;
368 	fs->fs_fsize = 1 << fs->fs_fshift;
369 	fs->fs_fmask = fs->fs_fsize - 1;
370 
371 	if (fs->fs_revision == EXT2_REV0) {
372 		fs->fs_isize = EXT2_R0_ISIZE;
373 		fs->fs_firstino = EXT2_R0_FIRSTINO;
374 	} else {
375 		fs->fs_isize = fs->fs_fd.fd_isize;
376 		fs->fs_firstino = fs->fs_fd.fd_firstino;
377 	}
378 	fs->fs_imask = fs->fs_isize - 1;
379 	fs->fs_ipb = fs->fs_bsize / fs->fs_isize;
380 	fs->fs_fsbtodb = (fs->fs_bsize / DEV_BSIZE) - 1;
381 
382 	/*
383 	 * we have to load in the "group descriptors" here
384 	 */
385 	groups = howmany(fs->fs_blocks - fs->fs_firstblk, fs->fs_bpg);
386 	bg_per_blk = fs->fs_bsize / sizeof(struct ext2blkgrp);
387 	blkgrps = howmany(groups, bg_per_blk);
388 	len = blkgrps * fs->fs_bsize;
389 
390 	fp->f_bg = malloc(len);
391 	twiddle();
392 	error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
393 	    EXT2_SBLOCK + EXT2_SBSIZE / DEV_BSIZE, len,
394 	    (char *)fp->f_bg, &buf_size);
395 	if (error)
396 		goto out;
397 
398 	/*
399 	 * XXX
400 	 * validation of values?  (blocksize, descriptors, etc?)
401 	 */
402 
403 	/*
404 	 * Calculate indirect block levels.
405 	 */
406 	mult = 1;
407 	for (i = 0; i < NIADDR; i++) {
408 		mult *= nindir(fs);
409 		fp->f_nindir[i] = mult;
410 	}
411 
412 	inumber = EXT2_ROOTINO;
413 	if ((error = read_inode(inumber, f)) != 0)
414 		goto out;
415 
416 	path = strdup(upath);
417 	if (path == NULL) {
418 		error = ENOMEM;
419 		goto out;
420 	}
421 	cp = path;
422 	while (*cp) {
423 		/*
424 		 * Remove extra separators
425 		 */
426 		while (*cp == '/')
427 			cp++;
428 		if (*cp == '\0')
429 			break;
430 
431 		/*
432 		 * Check that current node is a directory.
433 		 */
434 		if (! S_ISDIR(fp->f_di.di_mode)) {
435 			error = ENOTDIR;
436 			goto out;
437 		}
438 
439 		/*
440 		 * Get next component of path name.
441 		 */
442 		len = 0;
443 
444 		ncp = cp;
445 		while ((c = *cp) != '\0' && c != '/') {
446 			if (++len > EXT2_MAXNAMLEN) {
447 				error = ENOENT;
448 				goto out;
449 			}
450 			cp++;
451 		}
452 		*cp = '\0';
453 
454 		/*
455 		 * Look up component in current directory.
456 		 * Save directory inumber in case we find a
457 		 * symbolic link.
458 		 */
459 		parent_inumber = inumber;
460 		error = search_directory(ncp, f, &inumber);
461 		*cp = c;
462 		if (error)
463 			goto out;
464 
465 		/*
466 		 * Open next component.
467 		 */
468 		if ((error = read_inode(inumber, f)) != 0)
469 			goto out;
470 
471 		/*
472 		 * Check for symbolic link.
473 		 */
474 		if (S_ISLNK(fp->f_di.di_mode)) {
475 			int link_len = fp->f_di.di_size;
476 			int len;
477 
478 			len = strlen(cp);
479 			if (link_len + len > MAXPATHLEN ||
480 			    ++nlinks > MAXSYMLINKS) {
481 				error = ENOENT;
482 				goto out;
483 			}
484 
485 			bcopy(cp, &namebuf[link_len], len + 1);
486 			if (fp->f_di.di_nblk == 0) {
487 				bcopy(fp->f_di.di_shortlink,
488 				    namebuf, link_len);
489 			} else {
490 				/*
491 				 * Read file for symbolic link
492 				 */
493 				struct ext2fs *fs = fp->f_fs;
494 				daddr_t	disk_block;
495 				size_t buf_size;
496 
497 				if (! buf)
498 					buf = malloc(fs->fs_bsize);
499 				error = block_map(f, (daddr_t)0, &disk_block);
500 				if (error)
501 					goto out;
502 
503 				twiddle();
504 				error = (f->f_dev->dv_strategy)(f->f_devdata,
505 				    F_READ, fsb_to_db(fs, disk_block),
506 				    fs->fs_bsize, buf, &buf_size);
507 				if (error)
508 					goto out;
509 
510 				bcopy((char *)buf, namebuf, link_len);
511 			}
512 
513 			/*
514 			 * If relative pathname, restart at parent directory.
515 			 * If absolute pathname, restart at root.
516 			 */
517 			cp = namebuf;
518 			if (*cp != '/')
519 				inumber = parent_inumber;
520 			else
521 				inumber = (ino_t)EXT2_ROOTINO;
522 
523 			if ((error = read_inode(inumber, f)) != 0)
524 				goto out;
525 		}
526 	}
527 
528 	/*
529 	 * Found terminal component.
530 	 */
531 	error = 0;
532 out:
533 	if (buf)
534 		free(buf);
535 	if (path)
536 		free(path);
537 	if (error) {
538 		f->f_fsdata = NULL;
539 		if (fp->f_buf)
540 			free(fp->f_buf);
541 		free(fp->f_fs);
542 		free(fp);
543 	}
544 	return (error);
545 }
546 
547 /*
548  * Read a new inode into a file structure.
549  */
550 static int
551 read_inode(ino_t inumber, struct open_file *f)
552 {
553 	struct file *fp = (struct file *)f->f_fsdata;
554 	struct ext2fs *fs = fp->f_fs;
555 	struct ext2dinode *dp;
556 	char *buf;
557 	size_t rsize;
558 	int level, error = 0;
559 
560 	/*
561 	 * Read inode and save it.
562 	 */
563 	buf = malloc(fs->fs_bsize);
564 	twiddle();
565 	error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
566 	    ino_to_db(fs, fp->f_bg, inumber), fs->fs_bsize, buf, &rsize);
567 	if (error)
568 		goto out;
569 	if (rsize != fs->fs_bsize) {
570 		error = EIO;
571 		goto out;
572 	}
573 
574 	dp = (struct ext2dinode *)buf;
575 	fp->f_di = dp[ino_to_bo(fs, inumber)];
576 
577 	/* clear out old buffers */
578 	for (level = 0; level < NIADDR; level++)
579 		fp->f_blkno[level] = -1;
580 	fp->f_buf_blkno = -1;
581 
582 out:
583 	free(buf);
584 	return (error);
585 }
586 
587 /*
588  * Given an offset in a file, find the disk block number that
589  * contains that block.
590  */
591 static int
592 block_map(struct open_file *f, daddr_t file_block, daddr_t *disk_block_p)
593 {
594 	struct file *fp = (struct file *)f->f_fsdata;
595 	struct ext2fs *fs = fp->f_fs;
596 	daddr_t ind_block_num;
597 	daddr_t *ind_p;
598 	int idx, level;
599 	int error;
600 
601 	/*
602 	 * Index structure of an inode:
603 	 *
604 	 * di_db[0..NDADDR-1]	hold block numbers for blocks
605 	 *			0..NDADDR-1
606 	 *
607 	 * di_ib[0]		index block 0 is the single indirect block
608 	 *			holds block numbers for blocks
609 	 *			NDADDR .. NDADDR + NINDIR(fs)-1
610 	 *
611 	 * di_ib[1]		index block 1 is the double indirect block
612 	 *			holds block numbers for INDEX blocks for blocks
613 	 *			NDADDR + NINDIR(fs) ..
614 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 - 1
615 	 *
616 	 * di_ib[2]		index block 2 is the triple indirect block
617 	 *			holds block numbers for double-indirect
618 	 *			blocks for blocks
619 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2 ..
620 	 *			NDADDR + NINDIR(fs) + NINDIR(fs)**2
621 	 *				+ NINDIR(fs)**3 - 1
622 	 */
623 
624 	if (file_block < NDADDR) {
625 		/* Direct block. */
626 		*disk_block_p = fp->f_di.di_db[file_block];
627 		return (0);
628 	}
629 
630 	file_block -= NDADDR;
631 
632 	/*
633 	 * nindir[0] = NINDIR
634 	 * nindir[1] = NINDIR**2
635 	 * nindir[2] = NINDIR**3
636 	 *	etc
637 	 */
638 	for (level = 0; level < NIADDR; level++) {
639 		if (file_block < fp->f_nindir[level])
640 			break;
641 		file_block -= fp->f_nindir[level];
642 	}
643 	if (level == NIADDR) {
644 		/* Block number too high */
645 		return (EFBIG);
646 	}
647 
648 	ind_block_num = fp->f_di.di_ib[level];
649 
650 	for (; level >= 0; level--) {
651 		if (ind_block_num == 0) {
652 			*disk_block_p = 0;	/* missing */
653 			return (0);
654 		}
655 
656 		if (fp->f_blkno[level] != ind_block_num) {
657 			if (fp->f_blk[level] == NULL)
658 				fp->f_blk[level] =
659 					malloc(fs->fs_bsize);
660 			twiddle();
661 			error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
662 			    fsb_to_db(fp->f_fs, ind_block_num), fs->fs_bsize,
663 			    fp->f_blk[level], &fp->f_blksize[level]);
664 			if (error)
665 				return (error);
666 			if (fp->f_blksize[level] != fs->fs_bsize)
667 				return (EIO);
668 			fp->f_blkno[level] = ind_block_num;
669 		}
670 
671 		ind_p = (daddr_t *)fp->f_blk[level];
672 
673 		if (level > 0) {
674 			idx = file_block / fp->f_nindir[level - 1];
675 			file_block %= fp->f_nindir[level - 1];
676 		} else {
677 			idx = file_block;
678 		}
679 		ind_block_num = ind_p[idx];
680 	}
681 
682 	*disk_block_p = ind_block_num;
683 
684 	return (0);
685 }
686 
687 /*
688  * Read a portion of a file into an internal buffer.  Return
689  * the location in the buffer and the amount in the buffer.
690  */
691 static int
692 buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
693 {
694 	struct file *fp = (struct file *)f->f_fsdata;
695 	struct ext2fs *fs = fp->f_fs;
696 	long off;
697 	daddr_t file_block;
698 	daddr_t	disk_block;
699 	size_t block_size;
700 	int error = 0;
701 
702 	off = blkoff(fs, fp->f_seekp);
703 	file_block = lblkno(fs, fp->f_seekp);
704 	block_size = dblksize(fs, &fp->f_di, file_block);
705 
706 	if (file_block != fp->f_buf_blkno) {
707 		error = block_map(f, file_block, &disk_block);
708 		if (error)
709 			goto done;
710 
711 		if (fp->f_buf == NULL)
712 			fp->f_buf = malloc(fs->fs_bsize);
713 
714 		if (disk_block == 0) {
715 			bzero(fp->f_buf, block_size);
716 			fp->f_buf_size = block_size;
717 		} else {
718 			twiddle();
719 			error = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
720 			    fsb_to_db(fs, disk_block), block_size,
721 			    fp->f_buf, &fp->f_buf_size);
722 			if (error)
723 				goto done;
724 		}
725 		fp->f_buf_blkno = file_block;
726 	}
727 
728 	/*
729 	 * Return address of byte in buffer corresponding to
730 	 * offset, and size of remainder of buffer after that
731 	 * byte.
732 	 */
733 	*buf_p = fp->f_buf + off;
734 	*size_p = block_size - off;
735 
736 	/*
737 	 * But truncate buffer at end of file.
738 	 */
739 	if (*size_p > fp->f_di.di_size - fp->f_seekp)
740 		*size_p = fp->f_di.di_size - fp->f_seekp;
741 done:
742 	return (error);
743 }
744 
745 /*
746  * Search a directory for a name and return its
747  * i_number.
748  */
749 static int
750 search_directory(char *name, struct open_file *f, ino_t *inumber_p)
751 {
752 	struct file *fp = (struct file *)f->f_fsdata;
753 	struct ext2dirent *dp, *edp;
754 	char *buf;
755 	size_t buf_size;
756 	int namlen, length;
757 	int error;
758 
759 	length = strlen(name);
760 	fp->f_seekp = 0;
761 	while (fp->f_seekp < fp->f_di.di_size) {
762 		error = buf_read_file(f, &buf, &buf_size);
763 		if (error)
764 			return (error);
765 		dp = (struct ext2dirent *)buf;
766 		edp = (struct ext2dirent *)(buf + buf_size);
767 		while (dp < edp) {
768 			if (dp->d_ino == (ino_t)0)
769 				goto next;
770 			namlen = dp->d_namlen;
771 			if (namlen == length &&
772 			    strncmp(name, dp->d_name, length) == 0) {
773 				/* found entry */
774 				*inumber_p = dp->d_ino;
775 				return (0);
776 			}
777 		next:
778 			dp = (struct ext2dirent *)((char *)dp + dp->d_reclen);
779 		}
780 		fp->f_seekp += buf_size;
781 	}
782 	return (ENOENT);
783 }
784 
785 static int
786 ext2fs_close(struct open_file *f)
787 {
788 	struct file *fp = (struct file *)f->f_fsdata;
789 	int level;
790 
791 	f->f_fsdata = NULL;
792 	if (fp == NULL)
793 		return (0);
794 
795 	for (level = 0; level < NIADDR; level++) {
796 		if (fp->f_blk[level])
797 			free(fp->f_blk[level]);
798 	}
799 	if (fp->f_buf)
800 		free(fp->f_buf);
801 	if (fp->f_bg)
802 		free(fp->f_bg);
803 	free(fp->f_fs);
804 	free(fp);
805 	return (0);
806 }
807 
808 static int
809 ext2fs_read(struct open_file *f, void *addr, size_t size, size_t *resid)
810 {
811 	struct file *fp = (struct file *)f->f_fsdata;
812 	size_t csize, buf_size;
813 	char *buf;
814 	int error = 0;
815 
816 	while (size != 0) {
817 		if (fp->f_seekp >= fp->f_di.di_size)
818 			break;
819 
820 		error = buf_read_file(f, &buf, &buf_size);
821 		if (error)
822 			break;
823 
824 		csize = size;
825 		if (csize > buf_size)
826 			csize = buf_size;
827 
828 		bcopy(buf, addr, csize);
829 
830 		fp->f_seekp += csize;
831 		addr += csize;
832 		size -= csize;
833 	}
834 	if (resid)
835 		*resid = size;
836 	return (error);
837 }
838 
839 static off_t
840 ext2fs_seek(struct open_file *f, off_t offset, int where)
841 {
842 	struct file *fp = (struct file *)f->f_fsdata;
843 
844 	switch (where) {
845 	case SEEK_SET:
846 		fp->f_seekp = offset;
847 		break;
848 	case SEEK_CUR:
849 		fp->f_seekp += offset;
850 		break;
851 	case SEEK_END:
852 		fp->f_seekp = fp->f_di.di_size - offset;
853 		break;
854 	default:
855 		return (-1);
856 	}
857 	return (fp->f_seekp);
858 }
859 
860 static int
861 ext2fs_stat(struct open_file *f, struct stat *sb)
862 {
863 	struct file *fp = (struct file *)f->f_fsdata;
864 
865 	/* only important stuff */
866 	sb->st_mode = fp->f_di.di_mode;
867 	sb->st_uid = fp->f_di.di_uid;
868 	sb->st_gid = fp->f_di.di_gid;
869 	sb->st_size = fp->f_di.di_size;
870 	return (0);
871 }
872 
873 static int
874 ext2fs_readdir(struct open_file *f, struct dirent *d)
875 {
876 	struct file *fp = (struct file *)f->f_fsdata;
877 	struct ext2dirent *ed;
878 	char *buf;
879 	size_t buf_size;
880 	int error;
881 
882 	/*
883 	 * assume that a directory entry will not be split across blocks
884 	 */
885 again:
886 	if (fp->f_seekp >= fp->f_di.di_size)
887 		return (ENOENT);
888 	error = buf_read_file(f, &buf, &buf_size);
889 	if (error)
890 		return (error);
891 	ed = (struct ext2dirent *)buf;
892 	fp->f_seekp += ed->d_reclen;
893 	if (ed->d_ino == (ino_t)0)
894 		goto again;
895 	d->d_type = EXTFTODT(ed->d_type);
896 	strncpy(d->d_name, ed->d_name, ed->d_namlen);
897 	d->d_name[ed->d_namlen] = '\0';
898 	return (0);
899 }
900