Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 5 Feb 2003 17:01:48 -0800 (PST)
From:      Ed Alley <wea@llnl.gov>
To:        FreeBSD-gnats-submit@FreeBSD.org
Cc:        wea@llnl.gov
Subject:   kern/47982: Minix file-system offered
Message-ID:  <200302060101.h1611mpE007766@jordan.llnl.gov>

next in thread | raw e-mail | index | archive | help

>Number:         47982
>Category:       kern
>Synopsis:       Minix file-system offered
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Feb 05 17:10:07 PST 2003
>Closed-Date:
>Last-Modified:
>Originator:     Ed Alley
>Release:        FreeBSD 4.6.2-RELEASE i386
>Organization:
Lawrence Livermore National Laboratory
>Environment:
System: FreeBSD jordan.llnl.gov 4.6.2-RELEASE FreeBSD 4.6.2-RELEASE #0:
        Sun Sep 15 18:21:53 PDT 2002
        wea@jordan.llnl.gov:/usr/src/sys/compile/JORDAN i386

>Description:

  I originally posted a question in freebsd-questions
as to how I might submit a kernel module that implements
the Minix fs in FreeBSD. It was suggested by Giorgos Keramidas
that I submit it as a pr. 

"... send minixfs.patch in a new problem report with send-pr(1).
This way, anyone interested in a minix fs will have a way of
testing your implementation & providing feedback ..."

The above quote was from Giorgos: <keramida@freebsd.org>.

I have done as he suggested:

	 % cd /usr
	 % diff -ruN src.orig src > minixfs.patch

and am shipping the above patch with this pr. I would appreciate
any comments that interested people have and additions to the
TODO list that I have included in the source located in:
	/usr/src/sys/fs/minixfs/TODO

 --- Original post to freebsd-questions: ---

 ** In addition to being a FreeBSD enthusiast, I am also
 ** a Minix enthusiast. I've learned a great deal about
 ** how UNIX works by hacking the Minix OS myself.
 ** A by-product of my hacks was the development of a
 ** FreeBSD kernel module that implements the
 ** Minix Version 2 file system. The latest release
 ** that it runs on is FreeBSD 4.6.x.

 ** My question is whether the FreeBSD community
 ** is interested in such a module? If so, then
 ** how does one go about submitting a kernel
 ** module for evaluation and possible inclusion
 ** in the source?

 ** My impression is that the Minix fs is a little
 ** simplistic perhaps for inclusion in the FreeBSD
 ** source. If so, then is it possible to submit it
 ** as a port? I have never seen an example of a
 ** kernel module that was submitted as a port,
 ** however.

>How-To-Repeat:
	Nothing to repeat
>Fix:
    cut here: 8>< -------------------------------------><8
diff -ruN src.orig/sbin/Makefile src/sbin/Makefile
--- src.orig/sbin/Makefile	Mon Mar 18 00:40:00 2002
+++ src/sbin/Makefile	Wed Feb  5 12:45:53 2003
@@ -78,7 +78,7 @@
 	vinum
 
 .if ${MACHINE_ARCH} == i386
-SUBDIR+=	kget mount_nwfs mount_smbfs
+SUBDIR+=	kget mount_nwfs mount_smbfs mount_minix
 .endif
 
 .if exists(${.CURDIR}/${MACHINE})
diff -ruN src.orig/sbin/mount_minix/Makefile src/sbin/mount_minix/Makefile
--- src.orig/sbin/mount_minix/Makefile	Wed Dec 31 16:00:00 1969
+++ src/sbin/mount_minix/Makefile	Thu Nov  7 11:30:51 2002
@@ -0,0 +1,12 @@
+#	@(#)Makefile	8.3 (Berkeley) 3/27/94
+# $FreeBSD: src/sbin/mount_minixfs/Makefile,v 1.3.6.1 2002/11/05 00:00:30 ru Exp $
+
+PROG=	mount_minixfs
+SRCS=	mount_minixfs.c getmntopts.c
+MAN=	mount_minixfs.8
+
+MOUNT=	${.CURDIR}/../mount
+CFLAGS+= -I${MOUNT}
+.PATH:	${MOUNT}
+
+.include <bsd.prog.mk>
diff -ruN src.orig/sbin/mount_minix/mount_minixfs.8 src/sbin/mount_minix/mount_minixfs.8
--- src.orig/sbin/mount_minix/mount_minixfs.8	Wed Dec 31 16:00:00 1969
+++ src/sbin/mount_minix/mount_minixfs.8	Thu Nov  7 11:30:51 2002
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1993, 1994
+.\"	The Regents of the University of California.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"	This product includes software developed by the University of
+.\"	California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/sbin/mount_ext2fs/mount_ext2fs.8,v 1.6.2.4 2001/12/14 15:17:51 ru Exp $
+.\"
+.Dd November 5, 2002
+.Dt MOUNT_MINIXFS 8
+.Os
+.Sh NAME
+.Nm mount_minixfs
+.Nd mount a minixfs file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Ar special
+.Ar node
+.Sh DESCRIPTION
+The
+.Nm
+command attaches a minixfs file system
+.Ar special
+device on to the file system tree at the point
+.Ar node .
+.Pp
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+The
+.Nm
+function first appeared in
+.Fx 4.6 .
diff -ruN src.orig/sbin/mount_minix/mount_minixfs.c src/sbin/mount_minix/mount_minixfs.c
--- src.orig/sbin/mount_minix/mount_minixfs.c	Wed Dec 31 16:00:00 1969
+++ src/sbin/mount_minix/mount_minixfs.c	Thu Nov  7 11:30:51 2002
@@ -0,0 +1,139 @@
+/*-
+ * Copyright (c) 1993, 1994
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1993, 1994\n\
+	The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)mount_lfs.c	8.3 (Berkeley) 3/27/94";
+*/
+static const char rcsid[] =
+  "$FreeBSD: src/sbin/mount_minixfs/mount_minixfs.c,v 1.11 2002/11/05 00:00:00 phk Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <ufs/ufs/ufsmount.h>
+
+#include "mntopts.h"
+
+struct mntopt mopts[] = {
+	MOPT_STDOPTS,
+	MOPT_FORCE,
+	MOPT_SYNC,
+	MOPT_UPDATE,
+	{ NULL }
+};
+
+static void	usage __P((void)) __dead2;
+
+int
+main(argc, argv)
+	int argc;
+	char *argv[];
+{
+	struct ufs_args args;
+	int ch, mntflags;
+	char *fs_name, *options, mntpath[MAXPATHLEN];
+	struct vfsconf vfc;
+	int error;
+
+	options = NULL;
+	mntflags = 0;
+	while ((ch = getopt(argc, argv, "o:")) != -1)
+		switch (ch) {
+		case 'o':
+			getmntopts(optarg, mopts, &mntflags, 0);
+			break;
+		case '?':
+		default:
+			usage();
+		}
+	argc -= optind;
+	argv += optind;
+
+	if (argc != 2)
+		usage();
+
+        args.fspec = argv[0];	/* the name of the device file */
+	fs_name = argv[1];	/* the mount point */
+
+	/*
+	 * Resolve the mountpoint with realpath(3) and remove unnecessary
+	 * slashes from the devicename if there are any.
+	 */
+	(void)checkpath(fs_name, mntpath);
+	(void)rmslashes(args.fspec, args.fspec);
+
+#define DEFAULT_ROOTUID	-2
+	args.export.ex_root = DEFAULT_ROOTUID;
+	if (mntflags & MNT_RDONLY)
+		args.export.ex_flags = MNT_EXRDONLY;
+	else
+		args.export.ex_flags = 0;
+
+	error = getvfsbyname("minixfs", &vfc);
+	if (error && vfsisloadable("minixfs")) {
+		if (vfsload("minixfs")) {
+			err(EX_OSERR, "vfsload(minixfs)");
+		}
+		endvfsent();	/* flush cache */
+		error = getvfsbyname("minixfs", &vfc);
+	}
+	if (error)
+		errx(EX_OSERR, "minixfs filesystem is not available");
+
+	if (mount(vfc.vfc_name, mntpath, mntflags, &args) < 0)
+		err(EX_OSERR, "%s", args.fspec);
+	exit(0);
+}
+
+void
+usage()
+{
+	(void)fprintf(stderr,
+		"usage: mount_minixfs [-o options] special node\n");
+	exit(EX_USAGE);
+}
diff -ruN src.orig/sys/fs/minixfs/README src/sys/fs/minixfs/README
--- src.orig/sys/fs/minixfs/README	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/README	Wed Feb  5 12:50:36 2003
@@ -0,0 +1,20 @@
+This is the Minix fs. It was developed on FreeBSD-4.6.x.
+
+The fs should be located in: "/usr/src/sys/fs/minixfs".
+
+There is also a source file for mount located in: "/usr/src/sbin/mount_minix".
+
+The include file: vnode.h in "/usr/src/sys/sys" is modified in the enum vtagtype
+statement to include VT_MINIXFS after the last entry (which at this writing
+is: VT_SMBFS).
+
+The Makefile for the Minix fs is located in: "/usr/src/sys/modules/minixfs".
+
+The Makefile for sbin must be modified to compile minix_mountfs.c.
+
+The Makefile for the modules must be modified to compile the Minix fs module.
+
+I modified the i386 sections of these Makefiles because the fs only works on the
+i386 platform.
+
+					Ed
diff -ruN src.orig/sys/fs/minixfs/TODO src/sys/fs/minixfs/TODO
--- src.orig/sys/fs/minixfs/TODO	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/TODO	Wed Feb  5 12:29:09 2003
@@ -0,0 +1,42 @@
+To do list:       (Not necessarily in order of importance)
+
+	1. Modify to allow zone sizes larger than one block.
+		As of now: zone size = block size = 1024 bytes.
+		The routine: minix_bmapfs() shows a beginning
+		in that direction. There is an issue of whether
+		to save the zone fragments after reading, in case
+		another block is desired within a previously read
+		zone. I realize that these issues have already
+		been delt with in ufs and ext2fs and that these 
+		algorithms should be understood before writing one
+		for Minix.
+
+	2. Fix ip hashing.
+		As of now I get a panic (page fault from memory manager)
+		after a dismount then subsequent remount; the panic
+		occurs when I first reference the file system after
+		the remount, for example, by executing ls(1) within
+		the file system. Because of this I have disabled ip hashing
+		in this source until I can figure out what is going wrong.
+
+	3. Clean up redundant metadata writes.
+		This involves reducing the number of 'minix_update()' calls
+		to a minimum when metadata changes occur; I took a very
+		conservative approach in building this fs and did not
+		even consider trying to design it with asynchronous updates
+		of metadata; as a consequence, there are undoubtably too
+		many update() calls.
+
+	4. Make more routines static.
+		Need to reduce the number of routines with prototypes
+		lacking the "static" designation to reduce the number
+		of minix functions with global names.
+
+	5. Write a minix_fsck routine.
+		Probably not necessary, because Minix already has one
+		operating on its side of the fence. But it might be
+		fun nonetheless. :)
+
+	6. Make modifications for other platforms.
+		As of now the Minix fs only works on the i386 platform.
+	        This might be tricky if endian issues are involved.
diff -ruN src.orig/sys/fs/minixfs/bsd-copyright src/sys/fs/minixfs/bsd-copyright
--- src.orig/sys/fs/minixfs/bsd-copyright	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/bsd-copyright	Sat Jan 18 21:20:42 2003
@@ -0,0 +1,25 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
diff -ruN src.orig/sys/fs/minixfs/minix.h src/sys/fs/minixfs/minix.h
--- src.orig/sys/fs/minixfs/minix.h	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix.h	Sun Feb  2 19:20:41 2003
@@ -0,0 +1,368 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/****************************************************************************
+ *                                                                          *
+ *                  Minix File System include file                          *
+ *                                                                          *
+ *   The guide for this include file comes from Andrew Tannenbaum's MINIX   *
+ *   source which can be gotten from the official MINIX home page:          *
+ *                http://www.cs.vu.nl/~ast/minix.html                       *
+ *   Some of the names have been changed to avoid conflicts with FBSD. ;)   *
+ *                                                                          *
+ *   For further information there is also the book:                        *
+ *      Tannenbaum and Woodhull,                                            *
+ *          "Operating Systems Design and Implememtation", 2nd ed, 1997     *
+ *                  from Prentice Hall, New Jersey                          *
+ *                                                                          *
+ ****************************************************************************/
+
+#ifndef _SYS_PARAM_H_
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#endif
+
+#ifndef _SYS_MALLOC_H_
+#include <sys/malloc.h>
+#endif
+
+#define MINIXFS_VERSION 2     /* Version 2 Minix file system */
+                              /* Block size = zone size = 1024 bytes. */
+
+/* Some maximum sizes */
+
+#define MINIX_NAME_MAX  14   /* Max size of a file name */
+#define MINIX_LINK_MAX 127   /* Max links of a file */
+#define MINIX_PATH_MAX 255   /* Maximum path length */
+#define MINIX_PIPE_BUF 512   /* Maximum size of atomic pipe writes */
+
+/* Block size defines */
+
+#define BLOCK_SIZE 1024       /* # bytes in a disk block */
+#define BITCHUNK_SIZE 2       /* # bytes in a bitchunk */
+#define BITMAP_CHUNKS 512     /* # bitmap chunks in a disk block */
+#define BITCHUNK_BITS 16      /* # bits in a bitchunk */
+#define BITS_PER_BLOCK 8192   /* # bits in a block 8*BLOCK_SIZE */
+#define BITMAPSHIFT 13        /* log2(BITS_PER_BLOCK) */
+
+/* Inode defines */
+
+#define ROOT_INO 1            /* Minix uses inode 1 for the root inode */
+#define NO_INODE 0            /* Inode number to return if none found */
+#define V2_INODE_SIZE 64      /* size of inode in bytes */
+#define V2_INODES_PER_BLOCK (BLOCK_SIZE/V2_INODE_SIZE) /* # of inodes per block */
+#define INODE_MAP 2           /* position of first inode bitmap in file */
+#define V2_NR_DBLOCKS 7       /* # of direct block pointers in inode */
+#define V2_NR_TBLOCKS 10      /* # of block pointers: direct+single+double+triple */
+#define V2_NR_INDIRECTS 256   /* # of indirect pointers in a block */
+#define INDIRECT_SHIFT 8      /* log2(V2_NR_INDIRECTS) */
+
+/* These next defines count the number of addressable blocks */
+
+#define NR_DIRECT     V2_NR_DBLOCKS
+#define NR_SINDIRECT  V2_NR_INDIRECTS
+#define NR_DINDIRECT (V2_NR_INDIRECTS*NR_SINDIRECT)
+#define NR_TINDIRECT (V2_NR_INDIRECTS*NR_DINDIRECT)
+
+#define TOT_DIRECT     NR_DIRECT
+#define TOT_SINDIRECT (TOT_DIRECT    + NR_SINDIRECT)
+#define TOT_DINDIRECT (TOT_SINDIRECT + NR_DINDIRECT)
+#define TOT_TINDIRECT (TOT_DINDIRECT + NR_TINDIRECT)
+
+/* Minix flag bits for i_mode in the dinode. */
+
+#define I_TYPE          0170000	/* this field gives inode type */
+#define I_SOCK          0140000 /* UNIX domain socket */
+#define I_LINK          0120000 /* symbolic link */
+#define I_REGULAR       0100000	/* regular file, not dir or special */
+#define I_BLOCK_SPECIAL 0060000	/* block special file */
+#define I_DIRECTORY     0040000	/* file is a directory */
+#define I_CHAR_SPECIAL  0020000	/* character special file */
+#define I_NAMED_PIPE	0010000 /* named pipe (FIFO) */
+#define I_SET_UID_BIT   0004000	/* set effective uid_t on exec */
+#define I_SET_GID_BIT   0002000	/* set effective gid_t on exec */
+#define ALL_MODES       0006777	/* all bits for user, group and others */
+#define RWX_MODES       0000777	/* mode bits for RWX only */
+#define R_BIT           0000004	/* Rwx protection bit */
+#define W_BIT           0000002	/* rWx protection bit */
+#define X_BIT           0000001	/* rwX protection bit */
+#define I_NOT_ALLOC     0000000	/* this inode is free */
+
+/* Block defines */
+
+#define V2_BLOCK_NUM_SIZE 4  /* size of block address in bytes */
+#define V2_INDIRECTS   (BLOCK_SIZE/V2_BLOCK_NUM_SIZE)  /* # blocks per indir block */
+
+/* File system types. (Only SUPER_V2 is recognized in this implementation */
+
+#define SUPER_MAGIC   0x137F	/* magic number contained in super-block */
+#define SUPER_REV     0x7F13	/* magic # when 68000 disk read on PC or vv */
+#define SUPER_V2      0x2468	/* magic # for V2 file systems */
+#define SUPER_V2_REV  0x6824	/* V2 magic written on PC, read on 68K or vv */
+
+/* The type of sizeof may be (unsigned) long.  Use the following macro for
+ * taking the sizes of small objects so that there are no surprises like
+ * (small) long constants being passed to routines expecting an int.
+ */
+
+#define usizeof(t) ((unsigned) sizeof(t))
+
+/* Types used in disk, inode, etc. data structures. */
+
+typedef u_int16_t   mino_t;     /* Minix i-node number is 16 bits */
+typedef u_int32_t   block_t;    /* block number */
+typedef u_int32_t   bit_t;      /* bit number in a bitmap */
+typedef u_int16_t   bitchunk_t; /* a collection of bits from a bitmap */
+typedef long        mtime_t;    /* time in sec since 1 Jan 1970 0000 GMT */
+
+/* Directory layout (Minix only allows 14 characters in a file name) */
+
+struct minix_direct {
+	u_int16_t d_ino;                /*  2 */
+	char d_name[MINIX_NAME_MAX];    /* 14 */
+};
+
+struct minix_dirtemplate {   /* 32 bytes */
+	u_int16_t    dot_ino;
+	char         dot_name[MINIX_NAME_MAX];
+	u_int16_t    dotdot_ino;
+	char         dotdot_name[MINIX_NAME_MAX];
+};
+
+#define DIR_ENTRY_SIZE usizeof(struct minix_direct) /* size of dir entry in bytes = 16 */
+#define NR_DIR_ENTRIES (BLOCK_SIZE/DIR_ENTRY_SIZE) /* # of dirs/block = 64 */
+#define MAX_DIR_ENTRIES ((V2_NR_DBLOCKS + V2_NR_INDIRECTS) * NR_DIR_ENTRIES)
+
+#define MINIX_LINK_MAX 127 /* Maximum number of file links */
+#define MINIX_MAXSYMLINKLEN 4*V2_NR_TBLOCKS  /* Max chars in a short symlink */
+#define MINIX_MAXSYMLINK BLOCK_SIZE          /* Max chars in a long symlink  */
+
+/* Structure of an inode on disk. length = 64 bytes. */
+
+struct minix_dinode {
+	u_int16_t i_mode;     /* file type, protection, etc. 2b */
+	int16_t i_nlinks;     /* how many links to this file 2b */
+	int16_t i_uid;	      /* user id of the file's owner 2b */
+	int16_t i_gid;	      /* group number                2b */
+	u_int32_t i_size;     /* current file size in bytes  4b */
+	int32_t i_atime;      /* time of last access (V2 only) 4b */
+	int32_t i_mtime;      /* when was file data last changed 4b */
+	int32_t i_ctime;      /* when was inode itself changed (V2 only) 4b */
+	u_int32_t i_block[V2_NR_TBLOCKS]; /* direct,+ 1,2,3 indirect 40b */
+};
+
+/* Structure of an in-core inode. */
+
+struct minix_inode {
+        struct lock i_lock;             /* Inode lock. Must be first. */
+	LIST_ENTRY(minix_inode) i_hash; /* Hash chain */
+	struct minixmount *i_mmp;
+	struct vnode *i_vnode;          /* Vnode associated with this inode */
+	struct vnode *i_devvp;          /* Vnode for mounted on device */
+	mode_t    i_mode;               /* BSD style mode of file */
+	u_int32_t i_flag;               /* flags */
+	int       i_blocks;             /* Total number of blocks in file */
+	dev_t     i_dev;                /* specinfo for device associated with this inode */
+	ino_t     i_number;             /* The identity of this inode */
+	ino_t     i_parent;             /* The parent inode */
+	int       i_count;              /* Reference count */
+	int       i_entry;              /* Directory entry number from namei */
+	int       i_noent;              /* First no entry number if a directory */
+	struct minix_super_block *i_su; /* Super block */
+	struct minix_dinode i_dino;     /* The on-disk inode */
+};
+
+/* Some useful defines */
+
+#define i_nlink i_dino.i_nlinks
+#define i_zone i_dino.i_block
+#define i_shortlink i_dino.i_block
+#define i_rdev i_dino.i_block[0]
+
+#define ino_to_byte(fs,ino) ((fs->s_firstinode + (ino-1)/V2_INODES_PER_BLOCK)*BLOCK_SIZE)
+#define blk_to_byte(blk) ((blk)*BLOCK_SIZE)
+#define byte_to_blkn(byte) ((u_daddr_t)((byte) >> DEV_BSHIFT))
+#define ino_off(ino) (((ino-1) % V2_INODES_PER_BLOCK) * V2_INODE_SIZE)
+
+/* These flags are kept in i_flag. (some are not recognized by Minix) */
+#define	IN_ACCESS	0x0001		/* Access time update request. */
+#define	IN_CHANGE	0x0002		/* Inode change time update request. */
+#define	IN_UPDATE	0x0004		/* Modification time update request. */
+#define	IN_MODIFIED	0x0008		/* Inode has been modified. */
+#define	IN_RENAME	0x0010		/* Inode is being renamed. */
+#define	IN_SHLOCK	0x0020		/* File has shared lock. */
+#define	IN_EXLOCK	0x0040		/* File has exclusive lock. */
+#define	IN_HASHED	0x0080		/* Inode is on hash list */
+#define	IN_LAZYMOD	0x0100		/* Modified, but don't write yet. */
+
+/* These are the BSD-type mode bits present in the incore inode */
+
+/* File permissions. */
+#define	IEXEC		0000100		/* Executable. */
+#define	IWRITE		0000200		/* Writeable. */
+#define	IREAD		0000400		/* Readable. */
+#define	ISVTX		0001000		/* Sticky bit. */
+#define	ISGID		0002000		/* Set-gid. */
+#define	ISUID		0004000		/* Set-uid. */
+
+/* File types. */
+#define	IFMT		0170000		/* Mask of file type. */
+#define	IFIFO		0010000		/* Named pipe (fifo). */
+#define	IFCHR		0020000		/* Character device. */
+#define	IFDIR		0040000		/* Directory file. */
+#define	IFBLK		0060000		/* Block device. */
+#define	IFREG		0100000		/* Regular file. */
+#define	IFLNK		0120000		/* Symbolic link. */
+#define	IFSOCK		0140000		/* UNIX domain socket. */
+#define	IFWHT		0160000		/* Whiteout. */
+
+#define LSUPER_V2  BLOCK_SIZE        /* Byte location of V2 super block on disk */
+#define LSV2BLOCK ((u_daddr_t)(LSUPER_V2/DEV_BSIZE)) /* Record count on device */
+
+/* Structure of the superblock length on disk is 24b */
+
+struct minix_super_block {
+	u_short s_ninodes;	 /* # usable inodes on the minor device 2b */
+	u_short s_nzones;	 /* number of V1 fs zones 2b */
+	short s_imap_blocks;	 /* # of blocks used by inode bit map 2b */
+	short s_zmap_blocks;	 /* # of blocks used by zone bit map 2b */
+	u_short s_firstdatazone; /* number of first data zone 2b */
+	short s_log_zone_size;	 /* log2 of blocks/zone 2b */
+	u_long s_max_size;	 /* maximum file size on this device 4b */
+	short s_magic;		 /* magic number 2b */
+	short s_pad;		 /* pad it to a 4-byte boundary 2b */
+	u_long s_zones;		 /* number of V2 fs zones 4b */
+	
+	/* The rest of the structure belongs to the in-core superblock */
+	
+	struct minix_inode *s_root;   /* Inode for root dir of mounted file system */
+	ino_t s_imnton;          /* UFS inode number mounted on */
+	u_int16_t *s_ibmap;      /* Inode bit-map */
+	u_int16_t *s_bbmap;      /* Block bit-map */
+	u_int32_t imap_lock;     /* Inode bit-map lock variable */
+	u_int32_t bmap_lock;     /* Block bit-map lock variable */
+	dev_t s_dev;             /* Specinfo of device of mounted filesystem */
+	int s_rdonly;            /* Read only flag */
+	int s_version;           /* Version number of file system */
+	int s_firstinode;        /* Block no of first inode */
+	int s_boff;              /* Data block offset = s_firstdatazone - 1. */
+	int s_bsize;             /* Block size */
+};
+
+/* A block can be inodes, directories, a bootblock, a superblock or ... */
+
+union minix_block {
+	struct minix_dinode dinode[16];   /* 16 dinodes */
+	struct minix_super_block sp;      /*  1 superblock + space */
+	struct boot_block_s {             /*  1 bootblock + space */
+		char bootblock[294];
+		char pad[730];
+	} boot_block;
+	struct minix_direct dir[64];      /*   64 directories */
+	u_int32_t ind[256];               /*  256 indirect blocks */
+	u_int16_t bitchunk[512];          /*  512 bitchunks */
+	u_int8_t  data[1024];             /* 1024 bytes */
+};
+
+struct minixmount {
+	struct minix_super_block *mnx_su;            /* Superblock */
+	struct mount             *mnx_mp;            /* VFS mount structure */
+	struct vnode             *mnx_devvp;         /* Device vnode mounted on */
+	dev_t                     mnx_dev;           /* Specinfo of device */
+	struct malloc_type       *mnx_malloctype;
+};
+
+/* Inode hash routines (not successfully implemented) */
+/*
+void minix_ihashinit(void);
+void minix_ihashuninit(void);
+struct vnode *minix_ihashlookup(dev_t, ino_t);
+struct vnode *minix_ihashget(dev_t, ino_t);
+void minix_ihashins(struct minix_inode *);
+void minix_ihashrem(struct minix_inode *);
+*/
+
+/* Support routine prototypes */
+
+int minix_vinit(struct mount*, vop_t**, vop_t**, struct vnode**);
+int minix_vget(struct mount*, ino_t, struct vnode**);
+int minix_vfree(struct vnode*, ino_t, int);
+int minix_getblk(struct vnode*,block_t,struct buf**);
+int minix_putblk(struct buf*);
+void minix_freeblk(struct buf*);
+int minix_valloc(struct vnode*,int,struct ucred*, struct vnode**);
+int minix_balloc(struct vnode*,u_daddr_t,struct buf**);
+int minix_blkatoff(struct vnode*,off_t,char**,struct buf**);
+int minix_alloc(struct minix_super_block*,u_daddr_t*,u_daddr_t*);
+int minix_dalloc(struct minix_super_block*,u_daddr_t);
+int minix_makeinode(int,struct vnode*,struct vnode**,struct componentname*);
+int minix_addblk(struct minix_inode*,u_daddr_t,u_daddr_t);
+int minix_ialloc(struct minix_super_block*,int*);
+int minix_dialloc(struct minix_super_block*,int);
+int minix_update(struct vnode*);
+void minix_itimes(struct vnode*);
+int minix_truncate(struct vnode*,off_t,int,struct ucred*,struct proc*);
+int minix_iget(struct vnode*,struct minix_super_block*,
+               mino_t,struct minix_inode*);
+int minix_iput(struct minix_inode*);
+void minix_wipe_dinode(struct minix_dinode*);
+int minix_dinode_access(struct minix_dinode*,int,
+          struct ucred*,struct minix_super_block*);
+int minix_free_inode_count(struct minix_super_block*);
+int minix_free_block_count(struct minix_super_block*);
+int minix_next_free_inode(struct minix_super_block*);
+int minix_next_free_block(struct minix_super_block*);
+int minix_bmapfs(struct vnode*,u_daddr_t,u_daddr_t*,int*);
+int minix_get_vtype(struct minix_inode*);
+int minix_num_blocks(u_int16_t, u_int32_t);
+void minix_get_lock(u_int32_t*);
+void minix_free_lock(u_int32_t*);
+void minix_makedirentry(struct minix_inode*,struct componentname*,
+                        struct minix_direct*);
+int minix_direnter(struct vnode*,struct vnode*,struct minix_direct*,
+                   struct componentname*);
+int minix_dirremove(struct vnode*);
+int minix_dirempty(struct minix_inode*,ino_t,struct ucred*);
+int minix_dirrewrite(struct minix_inode*,struct minix_inode*,ino_t,int);
+int minix_checkpath(struct minix_inode*,struct minix_inode*,struct ucred*);
+
+/* VOP pointers */
+
+extern vop_t **minix_vnodeop_p;
+extern vop_t **minix_specop_p;
+extern vop_t **minix_fifoop_p;
+
+/* Misc declarations */
+
+MALLOC_DECLARE(M_MINIXMNT);
+MALLOC_DECLARE(M_MINIXNOD);
diff -ruN src.orig/sys/fs/minixfs/minix_alock.s src/sys/fs/minixfs/minix_alock.s
--- src.orig/sys/fs/minixfs/minix_alock.s	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_alock.s	Fri Jan 31 17:48:25 2003
@@ -0,0 +1,63 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ /*************************************************************
+ *                                                           *
+ *   OK so we have another TSL lock routine.                 *
+ *                                                           *
+ *   C-Prototype:   (long) _alock(long*);                    *
+ *                                                           *
+ *   I am not spin waiting here because I want the choice    *
+ *   of either spin waiting or giving up the processor.      *
+ *   This we can do by putting the call to this routine      *
+ *   in the argument of a while loop. The loop can either    *
+ *   spin or call a cpu giveup routine until the lock is     *
+ *   obtained:	                                             *
+ *               while (_minix_alock(&lock_var)) {           *
+ *    		    tsleep(&loc_var);                        *
+ *               }                                           *
+ *							     *
+ *  See the file: minix_locks.c, for the implementation.     *
+ *                                                           *
+ *************************************************************/
+
+.globl _minix_alock
+.align 16
+_minix_alock:
+	pushl %ebp
+	movl  %esp, %ebp
+
+	pushl %ecx
+
+	movl  8(%ebp), %ecx
+	movl  $1,  %eax
+
+	lock xchg (%ecx), %eax
+
+	popl %ecx
+
+	popl  %ebp
+	ret
diff -ruN src.orig/sys/fs/minixfs/minix_bio.c src/sys/fs/minixfs/minix_bio.c
--- src.orig/sys/fs/minixfs/minix_bio.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_bio.c	Fri Jan 31 17:54:45 2003
@@ -0,0 +1,430 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/buf.h>
+
+#include <fs/minixfs/minix.h>
+
+int  minix_read_bbit(struct minix_super_block*,int);
+void minix_write_bbit(struct minix_super_block*,int);
+void minix_delete_bbit(struct minix_super_block*,int);
+
+int
+minix_balloc(struct vnode *vp, u_daddr_t lbof1, struct buf **bpp)
+{
+     struct buf *bp;
+     struct vnode *devvp;
+     struct minix_inode *ip;
+     struct minix_dinode *dip;
+     struct minix_super_block *sp;
+     u_daddr_t lbof0, lbn, pbn, lb;
+     int error;
+
+     ip    = (struct minix_inode*)vp->v_data;
+     dip   = &(ip->i_dino);
+     sp    = ip->i_su;
+     devvp = ip->i_devvp;
+
+     *bpp = NULL;
+
+     if (dip->i_size > 0)
+	  lbof0 = (dip->i_size - 1)/BLOCK_SIZE;
+     else
+	  lbof0 = 0;
+
+     if (lbof1 <= lbof0 && dip->i_size > 0) {  /* Block already exists! just return it */
+	  if ((error = minix_bmapfs(vp, lbof1, &pbn, NULL)) != 0)
+	       return error;
+	  if ((error = bread(devvp, pbn, BLOCK_SIZE, NOCRED, &bp)) != 0)
+	       return error;
+	  *bpp = bp;
+	  return 0;
+     }
+
+     /* The new block extends beyond the file */
+
+     /* Allocate all the blocks between lbof0 and lbof1 and zero them out */
+
+     for (lb=lbof0+1; lb<lbof1; lb++) {
+	  if ((error = minix_alloc(sp, &lbn, &pbn)) != 0)
+	       return error;
+	  if ((error = minix_addblk(ip, lb, lbn)) != 0) /* add new block to inode */
+	       return error;
+	  if ((bp = getblk(devvp, pbn, BLOCK_SIZE, 0, 0)) == NULL)
+	       return ENOSPC;
+	  bzero(bp->b_data, (size_t)BLOCK_SIZE);
+	  bwrite(bp);
+     }
+     /* Finally allocate lbof1, the required new block */
+
+     if (dip->i_size == 0)
+	  lbof1 = 0;
+     
+     if ((error = minix_alloc(sp, &lbn, &pbn)) != 0)
+	  return error;
+     if ((error = minix_addblk(ip, lbof1, lbn)) != 0) /* add new block to inode */
+	  return error;
+     if ((bp = getblk(devvp, pbn, BLOCK_SIZE, 0, 0)) == NULL)
+	  return ENOSPC;
+     bzero(bp->b_data, (size_t)BLOCK_SIZE);
+
+     *bpp = bp;
+     return 0;
+}
+/*
+ * Returns the next free physical block number and sets
+ * the corresponding bit in the bitmap to active;
+ */
+int
+minix_alloc(struct minix_super_block *sp, u_daddr_t *lblkp, u_daddr_t *pblkp)
+{
+	int blk, bblk, ic, off, pbn, error;
+	struct buf *bp;
+	struct vnode *devvp = sp->s_root->i_devvp;
+	union minix_block *mbp;
+	u_int16_t *buf = sp->s_bbmap;
+
+	*lblkp = 0;
+	*pblkp = 0;
+
+	minix_get_lock(&sp->bmap_lock);
+
+	if ((blk = minix_next_free_block(sp)) == 0) {
+		minix_free_lock(&sp->bmap_lock);
+		return ENOSPC;
+	}
+
+	minix_write_bbit(sp, blk);
+
+	ic = blk >> 4;                  /* Chunk that bit resides in */
+	bblk = blk / BITS_PER_BLOCK;    /* Block that bit is in */
+	off  = blk % BITS_PER_BLOCK;    /* Bit offset in block */
+	off >>= 4;                      /* Chunk offset in block */
+	bblk += 2 + sp->s_imap_blocks;  /* Adjust for offset in file  */
+
+	bp = NULL;
+	if ((error = minix_getblk(devvp, bblk, &bp)) != 0) {
+		if (bp != NULL)
+			brelse(bp);
+		minix_delete_bbit(sp, blk);
+		minix_free_lock(&sp->bmap_lock);
+		return error;
+	}
+
+	mbp = (union minix_block*)bp->b_data;
+	mbp->bitchunk[off] = buf[ic];
+
+	if ((error = minix_putblk(bp)) != 0) {
+		minix_delete_bbit(sp, blk);
+		if (bp != NULL) {
+		     bp->b_flags |= B_INVAL;
+		     brelse(bp);
+		}
+		minix_free_lock(&sp->bmap_lock);
+		return error;
+	}
+
+	minix_free_lock(&sp->bmap_lock);
+
+	pbn = blk + sp->s_boff;        /* Physical block number */
+	
+	*lblkp = pbn;
+	*pblkp = byte_to_blkn(blk_to_byte(pbn)); /* Suitable for bread */
+
+	return 0;
+}
+/*
+ * Deallocates a file block given the block number
+ */
+int
+minix_dalloc(struct minix_super_block *sp, u_daddr_t blk)
+{
+        int bblk, ic, off, error;
+	struct buf *bp;
+	struct vnode *devvp = sp->s_root->i_devvp;
+	u_int16_t *buf = sp->s_bbmap;
+	union minix_block *mbp;
+
+	minix_get_lock(&sp->bmap_lock);
+
+	blk -= sp->s_boff;              /* Convert to bit offset */
+
+	minix_delete_bbit(sp, blk);
+	
+	ic = blk >> 4;                  /* Chunk that bit resides in */
+	bblk = blk / BITS_PER_BLOCK;    /* Block that bit is in */
+	off  = blk % BITS_PER_BLOCK;    /* Bit offset in block */
+	off >>= 4;                      /* Chunk offset in block */
+	bblk += 2 + sp->s_imap_blocks;  /* Adjust for offset in file  */
+
+	bp = NULL;
+	if ((error = minix_getblk(devvp, bblk, &bp)) != 0) {
+		if (bp != NULL) {
+			bp->b_flags |= B_INVAL;
+			brelse(bp);
+		}
+	        minix_write_bbit(sp, blk);
+		minix_free_lock(&sp->bmap_lock);
+		return error;
+	}
+
+	mbp = (union minix_block*)bp->b_data;
+	mbp->bitchunk[off] = buf[ic];
+
+	if ((error = minix_putblk(bp)) != 0) {
+		minix_write_bbit(sp, blk);
+		if (bp != NULL) {
+			bp->b_flags |= B_INVAL;
+			brelse(bp);
+		}
+		minix_free_lock(&sp->bmap_lock);
+		return error;
+	}
+
+	minix_free_lock(&sp->bmap_lock);
+	
+	return 0;
+}
+/*
+ * Returns a logical block in a buffer
+ */
+int
+minix_getblk(struct vnode *devvp, block_t blk, struct buf **bpp)
+{
+	u_daddr_t offset;
+	int error;
+
+	offset = (u_daddr_t)byte_to_blkn(blk_to_byte(blk));
+	if ((error = bread(devvp, offset, BLOCK_SIZE, NOCRED, bpp)) != 0)
+		return error;
+	return 0;
+}
+/*
+ * Writes the buffer to disk
+ */
+int
+minix_putblk(struct buf *bp)
+{
+	bp->b_flags &= ~B_ASYNC;
+	return bwrite(bp);
+}
+/*
+ * Releases the buffer block from any connection with the minixfs
+ */
+void
+minix_freeblk(struct buf *bp) {
+	bp->b_flags |= B_FREEBUF;
+	brelse(bp);
+}
+/*
+ * Return buffer with the contents of block "offset" from the beginning of
+ * directory "ip".  If "res" is non-zero, fill it in with a pointer to the
+ * remaining space in the directory.
+ */
+int
+minix_blkatoff(struct vnode *vp, off_t offset, char **res, struct buf **bpp)
+{
+	struct buf *bp;
+	u_daddr_t lbn, pbn;
+	struct minix_inode *ip = (struct minix_inode*)vp->v_data;
+	struct vnode *devvp = ip->i_devvp;
+	int error;
+	
+	lbn = offset / BLOCK_SIZE;
+
+        if ((error = minix_bmapfs(vp, lbn, &pbn, (int*)NULL)) != 0)
+		return error;
+
+	*bpp = NULL;
+	bp   = NULL;
+	if ((error = bread(devvp, pbn, BLOCK_SIZE, NOCRED, &bp)) != 0) {
+		if (bp != NULL)
+			brelse(bp);
+		return error;
+	}
+	if (res != NULL)
+		*res = (char *)bp->b_data + (offset % BLOCK_SIZE);
+	*bpp = bp;
+	return 0;
+}
+/*
+ * The next few routines read or alter the in-core
+ * block bitmaps. Any locking that is required is
+ * assumed to occur in the calling programs.
+ */
+
+/*
+ * Return the number of free blocks
+ */
+int
+minix_free_block_count(struct minix_super_block *sp)
+{
+     short nbits[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
+     u_int16_t *bits = sp->s_bbmap;
+     int b1,b2,b3,b4,j,n;
+     int sum;
+
+     sum = sp->s_zones - sp->s_firstdatazone + 1;
+
+     n = ((sum-1)>>4) + 1;
+     for (j=0; j<n; j++) {
+	     b4 = (bits[j] >> 12) & 0xf;
+	     b3 = (bits[j] >>  8) & 0xf;
+	     b2 = (bits[j] >>  4) & 0xf;
+	     b1 =  bits[j]        & 0xf;
+	     sum -= (nbits[b1] + nbits[b2] +
+		     nbits[b3] + nbits[b4]);
+     }
+     return(sum < 0 ? 0 : sum+1); /* Add one to counter bit zero in the bitmap */
+}
+/*
+ * Returns the next free block
+ */
+int
+minix_next_free_block(struct minix_super_block *sp)
+{
+	int i, n, s, nb;
+	u_int16_t *buf = sp->s_bbmap;
+
+	nb = sp->s_zones - sp->s_firstdatazone + 1;
+	n = nb >> 4;
+	if ((nb % 16) > 0)
+		n += 1;
+
+	/* Look for a hole in a chunk, then */
+	/* search the chunk for the bit */
+
+	for (i=0; i<n; i++)
+		if (buf[i] != 0xffff)
+			for (s=0; s<16; s++)
+				if (!((buf[i] >> s) & 1))
+					return(s + (i << 4));
+	return 0;
+}
+/*
+ * Read a bit from the in-core data block bitmap
+ */
+int
+minix_read_bbit(struct minix_super_block *sp, int bit)
+{
+	u_int16_t *buf = sp->s_bbmap;     
+	int w, s;
+     
+	w = bit >> 4;
+	s = bit % 16;
+
+	return ((int)((buf[w] >> s) & 0x1));
+}
+/*
+ * Write a bit into the in-core data block bitmap
+ */
+void
+minix_write_bbit(struct minix_super_block *sp, int bit)
+{
+	u_int16_t *buf = sp->s_bbmap; 
+	int w, s;
+
+	w = bit >> 4;
+	s = bit % 16;
+  
+	buf[w] |= (1 << s);
+}
+/*
+ * Delete a bit in the in-core data block bitmap
+ */
+void
+minix_delete_bbit(struct minix_super_block *sp, int bit)
+{
+	u_int16_t *buf = sp->s_bbmap;
+	int w, s;
+  
+	w = bit >> 4;
+	s = bit % 16;
+  
+	buf[w] &= ~(1 << s);
+}
+/*
+ * Figures out the number of data blocks in a file,
+ * including the indirect blocks, given the size
+ * of the file.
+ */
+int
+minix_num_blocks(u_int16_t mode, u_int32_t size)
+{
+     int nindirects, indirect = 0;
+     int nblocks = 1;
+
+     switch (mode & I_TYPE) {         
+     case I_LINK:
+	  if (size < MINIX_MAXSYMLINKLEN)
+	       return 0;   /* No data blocks for a short symlink */
+	  return 1;        /* One data block for a long symlink */
+     case I_REGULAR:
+	  break;
+     default:     /* This includes Sock,B_Spec,C_Spec, and Fifo. */
+	  return 0;
+     }
+     if (size == 0)
+	  return 0;
+     /*
+      * nblocks is set initially to one above. Add the extra blocks below.
+      */
+     nblocks += (size - 1) / BLOCK_SIZE;
+
+     /* Figure out whether we have indirect blocks */
+
+     if (nblocks > TOT_DIRECT)
+	  indirect++;         /* Single indirect */
+
+     if (nblocks > TOT_SINDIRECT)
+	  indirect++;         /* Double indirect */
+
+     if (nblocks > TOT_DINDIRECT)
+	  indirect++;         /* Triple indirect */
+	  
+     switch (indirect) {
+     case 0:
+	  return nblocks;
+     case 1:
+	  return (nblocks + 1);  /* One for the indirect */
+     case 2:
+	  nindirects = (nblocks - TOT_SINDIRECT - 1)/V2_NR_INDIRECTS;
+	  return (nblocks + nindirects + 2);
+     case 3:
+	  nindirects = (nblocks - TOT_DINDIRECT - 1) / V2_NR_INDIRECTS;
+	  nindirects += nindirects / V2_NR_INDIRECTS;  /* Number of extra doubles */
+	  return (nblocks + nindirects + V2_NR_INDIRECTS + 2);	  
+     default:
+	  break;
+     }
+     return 0;  /* NOTREACHED */
+}
diff -ruN src.orig/sys/fs/minixfs/minix_ihash.c src/sys/fs/minixfs/minix_ihash.c
--- src.orig/sys/fs/minixfs/minix_ihash.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_ihash.c	Mon Jan 20 23:22:43 2003
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This code was lifted from FreeBSD and modified for Minix.
+ * It doesn't work, however. If I follow ufs or ext2fs in
+ * implementing this code, it leads to a panic (page fault)
+ * after an umount then mount again. Somehow a vnode gets
+ * tarnished and not cleaned up during the dismount call.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <fs/minixfs/minix.h>
+
+static MALLOC_DEFINE(M_MNXIHASH, "MINIX ihash", "MINIX Inode hash tables");
+
+void
+minix_ihashclean(struct vnode*);
+/*
+ * Structures associated with inode cacheing.
+ */
+static LIST_HEAD(mnxihashhead, minix_inode) *mnxihashtb;
+static u_long	minix_ihash;		/* size of hash table - 1 */
+#define	MNXINOHSH(device, inum)	(&mnxihashtb[(minor(device) + (inum)) & minix_ihash])
+#ifndef NULL_SIMPLELOCKS
+static struct simplelock minix_ihash_slock;
+#endif
+
+/*
+ * Initialize inode hash table.
+ */
+void
+minix_ihashinit(void)
+{
+     if (1)
+	  return;
+     
+	mnxihashtb = hashinit(desiredvnodes, M_MNXIHASH, &minix_ihash);
+	simple_lock_init(&minix_ihash_slock);
+}
+/*
+ * Free the inode hash table
+ */
+void
+minix_ihashuninit(void)
+{
+     if (1)
+	  return;
+     
+     if (mnxihashtb) {
+	  free(mnxihashtb, M_MNXIHASH);
+	  mnxihashtb = NULL;
+     }
+}
+/*
+ * Clean out the hash table
+ */
+void
+minix_ihashclean(struct vnode *devvp)
+{
+	struct minix_inode *ip;
+
+	simple_lock(&minix_ihash_slock);
+	simple_unlock(&minix_ihash_slock);
+}
+
+/*
+ * Use the device/inum pair to find the incore inode, and return a pointer
+ * to it. If it is in core, return it, even if it is locked.
+ */
+struct vnode *
+minix_ihashlookup(dev_t dev, ino_t inum)
+{
+	struct minix_inode *ip;
+/*
+	simple_lock(&minix_ihash_slock);
+	for (ip = MNXINOHSH(dev, inum)->lh_first; ip; ip = ip->i_hash.le_next)
+		if (inum == ip->i_number && dev == ip->i_dev)
+			break;
+	simple_unlock(&minix_ihash_slock);
+
+	if (ip) {
+		ip->i_vnode->v_type = minix_get_vtype(ip);
+		return (ip->i_vnode);
+	}
+*/	
+	return (NULLVP);
+}
+
+/*
+ * Use the device/inum pair to find the incore inode, and return a pointer
+ * to it. If it is in core, but locked, wait for it.
+ */
+struct vnode *
+minix_ihashget(dev_t dev, ino_t inum)
+{
+	struct proc *p = curproc;	/* XXX */
+	struct minix_inode *ip;
+	struct vnode *vp;
+/*
+loop:
+	simple_lock(&minix_ihash_slock);
+	for (ip = MNXINOHSH(dev, inum)->lh_first; ip; ip = ip->i_hash.le_next) {
+		if (inum == ip->i_number && dev == ip->i_dev) {
+			vp = ip->i_vnode;
+			simple_lock(&vp->v_interlock);
+			simple_unlock(&minix_ihash_slock);
+			if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, p))
+				goto loop;
+			vp->v_type = minix_get_vtype(ip);
+			return (vp);
+		}
+	}
+	simple_unlock(&minix_ihash_slock);
+*/
+	return (NULLVP);
+}
+
+/*
+ * Insert the inode into the hash table, and return it locked.
+ */
+void
+minix_ihashins(struct minix_inode *ip)
+{
+	struct proc *p = curproc;		/* XXX */
+	struct mnxihashhead *ipp;
+
+	if (1)
+	     return;
+
+	/* lock the inode, then put it on the appropriate hash list */
+	lockmgr(&ip->i_lock, LK_EXCLUSIVE, (struct simplelock *)0, p);
+
+	simple_lock(&minix_ihash_slock);
+	ipp = MNXINOHSH(ip->i_dev, ip->i_number);
+	LIST_INSERT_HEAD(ipp, ip, i_hash);
+	ip->i_flag |= IN_HASHED;
+	simple_unlock(&minix_ihash_slock);
+}
+
+/*
+ * Remove the inode from the hash table.
+ */
+void
+minix_ihashrem(struct minix_inode *ip)
+{
+     if (1)
+	  return;
+     
+	simple_lock(&minix_ihash_slock);
+	if (ip->i_flag & IN_HASHED) {
+		ip->i_flag &= ~IN_HASHED;
+		LIST_REMOVE(ip, i_hash);
+#ifdef DIAGNOSTIC
+		ip->i_hash.le_next = NULL;
+		ip->i_hash.le_prev = NULL;
+#endif
+	}
+	simple_unlock(&minix_ihash_slock);
+}
+
diff -ruN src.orig/sys/fs/minixfs/minix_inode.c src/sys/fs/minixfs/minix_inode.c
--- src.orig/sys/fs/minixfs/minix_inode.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_inode.c	Fri Jan 31 17:56:23 2003
@@ -0,0 +1,830 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/lock.h>
+#include <sys/time.h>
+#include <sys/malloc.h>
+#include <sys/namei.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+
+#include <fs/minixfs/minix.h>
+
+int  minix_read_ibit(struct minix_super_block*,int);
+void minix_write_ibit(struct minix_super_block*,int);
+void minix_delete_ibit(struct minix_super_block*,int);
+static int minix_truncatefs(struct minix_inode*,u_daddr_t,off_t);
+
+int
+minix_makeinode(int mode, struct vnode *dvp,
+                struct vnode **vpp, struct componentname *cnp)
+{
+	struct minix_inode *dip, *ip;
+	struct vnode *tvp;
+	struct minix_direct newdir;
+	int error;
+
+	dip = (struct minix_inode*)(dvp->v_data);
+	*vpp = NULL;
+	
+	if ((mode & IFMT) == 0)
+		mode |= IFREG;
+
+	if ((error = minix_valloc(dvp, mode, cnp->cn_cred, &tvp)) != 0)
+		return error;
+
+	ip = (struct minix_inode*)(tvp->v_data);
+	ip->i_dino.i_gid = dip->i_dino.i_gid;
+	
+	/*
+	 * If we are not the owner of the directory,
+	 * and we are hacking owners here, (only do this where told to)
+	 * and we are not giving it TO root, (would subvert quotas)
+	 * then go ahead and give it to the other user.
+	 * Note that this drops off the execute bits for security.
+	 */
+	if ((dvp->v_mount->mnt_flag & MNT_SUIDDIR) &&
+	    (dip->i_dino.i_mode & ISUID) &&
+	    (dip->i_dino.i_uid != cnp->cn_cred->cr_uid) && dip->i_dino.i_uid) {
+		ip->i_dino.i_uid = dip->i_dino.i_uid;
+		mode &= ~07111;
+	} else
+		ip->i_dino.i_uid = cnp->cn_cred->cr_uid;
+
+	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+	ip->i_mode = mode;
+	ip->i_dino.i_mode = mode;
+	tvp->v_type = minix_get_vtype(ip); /* Rest init'd in getnewvnode(). */
+	ip->i_nlink = 1;
+
+	if ((ip->i_mode & ISGID) && !groupmember(ip->i_dino.i_gid, cnp->cn_cred) &&
+	    suser_xxx(cnp->cn_cred, 0, 0))
+		ip->i_mode &= ~ISGID;
+	/*
+	 * Make sure inode goes to disk before directory entry.
+	 */
+	if ((error = minix_update(tvp)) != 0)
+		goto bad;
+	
+	minix_makedirentry(ip, cnp, &newdir);
+	
+	if ((error = minix_direnter(dvp, tvp, &newdir, cnp)) != 0)
+		goto bad;
+	
+	*vpp = tvp;
+
+	return 0;
+bad:
+	/*
+	 * Write error occurred trying to update the inode
+	 * or the directory so must deallocate the inode.
+	 */
+	ip->i_nlink = 0;
+	ip->i_flag |= IN_CHANGE;
+	vput(tvp);
+	return error;
+}
+
+/*
+ * Add a block to the end of a file. lbn is the logical block
+ * offset in the file and pbn is the physical offset in the
+ * file system. It is the responsibility of the calling
+ * routine to guarantee that the next block to add is at the
+ * logical end of the file. This routine returns EIO otherwise.
+ */
+int
+minix_addblk(struct minix_inode *ip, u_daddr_t lbn, u_daddr_t pbn)
+{
+	struct vnode *devvp = ip->i_devvp;
+	struct minix_dinode *dip = &(ip->i_dino);
+	struct minix_super_block *sp = ip->i_su;
+	union minix_block *mbp;
+	struct buf *bp;
+	u_daddr_t pind;
+	u_daddr_t id0, id1, id2, id3, off;
+	int error, indirect = 0;
+
+	pind = lbn + 1;
+
+	if (pind > TOT_DIRECT)
+		indirect++;
+	if (pind > TOT_SINDIRECT)
+		indirect++;
+	if (pind > TOT_DINDIRECT)
+		indirect++;
+	if (pind > TOT_TINDIRECT)
+		indirect++;
+
+	switch (indirect) {
+	case 0:
+		if (dip->i_block[lbn] != 0)
+			return EIO;
+		dip->i_block[lbn] = pbn;
+		break;
+	case 3:
+		off = lbn - TOT_DINDIRECT;
+		id2 = off >> INDIRECT_SHIFT;
+		id3 = id2 >> INDIRECT_SHIFT;
+		id2 = id2 %  V2_NR_INDIRECTS;
+		id1 = off %  V2_NR_INDIRECTS;
+		if (dip->i_block[V2_NR_DBLOCKS+2] == 0) {
+			/* Generate a triple indirect block */
+			if ((error = minix_alloc(sp, &id0, &pind)) != 0)
+				return error;
+			if ((error = minix_getblk(devvp, id0, &bp)) != 0)
+				return error;
+			bzero(bp->b_data, BLOCK_SIZE);
+			dip->i_block[V2_NR_DBLOCKS+2] = id0;
+			bwrite(bp);
+		} else
+			id0 = dip->i_block[V2_NR_DBLOCKS+2];
+		
+		if ((error = minix_getblk(devvp, id0, &bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		id0 = mbp->ind[id3];
+		if (id0 == 0) {
+			/* Generate a double indirect block */
+			if ((error = minix_alloc(sp, &id0, &pind)) != 0)
+				return error;
+			mbp->ind[id3] = id0;
+			bwrite(bp);
+			if ((error = minix_getblk(devvp, id0, &bp)) != 0)
+				return error;
+			bzero(bp->b_data, BLOCK_SIZE);
+			bwrite(bp);
+		} else
+			bqrelse(bp);
+		goto rind2;
+		break;
+	case 2:
+		off = lbn - TOT_SINDIRECT;
+		id2 = off >> INDIRECT_SHIFT;
+		id1 = off % V2_NR_INDIRECTS;
+		if (dip->i_block[V2_NR_DBLOCKS+1] == 0) {
+			/* Generate double indirect block */
+			if ((error = minix_alloc(sp, &id0, &pind)) != 0)
+				return error;
+			if ((error = minix_getblk(devvp, id0, &bp)) != 0)
+				return error;
+			bzero(bp->b_data, BLOCK_SIZE);
+			dip->i_block[V2_NR_DBLOCKS+1] = id0;
+			bwrite(bp);
+		} else
+			id0 = dip->i_block[V2_NR_DBLOCKS+1];
+	rind2:
+		if ((error = minix_getblk(devvp, id0, &bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		id0 = mbp->ind[id2];
+		if (id0 == 0) {
+			/* Generate a single indirect block */
+			if ((error = minix_alloc(sp, &id0, &pind)) != 0)
+				return error;
+			mbp->ind[id2] = id0;
+			bwrite(bp);
+			if ((error = minix_getblk(devvp, id0, &bp)) != 0)
+				return error;
+			bzero(bp->b_data, BLOCK_SIZE);
+			bwrite(bp);
+		} else
+			bqrelse(bp);
+		goto rind1;
+		break;
+	case 1:
+		id1 = lbn - TOT_DIRECT;
+		if (dip->i_block[V2_NR_DBLOCKS] == 0) {
+			/* Generate a single indirect block */
+			if ((error = minix_alloc(sp, &id0, &pind)) != 0)
+				return error;
+			if ((error = minix_getblk(devvp, id0, &bp)) != 0)
+				return error;
+			bzero(bp->b_data, BLOCK_SIZE);
+			dip->i_block[V2_NR_DBLOCKS] = id0;
+			bwrite(bp);
+		} else
+			id0 = dip->i_block[V2_NR_DBLOCKS];
+	rind1:
+		if ((error = minix_getblk(devvp, id0, &bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		mbp->ind[id1] = pbn;
+		bwrite(bp);
+		break;
+	default:
+		printf("minix_addblk: too large: lbn = %d\n",(int)lbn);
+		return ENOSPC;
+	}
+
+	ip->i_blocks += 1;
+	ip->i_flag |= IN_CHANGE | IN_UPDATE;
+
+	return 0;
+}
+
+/*
+ * Frees all blocks from the logical offset lbn to the end of the file.
+ */
+static int
+minix_truncatefs(struct minix_inode *ip, u_daddr_t lbn, off_t length)
+{
+	struct buf *bp;
+	union minix_block *mbp;
+	struct vnode *devvp = ip->i_devvp;
+	struct minix_dinode *dip = &(ip->i_dino);
+	struct minix_super_block *sp = ip->i_su;
+	long lb0, lb, off = 0;
+	u_daddr_t blk, id0;
+	int error, indirect;
+	int id1, id2, id3;
+	int delete1, delete2, delete3;
+
+	if (dip->i_size == 0)
+		return 0;
+
+	lb = lb0 = (dip->i_size - 1)/BLOCK_SIZE;
+	
+	if (lb < lbn)
+		return EIO;
+	
+	do {
+		delete1  = 0;
+		delete2  = 0;
+		delete3  = 0;
+		indirect = 0;
+		if (lb >= TOT_DIRECT)
+			indirect++;
+		if (lb >= TOT_SINDIRECT)
+			indirect++;
+		if (lb >= TOT_DINDIRECT)
+			indirect++;
+		if (lb >= TOT_TINDIRECT)
+			indirect++;
+
+		switch (indirect) {
+		case 0:
+			if ((error = minix_dalloc(sp, dip->i_block[lb])) != 0)
+				return error;
+			dip->i_block[lb] = 0;
+			break;
+		case 3:
+			off = lb - TOT_DINDIRECT;
+			id2 = off >> INDIRECT_SHIFT;
+			id3 = id2 >> INDIRECT_SHIFT;
+			id2 = id2 % V2_NR_INDIRECTS;
+			id1 = off % V2_NR_INDIRECTS;
+			if (id1 == 0) {
+				delete1 = 1;
+				if (id2 == 0) {
+					delete2 = 1;
+					if (id3 == 0)
+						delete3 = 1;
+				}
+			}
+			if ((error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS+2],&bp)) != 0)
+				return 0;
+			mbp = (union minix_block*)bp->b_data;
+			id0 = mbp->ind[id3];
+			if (delete3) {
+				blk = dip->i_block[V2_NR_DBLOCKS+2];
+				dip->i_block[V2_NR_DBLOCKS+2] = 0;
+				if ((error = minix_dalloc(sp, blk)) != 0)
+					return error;
+				brelse(bp);
+			} else {
+				if (delete2) {
+					mbp->ind[id3] = 0;
+					bwrite(bp);
+				} else
+					bqrelse(bp);
+			}
+			goto rind2;
+		case 2:
+			off = lb - TOT_SINDIRECT;
+			id2 = off >> INDIRECT_SHIFT;
+			id1 = off % V2_NR_INDIRECTS;
+			if (id1 == 0) {
+				delete1 = 1;
+				if (id2 == 0)
+					delete2 = 1;
+			}
+			id0 = dip->i_block[V2_NR_DBLOCKS+1];
+			if (delete2)
+				dip->i_block[V2_NR_DBLOCKS+1] = 0;
+		rind2:
+			if (delete2) {
+				if ((error = minix_dalloc(sp,id0)) != 0)
+					return error;
+			}
+			if ((error = minix_getblk(devvp,id0,&bp)) != 0)
+				return error;
+			mbp = (union minix_block*)bp->b_data;
+			id0 = mbp->ind[id2];
+			if (delete2)
+				brelse(bp);
+			else {
+				if (delete1) {
+					mbp->ind[id2] = 0;
+					bwrite(bp); 
+				} else
+					bqrelse(bp);
+			}
+			goto rind1;
+		case 1:
+			id1 = lb - TOT_DIRECT;
+			if (id1 == 0)
+				delete1 = 1;
+			id0 = dip->i_block[V2_NR_DBLOCKS];
+			if (delete1)
+				dip->i_block[V2_NR_DBLOCKS] = 0;
+		rind1:
+			if ((error = minix_getblk(devvp,id0,&bp)) != 0)
+				return error;
+			mbp = (union minix_block*)bp->b_data;
+			if ((error = minix_dalloc(sp, mbp->ind[id1])) != 0)
+				return error;
+			if (delete1) {
+				if ((error = minix_dalloc(sp, id0)) != 0)
+					return error;
+				brelse(bp);
+			} else {
+				mbp->ind[id1] = 0;
+				bwrite(bp);
+			}
+			break;
+		default:
+			return EIO;
+		}
+
+	} while (lbn < lb--);
+
+	/* Clean up the tail end of the last block if necessary */
+
+	if (length < lbn*BLOCK_SIZE) {
+		if ((error = minix_getblk(devvp,lbn-1,&bp)) != 0)
+			return error;
+		lb0 = lbn*BLOCK_SIZE - length;
+		off = BLOCK_SIZE - lb0;
+		mbp = (union minix_block*)bp->b_data;
+		bzero(mbp->data+off,lb0);
+		bwrite(bp);
+	}
+	
+	ip->i_blocks = lbn;
+	ip->i_flag |= IN_CHANGE | IN_UPDATE;
+	
+	return 0;
+}
+/*
+ * Update the access, modified, and inode change times as specified by the
+ * IN_ACCESS, IN_UPDATE, and IN_CHANGE flags respectively.  Write the inode
+ * to disk if the IN_MODIFIED flag is set (it may be set initially, or by
+ * the timestamp update).
+ */
+int
+minix_update(struct vnode *vp)
+{
+	struct minix_inode *ip;
+
+	ip = (struct minix_inode *)vp->v_data;
+
+	minix_itimes(vp);
+	
+	if ((ip->i_flag & IN_MODIFIED) == 0)
+		return 0;
+	
+	ip->i_flag &= ~(IN_MODIFIED);
+	
+	if (vp->v_mount->mnt_flag & MNT_RDONLY)
+		return 0;
+
+	cache_purge(vp);
+
+	return minix_iput(ip);
+}
+
+void
+minix_itimes(struct vnode *vp)
+{
+     	struct minix_inode *ip;
+	struct timespec ts;
+
+	ip = (struct minix_inode *)vp->v_data;
+	
+	if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_UPDATE)) == 0)
+		return;
+
+	if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+		vfs_timestamp(&ts);
+		if (ip->i_flag & IN_ACCESS) {
+			ip->i_dino.i_atime = ts.tv_sec;
+		}
+		if (ip->i_flag & IN_UPDATE) {
+			ip->i_dino.i_mtime = ts.tv_sec;
+		}
+		if (ip->i_flag & IN_CHANGE) {
+			ip->i_dino.i_ctime = ts.tv_sec;
+		}
+		ip->i_flag |= IN_MODIFIED;
+	}
+	ip->i_flag &= ~(IN_ACCESS | IN_CHANGE | IN_UPDATE);
+}
+/*
+ * Truncate the inode to at most length size, freeing the
+ * disk blocks.
+ */
+int
+minix_truncate(struct vnode *vp,
+               off_t length,
+               int flags,
+               struct ucred *cred,
+               struct proc  *p)
+{
+	struct minix_inode *ip = (struct minix_inode*)vp->v_data;
+	struct minix_dinode *dip = &(ip->i_dino);
+	u_daddr_t lbn0, lbn1;
+	int error;
+
+	if (length < 0)
+	     return EINVAL;
+
+	/* Data in a short symlink is stored in the inode */
+
+	if (vp->v_type == VLNK && 
+	    dip->i_size < vp->v_mount->mnt_maxsymlinklen) {
+		bzero((char*)ip->i_shortlink, MINIX_MAXSYMLINKLEN);
+		dip->i_size = 0;
+		ip->i_flag |= IN_CHANGE | IN_UPDATE;
+		return minix_update(vp);
+	}
+	if (dip->i_size <= length) {
+	     ip->i_flag |= IN_CHANGE | IN_CHANGE;
+	     return minix_update(vp);
+	}
+
+	/* lbn1 is the last block in the file */
+
+	lbn1 = (dip->i_size - 1)/BLOCK_SIZE;
+
+	/* lbn0 is the first block to eliminate */
+	
+	if (length > 0)
+		lbn0 = (length - 1)/BLOCK_SIZE + 1;
+	else
+		lbn0 = 0;    /* Eliminate all blocks */
+
+	if (lbn0 > lbn1)     /* Silly but check anyway */
+		return EIO;
+
+	/* truncatefs will eliminate all blocks from lbn0 to lbn1 inclusive */
+
+	if ((error = minix_truncatefs(ip, lbn0, length)) != 0)
+		return error;
+	
+	dip->i_size = length;    /* The new length of the file */
+	ip->i_blocks = lbn0;     /* The number of blocks left in the file */
+	ip->i_flag |= IN_CHANGE | IN_UPDATE;
+	
+	return minix_update(vp);
+}
+
+/* Clean up the inode associated with the vnode before freeing it */
+
+/*
+ * Free an inode; put it back into the block that it belongs
+ * and then free the block. Update the superblock if necessary.
+ */
+
+int
+minix_vfree(struct vnode *vp, ino_t ino, int mode)
+{
+	struct minix_inode *ip;
+	struct minix_super_block *sp;
+
+	ip = (struct minix_inode *)vp->v_data;
+	sp = ip->i_su;
+
+        return minix_dialloc(sp,ino);
+}
+/*
+ * Get a minix inode with no vnode attached. Routine
+ * assumes that space for the inode, pointed to by ip,
+ * is supplied by the caller. Also notice that there
+ * is no vnode involved here, so the elements i_vnode
+ * and i_mmp are set to NULL and i_parent = 0;
+ */
+int
+minix_iget(struct vnode *devvp, struct minix_super_block *sp,
+           mino_t ino, struct minix_inode *ip)
+{
+     struct buf *bp;
+     block_t blk;
+     union minix_block *mbp;
+     int error;
+
+     bzero((caddr_t)ip, sizeof(struct minix_inode));
+     blk = (block_t)ino_to_byte(sp,ino)/BLOCK_SIZE;
+     
+     bp = NULL;
+     if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
+	     if (bp != NULL)
+		     brelse(bp);
+	     return error;
+     }
+
+     mbp = (union minix_block*)bp->b_data;
+     ip->i_dino = mbp->dinode[(ino-1)%V2_INODES_PER_BLOCK];
+     
+     bqrelse(bp);
+
+     ip->i_number = ino;
+     ip->i_parent = 0;
+     ip->i_su = sp;
+     ip->i_devvp = devvp;
+     ip->i_vnode = NULL;
+     ip->i_mode = ip->i_dino.i_mode;
+     ip->i_mmp = NULL;
+     ip->i_dev = devvp->v_rdev;
+     
+     return 0;
+}
+/*
+ * Write an inode; assumes that the caller will
+ * release the space pointed to by ip when finished.
+ */
+int
+minix_iput(struct minix_inode *ip)
+{
+	struct vnode *devvp;
+	struct buf *bp;
+	union minix_block *mbp;
+	mino_t ino;
+	block_t blk;
+	int error;
+
+	devvp = ip->i_devvp;
+	ino = ip->i_number;
+	blk = (block_t)ino_to_byte(ip->i_su,ino)/BLOCK_SIZE;
+
+	bp = NULL;
+	if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
+		if (bp != NULL)
+			brelse(bp);
+		return error;
+	}
+
+	mbp = (union minix_block*)bp->b_data;
+	mbp->dinode[(ino-1)%V2_INODES_PER_BLOCK] = ip->i_dino;
+	
+	return minix_putblk(bp);
+}
+/*
+ * Returns the next free inode number, sets the bitmap
+ * to active, writes the bitmap to disk, and cleans
+ * the on disk dinode.
+ */
+int
+minix_ialloc(struct minix_super_block *sp, int *inop)
+{
+	int ino, ic, blk, off, error;
+	u_int16_t *buf = sp->s_ibmap;
+	struct vnode *devvp = sp->s_root->i_devvp;
+	struct buf *bp;
+	struct minix_inode in;
+	union minix_block *mbp;
+
+	*inop = 0;
+
+	minix_get_lock(&sp->imap_lock);
+	
+	if ((ino = minix_next_free_inode(sp)) == 0) {
+		minix_free_lock(&sp->imap_lock);
+		printf("minix_next_free_inode: returned zero inode\n");
+		return ENOSPC;
+	}
+
+	minix_write_ibit(sp, ino);
+
+	ic  = ino >> 4;                   /* Chunk that bit resides in */
+	blk = ino / BITS_PER_BLOCK;       /* Block that bit is in */
+	off = ino % BITS_PER_BLOCK;       /* Bit offset in block */
+	off >>= 4;                        /* Chunk offset in block */
+	blk += 2;                         /* Adjust for offset in file */
+
+	bp = NULL;
+	if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
+		if (bp != NULL)
+			brelse(bp);
+		minix_delete_ibit(sp, ino);
+		minix_free_lock(&sp->imap_lock);
+		return error;
+	}
+
+	mbp = (union minix_block*)bp->b_data;
+	mbp->bitchunk[off] = buf[ic];
+
+	if ((error = minix_putblk(bp)) != 0) {
+		minix_delete_ibit(sp,ino);
+		if (bp != NULL) {
+			bp->b_flags |= B_INVAL;
+			brelse(bp);
+		}
+		minix_free_lock(&sp->imap_lock);
+		return error;
+	}
+
+	if ((error = minix_iget(devvp,sp,ino,&in)) != 0) {
+		minix_free_lock(&sp->imap_lock);
+		minix_dialloc(sp, ino);
+		return error;
+	}
+
+	minix_wipe_dinode(&(in.i_dino));
+
+	if ((error = minix_iput(&in)) != 0) {
+		minix_free_lock(&sp->imap_lock);
+		minix_dialloc(sp,ino);
+		return error;
+	}
+
+	minix_free_lock(&sp->imap_lock);
+
+	*inop = ino;
+
+	return 0;
+}
+int
+minix_dialloc(struct minix_super_block *sp, int ino)
+{
+	int blk, ic, off, error;
+	u_int16_t *buf = sp->s_ibmap;
+	struct vnode *devvp = sp->s_root->i_devvp;
+	struct buf *bp;
+	union minix_block *mbp;
+
+	minix_get_lock(&sp->imap_lock);
+
+	minix_delete_ibit(sp, ino);
+
+	ic  = ino >> 4;                   /* Chunk that bit resides in */
+	blk = ino / BITS_PER_BLOCK;       /* Block that bit is in */
+	off = ino % BITS_PER_BLOCK;       /* Bit offset in block */
+	off >>= 4;                        /* Chunk offset in block */
+	blk += 2;                         /* Adjust for offset in file */
+
+	bp = NULL;
+	if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
+		if (bp != NULL)
+			brelse(bp);
+		minix_write_ibit(sp, ino);
+		minix_free_lock(&sp->imap_lock);
+		return error;
+	}
+
+	mbp = (union minix_block*)bp->b_data;
+	mbp->bitchunk[off] = buf[ic];
+
+	if ((error = minix_putblk(bp)) != 0) {
+		minix_write_ibit(sp,ino);
+		if (bp != NULL) {
+			bp->b_flags |= B_INVAL;
+			brelse(bp);
+		}
+		minix_free_lock(&sp->imap_lock);
+		return error;
+	}
+
+	minix_free_lock(&sp->imap_lock);
+	
+	return 0;
+}
+/*
+ * The next few routines manipulate/read the inode bitmaps.
+ * Any locking that is needed is assumed to occur in the
+ * calling programs.
+ */
+/*
+ * Counts the number of free inodes
+ */
+int
+minix_free_inode_count(struct minix_super_block *sp)
+{
+     short nbits[] = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 };
+     u_int16_t *bits = sp->s_ibmap;
+     int b1,b2,b3,b4,j,n;
+     int sum;
+
+     sum = sp->s_ninodes;
+
+     n = ((sp->s_ninodes-1)>>4);
+     for (j=0; j<n; j++) {
+	     b4 = (bits[j] >> 12) & 0xf;
+	     b3 = (bits[j] >>  8) & 0xf;
+	     b2 = (bits[j] >>  4) & 0xf;
+	     b1 =  bits[j]        & 0xf;
+	     sum -= (nbits[b1] + nbits[b2] +
+		     nbits[b3] + nbits[b4]);
+     }
+     return(sum < 0 ? 0 : sum+1); /* Add one to counter bit zero in the bitmap */
+}
+/*
+ * Return the next free inode from the inode bitmap (very simple)
+ */
+int
+minix_next_free_inode(struct minix_super_block *sp)
+{
+	int i, n, s;
+	u_int16_t *buf = sp->s_ibmap;
+
+	n = sp->s_ninodes >> 4;
+	if ((sp->s_ninodes % 16) > 0)
+		n += 1;
+
+	/* Look for a hole in a chunk, then */
+	/* search the chunk for the bit */
+	
+	for (i=0; i<n; i++)
+		if (buf[i] != 0xffff)
+			for (s=0; s<16; s++)
+				if (!((buf[i] >> s) & 1))
+					return(s + (i << 4));
+	return 0;
+}
+/*
+ * Read a bit from the in-core inode bitmap
+ * note log2(16) = 4
+ */
+int
+minix_read_ibit(struct minix_super_block *sp, int bit)
+{
+	u_int16_t *buf = sp->s_ibmap;     
+	int w, s;
+     
+	w = bit >> 4;
+	s = bit % 16;
+
+	return ((int)((buf[w] >> s) & 0x1));
+}
+/*
+ * Write a bit into the in-core inode bitmap
+ */
+void
+minix_write_ibit(struct minix_super_block *sp, int bit)
+{
+	u_int16_t *buf = sp->s_ibmap; 
+	int w, s;
+  
+	w = bit >> 4;
+	s = bit % 16;
+  
+	buf[w] |= (1 << s);
+}
+/*
+ * Delete a bit in the in-core inode bitmap
+ */
+void
+minix_delete_ibit(struct minix_super_block *sp, int bit)
+{
+	u_int16_t *buf = sp->s_ibmap;
+	int w, s;
+  
+	w = bit >> 4;
+	s = bit % 16;
+  
+	buf[w] &= ~(1 << s);
+}
+
+void
+minix_wipe_dinode(struct minix_dinode *dip)
+{
+      bzero((char*)dip, sizeof(struct minix_dinode));
+}
diff -ruN src.orig/sys/fs/minixfs/minix_locks.c src/sys/fs/minixfs/minix_locks.c
--- src.orig/sys/fs/minixfs/minix_locks.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_locks.c	Thu Jan 23 23:19:21 2003
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/buf.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#include <fs/minixfs/minix.h>
+
+u_int32_t _minix_alock(u_int32_t*);
+
+void
+minix_get_lock(u_int32_t *lock)
+{
+     while(_minix_alock(lock))
+	  tsleep(lock, PINOD, "Mnxlck", 0);
+}
+
+void
+minix_free_lock(u_int32_t *lock)
+{
+	*lock = 0l;
+	wakeup(lock);
+}
diff -ruN src.orig/sys/fs/minixfs/minix_lookup.c src/sys/fs/minixfs/minix_lookup.c
--- src.orig/sys/fs/minixfs/minix_lookup.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_lookup.c	Sun Feb  2 19:19:37 2003
@@ -0,0 +1,1284 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Much of the code below follows FreeBSD's ufs implementation */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/ucred.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+
+#include <fs/minixfs/minix.h>
+
+struct minix_namei_args {
+	struct vnode *a_devvp;
+	struct minix_super_block *a_sp;
+	struct ucred *a_cred;
+	struct proc *a_pp;
+};
+
+int minix_lookup(struct vop_cachedlookup_args*);
+
+static int minix_namei(struct minix_namei_args*,char*,ino_t*,ino_t*,int*);
+static int minix_newdirent(struct vnode*);
+static int minix_dirsize(struct vnode*, u_long*);
+static int minix_dircount(struct vnode*, u_long*);
+static int minix_dirmove(struct vnode*, struct minix_direct*, u_long);
+static int minix_dirclean(struct vnode*);
+static char *minix_strtok(char*, char*);
+static char *minix_strtok_r(char*, char*, char**);
+
+ino_t root_inode, current_inode;  /* For communication with minix_namei(). */
+
+int
+minix_lookup(struct vop_cachedlookup_args *ap)
+     	/*
+       struct vop_cachedlookup_args
+         {
+	     struct vnode *a_dvp;
+	     struct vnode **a_vpp;
+	     struct componentname *a_cnp;
+         } *ap;
+     */
+{
+    struct vnode *dvp = ap->a_dvp;
+    struct vnode **vpp = ap->a_vpp;
+    struct componentname *cnp = ap->a_cnp;
+    struct vnode *vp = NULL;
+    int error, isdot, entry;
+    u_long flags, islastcn, lockparent, nameiop, wantparent;
+    struct proc *pp;
+    struct mount *mp;
+    struct ucred *cred;
+    struct minix_inode *dip;       /* minix inode for directory being searched */
+    struct minix_inode *ip;        /* target inode */
+    struct minixmount  *mmp;       /* minix mount information */
+    struct minix_super_block *msp;
+    struct minix_namei_args nia;
+    ino_t mdino;                  /* minix initial directory inode number */
+    ino_t mino, pino;             /* minix target inode number and parent */
+    char path[PATH_MAX+1];
+
+    nameiop = cnp->cn_nameiop;
+    flags   = cnp->cn_flags;
+    lockparent = flags & LOCKPARENT;
+    islastcn   = flags & ISLASTCN;
+    wantparent = flags & (LOCKPARENT|WANTPARENT);
+
+    pp   = cnp->cn_proc;
+    cred = pp->p_cred->pc_ucred;
+    dip  = (struct minix_inode*)dvp->v_data;
+    mmp  = dip->i_mmp;
+    mp   = mmp->mnx_mp;
+    msp  = mmp->mnx_su;
+
+    root_inode    = (ino_t)msp->s_root->i_number;
+    current_inode = (ino_t)dip->i_number;
+
+    mdino = current_inode;
+
+    isdot = ((cnp->cn_namelen) == 1 && (cnp->cn_nameptr[0] == '.'));
+    
+    if (mdino == 0)
+	    return ERANGE;
+    /*
+     * Check accessibility of directory.
+     */
+    if (dvp->v_type != VDIR)
+	return ENOTDIR;
+
+    if ((error = VOP_ACCESS(dvp, VEXEC, cred, pp)) != 0)
+	return error;
+
+    if (islastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&
+	(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == CREATE ||
+	 cnp->cn_nameiop == RENAME))
+	    return EROFS;
+
+    /*
+     * Search dvp for the component cnp->cn_nameptr.
+     */
+    nia.a_devvp = mmp->mnx_devvp;
+    nia.a_sp    = mmp->mnx_su;
+    nia.a_cred  = cred;
+    nia.a_pp    = pp;
+
+    /* We assume that the pathlength has been checked earlier */
+
+    bzero(path, PATH_MAX+1);
+    bcopy(cnp->cn_nameptr, path, cnp->cn_namelen);
+    
+    error = minix_namei(&nia, path, &mino, &pino, &entry);
+
+    if (error != 0) {
+
+	 if (error != ENOENT)
+		 return error;
+
+	 if ((nameiop == CREATE || nameiop == RENAME)
+	              && islastcn && wantparent
+	              && dip->i_dino.i_nlinks != 0) {
+	    /*
+	     * Check for write access on directory.
+	     */
+	      if((error = VOP_ACCESS(dvp, VWRITE, cred, pp)) != 0)
+		      return error;
+	    /*
+	     * Possibly record the position of a slot in the directory
+	     * large enough for the new component name.  This can be
+	     * recorded in the vnode private data for dvp.
+	     * Set the SAVENAME flag to hold onto the pathname for use
+	     * later in VOP_CREATE or VOP_RENAME.
+	     */
+	      
+	      dip->i_entry = entry;
+		
+	      cnp->cn_flags |= SAVENAME;
+	      if (!lockparent)
+		      /*
+		       * Note that the extra data recorded above is only
+		       * useful if lockparent is specified.
+		       */
+		      VOP_UNLOCK(dvp, 0, pp);
+
+	      return EJUSTRETURN;
+	}
+
+	/*
+	 * Consider inserting name into cache.
+	 */
+	if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
+	    cache_enter(dvp, NULL, cnp);
+
+	return ENOENT;
+    } else {
+	/*
+	 * If deleting, and at end of pathname, return parameters
+	 * which can be used to remove file.  If the wantparent flag
+	 * isn't set, we return only the directory, otherwise we go on
+	 * and lock the inode, being careful with ".".
+	 */
+	if (nameiop == DELETE && islastcn) {
+	    /*
+	     * Check for write access on directory.
+	     */
+		if ((error = VOP_ACCESS(dvp, VWRITE, cred, pp)) != 0)
+			return error;
+		
+		dip->i_entry = entry;
+
+		if (mino == dip->i_number || isdot) {
+			VREF(dvp);
+			*vpp = dvp;
+			return 0;
+		}
+
+		if ((error = VFS_VGET(dvp->v_mount, mino, &vp)) != 0)
+			return error;
+		
+		ip = (struct minix_inode*)vp->v_data;
+		ip->i_parent = pino;         /* Record the parent inode */
+	    
+/*          Minix does not support the concept of a sticky bit (:<)!
+	    
+	    if (directory is sticky
+	        && cred->cr_uid != 0
+		&& cred->cr_uid != owner of dvp
+		&& owner of vp != cred->cr_uid) {
+		vput(vp);
+		return EPERM;
+	    }
+*/
+		*vpp = vp;
+		if (!lockparent)
+			VOP_UNLOCK(dvp, 0, pp);
+
+		return 0;
+	}
+	/*
+	 * If rewriting (RENAME), return the inode and the
+	 * information required to rewrite the present directory
+	 * Must get inode of directory entry to verify it's a
+	 * regular file, or empty directory.
+	 */
+	if (nameiop == RENAME && wantparent && islastcn) {
+	    error = VOP_ACCESS(dvp, VWRITE, cred, pp);
+	    if (error)
+		return (error);
+
+	    dip->i_entry = entry;
+
+	    /*
+	     * Check for "."
+	     */
+	    if (mino == dip->i_number || isdot)
+		return EISDIR;
+
+	    error = VFS_VGET(dvp->v_mount, mino, &vp);
+	    if (error)
+		return error;
+	    *vpp = vp;
+	    /*
+	     * Save the name for use in VOP_RENAME later.
+	     */
+	    cnp->cn_flags |= SAVENAME;
+	    if (!lockparent)
+		VOP_UNLOCK(dvp, 0, pp);
+
+	    return 0;
+	}
+
+	/*
+	 * Step through the translation in the name.  We do not `vput' the
+	 * directory because we may need it again if a symbolic link
+	 * is relative to the current directory.  Instead we save it
+	 * unlocked as "pdp".  We must get the target inode before unlocking
+	 * the directory to insure that the inode will not be removed
+	 * before we get it.  We prevent deadlock by always fetching
+	 * inodes from the root, moving down the directory tree. Thus
+	 * when following backward pointers ".." we must unlock the
+	 * parent directory before getting the requested directory.
+	 * There is a potential race condition here if both the current
+	 * and parent directories are removed before the VFS_VGET for the
+	 * inode associated with ".." returns.  We hope that this occurs
+	 * infrequently since we cannot avoid this race condition without
+	 * implementing a sophisticated deadlock detection algorithm.
+	 * Note also that this simple deadlock detection scheme will not
+	 * work if the file system has any hard links other than ".."
+	 * that point backwards in the directory structure.
+	 */
+	if (flags & ISDOTDOT) {
+	    VOP_UNLOCK(dvp, 0, pp);	/* race to get the inode */
+	    error = VFS_VGET(dvp->v_mount, mino, &vp);
+	    if (error) {
+		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, pp);
+		return (error);
+	    }
+	    if (lockparent && islastcn) {
+		error = vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, pp);
+		if (error) {
+		    vput(vp);
+		    return error;
+		}
+	    }
+	    *vpp = vp;
+	} else if (mino == dip->i_number || isdot) {
+	    VREF(dvp);	/* we want ourself, ie "." */
+	    *vpp = dvp;
+	} else {
+	    error = VFS_VGET(dvp->v_mount, mino, &vp);
+	    if (error)
+		return (error);
+	    if (!lockparent || !islastcn)
+		VOP_UNLOCK(dvp, 0, pp);
+	    *vpp = vp;
+	}
+
+	/*
+	 * Insert name into cache if appropriate.
+	 */
+	if (cnp->cn_flags & MAKEENTRY)
+	    cache_enter(dvp, *vpp, cnp);
+	return (0);
+    }
+}
+
+/***********************************************************************
+ *                                                                     *
+ *   namei() parses the directory path and returns the inode number    *
+ *   of the last token in the path. If there is no path then the       *
+ *   current inode number is returned. If the path leads to no inode   *
+ *   then zero is returned and ENOENT is returned as an error.         *
+ *                                     by Ed Alley 021006              *
+ *                                                                     *
+ ***********************************************************************/
+
+static int
+minix_namei(struct minix_namei_args *ap, char *path, ino_t *ino, ino_t *pino, int *entp)
+{
+	struct vnode *devvp = ap->a_devvp;
+	struct minix_super_block *sp = ap->a_sp;
+	struct ucred *cred = ap->a_cred;
+	struct buf *bp;
+	struct minix_inode *ip, in;
+	struct minix_dinode *dip;
+	int i, id, j, error, len;
+	char ppath[256], *pname;
+	struct minix_direct dir[NR_DIR_ENTRIES];
+	unsigned long ind1[V2_INDIRECTS];
+	ino_t inum, inuml, noent = -1, entry = -1;
+
+	*ino = 0;
+	*pino = 0;
+	*entp = -1;
+
+	if ((len = strlen(path)) > 255)
+		return ENAMETOOLONG;
+
+	if (path == NULL) {
+		*ino = current_inode;
+		return ENOENT;
+	}
+
+	bzero(ppath, 256);
+     
+	if (path[0] == '/')
+		inum = root_inode;
+	else
+		inum = current_inode;
+
+	strncpy(ppath, path, len+1);
+
+	if (ppath[0] == '\0') {
+		*ino = inum;
+		return 0;
+	}
+
+	/* Get the current directory */
+     
+	if ((error = minix_iget(devvp,sp,(mino_t)inum,&in)) != 0)
+		return error;
+
+	ip  = &in;
+	dip = &(in.i_dino);    /* the dinode of current directory */
+
+	pname = NULL;
+	pname = minix_strtok(ppath, "/");
+	inuml = inum;
+
+	ip->i_noent = -1;
+     
+	while(pname != NULL) {
+
+		ip->i_noent = -1;
+		noent  = 0;
+	  
+		if (!(ip->i_mode & IFDIR))
+			return ENOTDIR;
+
+		if (minix_dinode_access(dip,VEXEC,cred,sp) != 0)
+			return EACCES;
+
+		/* Search directory */
+
+		entry = -1;
+		for (i=0; i<V2_NR_DBLOCKS; i++) {
+			if (dip->i_block[i] == 0) {
+				if (ip->i_noent < 0)
+					ip->i_noent = noent;
+				return ENOENT;
+			}
+			if((error = minix_getblk(devvp,dip->i_block[i],&bp)) != 0)
+				return error;
+			bcopy(bp->b_data, dir, BLOCK_SIZE);
+			bqrelse(bp);
+			for (j=0; j<NR_DIR_ENTRIES; j++) {
+				entry++;
+				if (dir[j].d_ino == 0) {
+					if (ip->i_noent < 0)
+						ip->i_noent = noent++;
+					continue;
+				}
+
+				if (!strncmp(pname, dir[j].d_name,MINIX_NAME_MAX))
+					break;
+				noent++;
+			}
+			if (j < NR_DIR_ENTRIES)
+				break;
+		}
+	  
+		if (i >= V2_NR_DBLOCKS) {
+			if (dip->i_block[V2_NR_DBLOCKS] == 0) {
+				if (ip->i_noent < 0)
+					ip->i_noent = noent;
+				return ENOENT;
+			}
+			if ((error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS],&bp)) != 0)
+				return error;
+			bcopy(bp->b_data, ind1, BLOCK_SIZE);
+			bqrelse(bp);
+			for (id=0; id<V2_INDIRECTS; id++) {
+				if (ind1[id] == 0) {
+					if (ip->i_noent < 0)
+						ip->i_noent = noent;
+					return ENOENT;
+				}
+				if ((error = minix_getblk(devvp,ind1[id],&bp)) != 0)
+					return error;
+				bcopy(bp->b_data, dir, BLOCK_SIZE);
+				bqrelse(bp);
+				for (j=0; j<NR_DIR_ENTRIES; j++) {
+					entry++;
+					if (dir[j].d_ino == 0) {
+						if (ip->i_noent < 0)
+							ip->i_noent = noent++;
+						continue;
+					}
+					if (!strncmp(pname, dir[j].d_name,MINIX_NAME_MAX))
+						break;
+					noent++;
+				}
+				if (j < NR_DIR_ENTRIES)
+					break;
+			}
+		}
+
+		if (j < NR_DIR_ENTRIES) {
+			inuml = inum;
+			if (!strcmp(dir[j].d_name,"..") && inum == root_inode)
+				inum = root_inode;
+			else {
+				inum  = dir[j].d_ino;
+			}
+			if ((error = minix_iget(devvp,sp,(mino_t)inum,ip)) != 0)
+				return error;
+	       
+			/* Don't follow soft links */
+/*
+			while ((ip->i_mode & I_TYPE) == I_LINK) {
+				if ((error = minix_getblk(devvp,dip->i_block[0],&bp)) != 0)
+					return error;
+				n = (sizeof(bp->b_data) > 255) ? 255 : sizeof(bp->b_data);
+				bcopy(bp->b_data, ppath, n);
+				bqrelse(bp);
+				ppath[n] = '\0';
+				if ((error = minix_namei(ap,ppath,(ino_t*)&inum,(ino_t*)&inuml)) != 0) {
+					return error;
+				}
+				pname = (char*)NULL;
+				if ((error = minix_iget(devvp,sp,(mino_t)inum,ip)) != 0)
+					return error;
+			}
+*/
+		} else {
+			if (ip->i_noent < 0)
+				ip->i_noent = noent;
+			return ENOENT;
+		}
+	  
+		pname = minix_strtok((char*)NULL, "/");
+	}
+
+	if (inum <= 0) {
+		if (ip->i_noent < 0)
+			ip->i_noent = noent;
+		return ENOENT;
+	}
+
+	*ino  = inum;
+	*pino = inuml;
+	*entp = entry;
+     
+	return 0;
+}
+int
+minix_dinode_access(struct minix_dinode *dip, int mode,
+                    struct ucred *cred, struct minix_super_block *sp)
+{
+	int i,mask;
+	gid_t *gp;
+	
+        /*
+	 * Disallow write attempts on read-only file systems;
+	 * unless the file is a socket, fifo, or a block or
+	 * character device resident on the file system.
+	 */
+	
+	if (mode & VWRITE) {
+		switch ((dip->i_mode) & I_TYPE) {
+		case I_DIRECTORY:
+		case I_LINK:
+		case I_REGULAR:
+			if (sp->s_rdonly != 0)
+				return EROFS;
+			break;
+		}
+	}
+
+        /* No immutable bit in minix */
+
+        /* user id 0 always gets access. */
+	
+	if (cred->cr_uid == 0)
+		return 0;
+
+	mask = 0;
+
+        /* check the owner. */
+	
+	if (cred->cr_uid == dip->i_uid) {
+		if (mode & VEXEC)
+			mask |= S_IXUSR;
+		if (mode & VREAD)
+			mask |= S_IRUSR;
+		if (mode & VWRITE)
+			mask |= S_IWUSR;
+		return ((dip->i_mode & mask) == mask ? 0 : EACCES);
+	}
+
+        /* check the groups. */
+	
+	for (i = 0, gp = cred->cr_groups; i < cred->cr_ngroups; i++, gp++)
+		if (dip->i_gid == *gp) {
+			if (mode & VEXEC)
+				mask |= S_IXGRP;
+			if (mode & VREAD)
+				mask |= S_IRGRP;
+			if (mode & VWRITE)
+				mask |= S_IWGRP;
+			return ((dip->i_mode & mask) == mask ? 0 : EACCES);
+		}
+
+        /* check everyone else. */
+	
+	if (mode & VEXEC)
+		mask |= S_IXOTH;
+	if (mode & VREAD)
+		mask |= S_IROTH;
+	if (mode & VWRITE)
+		mask |= S_IWOTH;
+	return ((dip->i_mode & mask) == mask ? 0 : EACCES);	
+}
+/*
+ * Construct a new directory entry after a call to namei, using the
+ * parameters that it left in the componentname argument cnp. The
+ * argument ip is the inode to which the new directory entry will refer.
+ */
+void
+minix_makedirentry(ip, cnp, newdirp)
+	struct minix_inode *ip;
+	struct componentname *cnp;
+	struct minix_direct *newdirp;
+{
+	int namelen;
+	bzero(newdirp->d_name, MINIX_NAME_MAX);
+	
+	newdirp->d_ino = (short)ip->i_number;
+	
+        namelen = strlen(cnp->cn_nameptr);
+	if (namelen > MINIX_NAME_MAX)
+	     namelen = MINIX_NAME_MAX;
+	bcopy(cnp->cn_nameptr, newdirp->d_name, namelen);
+}
+/*
+ * Write a directory entry after a call to namei, using the parameters
+ * that it left in nameidata. The argument dirp is the new directory
+ * entry contents. Dvp is a pointer to the directory to be written,
+ * which was left locked by namei. Remaining parameter: dp->i_noent
+ * was left by namei and indicates the entry into the directory list
+ * where a new entry may be placed.
+ */
+int
+minix_direnter(dvp, tvp, dirp, cnp)
+	struct vnode *dvp;
+	struct vnode *tvp;
+	struct minix_direct *dirp;
+	struct componentname *cnp;
+{
+	struct ucred *cr;
+	struct proc *p;
+	int newentrysize, newent, bln, off;
+	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct minix_super_block *sp;
+	struct vnode *devvp;
+	struct buf *bp;
+	unsigned long newsize, ind1[V2_INDIRECTS];
+	union minix_block *mbp;
+	int blk = 0, error;
+
+	p  = curproc;
+	cr = p->p_ucred;
+
+	ip = (struct minix_inode*)dvp->v_data;
+	sp = ip->i_su;
+	devvp = ip->i_devvp;
+	dip = &(ip->i_dino);
+	newentrysize = sizeof(struct minix_direct);
+
+	if ((error = minix_newdirent(dvp)) != 0)
+		return error;
+	if (ip->i_noent < 0 && ip->i_entry < 0)
+		return ENOSPC;
+	
+	if (ip->i_noent < 0 && ip->i_entry > 0) {
+		if (!(ip->i_entry < MAX_DIR_ENTRIES))
+			return ENOSPC;
+		blk = ip->i_entry / NR_DIR_ENTRIES;
+		if ((error = minix_balloc(dvp, blk, &bp)) != 0)
+			return error;
+		bwrite(bp);
+	}
+
+	if (ip->i_noent > 0)
+		newent = ip->i_noent;
+	else
+		newent = ip->i_entry;
+
+	bln = newent / NR_DIR_ENTRIES;
+	off = newent % NR_DIR_ENTRIES;
+
+	if (bln < V2_NR_DBLOCKS) {
+		bln = dip->i_block[bln];
+		if ((error = minix_getblk(devvp,bln,&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		mbp->dir[off].d_ino = dirp->d_ino;
+		bcopy(dirp->d_name, mbp->dir[off].d_name, MINIX_NAME_MAX);
+		minix_putblk(bp);
+	} else {
+		bln -= V2_NR_DBLOCKS;
+		error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS],&bp);
+		if (error)
+			return error;
+		bcopy(bp->b_data, ind1, BLOCK_SIZE);
+	        bqrelse(bp);
+		if ((error = minix_getblk(devvp,ind1[bln],&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		mbp->dir[off].d_ino = dirp->d_ino;
+		bcopy(dirp->d_name, mbp->dir[off].d_name, MINIX_NAME_MAX);
+		minix_putblk(bp);
+	}
+	if ((error = minix_dirsize(dvp, &newsize)) != 0)
+	     return error;
+	
+	if (newsize > 0)
+	     bln = (newsize-1)/512 + 1;
+	else
+	     return ENOSPC;
+	
+	bln *= 512;
+	if ((bln % 512) > 256)
+	     bln += 256;
+	if (bln > dip->i_size)
+	     vnode_pager_setsize(dvp, (u_long)bln);
+	
+	dip->i_size = bln;
+	ip->i_flag |= IN_CHANGE | IN_UPDATE;
+	return minix_update(dvp);
+}
+/*
+ * Removes an entry from a directory.
+ * NOTE: Will not remove . or .. but returns ENOENT
+ *       in that case.
+ */
+int
+minix_dirremove(struct vnode *dvp)
+{
+     struct vnode *devvp;
+     struct buf *bp;
+     struct minix_inode *dip;  /* Directory inode */
+     struct minix_dinode *dinp;
+     union minix_block *mbp;
+     struct minix_direct *dirp;
+     u_daddr_t ind, bln, off, dsiz;
+     u_daddr_t blks, blkc;
+     u_long count, size;
+     int entry, error;
+
+     dip = (struct minix_inode*)dvp->v_data;
+     dinp = &(dip->i_dino);
+     devvp = dip->i_devvp;
+
+     if ((entry = dip->i_entry) < 2)
+	     return ENOENT;
+     
+     bln = entry / NR_DIR_ENTRIES;
+     off = entry % NR_DIR_ENTRIES;
+
+     if (bln < V2_NR_DBLOCKS) {
+	  bln = dinp->i_block[bln];
+	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
+	       return error;
+	  mbp = (union minix_block*)bp->b_data;
+	  mbp->dir[off].d_ino = 0;
+	  bwrite(bp);
+     } else {
+	  bln -= V2_NR_DBLOCKS;
+	  if ((error = minix_getblk(devvp,dinp->i_block[V2_NR_DBLOCKS],&bp)) != 0)
+	       return error;
+	  mbp = (union minix_block*)bp->b_data;
+	  ind = mbp->ind[bln];
+	  bqrelse(bp);
+	  if ((error = minix_getblk(devvp,ind,&bp)) != 0)
+	       return error;
+	  mbp = (union minix_block*)bp->b_data;
+	  mbp->dir[off].d_ino = 0;
+	  bwrite(bp);
+     }
+
+     if ((error = minix_dirsize(dvp, &size)) != 0)
+	     return error;
+     if ((error = minix_dircount(dvp, &count)) != 0)
+	     return error;
+
+     blks = size/BLOCK_SIZE;
+     blkc = count/NR_DIR_ENTRIES;
+
+     if (blkc < blks) {
+	     dsiz = BLOCK_SIZE*(blkc+1);
+	     dirp = (struct minix_direct*)malloc(dsiz, M_MINIXNOD, M_WAITOK);
+	     bzero((char*)dirp, dsiz);
+	     if ((error = minix_dirmove(dvp, dirp, count)) != 0)
+		     return error;
+	     if ((error = minix_dirclean(dvp)) != 0)
+		     return error;
+	     for (ind=2; ind<count; ind++)
+		     if ((error = minix_direnter(dvp, NULL, &(dirp[ind]), NULL)) != 0)
+			     return error;
+
+	     free(dirp, M_MINIXNOD);
+     }
+
+     /*
+      * Need a directory cleanup routine here to guard against
+      * empty directory entries growing out of proportion.
+      */
+     
+     return (minix_dirsize(dvp, (u_long*)&(dip->i_dino.i_size)));
+}
+/*
+ * Find an empty directory entry if it wasn't previously
+ * found by minix_namei(); place it in ip->i_noent.
+ * Routine returns empty entry number in ip->i_noent
+ * and zero as the error number. If no entry is because
+ * the available space is full, then the routine returns
+ * the next entry number and ENOENT as an error.
+ * Finally ENOSPC is returned if we are out of free entries.
+ *
+ * Need to improve this by getting an idea of the
+ * size of the search, so we don't have to search through
+ * the entire space as we do here.
+ */
+static int
+minix_newdirent(struct vnode *vp)
+{
+	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct vnode *devvp;
+	struct buf *bp;
+	struct minix_direct dir[NR_DIR_ENTRIES];
+	unsigned long ind1[V2_INDIRECTS];
+	int ib, id, error, noent = 0;
+
+	ip = (struct minix_inode*)vp->v_data;
+	devvp = ip->i_devvp;
+	dip = &(ip->i_dino);
+
+	for (ib=0; ib<V2_NR_DBLOCKS; ib++) {
+		if (dip->i_block[ib] == 0) {
+			ip->i_noent = -1;
+			ip->i_entry = noent;
+			return 0;
+		}
+		if ((error = minix_getblk(devvp,dip->i_block[ib],&bp)) != 0)
+			return error;
+		bcopy(bp->b_data, dir, BLOCK_SIZE);
+		bqrelse(bp);
+		for (id=0; id<NR_DIR_ENTRIES; id++) {
+			if (dir[id].d_ino == 0) {
+				ip->i_noent = noent;
+				ip->i_entry = -1;
+				return 0;
+			}
+			noent++;
+		}
+	}
+
+	if (dip->i_block[V2_NR_DBLOCKS] == 0) {
+	     ip->i_noent = -1;
+	     ip->i_entry = noent;
+	     return 0;
+	}
+	
+	if ((error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS],&bp)) != 0)
+		return error;
+	bcopy(bp->b_data, ind1, BLOCK_SIZE);
+	bqrelse(bp);
+	
+	for (ib=0; ib<V2_INDIRECTS; ib++) {
+		if (ind1[ib] == 0) {
+			ip->i_noent = -1;
+			ip->i_entry = noent;
+			return 0;
+		}
+		if ((error = minix_getblk(devvp,ind1[ib],&bp)) != 0)
+			return error;
+		bcopy(bp->b_data, dir, BLOCK_SIZE);
+		bqrelse(bp);
+		for (id=0; id<NR_DIR_ENTRIES; id++) {
+			if (dir[id].d_ino == 0) {
+				ip->i_noent = noent;
+				ip->i_entry = -1;
+				return 0;
+			}
+			noent++;
+		}
+	}
+	ip->i_noent = -1;
+	ip->i_entry = -1;
+	return ENOSPC;
+}
+
+/*
+ * The size of a directory corresponds to the index+1 of the
+ * last entry (including all null entries between) times
+ * the size of a directory entry in bytes.
+ */
+static int
+minix_dirsize(struct vnode *vp, u_long *last)
+{
+	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct vnode *devvp;
+	struct buf *bp;
+	unsigned long ind1[V2_INDIRECTS];
+	union minix_block *mbp;
+	int ib, id, error;
+	u_long count = 0;
+
+	ip = (struct minix_inode*)vp->v_data;
+	devvp = ip->i_devvp;
+	dip = &(ip->i_dino);
+
+	*last = count;
+	for (ib=0; ib<V2_NR_DBLOCKS; ib++) {
+		if (dip->i_block[ib] == 0)
+			return 0;
+		if ((error = minix_getblk(devvp,dip->i_block[ib],&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		for (id=0; id<NR_DIR_ENTRIES; id++) {
+			count += DIR_ENTRY_SIZE;
+			if (mbp->dir[id].d_ino > 0)
+				*last = count;
+		}
+		bqrelse(bp);
+	}
+
+	if (dip->i_block[V2_NR_DBLOCKS] == 0)
+	     return 0;
+	
+	if ((error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS],&bp)) != 0)
+		return error;
+	bcopy(bp->b_data, ind1, BLOCK_SIZE);
+	bqrelse(bp);
+	
+	for (ib=0; ib<V2_INDIRECTS; ib++) {
+		if (ind1[ib] == 0)
+			return 0;
+		if ((error = minix_getblk(devvp,ind1[ib],&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		for (id=0; id<NR_DIR_ENTRIES; id++) {
+			count += DIR_ENTRY_SIZE;
+			if (mbp->dir[id].d_ino > 0)
+				*last = count;
+		}
+		bqrelse(bp);
+	}
+	return 0;	
+}
+
+/*
+ * Count the number of entries in a directory
+ */
+static int
+minix_dircount(struct vnode *dvp, unsigned long *count)
+{
+     	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct vnode *devvp;
+	struct buf *bp;
+	u_int32_t ind1[V2_INDIRECTS];
+	union minix_block *mbp;
+	int ib, id, error;
+
+	ip = (struct minix_inode*)dvp->v_data;
+	devvp = ip->i_devvp;
+	dip = &(ip->i_dino);
+
+	*count = 0;
+	for (ib=0; ib<V2_NR_DBLOCKS; ib++) {
+		if (dip->i_block[ib] == 0)
+			return 0;
+		if ((error = minix_getblk(devvp,dip->i_block[ib],&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		for (id=0; id<NR_DIR_ENTRIES; id++) {
+			if (mbp->dir[id].d_ino > 0)
+				(*count) += 1;
+		}
+		bqrelse(bp);
+	}
+
+	if (dip->i_block[V2_NR_DBLOCKS] == 0)
+		return 0;
+	
+	if ((error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS],&bp)) != 0)
+		return error;
+	bcopy(bp->b_data, ind1, BLOCK_SIZE);
+	bqrelse(bp);
+	
+	for (ib=0; ib<V2_INDIRECTS; ib++) {
+		if (ind1[ib] == 0)
+			return 0;
+		if ((error = minix_getblk(devvp,ind1[ib],&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		for (id=0; id<NR_DIR_ENTRIES; id++) {
+			if (mbp->dir[id].d_ino > 0)
+				(*count) += 1;
+		}
+		bqrelse(bp);
+	}
+	return 0;
+}
+
+static int
+minix_dirmove(struct vnode *dvp, struct minix_direct *dirp, unsigned long count)
+{
+     	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct vnode *devvp;
+	struct buf *bp;
+	u_int32_t ind1[V2_INDIRECTS];
+	unsigned long idc;
+	union minix_block *mbp;
+	int ib, id, error;
+
+	if (count < 2)
+	     return EIO;
+	
+	ip = (struct minix_inode*)dvp->v_data;
+	devvp = ip->i_devvp;
+	dip = &(ip->i_dino);
+
+	idc = 0;
+	for (ib=0; ib<V2_NR_DBLOCKS; ib++) {
+		if (dip->i_block[ib] == 0)
+			return 0;
+		if ((error = minix_getblk(devvp,dip->i_block[ib],&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		for (id=0; id<NR_DIR_ENTRIES; id++) {
+			if (mbp->dir[id].d_ino > 0) {
+				dirp[idc].d_ino = mbp->dir[id].d_ino;
+				strncpy(dirp[idc].d_name, mbp->dir[id].d_name, MINIX_NAME_MAX);
+				if (++idc == count)
+					break;
+			}
+		}
+		bqrelse(bp);
+	}
+
+	if (dip->i_block[V2_NR_DBLOCKS] == 0)
+		return 0;
+	
+	if ((error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS],&bp)) != 0)
+		return error;
+	bcopy(bp->b_data, ind1, BLOCK_SIZE);
+	bqrelse(bp);
+	
+	for (ib=0; ib<V2_INDIRECTS; ib++) {
+		if (ind1[ib] == 0)
+			return 0;
+		if ((error = minix_getblk(devvp,ind1[ib],&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		for (id=0; id<NR_DIR_ENTRIES; id++) {
+			if (mbp->dir[id].d_ino > 0) {
+				dirp[idc].d_ino = mbp->dir[id].d_ino;
+				strncpy(dirp[idc].d_name, mbp->dir[id].d_name, MINIX_NAME_MAX);
+				if (++idc == count)
+					break;
+			}
+		}
+		bqrelse(bp);
+	}
+	return 0;	
+}
+static int
+minix_dirclean(struct vnode *dvp)
+{
+     	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct minix_super_block *sp;
+	struct vnode *devvp;
+	struct buf *bp;
+	u_int32_t ind1[V2_INDIRECTS];
+	union minix_block *mbp;
+	int ib, id, error;
+		    
+	ip = (struct minix_inode*)dvp->v_data;
+	sp = ip->i_su;
+	devvp = ip->i_devvp;
+	dip = &(ip->i_dino);
+
+	if (dip->i_block[0] == 0) /* This block must always exist */
+		return EIO;
+
+	ip->i_blocks = 1;
+	dip->i_size = 2*DIR_ENTRY_SIZE;
+
+        if ((error = minix_getblk(devvp,dip->i_block[0], &bp)) != 0)
+		return error;
+	
+	mbp = (union minix_block*)bp->b_data;
+	for (id=2; id<NR_DIR_ENTRIES; id++)
+		mbp->dir[id].d_ino = 0;
+	bwrite(bp);
+
+	for (ib=1; ib<V2_NR_DBLOCKS; ib++) {
+		if (dip->i_block[ib] == 0)
+			return 0;
+		if ((error = minix_dalloc(sp, dip->i_block[ib])) != 0)
+			return error;
+		dip->i_block[ib] = 0;
+	}
+
+	if (dip->i_block[V2_NR_DBLOCKS] == 0)
+		return 0;
+	
+	if ((error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS],&bp)) != 0)
+		return error;
+	bcopy(bp->b_data, ind1, BLOCK_SIZE);
+	brelse(bp);
+
+	if ((error = minix_dalloc(sp, dip->i_block[V2_NR_DBLOCKS])) != 0)
+	     return 0;
+	dip->i_block[V2_NR_DBLOCKS] = 0;
+
+	for (ib=0; ib<V2_INDIRECTS; ib++) {
+		if (ind1[ib] == 0)
+			return 0;
+		if ((error = minix_dalloc(sp, ind1[ib])) != 0)
+			return error;
+	}
+	return 0;	
+}
+
+int
+minix_dirempty(struct minix_inode *ip, ino_t parentino, struct ucred *cred)
+{
+	off_t off;
+	struct minix_direct dbuf;
+	struct minix_direct *dp = (struct minix_direct*)&dbuf;
+	struct minix_dinode *dip = &(ip->i_dino);
+	int error, count, namelen;
+	int mindirsiz = sizeof(struct minix_direct);
+
+	for (off=0; off<dip->i_size; off+=mindirsiz) {
+		error = vn_rdwr(UIO_READ,ip->i_vnode,(caddr_t)dp,
+		    mindirsiz, off, UIO_SYSSPACE, IO_NODELOCKED,
+		    cred, &count, (struct proc*)0);
+		if (error || count !=0)
+			return 0;
+		if (dp->d_ino == 0)
+			continue;
+		namelen = strlen(dp->d_name);
+		if (namelen > 2)
+			return 0;
+		if (dp->d_name[0] != '.')
+			return 0;
+		if (namelen == 1 && dp->d_ino == ip->i_number)
+			continue;
+		if (dp->d_name[1] == '.' && dp->d_ino == parentino)
+			continue;
+		return 0;
+	}
+	return 1;
+}
+/*
+ * Check if source directory is in the path of the target directory.
+ * Target is supplied locked, source is unlocked.
+ * The target is always vput before returning.
+ */
+int
+minix_checkpath(struct minix_inode *source, struct minix_inode *target,
+                struct ucred *cred)
+{
+	struct vnode *vp;
+	struct minix_dirtemplate dirbuf;
+	int error, rootino;
+
+	vp = target->i_vnode;
+	if (target->i_number == source->i_number) {
+		error = EEXIST;
+		goto out;
+	}
+	rootino = ROOT_INO;
+	error = 0;
+	if (target->i_number == rootino)
+		goto out;
+
+	/*
+	 * Start at target and move to root, checking as we go.
+	 */
+	for (;;) {
+		if (vp->v_type != VDIR) {
+			error = ENOTDIR;
+			break;
+		}
+		error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
+		    sizeof(struct minix_dirtemplate), (off_t)0, UIO_SYSSPACE,
+		    IO_NODELOCKED, cred, (int*)0, (struct proc*)0);
+		if (error != 0)
+			break;
+		if (dirbuf.dotdot_name[0] != '.' ||
+		    dirbuf.dotdot_name[1] != '.') {
+			error = ENOTDIR;
+			break;
+		}
+		if (dirbuf.dotdot_ino == source->i_number) {
+			error = EINVAL;
+			break;
+		}
+		if (dirbuf.dotdot_ino == rootino) /* Check until root */
+			break;
+		vput(vp);
+		error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp);
+		if (error) {
+			vp = NULL;
+			break;
+		}
+	}
+
+out:
+	if (vp != NULL)
+		vput(vp);
+	return error;
+}
+/*
+ * Rewrite an existing directory entry to point
+ * to the inode supplied.
+ */
+int
+minix_dirrewrite(struct minix_inode *dp, struct minix_inode *ip,
+                 ino_t ino, int entry)
+{
+	struct buf *bp;
+	union minix_block *mbp;
+	int error;
+
+	if ((error = minix_blkatoff(dp->i_vnode, 0, NULL, &bp)) != 0)
+	     return error;
+
+	mbp = (union minix_block*)bp->b_data;
+	mbp->dir[entry].d_ino = ino;
+	
+	ip->i_nlink--;
+	ip->i_flag |= IN_CHANGE;
+	dp->i_flag |= IN_CHANGE | IN_UPDATE;
+	
+	return bwrite(bp);
+}
+/*
+ *  The following was taken from FreeBSD's libc strtok.c and
+ *  slightly modified to use to parse the path in minix_namei().
+ */
+static char *
+minix_strtok_r(char *s, char *delim, char **last)
+{
+    char *spanp;
+    int c, sc;
+    char *tok;
+
+    if (s == NULL && (s = *last) == NULL)
+    {
+	return NULL;
+    }
+
+    /*
+     * Skip (span) leading delimiters (s += strspn(s, delim), sort of).
+     */
+cont:
+    c = *s++;
+    for (spanp = (char *)delim; (sc = *spanp++) != 0; )
+    {
+	if (c == sc)
+	{
+	    goto cont;
+	}
+    }
+
+    if (c == 0)		/* no non-delimiter characters */
+    {
+	*last = NULL;
+	return NULL;
+    }
+    tok = s - 1;
+
+    /*
+     * Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
+     * Note that delim must have one NUL; we stop if we see that, too.
+     */
+    for (;;)
+    {
+	c = *s++;
+	spanp = (char *)delim;
+	do
+	{
+	    if ((sc = *spanp++) == c)
+	    {
+		if (c == 0)
+		{
+		    s = NULL;
+		}
+		else
+		{
+		    char *w = s - 1;
+		    *w = '\0';
+		}
+		*last = s;
+		return tok;
+	    }
+	}
+	while (sc != 0);
+    }
+    /* NOTREACHED */
+}
+
+static char *
+minix_strtok(char *s, char *delim)
+{
+	static char *last = NULL;
+
+	return minix_strtok_r(s, delim, &last);
+}
diff -ruN src.orig/sys/fs/minixfs/minix_subr.c src/sys/fs/minixfs/minix_subr.c
--- src.orig/sys/fs/minixfs/minix_subr.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_subr.c	Sun Feb  2 19:36:02 2003
@@ -0,0 +1,369 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/stat.h>
+
+#include <fs/minixfs/minix.h>
+
+int minix_to_dirent_type(struct minix_inode*);
+int minix_chown(struct vnode*,uid_t,gid_t,struct ucred*,struct proc*);
+int minix_chmod(struct vnode*,int,struct ucred*,struct proc*);
+
+/*
+ * Allocate a new inode in the file system and
+ * return its vnode.
+ */
+int
+minix_valloc(struct vnode *pvp, int mode, struct ucred *cred, struct vnode **vpp)
+{
+	struct mount *mp;
+	struct minix_inode *ip, *pip;
+	struct minix_dinode *dip;
+	struct minix_super_block *sp;
+	int ino, error;
+
+	pip = (struct minix_inode*)pvp->v_data;  /* parent inode */
+	mp  = pip->i_mmp->mnx_mp;
+	sp  = pip->i_su;
+	
+	if ((error = minix_ialloc(sp, &ino)) != 0)
+		return error;
+
+	if ((error = minix_vget(mp, ino, vpp)) != 0) {
+		(void)minix_dialloc(sp, ino);
+		return error;
+	}
+
+	ip = (struct minix_inode*)(*vpp)->v_data;
+	ip->i_parent = pip->i_number;
+	ip->i_count  = 1;
+	ip->i_blocks = 0;
+	ip->i_mode = mode;
+	ip->i_flag = IN_MODIFIED;
+	ip->i_su = sp;
+	
+	dip = &(ip->i_dino);
+	dip->i_mode = mode;        /* XXX Check this! */
+	dip->i_size = 0;
+	dip->i_nlinks = 1;
+	dip->i_uid = cred->cr_uid;
+	dip->i_gid = pip->i_dino.i_gid;
+
+	(*vpp)->v_type = minix_get_vtype(ip);
+
+	(void)minix_vinit(mp, minix_specop_p, minix_fifoop_p, vpp);
+	
+	return 0;
+}
+/*
+ * Initialize the vnode associated with a new inode, handle aliased
+ * vnodes.
+ */
+int
+minix_vinit(struct mount *mntp, vop_t **specops,
+    vop_t **fifoops, struct vnode **vpp)
+{
+	struct vnode *vp;
+	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	int maj, min;
+
+	vp = *vpp;
+	ip = (struct minix_inode*)(vp->v_data);
+	dip = &(ip->i_dino);
+	
+	switch(vp->v_type) {
+	case VCHR:
+	case VBLK:
+		vp->v_op = specops;
+		maj = (dip->i_block[0] >> 8) & 0xff;
+		min =  dip->i_block[0] & 0xff;
+		addaliasu(vp, dev2udev(makedev(maj,min)));
+		break;
+	case VFIFO:
+		vp->v_op = fifoops;
+		break;
+	default:
+		break;
+	}
+	
+	if (ip->i_number == ROOT_INO)
+		vp->v_flag |= VROOT;
+
+	*vpp = vp;
+	
+	return 0;
+}
+/*
+ *  bmap returns the buffer block offset given the logical block offset.
+ *  The offset returned is converted to buffer block units with DEV_BSHIFT.
+ */
+int
+minix_bmapfs(struct vnode *vp,        /* vnode of file */
+	     u_daddr_t lbk,             /* logical zone number */
+             u_daddr_t *pbk,            /* returned physical block number */
+             int *runp)               /* number of dev chunks to add */
+{
+	struct vnode *devvp;
+	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct minix_super_block *sp;
+	union  minix_block *mbp;
+	struct buf *bp;
+
+	int ndb   = TOT_DIRECT;
+	int nsidb = TOT_SINDIRECT;
+	int ndidb = TOT_DINDIRECT;
+	int ntidb = TOT_TINDIRECT;
+     
+	int ndbpb;
+	unsigned long id, idp, id0=0, id1, id2, id3;
+	int error, indirect = 0;
+
+	if (runp != NULL) {
+		ndbpb = BLOCK_SIZE >> DEV_BSHIFT; /* buffer blocks per minix block */
+		if (ndbpb > 0)
+		     *runp = ndbpb - 1;
+		else
+		     *runp = 0;
+	}
+
+	ip = (struct minix_inode*)vp->v_data;
+     
+	dip = &(ip->i_dino);
+	devvp = ip->i_devvp;
+	sp    = ip->i_su;
+
+	id = lbk >> sp->s_log_zone_size;   /* index into inode table by zones */
+	idp = id + 1;
+
+	if (idp > ndb)
+		indirect++;
+	if (idp > nsidb)
+		indirect++;
+	if (idp > ndidb)
+		indirect++;
+	if (idp > ntidb)
+		indirect++;
+
+	switch (indirect) {
+	case 0:                /* Direct */
+		if ((*pbk = (dip->i_block[id] << sp->s_log_zone_size)) == 0)
+			return EFAULT;
+		*pbk = ((*pbk)*BLOCK_SIZE) >> DEV_BSHIFT;
+		return 0;
+	case 3:                /* Triple indirect */
+		if ((error = minix_getblk(devvp,dip->i_block[V2_NR_DBLOCKS+2],&bp)) != 0) {
+			brelse(bp);
+			return error;
+		}
+		mbp = (union minix_block*)bp->b_data;
+		id -= ndidb;
+		id3 = (id >> INDIRECT_SHIFT) >> INDIRECT_SHIFT;
+		id2 = (id >> INDIRECT_SHIFT) % V2_NR_INDIRECTS;
+		id1 = id % V2_NR_INDIRECTS;
+		id0 = mbp->ind[id3];
+		bqrelse(bp);
+		goto rind2;
+	case 2:                /* Double indirect */
+		id  -= nsidb;
+		id2 = id >> INDIRECT_SHIFT;
+		id1 = id % V2_NR_INDIRECTS;
+		id0 = dip->i_block[V2_NR_DBLOCKS+1];
+	rind2:
+		if ((error = minix_getblk(devvp,id0,&bp)) != 0) {
+			brelse(bp);
+			return error;
+		}
+		mbp = (union minix_block*)bp->b_data;
+		id0 = mbp->ind[id2];
+		bqrelse(bp);
+		goto rind1;
+	case 1:                /* Single indirect */
+		id1 = id - ndb;
+		id0 = dip->i_block[V2_NR_DBLOCKS];
+	rind1:
+		if ((error = minix_getblk(devvp,id0,&bp)) != 0)
+			return error;
+		mbp = (union minix_block*)bp->b_data;
+		if ((*pbk = mbp->ind[id1] << sp->s_log_zone_size) == 0) {
+			brelse(bp);
+			return EFAULT;
+		}
+		bqrelse(bp);
+		*pbk = ((*pbk)*BLOCK_SIZE) >> DEV_BSHIFT;
+		return 0;
+	default:
+		return ENOSPC;
+	}
+
+	/* NOTREACHED */
+}
+/*
+ * Perform chown operation on inode ip;
+ * inode must be locked prior to call.
+ * Modified from ufs; PRISON_ROOT is not recognize by
+ * Minix at this time, so is a no-op.
+ */
+int
+minix_chown(struct vnode *vp, uid_t uid, gid_t gid, struct ucred *cred,
+   struct proc *p)
+{
+	struct minix_inode *ip = (struct minix_inode*)vp->v_data;
+	struct minix_dinode *dip = &(ip->i_dino);
+	uid_t ouid;
+	gid_t ogid;
+	int error = 0;
+
+	if (uid == (uid_t)VNOVAL)
+		uid = dip->i_uid;
+	if (gid == (gid_t)VNOVAL)
+		gid = dip->i_gid;
+	/*
+	 * If we don't own the file, are trying to change the owner
+	 * of the file, or are not a member of the target group,
+	 * the caller must be superuser or the call fails.
+	 */
+	if ((cred->cr_uid != dip->i_uid || uid != dip->i_uid ||
+	    (gid != dip->i_gid && !groupmember((gid_t)gid, cred))) &&
+	    (error = suser_xxx(cred, p, PRISON_ROOT)))
+		return (error);
+	ogid = dip->i_gid;
+	ouid = dip->i_uid;
+
+	dip->i_gid = gid;
+	dip->i_uid = uid;
+
+	ip->i_flag |= IN_CHANGE;
+	if (cred->cr_uid != 0 && (ouid != uid || ogid != gid))
+		ip->i_mode &= ~(ISUID | ISGID);	
+	
+	return 0;
+}
+/*
+ * Change the mode on a file.
+ * Inode must be locked before calling.
+ * Modified from ufs; there are some options that are
+ * not recognized by minix at this time: PRISON_ROOT, S_ISTXT.
+ */
+int
+minix_chmod(struct vnode *vp, int mode, struct ucred *cred, struct proc *p)
+{
+	struct minix_inode *ip = (struct minix_inode*)vp->v_data;
+	struct minix_dinode *dip = &(ip->i_dino);
+	int error;
+
+     	if (cred->cr_uid != dip->i_uid) {
+		error = suser_xxx(cred, p, PRISON_ROOT);
+		if (error)
+			return (error);
+	}
+	if (cred->cr_uid) {
+		if (vp->v_type != VDIR && (mode & S_ISTXT))
+			return (EFTYPE);
+		if (!groupmember(dip->i_gid, cred) && (mode & ISGID))
+			return (EPERM);
+	}
+	ip->i_mode &= ~ALLPERMS;
+	ip->i_mode |= (mode & ALLPERMS);
+	dip->i_mode = ip->i_mode;
+	ip->i_flag |= IN_CHANGE;
+	return 0;
+}
+
+#include <sys/dirent.h>
+
+int
+minix_to_dirent_type(struct minix_inode *ip)
+{
+	struct minix_dinode *dip = &(ip->i_dino);
+	int f_type;
+
+	switch(dip->i_mode & I_TYPE) {
+	case I_NAMED_PIPE:
+		f_type = DT_FIFO;
+		break;
+	case I_CHAR_SPECIAL:
+		f_type = DT_CHR;
+		break;
+	case I_DIRECTORY:
+		f_type = DT_DIR;
+		break;
+	case I_BLOCK_SPECIAL:
+		f_type = DT_BLK;
+		break;
+	case I_REGULAR:
+		f_type = DT_REG;
+		break;
+	case I_LINK:
+		f_type = DT_LNK;
+		break;
+	case I_SOCK:
+		f_type = DT_SOCK;
+		break;
+	default:
+		f_type = DT_UNKNOWN;
+		break;
+	}
+
+	return f_type;
+}
+
+/* Returns ufs vnode type given a minix inode */
+
+int
+minix_get_vtype(struct minix_inode *ip)
+{
+	switch (ip->i_mode & I_TYPE) {
+	case I_DIRECTORY:
+		return VDIR;
+	case I_REGULAR:
+		return VREG;
+	case I_BLOCK_SPECIAL:
+		return VBLK;
+	case I_CHAR_SPECIAL:
+		return VCHR;
+	case I_NAMED_PIPE:
+		return VFIFO;
+	case I_LINK:
+		return VLNK;
+	case I_SOCK:
+		return VSOCK;
+	default:
+		return VNON;
+	}
+	return VBAD;
+}
diff -ruN src.orig/sys/fs/minixfs/minix_ufs.c src/sys/fs/minixfs/minix_ufs.c
--- src.orig/sys/fs/minixfs/minix_ufs.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_ufs.c	Sat Jan 18 21:30:57 2003
@@ -0,0 +1,60 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This little bit of code needs to be separate from the rest of
+ * the Minix code because of name conflicts between minix.h and
+ * the ufs include files.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+
+ino_t ufs_parent_ino(struct mount*);
+ino_t ufs_vnode_ino(struct vnode*);
+
+ino_t
+ufs_parent_ino(struct mount *mp)
+{
+     struct vnode *vp = mp->mnt_vnodecovered;
+     struct inode *ip = (struct inode*)vp->v_data;
+
+     return (ino_t)ip->i_number;
+}
+
+ino_t
+ufs_vnode_ino(struct vnode *vp)
+{
+     struct inode *ip = (struct inode*)vp->v_data;
+
+     return (ino_t)ip->i_number;
+}
diff -ruN src.orig/sys/fs/minixfs/minix_vfsops.c src/sys/fs/minixfs/minix_vfsops.c
--- src.orig/sys/fs/minixfs/minix_vfsops.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_vfsops.c	Tue Feb  4 08:25:33 2003
@@ -0,0 +1,706 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * This file is modeled after both the ufs and ext2fs code.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/lock.h>
+#include <sys/conf.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/disklabel.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/buf.h>
+
+#include <fs/minixfs/minix.h>
+
+/* Various locks used throughout */
+
+int minix_inode_hash_lock;
+
+static int minix_mount(struct mount*, char*, caddr_t,
+    struct nameidata*, struct proc*);
+static int minix_unmount(struct mount*, int, struct proc*);
+static int minix_root(struct mount*, struct vnode**);
+static int minix_statfs(struct mount*, struct statfs*, struct proc*);
+static int minix_sync(struct mount*, int, struct ucred*, struct proc*);
+static int minix_start(struct mount*, int, struct proc*);
+static int minix_init(struct vfsconf*);
+static int minix_uninit(struct vfsconf*);
+ino_t ufs_parent_ino(struct mount*);
+ino_t ufs_vnode_ino(struct vnode*);
+
+struct minix_args {
+	char               *fspec;
+	struct export_args export;
+};
+
+MALLOC_DEFINE(M_MINIXMNT, "Minixfs mount", "Minixfs mount structure");
+MALLOC_DEFINE(M_MINIXNOD, "Minixfs node", "Minixfs vnode");
+
+static int
+minix_mount(struct mount *mp, char *path, caddr_t data,
+    struct nameidata *ndp, struct proc *p)
+{
+	struct minix_args args;
+	struct minixmount *mmp = NULL;
+	struct vnode *devvp, *vproot;
+	struct buf *bp;
+	struct minix_super_block *es;
+	struct ucred *cred = p->p_ucred;
+	int error = 0, ronly = 0, size;
+	u_daddr_t iblkn, bblkn;
+	long isize, bsize;
+	mode_t accessmode;
+
+	if (p->p_ucred->cr_uid != 0)
+		return EACCES;
+
+	if (mp->mnt_flag & MNT_UPDATE)
+		return EOPNOTSUPP;
+
+	mp->mnt_flag &= ~(MNT_ASYNC | MNT_SOFTDEP);
+	
+	error = copyin(data, (caddr_t)&args, sizeof(struct minix_args));
+	if (error)
+		return error;
+
+	NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+	ndp->ni_vp = NULL;
+	if ((error = namei(ndp)) != 0)
+		return error;	
+	NDFREE(ndp, NDF_ONLY_PNBUF);
+
+	if (ndp->ni_vp != NULL)
+		devvp = ndp->ni_vp;
+	else {
+		printf("devvp is NULL!\n");
+		return ENXIO;
+	}
+	
+	if (!vn_isdisk(devvp, &error)) {
+		printf("vn_isdisk error = %d\n",error);
+		goto out;
+	}
+
+	if (vcount(devvp) < 1) {
+		printf("devvp: vcount < 1!\n");
+		return ENXIO;
+	}
+
+	/*
+	 * Disallow multiple mounts of the same device.
+	 * Disallow mounting of a device that is currently in use
+	 * (except for root, which might share swap device for miniroot).
+	 * Flush out any old buffers remaining from a previous use.
+	 */
+
+	if ((error = vfs_mountedon(devvp)) != 0) {
+		printf("vfs_mountedon error\n");
+		goto out;
+	}
+	
+	if (vcount(devvp) > 1 && devvp != rootvp) {
+		printf("vcount error\n");
+		error = EBUSY;
+		goto out;
+	}
+
+	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
+	error = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0);
+	VOP_UNLOCK(devvp, 0, p);
+
+	if (error) {
+		printf("vinvalbuf error\n");
+		goto out;
+	}
+
+	/*
+	 * If mount by non-root, then verify that user has the necessary
+	 * permissions on the device.
+	 */
+	if (cred->cr_uid != 0) {
+		accessmode = VREAD;
+		if ((mp->mnt_flag & MNT_RDONLY) == 0)
+			accessmode |= VWRITE;
+		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
+		if ((error = VOP_ACCESS(devvp, accessmode, cred, p)) != 0) {
+			vput(devvp);
+			return (error);
+		}
+		VOP_UNLOCK(devvp, 0, p);
+	}
+
+	/*
+	 * Only VMIO the backing device if the backing device is a real
+	 * block device.  This excludes the original MFS implementation.
+	 * Note that it is optional that the backing device be VMIOed.  This
+	 * increases the opportunity for metadata caching.
+	 */
+	if (devvp->v_tag != VT_MFS && vn_isdisk(devvp, NULL)) {
+		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
+		vfs_object_create(devvp, p, p->p_ucred);
+		simple_lock(&devvp->v_interlock);
+		VOP_UNLOCK(devvp, LK_INTERLOCK, p);
+	}
+	/***  FORCE READ-ONLY FOR DEBUGGING  ***/
+/*
+	    mp->mnt_flag |= MNT_RDONLY;
+*/
+	/***  FORCE NODEV and NOEXEC for safety ***/
+
+	mp->mnt_flag |= (MNT_NODEV | MNT_NOEXEC);
+	
+	/*********************************/
+
+	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
+	error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p);
+	VOP_UNLOCK(devvp, 0, p);
+	
+	if (error) {
+		printf("VOP_OPEN error\n");
+		goto out;
+	}
+
+	if (devvp->v_rdev->si_iosize_max != 0)
+		mp->mnt_iosize_max = devvp->v_rdev->si_iosize_max;
+	if (mp->mnt_iosize_max > MAXPHYS)
+		mp->mnt_iosize_max = MAXPHYS;
+
+	bp  = NULL;
+	if ((error = bread(devvp, LSV2BLOCK, BLOCK_SIZE, NOCRED, &bp)) != 0) {
+		printf("bread error reading superblock: %d\n",error);
+		goto out;
+	}
+	
+	es = (struct minix_super_block *)bp->b_data;
+
+	if (es->s_magic != SUPER_V2) {
+		printf("Invalid super block = 0x%x\n",es->s_magic);
+		error = EINVAL;
+		goto out;
+	}
+
+	if ((mmp = malloc(sizeof(struct minixmount), M_MINIXMNT, M_WAITOK)) == NULL) {
+		error = ENOMEM;
+		goto out;
+	}
+	bzero(mmp, sizeof(struct minixmount));
+
+	mp->mnt_data = (qaddr_t)mmp;
+	
+	if ((mmp->mnx_su = malloc(sizeof(struct minix_super_block),
+	    M_MINIXMNT, M_WAITOK)) == NULL) {
+		error = ENOMEM;
+		goto out;
+	}
+	bcopy(es, mmp->mnx_su, sizeof(struct minix_super_block));
+	
+	brelse(bp);
+	bp = NULL;
+
+	es = mmp->mnx_su;
+	
+	es->s_firstinode = 2 + es->s_imap_blocks + es->s_zmap_blocks;
+	es->s_boff = es->s_firstdatazone - 1;
+	es->s_version = 2;
+	es->s_dev = devvp->v_rdev;
+	es->s_rdonly = ronly;
+	es->s_bsize = BLOCK_SIZE;
+	es->s_imnton = ufs_parent_ino(mp);
+	es->s_max_size = (es->s_zones - es->s_boff)*BLOCK_SIZE;
+
+	isize = es->s_imap_blocks*BLOCK_SIZE;
+	bsize = es->s_zmap_blocks*BLOCK_SIZE;
+	iblkn = byte_to_blkn(blk_to_byte(2));
+	bblkn = byte_to_blkn(blk_to_byte(es->s_imap_blocks + 2));
+
+	es->s_ibmap = (u_int16_t*)malloc(isize, M_MINIXMNT, M_WAITOK);
+	if (es->s_ibmap == NULL) {
+		error = ENOMEM;
+		goto out;
+	}
+	es->s_bbmap = (u_int16_t*)malloc(bsize, M_MINIXMNT, M_WAITOK);
+	if (es->s_bbmap == NULL) {
+		error = ENOMEM;
+		free(es->s_ibmap, M_MINIXMNT);
+		goto out;
+	}
+	bzero(es->s_ibmap, isize);
+	bzero(es->s_bbmap, bsize);
+	es->imap_lock = 0l;
+	es->bmap_lock = 0l;
+
+	if ((error = bread(devvp, iblkn, isize, NOCRED, &bp)) != 0) {
+		printf("bread error reading inode bitmap\n");
+		free(es->s_ibmap, M_MINIXMNT);
+		free(es->s_bbmap, M_MINIXMNT);
+		goto out;
+	}
+	bcopy((caddr_t)bp->b_data, es->s_ibmap, isize);
+	brelse(bp);
+	bp = NULL;
+
+	if ((error = bread(devvp, bblkn, bsize, NOCRED, &bp)) != 0) {
+		printf("bread error reading block bitmap\n");
+		free(es->s_ibmap, M_MINIXMNT);
+		free(es->s_bbmap, M_MINIXMNT);
+		goto out;
+	}
+	bcopy((caddr_t)bp->b_data, es->s_bbmap, bsize);
+	brelse(bp);
+	bp = NULL;
+
+	mmp->mnx_devvp = devvp;
+	mmp->mnx_dev   = devvp->v_rdev;
+	mmp->mnx_mp    = mp;
+       	mmp->mnx_malloctype = M_MINIXNOD;
+
+	mp->mnt_stat.f_fsid.val[0] = (long)dev2udev(devvp->v_rdev);
+	mp->mnt_stat.f_fsid.val[1] = mp->mnt_vfc->vfc_typenum;
+	mp->mnt_maxsymlinklen = MINIX_MAXSYMLINKLEN; /* Max short symlink */
+	mp->mnt_flag |= MNT_LOCAL;
+	
+	devvp->v_specmountpoint = mp;
+
+	/**************/
+
+	/* Save "last mounted on" info for mount point (NULL pad)*/
+	copyinstr(	path,				/* mount point*/
+	    mp->mnt_stat.f_mntonname,	                /* save area*/
+	    MNAMELEN - 1,			        /* max size*/
+	    &size);				        /* real size*/
+	bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+
+	/* Save "mounted from" info for mount point (NULL pad)*/
+	copyinstr(	args.fspec,			/* device name*/
+	    mp->mnt_stat.f_mntfromname,	                /* save area*/
+	    MNAMELEN - 1,			        /* max size*/
+	    &size);				        /* real size*/
+	bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+
+	mp->mnt_stat.f_type = mp->mnt_vfc->vfc_typenum;
+	mp->mnt_stat.f_owner = p->p_ucred->cr_uid;
+	mp->mnt_stat.f_flags = mp->mnt_flag;
+
+	minix_statfs(mp, &mp->mnt_stat, p);
+
+	/* minix_root increments usecount as well as locks the vnode */
+	
+	if ((error = minix_root(mp, &vproot)) != 0) {
+		free(es->s_ibmap, M_MINIXMNT);
+		free(es->s_bbmap, M_MINIXMNT);
+		goto out;
+	}
+	
+	es->s_root = (struct minix_inode*)vproot->v_data;
+	es->s_root->i_parent = es->s_imnton;
+
+	vput(vproot);
+
+	return 0;
+
+out:
+	devvp->v_specmountpoint = NULL;
+	if (bp)
+		brelse(bp);
+	(void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p);
+	if (vcount(devvp) > 0)
+	     vrele(devvp);
+	if (mmp) {
+	     	if (mmp->mnx_su)
+			free(mmp->mnx_su, M_MINIXMNT);
+		free(mmp, M_MINIXMNT);
+	}
+	
+	mp->mnt_data = (qaddr_t)0;
+		
+	return error;
+}
+
+static int
+minix_unmount(struct mount *mp, int mntflags, struct proc *p)
+{
+	struct minixmount *mmp = (struct minixmount*)(mp->mnt_data);
+/*	struct minix_inode *rip = mmp->mnx_su->s_root;  */
+	struct vnode *devvp;
+	int error, ronly, flags;
+
+	flags = 0;
+	if (mntflags & MNT_FORCE)
+	     flags |= FORCECLOSE;
+
+	/* Flush all the vnodes associated with mp. */
+	
+	if ((error = vflush(mp, 0, flags)) != 0)
+	     return error;
+
+	cache_purgevfs(mp);
+
+	devvp = mmp->mnx_devvp;
+
+	/* Sync up metadata */
+
+	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, p);
+	error = VOP_FSYNC(devvp, p->p_ucred, MNT_WAIT, p);
+	VOP_UNLOCK(devvp, 0, p);
+
+/*	minix_ihashrem(rip);   */
+
+	devvp->v_specmountpoint = NULL;
+	vinvalbuf(devvp, V_SAVE, NOCRED, p, 0, 0);
+	ronly =(mp->mnt_flag & MNT_RDONLY) != 0;
+	error = VOP_CLOSE(devvp,ronly ? FREAD : FREAD|FWRITE,NOCRED,p);
+	vrele(devvp);
+
+	free(mmp->mnx_su->s_ibmap, M_MINIXMNT);
+	free(mmp->mnx_su->s_bbmap, M_MINIXMNT);
+	free(mmp->mnx_su, M_MINIXMNT);
+	free(mmp, M_MINIXMNT);
+	mp->mnt_data = (qaddr_t)0;
+	mp->mnt_flag &= ~MNT_LOCAL;
+	
+	return error;
+}
+
+static int
+minix_root(struct mount *mp, struct vnode **vpp)
+{
+	return minix_vget(mp, (ino_t)ROOT_INO, vpp);
+}
+
+static int
+minix_statfs(struct mount *mp, struct statfs *sbp, struct proc *p)
+{
+	struct minixmount *mmp;
+	struct minix_super_block *sp;
+	size_t size;
+
+	mmp = (struct minixmount*)(mp->mnt_data);
+	sp  = mmp->mnx_su;
+	
+	sbp->f_bsize  = BLOCK_SIZE;
+	sbp->f_iosize = BLOCK_SIZE;
+	sbp->f_blocks = sp->s_zones - sp->s_firstdatazone + 1;
+	sbp->f_bfree  = minix_free_block_count(sp);
+	sbp->f_ffree  = minix_free_inode_count(sp);
+	sbp->f_files  = sp->s_ninodes;
+	sbp->f_bavail = sbp->f_bfree;
+	copystr("minixfs", sbp->f_fstypename,8, &size);
+
+	if (sbp != &mp->mnt_stat) {
+		sbp->f_type  = mp->mnt_vfc->vfc_typenum;
+		sbp->f_owner = mp->mnt_stat.f_owner;
+		sbp->f_flags = mp->mnt_flag;
+		bcopy((caddr_t)mp->mnt_stat.f_mntonname,
+		    (caddr_t)&sbp->f_mntonname[0], MNAMELEN);
+		bcopy((caddr_t)mp->mnt_stat.f_mntfromname,
+		    (caddr_t)&sbp->f_mntfromname[0], MNAMELEN);
+	}
+	
+	return 0;
+}
+/*
+ * Convert an inode number into a locked vnode.
+ */
+int
+minix_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
+{
+	struct minix_super_block *su;
+	struct minix_inode *ip;
+	struct minixmount *mmp;
+	struct buf *bp;
+	struct vnode *vp;
+	caddr_t baddr;
+	dev_t dev;
+	int error;
+
+	*vpp = NULL;
+	mmp  = (struct minixmount*)(mp->mnt_data);
+	dev  = mmp->mnx_dev;
+
+	/* Look for the vnode in the hash table first */
+/*
+restart:
+	if ((*vpp = minix_ihashget(dev, ino)) != NULL) {
+
+		vp = *vpp;
+		ip = (struct minix_inode*)vp->v_data;
+
+		(void)minix_vinit(mp, minix_specop_p, minix_fifoop_p, vpp);
+
+		if (ino == ROOT_INO)
+			vp->v_flag |= VROOT;
+		
+		return 0;
+	}
+	
+	if (minix_inode_hash_lock) {
+		while (minix_inode_hash_lock) {
+			minix_inode_hash_lock = -1;
+			tsleep(&minix_inode_hash_lock, PVM, "mnxfsgt", 0);
+		}
+		goto restart;
+	}
+	minix_inode_hash_lock = 1;
+*/
+	/* Not in the hash table; so make a new vnode/inode pair. */
+
+	MALLOC(ip, struct minix_inode*,sizeof(struct minix_inode),
+	    M_MINIXNOD, M_WAITOK);
+	bzero((caddr_t)ip, sizeof(struct minix_inode));
+
+	/* Allocate a new vnode */
+	
+	error = getnewvnode(VT_MINIXFS, mp, minix_vnodeop_p, &vp);
+     	if (error) {
+	     /*
+		if (minix_inode_hash_lock < 0)
+			wakeup(&minix_inode_hash_lock);
+		minix_inode_hash_lock = 0;
+	     */
+		*vpp = NULL;
+		FREE(ip, M_MINIXNOD);
+		return (error);
+	}
+
+	vp->v_data = ip;     /* connect vnode to inode */
+
+	/* Set up lock sharing in the stack of vnodes */
+	
+	lockinit(&ip->i_lock, PINOD, "minix_inode", VLKTIMEOUT, LK_CANRECURSE);
+/*	vp->v_vnlock = &ip->i_lock; */
+
+	/* Load up the inode with some info */
+	
+	ip->i_vnode = vp;
+	ip->i_su = su = mmp->mnx_su;
+	ip->i_dev = dev;       /* Specinfo pointer */
+	ip->i_number = ino;
+	ip->i_parent = 0;      /* Don't know this yet */
+	ip->i_mmp    = mmp;
+	ip->i_devvp  = mmp->mnx_devvp;
+	ip->i_flag   = 0;
+
+	/* Put the inode into the hash table */
+	
+/*	minix_ihashins(ip);   This leads to panics after remounts! */
+
+	/* We just lock the inode here and forget about hashing for now. */
+
+	lockmgr(&ip->i_lock, LK_EXCLUSIVE, (struct simplelock *)0, curproc);
+/*
+	if (minix_inode_hash_lock < 0)
+		wakeup(&minix_inode_hash_lock);
+	minix_inode_hash_lock = 0;
+*/
+        /* Find the dinode data and read it into bp */
+
+	error = bread(mmp->mnx_devvp,
+	              byte_to_blkn(ino_to_byte(mmp->mnx_su,ino)),
+	              BLOCK_SIZE, NOCRED, &bp);
+	if (error) {
+	     /* minix_ihashrem(ip);  */
+		FREE(ip, M_MINIXNOD);
+		brelse(bp);
+		vput(vp);
+		*vpp = NULL;
+		return error;
+	}
+	/* Figure out the offset of the dinode within the block */
+	baddr = bp->b_data + ino_off(ino);
+	/* Pull the dinode out of the block and into the inode. */
+	bcopy(baddr, &ip->i_dino, V2_INODE_SIZE);
+
+	ip->i_mode = ip->i_dino.i_mode;
+	ip->i_blocks = minix_num_blocks(ip->i_mode, ip->i_dino.i_size);
+	
+	/* Use bqrelse, since there are 64 dinodes per block in a Minix file */
+	bqrelse(bp);
+
+	/* Put more good stuff into the vnode */
+
+	vp->v_type = minix_get_vtype(ip);
+
+	if (ino == ROOT_INO) {
+	     vp->v_flag |= VROOT;
+	     ip->i_flag |= VROOT;
+	}
+	
+	error = minix_vinit(mp, minix_specop_p, minix_fifoop_p, &vp);
+	if (error) {
+	     /* minix_ihashrem(ip);  */
+		FREE(ip, M_MINIXNOD);
+		vput(vp);
+		*vpp = NULL;
+		return error;
+	}
+
+	/* Reference the device vnode */
+	
+	VREF(ip->i_devvp);
+  
+	*vpp = vp;
+     
+	return 0;
+}
+
+static int
+minix_sync(struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)
+{
+	struct vnode *vp, *nvp;
+	struct minix_inode *ip;
+	struct minixmount *mmp;
+	struct minix_super_block *su;
+	int error, allerror = 0;
+
+	mmp = (struct minixmount*)(mp->mnt_data);
+        su = mmp->mnx_su;
+
+	/*
+	 *  Write back each (modified) inode.
+	 */
+	simple_lock(&mntvnode_slock);
+loop:
+	for(vp=TAILQ_FIRST(&mp->mnt_nvnodelist); vp != NULL; vp = nvp) {
+		if (vp->v_mount != mp)
+			goto loop;
+		nvp = TAILQ_NEXT(vp, v_nmntvnodes);
+		ip = vp->v_data;
+		if (vp->v_type == VNON || ((ip->i_flag *
+		    (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
+		    TAILQ_EMPTY(&vp->v_dirtyblkhd)))
+			continue;
+		if (vp->v_type != VCHR) {
+						simple_unlock(&mntvnode_slock);
+			error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT, p);
+			if (error) {
+				simple_lock(&mntvnode_slock);
+				if (error == ENOENT)
+					goto loop;
+			} else {
+				if ((error = VOP_FSYNC(vp, cred, waitfor, p)) != 0)
+					allerror = error;
+				VOP_UNLOCK(vp, 0, p);
+				vrele(vp);
+				simple_lock(&mntvnode_slock);
+			}
+			
+		} else {
+                        /*
+			 * We must reference the vp to prevent it from
+			 * getting ripped out from under UFS_UPDATE, since
+			 * we are not holding a vnode lock.  XXX why aren't
+			 * we holding a vnode lock?
+			 */
+			VREF(vp);
+			simple_unlock(&mntvnode_slock);
+			/*
+			UFS_UPDATE(vp, waitfor == MNT_WAIT);
+			*/
+                        minix_update(vp);
+			vrele(vp);
+			simple_lock(&mntvnode_slock);
+		}
+		if (TAILQ_NEXT(vp, v_nmntvnodes) != nvp)
+			goto loop;
+	}
+	simple_unlock(&mntvnode_slock);
+	/*
+	 * Force stale file system control information to be flushed.
+	 */
+	if (waitfor != MNT_LAZY) {
+		if (mmp->mnx_mp->mnt_flag & MNT_SOFTDEP)
+			waitfor = MNT_NOWAIT;
+		vn_lock(mmp->mnx_devvp, LK_EXCLUSIVE | LK_RETRY, p);
+		if ((error = VOP_FSYNC(mmp->mnx_devvp, cred, waitfor, p)) != 0)
+			allerror = error;
+		VOP_UNLOCK(mmp->mnx_devvp, 0, p);
+	}
+	return 0;
+}
+
+static int
+minix_start(struct mount *mp, int flags, struct proc *p)
+{
+	return 0;
+}
+
+/*
+ *  minix_init is called by kld when the minixfs is loaded.
+ */
+static int
+minix_init(struct vfsconf *vfsp)
+{
+	static int done = 0;
+
+	if (done == 1)
+		return 0;
+
+	minix_inode_hash_lock = 0;
+/*	minix_ihashinit();   */
+	done = 1;
+     
+	return 0;
+}
+
+/*
+ *  minix_uninit is called by kld when the minixfs is unloaded.
+ */
+static int
+minix_uninit(struct vfsconf *vfsp)
+{
+/*	minix_ihashuninit();  */
+	return 0;
+}
+
+static struct vfsops minixfs_vfsops = {
+	minix_mount,
+	minix_start,
+	minix_unmount,
+	minix_root,
+	vfs_stdquotactl,
+	minix_statfs,
+	minix_sync,
+	minix_vget,
+	vfs_stdfhtovp,
+	vfs_stdcheckexp,
+	vfs_stdvptofh,
+	minix_init,
+	minix_uninit,
+	vfs_stdextattrctl,
+};
+
+VFS_SET(minixfs_vfsops, minixfs, 0);
diff -ruN src.orig/sys/fs/minixfs/minix_vnops.c src/sys/fs/minixfs/minix_vnops.c
--- src.orig/sys/fs/minixfs/minix_vnops.c	Wed Dec 31 16:00:00 1969
+++ src/sys/fs/minixfs/minix_vnops.c	Fri Jan 31 17:44:00 2003
@@ -0,0 +1,1769 @@
+/*-
+ * Copyright (c) 2003 Ed Alley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/*
+ * This file has borrowed heavily from FreeBSD's ufs and ext2fs
+ * implementations.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/dirent.h>
+#include <sys/fcntl.h>
+#include <sys/buf.h>
+#include <sys/namei.h>
+#include <sys/unistd.h>
+
+#include <vm/vm.h>
+#include <vm/vm_extern.h>
+#include <vm/vm_zone.h>
+#include <vm/vnode_pager.h>
+
+#include <miscfs/fifofs/fifo.h>
+
+#include <fs/minixfs/minix.h>
+
+int minix_lookup(struct vop_cachedlookup_args*);
+int minix_to_dirent_type(struct minix_inode*);
+int minix_chown(struct vnode*,uid_t,gid_t,struct ucred*,struct proc*);
+int minix_chmod(struct vnode*,int,struct ucred*,struct proc*);
+
+static int minix_fsync(struct vop_fsync_args*);
+static int minix_inactive(struct vop_inactive_args*);
+static int minix_reclaim(struct vop_reclaim_args*);
+static int minix_readdir(struct vop_readdir_args*);
+static int minix_readlink(struct vop_readlink_args*);
+static int minix_symlink(struct vop_symlink_args*);
+static int minix_open(struct vop_open_args*);
+static int minix_close(struct vop_close_args*);
+static int minix_create(struct vop_create_args*);
+static int minix_remove(struct vop_remove_args*);
+static int minix_link(struct vop_link_args*);
+static int minix_mkdir(struct vop_mkdir_args*);
+static int minix_rmdir(struct vop_rmdir_args*);
+static int minix_read(struct vop_read_args*);
+static int minix_write(struct vop_write_args*);
+static int minix_bmap(struct vop_bmap_args*);
+static int minix_getpages(struct vop_getpages_args*);
+static int minix_putpages(struct vop_putpages_args*);
+static int minix_access(struct vop_access_args*);
+static int minix_getattr(struct vop_getattr_args*);
+static int minix_rename(struct vop_rename_args*);
+static int minix_setattr(struct vop_setattr_args*);
+static int minix_strategy(struct vop_strategy_args*);
+static int minix_mknod(struct vop_mknod_args*);
+static int minixfifo_read(struct vop_read_args*);
+static int minixfifo_write(struct vop_write_args*);
+static int minixfifo_close(struct vop_close_args*);
+static int minix_pathconf(struct vop_pathconf_args*);
+
+#define PRINTD(x) printf(x);
+#undef PRINTD(x)
+#define PRINTD(x)
+
+#undef MINIX_DEBUG
+
+vop_t **minix_vnodeop_p;
+static struct vnodeopv_entry_desc minix_vnodeop_entries[] = {
+	{ &vop_default_desc,		(vop_t *) vop_defaultop },
+	{ &vop_cachedlookup_desc,       (vop_t *) minix_lookup },
+	{ &vop_lookup_desc,             (vop_t *) vfs_cache_lookup },
+	{ &vop_fsync_desc,		(vop_t *) minix_fsync },
+	{ &vop_inactive_desc,		(vop_t *) minix_inactive },
+	{ &vop_reclaim_desc,            (vop_t *) minix_reclaim },
+	{ &vop_read_desc,		(vop_t *) minix_read },
+	{ &vop_readdir_desc,		(vop_t *) minix_readdir },
+	{ &vop_readlink_desc,           (vop_t *) minix_readlink },
+	{ &vop_symlink_desc,            (vop_t *) minix_symlink },
+	{ &vop_bmap_desc,               (vop_t *) minix_bmap },
+	{ &vop_write_desc,		(vop_t *) minix_write },
+	{ &vop_access_desc,             (vop_t *) minix_access },
+	{ &vop_getattr_desc,            (vop_t *) minix_getattr },
+	{ &vop_setattr_desc,            (vop_t *) minix_setattr },
+	{ &vop_strategy_desc,           (vop_t *) minix_strategy },
+        { &vop_open_desc,               (vop_t *) minix_open },
+	{ &vop_close_desc,              (vop_t *) minix_close },
+	{ &vop_create_desc,             (vop_t *) minix_create },
+	{ &vop_remove_desc,             (vop_t *) minix_remove },
+	{ &vop_link_desc,		(vop_t *) minix_link },
+	{ &vop_mkdir_desc,              (vop_t *) minix_mkdir },
+	{ &vop_rmdir_desc,              (vop_t *) minix_rmdir },
+	{ &vop_rename_desc,		(vop_t *) minix_rename },
+	{ &vop_mknod_desc,		(vop_t *) minix_mknod },
+	{ &vop_pathconf_desc,           (vop_t *) minix_pathconf },
+	{ &vop_islocked_desc,           (vop_t *) vop_stdislocked },
+	{ &vop_lock_desc,               (vop_t *) vop_stdlock },
+	{ &vop_poll_desc,               (vop_t *) vop_stdpoll },
+	{ &vop_unlock_desc,             (vop_t *) vop_stdunlock },
+	{ &vop_getpages_desc,		(vop_t *) minix_getpages },
+	{ &vop_putpages_desc,		(vop_t *) minix_putpages },
+	{ NULL, NULL }
+};
+
+static struct vnodeopv_desc minix_vnodeop_opv_desc =
+{ &minix_vnodeop_p, minix_vnodeop_entries };
+
+/* We do not implement read or write of device files for safety */
+
+vop_t **minix_specop_p;
+static struct vnodeopv_entry_desc minix_specop_entries[] = {
+     { &vop_default_desc,              (vop_t *) vop_defaultop },
+     { &vop_fsync_desc,                (vop_t *) minix_fsync },
+     { &vop_inactive_desc,             (vop_t *) minix_inactive },
+     { &vop_reclaim_desc,              (vop_t *) minix_reclaim },
+     { &vop_access_desc,               (vop_t *) minix_access },
+     { &vop_getattr_desc,              (vop_t *) minix_getattr },
+     { &vop_islocked_desc,             (vop_t *) vop_stdislocked },
+     { &vop_lock_desc,                 (vop_t *) vop_stdlock },
+     { &vop_unlock_desc,               (vop_t *) vop_stdunlock },
+     { NULL, NULL}
+};
+static struct vnodeopv_desc minix_specop_opv_desc =
+  { &minix_specop_p, minix_specop_entries };
+
+vop_t **minix_fifoop_p;
+static struct vnodeopv_entry_desc minix_fifoop_entries[] = {
+     { &vop_default_desc,              (vop_t *) fifo_vnoperate },
+     { &vop_fsync_desc,                (vop_t *) minix_fsync },
+     { &vop_inactive_desc,             (vop_t *) minix_inactive },
+     { &vop_reclaim_desc,              (vop_t *) minix_reclaim },
+     { &vop_access_desc,               (vop_t *) minix_access },
+     { &vop_getattr_desc,              (vop_t *) minix_getattr },
+     { &vop_setattr_desc,              (vop_t *) minix_setattr },
+     { &vop_read_desc,                 (vop_t *) minixfifo_read },
+     { &vop_write_desc,                (vop_t *) minixfifo_write },
+     { &vop_close_desc,                (vop_t *) minixfifo_close },
+     { &vop_islocked_desc,             (vop_t *) vop_stdislocked },
+     { &vop_lock_desc,                 (vop_t *) vop_stdlock },
+     { &vop_unlock_desc,               (vop_t *) vop_stdunlock },     
+     { NULL, NULL }
+};
+
+static struct vnodeopv_desc minix_fifoop_opv_desc =
+  { &minix_fifoop_p, minix_fifoop_entries };
+
+VNODEOP_SET(minix_vnodeop_opv_desc);
+VNODEOP_SET(minix_specop_opv_desc);
+VNODEOP_SET(minix_fifoop_opv_desc);
+
+/*
+ * Called by vput(), vrele(), if usecount drops to zero.
+ * Also called by vclean() if use count has
+ * dropped to zero.
+ * This routine should clean release buffers associated
+ * with the vnode/inode back to the buffer cache,
+ * however, the vnode maintains its association with
+ * the filesystem.
+ */
+
+/*
+ * Vnode usecount has dropped to zero; write or delete it.
+ * The node is locked when inactive is called, and is
+ * unlocked by inactive before it returns.
+ * Typically called from vrele, vput, vclean.
+ */
+
+int prtactive = 0;  /* 1 => print out reclaim of active nodes */
+
+static int
+minix_inactive(struct vop_inactive_args *ap)
+{
+	struct vnode *vp;
+	struct minix_inode *ip;
+	struct proc *p;
+	int mode, error = 0;
+     
+	PRINTD("Entering: minix_inactive\n")
+
+	vp = ap->a_vp;
+	ip = (struct minix_inode*)vp->v_data;
+	p  = ap->a_p;
+
+	if (prtactive && vp->v_usecount != 0)
+		vprint("minix_inactive: called with active vnode",vp);
+
+	if (ip->i_mode == 0)
+		goto out;
+
+	if (ip->i_nlink <= 0) {
+		error = minix_truncate(vp, (off_t)0, IO_SYNC, NOCRED, p);
+		ip->i_dev = 0;
+		mode = ip->i_mode;
+		ip->i_mode = 0;
+		ip->i_flag |= IN_CHANGE | IN_UPDATE | IN_MODIFIED;
+		minix_vfree(vp, ip->i_number, mode);
+	}
+	if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE))
+		minix_update(vp);
+out:
+	VOP_UNLOCK(vp, 0, p);
+	/*
+	 * If we are done with the inode, reclaim it
+	 * so that it can be reused immediately.
+	 */
+	if (ip->i_mode == 0)
+		vrecycle(vp, (struct simplelock *)0, p);
+	
+	return error;
+}
+
+/*
+ * Called by vclean() after the buffers associated with the
+ * vnode have been released. VOP_INACTIVE is called if the
+ * usercount is zero to clean out the vnode.
+ * This routine actually frees the inode associated with
+ * the vnode to disassociate it from the file system.
+ */
+static int
+minix_reclaim(struct vop_reclaim_args *ap)
+{
+     struct minix_inode *ip;
+     struct vnode *vp;
+
+     vp = ap->a_vp;
+     ip = (struct minix_inode*)vp->v_data;
+
+     if (prtactive && vp->v_usecount != 0)
+	     vprint("minix_reclaim: called with active vnode",vp);
+
+/*     minix_ihashrem(ip);  */
+     cache_purge(vp);
+     
+     if (ip->i_devvp) {
+	  vrele(ip->i_devvp);
+	  ip->i_devvp = 0;
+     }
+
+     FREE(ip, M_MINIXNOD); /* release inode space */
+     vp->v_data = 0;
+     
+     return 0;
+}
+
+static int
+minix_fsync(struct vop_fsync_args *ap)
+    /*
+	  struct vop_fsync_args {
+	          struct vnode *a_vp;
+		  struct ucred *a_cred;
+		  int a_waitfor;
+		  struct proc *a_p;
+	 };
+    */
+{
+	struct vnode *vp = ap->a_vp;
+	struct buf *bp, *nbp;
+	int s;
+
+loop:
+	s = splbio();
+	for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
+		nbp = TAILQ_NEXT(bp, b_vnbufs);
+/*
+ * Ignore buffers that are already being written.
+ */
+		if (bp->b_flags & B_WRITEINPROG)
+			continue;
+/*
+ * Make sure the buffer is dirty.
+ */
+		if ((bp->b_flags & B_DELWRI) == 0)
+			panic("minix_fsync: not dirty");
+
+		vfs_bio_awrite(bp);
+		splx(s);
+		goto loop;
+	}
+	splx(s);
+
+	if (ap->a_waitfor == MNT_WAIT) {
+		s = splbio();
+		while (vp->v_numoutput) {
+			vp->v_flag |= VBWAIT;
+			tsleep((caddr_t)&vp->v_numoutput, PRIBIO + 1, "mnxfsn",0);
+		}
+		splx(s);
+#ifdef DIAGNOSTIC
+		if (!TAILQ_EMPTY(&vp->v_dirtyblkhd)) {
+			vprint("minix_fsync: dirty", vp);
+			goto loop;
+		}
+#endif
+	}
+	splx(s);
+	(void)minix_update(vp);
+	return 0;
+}
+
+static int
+minix_access(struct vop_access_args *ap)
+/*
+  struct vop_access_args {
+          struct vnode *a_vp;
+	  int a_mode;
+	  struct ucred *a_cred;
+	  struct proc *a_p;
+	  };
+*/
+{
+	struct vnode *vp = ap->a_vp;
+	struct ucred *cred = ap->a_cred;
+	int mode = ap->a_mode;
+	
+	struct minix_dinode *dip;
+	struct minix_inode  *ip;
+
+	ip  = (struct minix_inode*)vp->v_data;
+	dip = &ip->i_dino;
+
+	return minix_dinode_access(dip, mode, cred, ip->i_su);
+}
+
+static int
+minix_getattr(struct vop_getattr_args *ap)
+     /*
+       struct vop_getattr_args {
+               struct vnode *a_vp;
+	       struct vattr *a_vap;
+	       struct ucred *a_cred;
+	       struct proc  *a_p;
+	       }
+     */
+{
+     struct vnode *vp = ap->a_vp;
+     struct minix_inode *ip = (struct minix_inode*)(vp->v_data);
+     struct vattr *vap = ap->a_vap;
+     struct minix_dinode *dip = &(ip->i_dino);
+     
+     minix_itimes(vp);
+
+     vap->va_fsid = dev2udev(ip->i_dev);
+     vap->va_fileid = ip->i_number;
+     vap->va_mode = ip->i_mode & ALL_MODES;  /* Minix and ufs agree here */
+     vap->va_nlink = dip->i_nlinks;
+     vap->va_uid   = dip->i_uid;
+     vap->va_gid   = dip->i_gid;
+     vap->va_rdev  = dip->i_block[0];
+     vap->va_size  = dip->i_size;
+     vap->va_atime.tv_sec = dip->i_atime;
+     vap->va_atime.tv_nsec = (dip->i_atime)*1000000000;
+     vap->va_mtime.tv_sec = dip->i_mtime;
+     vap->va_mtime.tv_nsec = (dip->i_mtime)*1000000000;
+     vap->va_ctime.tv_sec = dip->i_ctime;
+     vap->va_ctime.tv_nsec = (dip->i_ctime)*1000000000;
+     vap->va_flags = 0;                  /* Minix has no chflags command */
+     vap->va_gen   = 0;                  /* I don't know what this is */
+     vap->va_blocksize = BLOCK_SIZE;
+     vap->va_bytes = ((dip->i_size + BLOCK_SIZE-1) & ~(BLOCK_SIZE-1)) +
+	              BLOCK_SIZE;
+     vap->va_filerev = 0;                     /* NFS is not relevant yet */
+/*     vap->va_type = minix_get_vtype(ip); */
+     vap->va_type = IFTOVT(ip->i_mode);      /* IFTOVT() also works for minix */
+
+     return 0;
+}
+
+static int
+minix_setattr(struct vop_setattr_args *ap)
+	/*
+       struct vop_setattr_args {
+               struct vnode *a_vp;
+	       struct vattr *a_vap;
+	       struct ucred *a_cred;
+	       struct proc  *a_p;
+	       }
+     */
+{
+	struct vattr *vap = ap->a_vap;
+	struct vnode *vp = ap->a_vp;
+	struct minix_inode *ip = (struct minix_inode*)vp->v_data;
+	struct minix_dinode *dip = &(ip->i_dino);
+	struct ucred *cred = ap->a_cred;
+	struct proc *p = ap->a_p;
+	int error;
+	/*
+	 * Check for unsettable attributes.
+	 */
+	if ((vap->va_type != VNON) || (vap->va_nlink != VNOVAL) ||
+	    (vap->va_fsid != VNOVAL) || (vap->va_fileid != VNOVAL) ||
+	    (vap->va_blocksize != VNOVAL) || (vap->va_rdev != VNOVAL) ||
+	    ((int)vap->va_bytes != VNOVAL) || (vap->va_gen != VNOVAL)) {
+		return EINVAL;
+	}
+	
+	/* Minix doesn't have chflags capability */
+	
+	if (vap->va_flags != VNOVAL)
+		printf("minix_setattr: FLAGS NOT SET\n");
+	/*
+	 * Go through the fields and update iff not VNOVAL.
+	 */
+	if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
+		if (vp->v_mount->mnt_flag & MNT_RDONLY)
+			return EROFS;
+		if ((error = minix_chown(vp, vap->va_uid, vap->va_gid, cred, p)) != 0)
+			return error;
+	}
+	if (vap->va_size != VNOVAL) {
+		/*
+		 * Disallow write attempts on read-only file systems;
+		 * unless the file is a socket, fifo, or a block or
+		 * character device resident on the file system.
+		 */
+		switch (vp->v_type) {
+		case VDIR:
+			return EISDIR;
+		case VLNK:
+		case VREG:
+			if (vp->v_mount->mnt_flag & MNT_RDONLY)
+				return EROFS;
+			break;
+		default:
+			break;
+		}
+		if ((error = minix_truncate(vp, vap->va_size, IO_SYNC, cred, p)) != 0)
+			return error;
+	}
+	if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
+		if (vp->v_mount->mnt_flag & MNT_RDONLY)
+			return EROFS;
+		if (cred->cr_uid != dip->i_uid &&
+		    (error = suser_xxx(cred, p, PRISON_ROOT)) &&
+		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
+			(error = VOP_ACCESS(vp, VWRITE, cred, p))))
+			return error;
+		if (vap->va_atime.tv_sec != VNOVAL)
+			ip->i_flag |= IN_ACCESS;
+		if (vap->va_mtime.tv_sec != VNOVAL)
+			ip->i_flag |= IN_CHANGE | IN_UPDATE;
+		minix_itimes(vp);
+		if (vap->va_atime.tv_sec != VNOVAL) {
+			dip->i_atime = vap->va_atime.tv_sec;
+		}
+		if (vap->va_mtime.tv_sec != VNOVAL) {
+			dip->i_mtime = vap->va_mtime.tv_sec;
+		}
+		if ((error = minix_update(vp)) != 0)
+			return error;
+	}
+	error = 0;
+	if (vap->va_mode != (mode_t)VNOVAL) {
+		if (vp->v_mount->mnt_flag & MNT_RDONLY)
+			return EROFS;
+		error = minix_chmod(vp, (int)vap->va_mode, cred, p);
+	}
+
+	return error;
+}
+
+static int
+minix_readdir(struct vop_readdir_args *ap)
+     /*
+        struct vop_readdir_args {
+                struct vnode *a_vp;
+                struct uio *a_uio;
+                struct ucred *a_cred;
+		int *a_eofflag;
+		int *ncookies;
+		u_long **a_cookies;
+        };
+     */
+{
+        struct uio *uio = ap->a_uio;
+        int count, lost, error, err;
+	struct vnode *vp = ap->a_vp;
+	struct vnode *devvp;
+	struct minix_inode in, *ip;
+	struct minix_super_block *sp;
+	struct mount *mp;
+	struct minixmount *mmp;
+
+	struct minix_direct *edp, *dp;
+	int ncookies;
+	struct dirent dstdp;
+	struct uio auio;
+	struct iovec aiov;
+	caddr_t dirbuf;
+	int DIRBLKSIZ = BLOCK_SIZE;
+	int smdsize = sizeof(struct minix_direct);
+	int readcnt;
+	off_t startoffset = uio->uio_offset;
+
+	ip = (struct minix_inode*)vp->v_data;
+	mp = vp->v_mount;
+	mmp = (struct minixmount*)mp->mnt_data;
+	sp  = mmp->mnx_su;
+	devvp = mmp->mnx_devvp;
+
+	count = uio->uio_resid;
+	/*
+         * Make sure we don't return partial entries.
+	 */
+	if (count <= ((uio->uio_offset + count) & (DIRBLKSIZ -1)))
+		return EINVAL;
+	count -= (uio->uio_offset + count) & (DIRBLKSIZ -1);
+	lost   = uio->uio_resid - count;
+	uio->uio_resid = count;
+	uio->uio_iov->iov_len = count;
+        
+	auio = *uio;
+	auio.uio_iov = &aiov;
+	auio.uio_iovcnt = 1;
+	auio.uio_resid = count;
+	auio.uio_segflg = UIO_SYSSPACE;
+	aiov.iov_len = count;
+	MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
+	aiov.iov_base = dirbuf;
+	if ((error = VOP_READ(vp, &auio, 0, ap->a_cred)) == 0) {
+		readcnt = count - auio.uio_resid;
+		edp = (struct minix_direct *)&dirbuf[readcnt];
+		ncookies = 0;
+		bzero(&dstdp, offsetof(struct dirent, d_name));
+		for (dp = (struct minix_direct *)dirbuf; 
+		    !error && uio->uio_resid > 0 && dp < edp; ) {
+			/*
+			 * Minix directory entries:
+			 * - the name is NUL-terminated except for max length name.
+			 * - no file type and no namelength.
+			 * - so we get the file type from the inode
+			 * - and the namelength from strlen().
+			 * - The record size for each entry is calculated
+			 * - from GENERIC_DIRSIZ().
+			 */
+			bzero(dstdp.d_name, MAXNAMLEN+1);
+			if (dp->d_ino > 0) {
+				dstdp.d_fileno = dp->d_ino;
+				if ((err = minix_iget(devvp,sp,dp->d_ino,&in)) != 0)
+				     return err;
+				dstdp.d_type = minix_to_dirent_type(&in);
+				strncpy(dstdp.d_name, dp->d_name, MINIX_NAME_MAX);
+				dstdp.d_name[MINIX_NAME_MAX] = '\0';
+				dstdp.d_namlen = strlen(dstdp.d_name);
+				dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
+			} else {
+				dstdp.d_fileno = 0;
+				dstdp.d_type   = DT_UNKNOWN;
+				dstdp.d_namlen = 0;
+				dstdp.d_name[0] = '\0';
+				dstdp.d_reclen = GENERIC_DIRSIZ(&dstdp);
+			}
+
+			if(dstdp.d_reclen <= uio->uio_resid) {
+				if (dstdp.d_fileno > 0) /* Only move entries that are valid */
+					error = uiomove((caddr_t)&dstdp, dstdp.d_reclen, uio);
+				else {  /* Invalid entry so go to the next entry */
+					error = 0;
+				}
+				if (!error)
+					ncookies++;
+			} else
+				break;
+			
+			dp++;
+		}
+		/* we need to correct uio_offset */
+		uio->uio_offset = startoffset + (caddr_t)dp - dirbuf;
+
+		if (!error && ap->a_ncookies != NULL) {
+			u_long *cookiep, *cookies, *ecookies;
+			off_t off;
+
+			if (uio->uio_segflg != UIO_SYSSPACE || uio->uio_iovcnt != 1)
+				panic("minix_readdir: unexpected uio from NFS server");
+			MALLOC(cookies, u_long *, ncookies * sizeof(u_long), M_TEMP,
+			       M_WAITOK);
+			off = startoffset;
+			for (dp = (struct minix_direct *)dirbuf,
+			     cookiep = cookies, ecookies = cookies + ncookies;
+			     cookiep < ecookies;
+			     dp = (struct minix_direct *)((caddr_t) dp + smdsize)) {
+				off += smdsize;
+				*cookiep++ = (u_long) off;
+			}
+			*ap->a_ncookies = ncookies;
+			*ap->a_cookies = cookies;
+		}
+	}
+	FREE(dirbuf, M_TEMP);
+	uio->uio_resid += lost;
+	if (ap->a_eofflag)
+		*ap->a_eofflag = ip->i_dino.i_size <= uio->uio_offset;
+        return error;
+}
+/*
+ * Make a symbolic link; follows ufs treatment.
+ *
+ * Symbolic links are not supported in Minix 2.0
+ * but are useful in FreeBSD for amoung other things
+ * running Emacs on a file in the Minixfs. :)
+ */
+static int
+minix_symlink(struct vop_symlink_args *ap)
+     /*
+       	struct vop_symlink_args {
+		struct vnode *a_dvp;
+		struct vnode **a_vpp;
+		struct componentname *a_cnp;
+		struct vattr *a_vap;
+		char *a_target;
+		};
+     */
+{
+	struct vnode *vp, **vpp = ap->a_vpp;
+	struct minix_inode *ip;
+	int len, error;
+
+	error = minix_makeinode(IFLNK | ap->a_vap->va_mode, ap->a_dvp,
+	    vpp, ap->a_cnp);
+	if (error)
+		return error;
+
+	vp = *vpp;
+	len = strlen(ap->a_target);
+	if (len > MINIX_MAXSYMLINK)
+	     return ENAMETOOLONG;
+	if (len < vp->v_mount->mnt_maxsymlinklen) {
+		ip = (struct minix_inode*)vp->v_data;
+		bzero((char*)ip->i_shortlink, MINIX_MAXSYMLINKLEN);
+		bcopy(ap->a_target, (char*)ip->i_shortlink, len);
+		ip->i_dino.i_size = len;
+		ip->i_flag |= IN_CHANGE | IN_UPDATE;
+	} else
+		error = vn_rdwr(UIO_WRITE, vp, ap->a_target, len, (off_t)0,
+		    UIO_SYSSPACE, IO_NODELOCKED, ap->a_cnp->cn_cred, (int*)0,
+		    (struct proc*)0);
+	if (error)
+		vput(vp);
+	
+        cache_purge(ap->a_dvp);
+	return minix_update(vp);
+}
+/*
+ * Read a symbolic link; follows ufs treatment
+ */
+static int
+minix_readlink(struct vop_readlink_args *ap)
+	/*
+	  struct vop_reaklink_args {
+	          struct vnode *a_vp;
+		  struct uio   *a_uio;
+		  struct ucred *a_cred;
+		  };
+	*/
+{
+	struct vnode *vp = ap->a_vp;
+	struct minix_inode *ip = (struct minix_inode*)vp->v_data;
+	int isize;
+
+	isize = ip->i_dino.i_size;
+	if (isize < vp->v_mount->mnt_maxsymlinklen) {
+		uiomove((char*)ip->i_shortlink, isize, ap->a_uio);
+		return 0;
+	}
+	return VOP_READ(ap->a_vp, ap->a_uio, 0, ap->a_cred);
+}
+
+static int
+minix_bmap(struct vop_bmap_args *ap)
+     /*
+       struct vop_bmap_args {
+               struct vnode *a_vp;
+	       struct daddr_t a_bn;
+	       struct vnode **a_vpp;
+	       struct daddr_t *a_bnp;
+	       int *a_runp;
+	       int *a_runb;
+	};
+     */
+{
+	struct minixmount *mmp;
+     
+	if (ap->a_vpp != NULL) {
+		mmp = (struct minixmount*)(ap->a_vp->v_mount->mnt_data);
+		*ap->a_vpp = mmp->mnx_devvp;
+	}
+	if (ap->a_bnp == NULL)
+		return 0;
+
+	if (ap->a_runb != NULL)  /* No cluster reads */
+		*ap->a_runb = 0;
+
+	return minix_bmapfs(ap->a_vp, ap->a_bn, ap->a_bnp, ap->a_runp);
+}
+static int
+minix_open(struct vop_open_args *ap)
+{
+	return 0;
+}
+static int
+minix_close(struct vop_close_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	
+	simple_lock(&vp->v_interlock);
+	if (vp->v_usecount > 1)
+		minix_itimes(vp);
+	simple_unlock(&vp->v_interlock);
+	
+	return 0;
+}
+/*
+ * Vnode op for reading.
+ */
+static int
+minix_read(struct vop_read_args *ap)
+	/*
+	struct vop_read_args {
+		struct vnode *a_vp;
+		struct uio *a_uio;
+		int a_ioflag;
+		struct ucred *a_cred;
+	}
+     */
+{
+	struct vnode  *vp  = ap->a_vp;
+	struct uio   *uio  = ap->a_uio;
+
+	struct vnode *devvp;
+	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct minix_super_block *sp;
+
+	struct buf *bp;
+	off_t bytesinfile;
+	u_daddr_t lbn, pbn;
+	long size, xfersize, blkoffset;
+	int error, orig_resid;
+
+	ip = (struct minix_inode*)vp->v_data;
+	dip = &(ip->i_dino);
+	devvp = ip->i_devvp;
+	sp    = ip->i_su;
+
+	size = BLOCK_SIZE;
+
+	orig_resid = uio->uio_resid;
+	for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
+		bytesinfile = (off_t)dip->i_size - uio->uio_offset;
+		if (bytesinfile <= 0)
+			break;
+
+		lbn = uio->uio_offset / size;
+		blkoffset = uio->uio_offset - lbn * size;
+
+		xfersize = size - blkoffset;
+		if (uio->uio_resid < xfersize)
+			xfersize = uio->uio_resid;
+		if (bytesinfile < xfersize)
+			xfersize = bytesinfile;
+		
+		if ((error = VOP_BMAP(vp, lbn, NULL, &pbn, NULL, NULL)) != 0)
+			return error;
+
+		if (pbn < 0)
+		     return EIO;
+		
+		if ((error = bread(devvp, pbn, size, NOCRED, &bp)) != 0) {
+			brelse(bp);
+			bp = NULL;
+			break;
+		}
+
+		/*
+		 * We should only get non-zero b_resid when an I/O error
+		 * has occurred, which should cause us to break above.
+		 * However, if the short read did not cause an error,
+		 * then we want to ensure that we do not uiomove bad
+		 * or uninitialized data.
+		 */
+		size -= bp->b_resid;
+		if (size < xfersize) {
+			if (size == 0)
+				break;
+			xfersize = size;
+		}
+		error = uiomove((char *)bp->b_data+blkoffset, (int)xfersize, uio);
+		if (error)
+			break;
+
+		bqrelse(bp);
+	}
+	if (bp != NULL)
+		bqrelse(bp);
+	
+	if (orig_resid > 0 && (error == 0 || uio->uio_resid != orig_resid) &&
+	    (vp->v_mount->mnt_flag & MNT_NOATIME) == 0) {
+		ip->i_flag |= IN_ACCESS;
+		(void)minix_update(vp);
+	}
+
+	return error;
+}
+/*
+ * Vnode op for writing.
+ */
+static int
+minix_write(struct vop_write_args *ap)
+	/*
+	struct vop_write_args {
+		struct vnode *a_vp;
+		struct uio *a_uio;
+		int a_ioflag;
+		struct ucred *a_cred;
+	}
+     */
+{
+	struct vnode *vp = ap->a_vp;
+	struct uio  *uio = ap->a_uio;
+	struct ucred *cred = ap->a_cred;
+	int ioflag = ap->a_ioflag;
+	struct buf *bp;
+	struct minix_inode *ip;
+	struct minix_dinode *dip;
+	struct minix_super_block *sp;
+	struct vnode *devvp;
+	u_daddr_t lbn, pbn;
+	off_t osize;
+	long size, resid, blkoffset, xfersize = 0;
+	int flags, error;
+
+	ip = (struct minix_inode*)vp->v_data;
+	dip = &(ip->i_dino);
+	devvp = ip->i_devvp;
+	sp    = ip->i_su;
+	
+	osize = dip->i_size;
+	size = BLOCK_SIZE;
+	resid = uio->uio_resid;
+	
+	if (ioflag & IO_SYNC)
+		flags = B_SYNC;
+	else
+		flags = 0;
+
+	for (error = 0; uio->uio_resid > 0;) {
+	     lbn = uio->uio_offset/size;
+	     blkoffset = uio->uio_offset - lbn*size;
+
+	     xfersize = size - blkoffset;
+	     if (uio->uio_resid < xfersize)
+		  xfersize = uio->uio_resid;
+
+	     flags |= B_CLRBUF;    /* All the time */
+
+	     if (uio->uio_offset + xfersize > dip->i_size) {
+		  vnode_pager_setsize(vp, uio->uio_offset + xfersize);
+		  if ((error = minix_balloc(vp, lbn, &bp)) != 0)
+		       break;
+	     } else {
+		  if ((error = minix_bmapfs(vp, lbn, &pbn, NULL)) != 0)
+		       break;
+		  if ((error = bread(devvp, pbn, BLOCK_SIZE, NOCRED, &bp)) != 0)
+		       break;
+	     }
+
+	     if (uio->uio_offset + xfersize > dip->i_size)
+		  dip->i_size = uio->uio_offset + xfersize;
+
+	     error = uiomove((char*)bp->b_data+blkoffset, (int)xfersize, uio);
+
+	     if (ioflag & IO_VMIO)
+		  bp->b_flags |= B_RELBUF;
+
+	     if (ioflag & IO_SYNC)
+		  bwrite(bp);
+	     else if (xfersize + blkoffset == BLOCK_SIZE)
+		  bawrite(bp);
+	     else
+		  bdwrite(bp);
+
+	     if (error || xfersize == 0)
+		  break;
+	}
+    
+	if (error) {
+	     if (ioflag & IO_UNIT) {
+		  (void)minix_truncate(vp,osize,ioflag & IO_SYNC, cred, uio->uio_procp);
+		  uio->uio_offset -= resid - uio->uio_resid;
+		  uio->uio_resid = resid;
+	     } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) {
+		  (void)minix_update(vp);
+	     }
+	}
+	/*
+	 * If we successfully wrote any data, and we are not the superuser
+	 * we clear the setuid and setgid bits as a precaution against
+	 * tampering.
+	 */
+	if (resid > uio->uio_resid && ap->a_cred && ap->a_cred->cr_uid != 0)
+		ip->i_mode &= ~(ISUID | ISGID);
+	
+	if (xfersize > 0 && error == 0) {
+		ip->i_flag |= IN_CHANGE | IN_UPDATE;
+		(void)minix_update(vp);
+	}
+	
+	return error;
+}
+
+/* I don't think we need this; it is in ufs, however. */
+
+static int
+minix_strategy(struct vop_strategy_args *ap)
+     /*
+	struct vop_strategy_args {
+		struct vnode *a_vp;
+		struct buf *a_bp;
+	};
+      */
+{
+	struct buf *bp = ap->a_bp;
+	struct vnode *vp = ap->a_vp;
+	struct minix_inode *ip;
+	int error;
+
+	ip = (struct minix_inode*)vp->v_data;
+	if (vp->v_type == VBLK || vp->v_type == VCHR)
+		panic("minix_strategy: trying to bmap a special file");
+	if (bp->b_blkno == bp->b_lblkno) {
+		error = VOP_BMAP(vp, bp->b_lblkno, NULL, &bp->b_blkno, NULL, NULL);
+		if (error) {
+			bp->b_error = error;
+			bp->b_flags |= B_ERROR;
+			biodone(bp);
+			return error;
+		}
+		if ((long)bp->b_blkno == -1)
+			vfs_bio_clrbuf(bp);
+	}
+	if ((long)bp->b_blkno == -1) {
+		biodone(bp);
+		return 0;
+	}
+	vp = ip->i_devvp;
+	bp->b_dev = vp->v_rdev;
+	VOP_STRATEGY(vp, bp);
+	return 0;
+}
+
+static int
+minix_create(struct vop_create_args *ap)
+{
+	struct componentname *cnp = ap->a_cnp;
+	int mode;
+
+	if (strlen(cnp->cn_nameptr) > MINIX_NAME_MAX)
+		return ENAMETOOLONG;
+	
+	mode = MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode);
+	
+	return minix_makeinode(mode,ap->a_dvp, ap->a_vpp, cnp);
+}
+/*
+ * Just remove the directory entry and decrease the link count.
+ */
+static int
+minix_remove(struct vop_remove_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+	struct vnode *dvp = ap->a_dvp;
+	struct minix_inode *ip;
+	int error;
+	int prtrmv = 0;
+
+	ip = (struct minix_inode*)vp->v_data;
+	
+	if ((error = minix_dirremove(dvp)) == 0) {
+	     ip->i_nlink--;
+	     ip->i_flag |= IN_CHANGE;
+	}
+
+	if (error)
+	     return error;
+
+	if (prtrmv) {
+		vprint("remove: parent directory",dvp);
+		vprint("remove: source file",vp);
+	}
+	
+	return minix_update(dvp);
+}
+/*
+ * Add a link in a directory
+ */
+static int
+minix_link(struct vop_link_args *ap)
+	/*
+       struct vop_link_args {
+               struct vnode *a_tdvp;
+	       struct vnode *a_vp;
+	       struct componentname *a_cnp;
+	       };
+     */
+{
+	struct vnode *vp = ap->a_vp;
+	struct vnode *tdvp = ap->a_tdvp;
+	struct componentname *cnp = ap->a_cnp;
+	struct proc *p = cnp->cn_proc;
+	struct minix_inode *ip;
+	struct minix_direct newdir;
+	int error;
+
+	if (tdvp->v_mount != vp->v_mount)
+		return EXDEV;
+
+	if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p)))
+		return error;
+
+	ip = (struct minix_inode *)vp->v_data;
+	if (ip->i_nlink >= MINIX_LINK_MAX) {
+		error = EMLINK;
+		goto out;
+	}
+	ip->i_nlink++;
+	ip->i_flag |= IN_CHANGE;
+	if ((error = minix_update(vp)) == 0) {
+		minix_makedirentry(ip, cnp, &newdir);
+		if ((error = minix_direnter(tdvp, vp, &newdir, cnp)) != 0) {
+			ip->i_nlink--;
+			ip->i_flag |= IN_CHANGE;
+			(void)minix_update(vp);
+		}
+	}
+out:
+	if (tdvp != vp)
+		VOP_UNLOCK(vp, 0, p);
+     
+	return error;
+}
+/*
+ * Mkdir system call.
+ */
+static int
+minix_mkdir(struct vop_mkdir_args *ap)
+     /*
+       	struct vop_mkdir_args {
+		struct vnode *a_dvp;
+		struct vnode **a_vpp;
+		struct componentname *a_cnp;
+		struct vattr *a_vap;
+	};
+     */
+{
+	struct vnode *dvp = ap->a_dvp;
+	struct vattr *vap = ap->a_vap;
+	struct componentname *cnp = ap->a_cnp;
+	struct minix_inode *dp = (struct minix_inode*)dvp->v_data;
+	struct vnode *tvp;
+	struct minix_inode *ip = NULL;
+	struct minix_dinode *dip;
+	struct buf *bp;
+	struct minix_dirtemplate dirtemplate, *dtp;
+	struct minix_direct newdir;
+	int error, dmode;
+	u_daddr_t lbn, pbn;
+	struct minix_dirtemplate mastertemplate = {
+	     0, ".",
+	     0, ".."
+	};
+
+	if (dp->i_nlink >= MINIX_LINK_MAX) {
+		error = EMLINK;
+		goto out;
+	}
+	dmode = vap->va_mode &0777;
+	dmode |= IFDIR;
+	
+	if ((error = minix_valloc(dvp, dmode, cnp->cn_cred, &tvp)) != 0)
+		goto out;
+	
+	ip  = (struct minix_inode*)tvp->v_data;
+	dip = &(ip->i_dino);
+	ip->i_dino.i_gid = dp->i_dino.i_gid;
+	ip->i_dino.i_uid = cnp->cn_cred->cr_uid;
+	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+	ip->i_mode = dmode;
+	ip->i_dino.i_mode = dmode;
+	tvp->v_type = VDIR;
+	ip->i_nlink = 2;
+	/*
+	 * Bump link count in parent directory to reflect work done below.
+	 * Should be done before reference is created so cleanup is
+	 * possible if we crash.
+	 */
+	dp->i_nlink++;
+	dp->i_flag |= IN_CHANGE;
+	if ((error = minix_update(tvp)) != 0)
+		goto bad;
+
+	/*
+	 * Initialize directory with "." and ".." from static template.
+	 */
+	dtp = &mastertemplate;
+	dirtemplate = *dtp;
+	dirtemplate.dot_ino = ip->i_number;
+	dirtemplate.dotdot_ino = dp->i_number;
+	if ((error = minix_alloc(ip->i_su, &lbn, &pbn)) != 0)
+		goto bad;
+	if ((error = minix_addblk(ip, (u_daddr_t)0, lbn)) != 0)
+		goto bad;
+	if ((error = minix_getblk(ip->i_devvp, lbn, &bp)) != 0)
+		goto bad;
+	dip->i_size = sizeof(dirtemplate);
+	ip->i_flag |= IN_CHANGE | IN_UPDATE;
+	vnode_pager_setsize(tvp, (u_long)BLOCK_SIZE);
+	bcopy((caddr_t)&dirtemplate, (caddr_t)bp->b_data, sizeof(dirtemplate));
+
+	if ((error = minix_update(tvp)) != 0) {
+		(void)bwrite(bp);
+		goto bad;
+	}
+	/*
+	 * Directory set up, now install its entry in the parent directory.
+	 *
+	 * If we are not doing soft dependencies, then we must write out the
+	 * buffer containing the new directory body before entering the new 
+	 * name in the parent. If we are doing soft dependencies, then the
+	 * buffer containing the new directory body will be passed to and
+	 * released in the soft dependency code after the code has attached
+	 * an appropriate ordering dependency to the buffer which ensures that
+	 * the buffer is written before the new name is written in the parent.
+	 */
+	if ((error = bwrite(bp)) != 0)
+		goto bad;
+	
+	minix_makedirentry(ip, cnp, &newdir);
+	error = minix_direnter(dvp, tvp, &newdir, cnp);
+bad:
+	if (error == 0) {
+		*ap->a_vpp = tvp;
+	} else {
+		dp->i_nlink--;
+		dp->i_flag |= IN_CHANGE;
+		/*
+		 * No need to do an explicit VOP_TRUNCATE here, vrele will
+		 * do this for us because we set the link count to 0.
+		 */
+		ip->i_nlink = 0;
+		ip->i_flag |= IN_CHANGE;
+		vput(tvp);
+	}
+out:
+	return error;
+}
+/*
+ * Rmdir system call
+ */
+static int
+minix_rmdir(struct vop_rmdir_args *ap)
+    /*
+	struct vop_rmdir_args {
+	   struct vnode *a_dvp;
+	   struct vnode *a_vp;
+	   struct componentname *a_cnp;
+	};
+    */
+{
+	struct vnode *vp = ap->a_vp;
+	struct vnode *dvp = ap->a_dvp;
+	struct componentname *cnp = ap->a_cnp;
+	struct minix_inode *ip, *dp;
+	int error, ioflag = IO_SYNC;
+	int prmdir = 0;
+
+	ip = (struct minix_inode*)vp->v_data;
+	dp = (struct minix_inode*)dvp->v_data;
+
+	/*
+	 * Do not remove a directory that is in the process of being renamed.
+	 * Verify the directory is empty (and valid). Rmdir ".." will not be
+	 * valid since ".." will contain a reference to the current directory
+	 * and thus be non-empty. Do not allow the removal of mounted on
+	 * directories (this can happen when an NFS exported filesystem
+	 * tries to remove a locally mounted on directory).
+	 */
+	error = 0;
+	if (ip->i_flag & IN_RENAME) {
+		error = EINVAL;
+		goto out;
+	}
+	if (ip->i_nlink > 2 ||
+	    !minix_dirempty(ip, dp->i_number, cnp->cn_cred)) {
+		error = ENOTEMPTY;
+		goto out;
+	}
+	if (vp->v_mountedhere != 0) {
+		error = EINVAL;
+		goto out;
+	}
+	/*
+	 * Delete reference to directory before purging
+	 * inode.  If we crash in between, the directory
+	 * will be reattached to lost+found,
+	 */
+	if ((error = minix_dirremove(dvp)) != 0)
+		goto out;
+	dp->i_nlink--;
+	dp->i_flag |= IN_CHANGE;
+	(void)minix_update(dvp);
+	/*
+	 * Truncate inode. The only stuff left in the directory is "." and
+	 * "..". The "." reference is inconsequential since we are quashing
+	 * it.
+	 */
+	ip->i_nlink -= 2;
+	ip->i_flag |= IN_CHANGE;
+	error = minix_truncate(vp, (off_t)0, ioflag, cnp->cn_cred,
+	                     cnp->cn_proc);
+	cache_purge(vp);
+out:
+	if (prmdir) {
+		vprint("rmdir: parent directory",dvp);
+		vprint("rmdir: source directory",vp);
+	}
+	return error;
+}
+/*
+ * mknod system call
+ */
+static int
+minix_mknod(struct vop_mknod_args *ap)
+     /*
+     	struct vop_mknod_args {
+		struct vnode *a_dvp;
+		struct vnode **a_vpp;
+		struct componentname *a_cnp;
+		struct vattr *a_vap;
+	};
+     */
+{
+	struct vnode **vpp = ap->a_vpp;
+	struct vattr  *vap = ap->a_vap;
+	struct minix_inode *ip;
+	ino_t ino;
+	int error;
+
+	error = minix_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
+	    ap->a_dvp, vpp, ap->a_cnp);
+	if (error)
+		return error;
+	ip = (struct minix_inode*)(*vpp)->v_data;
+	ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+	if (vap->va_rdev != VNOVAL)
+		ip->i_rdev = vap->va_rdev;
+	/*
+	 * Remove inode, then reload it through VFS_VGET so it is
+	 * checked to see if it is an alias of an existing entry in
+	 * the inode cache.
+	 */
+	vput(*vpp);
+	(*vpp)->v_type = VNON;
+	ino = ip->i_number;	/* Save this before vgone() invalidates ip. */
+	vgone(*vpp);
+	if ((error = VFS_VGET(ap->a_dvp->v_mount, ino, vpp)) != 0) {
+		*vpp = NULL;
+		return error;
+	}
+	return minix_update(*vpp);
+}
+/*
+ * Rename system call. Follows FreeBSD's ufs.
+ * 	rename("foo", "bar");
+ * is essentially
+ *	unlink("bar");
+ *	link("foo", "bar");
+ *	unlink("foo");
+ * but ``atomically''.  Can't do full commit without saving state in the
+ * inode on disk which isn't feasible at this time.  Best we can do is
+ * always guarantee the target exists.
+ *
+ * Basic algorithm is:
+ *
+ * 1) Bump link count on source while we're linking it to the
+ *    target.  This also ensures the inode won't be deleted out
+ *    from underneath us while we work (it may be truncated by
+ *    a concurrent `trunc' or `open' for creation).
+ * 2) Link source to destination.  If destination already exists,
+ *    delete it first.
+ * 3) Unlink source reference to inode if still around. If a
+ *    directory was moved and the parent of the destination
+ *    is different from the source, patch the ".." entry in the
+ *    directory.
+ */
+static int
+minix_rename(struct vop_rename_args *ap)
+	/*
+       struct vop_rename_args {
+          struct vnode *ap_fdvp;
+	  struct vnode *ap_fvp;
+	  struct componentname *ap_fcnp;
+	  struct vnode *ap_tdvp;
+	  struct vnode *ap_tvp;
+	  struct componentname *ap_tcnp;
+	  };
+     */
+{
+     	struct vnode *tvp = ap->a_tvp;
+	register struct vnode *tdvp = ap->a_tdvp;
+	struct vnode *fvp = ap->a_fvp;
+	struct vnode *fdvp = ap->a_fdvp;
+	struct componentname *tcnp = ap->a_tcnp;
+	struct componentname *fcnp = ap->a_fcnp;
+	struct proc *p = fcnp->cn_proc;
+	struct minix_inode *ip, *xp, *dp;
+	struct minix_direct newdir;
+	int doingdirectory = 0, oldparent = 0, newparent = 0;
+	int entry, error = 0, ioflag = IO_SYNC;
+
+	/*
+	 * Check for cross-device rename.
+	 */
+	if ((fvp->v_mount != tdvp->v_mount) ||
+	    (tvp && (fvp->v_mount != tvp->v_mount))) {
+		error = EXDEV;
+	abortit:
+		if (tdvp == tvp)
+			vrele(tdvp);
+		else
+			vput(tdvp);
+		if (tvp)
+			vput(tvp);
+		vrele(fdvp);
+		vrele(fvp);
+		return error;
+	}
+        /*
+	 * Check if just deleting a link name.
+	 */
+	if (fvp == tvp) {
+		if (fvp->v_type == VDIR) {
+			error = ENOENT;
+			goto abortit;
+		}
+
+		/*
+		 * Release destination.
+		 */
+		vput(tdvp);
+		vput(tvp);
+                /*
+		 * Delete source.  Pretty bizarre stuff.
+		 */
+		vrele(fdvp);
+		vrele(fvp);
+		fcnp->cn_flags &= ~MODMASK;
+		fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+		fcnp->cn_nameiop = DELETE;
+		VREF(fdvp);
+		error = relookup(fdvp, &fvp, fcnp);
+		if (error == 0)
+			vrele(fdvp);
+		if (fvp == NULL)
+			return ENOENT;
+		error = VOP_REMOVE(fdvp, fvp, fcnp);
+		if (fdvp == fvp)
+			vrele(fdvp);
+		else
+			vput(fdvp);
+		if (fvp != NULL)
+			vput(fvp);
+		return error;
+	}
+	
+	if ((error = vn_lock(fvp, LK_EXCLUSIVE, p)) != 0)
+		goto abortit;
+
+	dp = (struct minix_inode*)fdvp->v_data;  /* from directory */
+	ip = (struct minix_inode*)fvp->v_data;   /* from inode */
+
+	if (ip->i_nlink >= MINIX_LINK_MAX) {
+		VOP_UNLOCK(fvp, 0, p);
+		error = EMLINK;
+		goto abortit;
+	}
+
+	if ((ip->i_mode & IFMT) == IFDIR) {
+		/*
+		 * Avoid ".", "..", and aliases of "." for obvious reasons.
+		 */
+		if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')
+		    || dp->i_number == ip->i_number
+		    || ((fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT)) {
+			VOP_UNLOCK(fvp, 0, p);
+			error = EINVAL;
+			goto abortit;
+		}
+		ip->i_flag |= IN_RENAME;
+		oldparent = dp->i_number;
+		doingdirectory = 1;
+	}
+	vrele(fdvp);
+
+	/*
+	 * When the target exists, both the directory
+	 * and target vnodes are returned locked.
+	 */
+	dp = (struct minix_inode*)tdvp->v_data;
+	xp = NULL;
+	if (tvp)
+		xp = (struct minix_inode*)tvp->v_data;
+	
+	/*
+	 * Bump link count on fvp while we are moving stuff around.  If we
+	 * crash before completing the work, the link count may be wrong
+	 * but correctable.
+	 */
+	ip->i_nlink++;
+	ip->i_flag |= IN_CHANGE;
+	if ((error = minix_update(fvp)) != 0) {
+		VOP_UNLOCK(fvp, 0, p);
+		goto bad;
+	}
+
+        /*
+	 * If ".." must be changed (ie the directory gets a new
+	 * parent) then the source directory must not be in the
+	 * directory hierarchy above the target, as this would
+	 * orphan everything below the source directory. Also
+	 * the user must have write permission in the source so
+	 * as to be able to change "..".
+	 */
+	error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
+	VOP_UNLOCK(fvp, 0, p);
+	if (oldparent != dp->i_number)
+		newparent = dp->i_number;
+	if (doingdirectory && newparent) {
+		if (error)	/* write access check above */
+			goto bad;
+		if (xp != NULL)
+			vput(tvp);
+		
+		if ((error = minix_checkpath(ip, dp, tcnp->cn_cred)) != 0)
+			goto out;
+		VREF(tdvp);
+		error = relookup(tdvp, &tvp, tcnp);
+		if (error)
+			goto out;
+		vrele(tdvp);
+		dp = (struct minix_inode*)tdvp->v_data;
+		xp = NULL;
+		if (tvp)
+			xp = (struct minix_inode*)tvp->v_data;
+	}
+
+/*
+ * If the target doesn't exist, link the target to the source and
+ * unlink the source.  Otherwise, rewrite the target directory to
+ * reference the source and remove the original entry.
+ */
+	if (xp == NULL) {
+		
+		if (dp->i_dev != ip->i_dev)
+			panic("minix_rename: EXDEV");
+		/*
+		 * Account for ".." in new directory.
+		 * When source and destination have the same
+		 * parent we don't fool with the link count.
+		 */
+		if (doingdirectory && newparent) {
+			if ((nlink_t)dp->i_nlink >= MINIX_LINK_MAX) {
+				error = EMLINK;
+				goto bad;
+			}
+			dp->i_nlink++;
+			dp->i_flag |= IN_CHANGE;
+			if ((error = minix_update(tdvp)) != 0)
+				goto bad;
+		}
+		minix_makedirentry(ip, tcnp, &newdir);
+		error = minix_direnter(tdvp, NULL, &newdir, tcnp);
+		
+		if (error) {
+			if (doingdirectory && newparent) {
+				dp->i_nlink--;
+				dp->i_flag |= IN_CHANGE;
+				(void)minix_update(tdvp);
+			}
+			goto bad;
+		}
+		vput(tdvp);
+	} else {
+		
+		if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
+			panic("minix_rename: EXDEV");
+		/*
+		 * Target must be empty if a directory and have no links
+		 * to it. Also, ensure source and target are compatible
+		 * (both directories, or both not directories).
+		 */
+		if ((xp->i_mode&IFMT) == IFDIR) {
+			if ((xp->i_nlink > 2) ||
+			    !minix_dirempty(xp, dp->i_number, tcnp->cn_cred)) {
+				error = ENOTEMPTY;
+				goto bad;
+			}
+			if (!doingdirectory) {
+				error = ENOTDIR;
+				goto bad;
+			}
+			/*
+			 * Update name cache since directory is going away.
+			 */
+			cache_purge(tdvp);
+			
+		} else if (doingdirectory) {
+			error = EISDIR;
+			goto bad;
+		}
+
+		/*
+		 * Change name tcnp in tdvp to point at fvp.
+		 */
+
+		if ((entry = dp->i_entry) < 2) {
+		     printf("rename: i_entry < 2\n");
+		     error = EIO;
+		     goto bad;
+		}
+		  
+		if ((error = minix_dirrewrite(dp, xp, ip->i_number, entry)) != 0)
+			goto bad;
+
+		/*
+		 * If the target directory is in same directory as the source
+		 * directory, decrement the link count on the parent of the
+		 * target directory.  This accounts for the fact that a
+		 * directory links back to its parent with ..
+		 */
+		
+		if (doingdirectory) {
+		     if (!newparent) {
+			  /* Decrement the link count of tdvp */
+			  dp->i_nlink--;
+			  dp->i_flag |= IN_CHANGE;
+		     }
+		     xp->i_nlink--;
+		     xp->i_flag |= IN_CHANGE;
+		     if ((error = minix_truncate(tvp, (off_t)0, ioflag,
+			tcnp->cn_cred, tcnp->cn_proc)) != 0)
+			  goto bad;
+		}
+		
+		vput(tdvp);
+		vput(tvp);
+		xp = NULL;
+	}
+/*
+ * Unlink the source.  If a directory was moved to a new parent,
+ * update its ".." entry.  Gobs of ugly UFS code omitted here.
+ */
+	fcnp->cn_flags &= ~MODMASK;
+	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+	VREF(fdvp);
+	error = relookup(fdvp, &fvp, fcnp);
+	if (error == 0)
+		vrele(fdvp);
+	if (fvp != NULL) {
+		xp = (struct minix_inode*)fvp->v_data;
+		dp = (struct minix_inode*)fdvp->v_data;
+	} else {
+		/*
+		 * From name has disappeared.
+		 */
+		if (doingdirectory)
+			panic("minix_rename: lost dir entry");
+		vrele(ap->a_fvp);
+		return (0);
+	}
+/*
+ * Ensure that the directory entry still exists and has not
+ * changed while the new name has been entered. If the source is
+ * a file then the entry may have been unlinked or renamed. In
+ * either case there is no further work to be done. If the source
+ * is a directory then it cannot have been rmdir'ed; the IN_RENAME
+ * flag ensures that it cannot be moved by another rename or removed
+ * by a rmdir.
+ */
+	if (xp->i_number != ip->i_number) {
+		if (doingdirectory)
+			panic("minix_rename: lost dir entry");
+	} else {
+		/*
+		 * If the source is a directory with a
+		 * new parent, the link count of the old
+		 * parent directory must be decremented
+		 * and ".." set to point to the new parent.
+		 */
+		if (doingdirectory && newparent) {
+			entry = 1;  /* entry #1 is '..' in a Minix directory */
+			minix_dirrewrite(xp, dp, newparent, entry);
+			cache_purge(fdvp);
+		}
+		error = minix_dirremove(fdvp);
+		xp->i_nlink--;
+		xp->i_flag |= IN_CHANGE;
+		xp->i_flag &= ~IN_RENAME;
+		(void)minix_update(xp->i_vnode);
+		dp->i_flag |= IN_CHANGE | IN_UPDATE;
+		(void)minix_update(dp->i_vnode);
+		if ((xp->i_mode&IFMT) == IFDIR) {
+			dp = (struct minix_inode*)fdvp->v_data;
+			dp->i_nlink--;
+			dp->i_flag |= IN_CHANGE;
+			dp->i_flag &= ~IN_RENAME;
+		}
+	}
+	if (dp)
+		vput(dp->i_vnode);
+	if (xp)
+		vput(xp->i_vnode);
+	vrele(ap->a_fvp);
+	return error;
+
+bad:
+	if (xp)
+	     vput(xp->i_vnode);
+	if (dp)
+	     vput(dp->i_vnode);
+out:
+	if (doingdirectory)
+	     ip->i_flag &= ~IN_RENAME;
+	if (vn_lock(fvp, LK_EXCLUSIVE, p) == 0) {
+	     ip->i_nlink--;
+	     ip->i_flag |= IN_CHANGE;
+	     ip->i_flag &= ~IN_RENAME;
+	     vput(fvp);
+	} else
+	     vrele(fvp);
+
+	return error;
+}
+/*
+ * Return POSIX pathconf information applicable to minix filesystems.
+ */
+static int
+minix_pathconf(ap)
+	struct vop_pathconf_args /* {
+		struct vnode *a_vp;
+		int a_name;
+		int *a_retval;
+	} */ *ap;
+{
+	switch (ap->a_name) {
+	case _PC_LINK_MAX:
+		*ap->a_retval = MINIX_LINK_MAX;
+		return (0);
+	case _PC_NAME_MAX:
+		*ap->a_retval = MINIX_NAME_MAX;
+		return (0);
+	case _PC_PATH_MAX:
+		*ap->a_retval = MINIX_PATH_MAX;
+		return (0);
+	case _PC_PIPE_BUF:
+		*ap->a_retval = MINIX_PIPE_BUF;
+		return (0);
+	case _PC_CHOWN_RESTRICTED:
+		*ap->a_retval = 1;
+		return (0);
+	case _PC_NO_TRUNC:
+		*ap->a_retval = 1;
+		return (0);
+	default:
+		return (EINVAL);
+	}
+	/* NOTREACHED */
+}
+/* The fifo calls below follow ufs's fifofs */
+/*
+ * Update the times on the inode for the fifo then close
+ */
+static int
+minixfifo_close(struct vop_close_args *ap)
+{
+	struct vnode *vp = ap->a_vp;
+
+	simple_lock(&vp->v_interlock);
+	if (vp->v_usecount > 1)
+		minix_itimes(vp);
+	simple_unlock(&vp->v_interlock);
+	return (VOCALL(fifo_vnodeop_p, VOFFSET(vop_close), ap));
+}
+/*
+ * Read wrapper for fifos.
+ */
+static int
+minixfifo_read(struct vop_read_args *ap)
+{
+	int error, resid;
+	struct uio *uio = ap->a_uio;
+	struct vnode *vp = ap->a_vp;
+	struct minix_inode *ip;
+
+	resid = uio->uio_resid;
+	error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap);
+	ip = (struct minix_inode*)vp->v_data;
+	if ((vp->v_mount->mnt_flag & MNT_NOATIME) == 0 &&
+	    ip != NULL && (uio->uio_resid != resid ||
+		(error == 0 && resid != 0)))
+		ip->i_flag |= IN_ACCESS;
+	return error;
+}
+/*
+ * Write wrapper for fifos.
+ */
+static int
+minixfifo_write(struct vop_write_args *ap)
+{
+	int error, resid;
+	struct uio *uio = ap->a_uio;
+	struct vnode *vp = ap->a_vp;
+	struct minix_inode *ip;
+
+	resid = uio->uio_resid;
+	error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap);
+	ip = (struct minix_inode*)vp->v_data;
+	if (ip != NULL && (uio->uio_resid != resid ||(error == 0 && resid != 0)))
+		ip->i_flag |= IN_CHANGE | IN_UPDATE;
+	return error;
+}
+static int
+minix_getpages(struct vop_getpages_args *ap)
+{
+	return vnode_pager_generic_getpages(ap->a_vp, ap->a_m, ap->a_count,
+		ap->a_reqpage);
+}
+static int
+minix_putpages(struct vop_putpages_args *ap)
+{
+	return vnode_pager_generic_putpages(ap->a_vp, ap->a_m, ap->a_count,
+		ap->a_sync, ap->a_rtvals);
+}
diff -ruN src.orig/sys/modules/Makefile src/sys/modules/Makefile
--- src.orig/sys/modules/Makefile	Sat May  4 01:23:52 2002
+++ src/sys/modules/Makefile	Thu Nov  7 11:27:59 2002
@@ -102,6 +102,7 @@
 	gnufpu \
 	ibcs2 \
 	linprocfs \
+	minixfs \
 	mly \
 	ncv \
 	nsp \
diff -ruN src.orig/sys/modules/minixfs/Makefile src/sys/modules/minixfs/Makefile
--- src.orig/sys/modules/minixfs/Makefile	Wed Dec 31 16:00:00 1969
+++ src/sys/modules/minixfs/Makefile	Fri Jan  3 13:02:16 2003
@@ -0,0 +1,11 @@
+# $FreeBSD: src/sys/modules/minixfs/Makefile,v 1.10 021105 Ed Exp $
+
+.PATH:	${.CURDIR}/../../fs/minixfs
+KMOD=	minixfs
+SRCS=	vnode_if.h \
+	minix_subr.c minix_vfsops.c minix_vnops.c minix_lookup.c \
+	minix_bio.c minix_inode.c minix_ufs.c minix_locks.c \
+	minix_alock.s
+NOMAN=
+
+.include <bsd.kmod.mk>
diff -ruN src.orig/sys/sys/vnode.h src/sys/sys/vnode.h
--- src.orig/sys/sys/vnode.h	Mon Dec 24 17:44:44 2001
+++ src/sys/sys/vnode.h	Mon Nov 11 14:17:28 2002
@@ -65,7 +65,7 @@
 	VT_NON, VT_UFS, VT_NFS, VT_MFS, VT_PC, VT_LFS, VT_LOFS, VT_FDESC,
 	VT_PORTAL, VT_NULL, VT_UMAP, VT_KERNFS, VT_PROCFS, VT_AFS, VT_ISOFS,
 	VT_UNION, VT_MSDOSFS, VT_TFS, VT_VFS, VT_CODA, VT_NTFS,
-	VT_HPFS, VT_NWFS, VT_SMBFS
+	VT_HPFS, VT_NWFS, VT_SMBFS, VT_MINIXFS
 };
 
 /*
>Release-Note:
>Audit-Trail:
>Unformatted:

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200302060101.h1611mpE007766>