From alley1@llnl.gov  Wed Feb  5 17:02:00 2003
Return-Path: <alley1@llnl.gov>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id 7FFF437B405
	for <FreeBSD-gnats-submit@freebsd.org>; Wed,  5 Feb 2003 17:02:00 -0800 (PST)
Received: from jordan.llnl.gov (jordan.llnl.gov [128.115.36.14])
	by mx1.FreeBSD.org (Postfix) with ESMTP id F247D43F93
	for <FreeBSD-gnats-submit@freebsd.org>; Wed,  5 Feb 2003 17:01:54 -0800 (PST)
	(envelope-from alley1@llnl.gov)
Received: from jordan.llnl.gov (dcfd5ade5b47b7fdb617ea93e5fffb2b@localhost [127.0.0.1])
	by jordan.llnl.gov (8.12.6/8.12.6) with ESMTP id h1611mFs007767;
	Wed, 5 Feb 2003 17:01:48 -0800 (PST)
Received: (from wea@localhost)
	by jordan.llnl.gov (8.12.6/8.12.6/Submit) id h1611mpE007766;
	Wed, 5 Feb 2003 17:01:48 -0800 (PST)
Message-Id: <200302060101.h1611mpE007766@jordan.llnl.gov>
Date: Wed, 5 Feb 2003 17:01:48 -0800 (PST)
From: Ed Alley <wea@llnl.gov>
Reply-To: Ed Alley <wea@llnl.gov>
To: FreeBSD-gnats-submit@freebsd.org
Cc: wea@llnl.gov
Subject: Minix file-system offered
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         47982
>Category:       kern
>Synopsis:       Minix file-system offered
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed Feb 05 17:10:07 PST 2003
>Closed-Date:    Fri May 16 00:56:32 PDT 2003
>Last-Modified:  Fri May 16 00:56:32 PDT 2003
>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:

From: Ed.Alley.wea@llnl.gov
To: FreeBSD-gnats-submit@freebsd.org
Cc: wea@llnl.gov
Subject: kern/47982: Minix file-system offered
Date: Fri, 28 Feb 2003 22:30:31 -0800 (PST)

 >Submitter-Id:	current-users
 >Originator:	Ed Alley
 >Organization:	Lawrence Livermore National Laboratory
 >Confidential:	no
 >Synopsis:	kern/47982: Minix file-system offered
 >Severity:	non-critical
 >Priority:	low
 >Category:	kern
 >Class:		update
 >Release:	FreeBSD 4.6.2-RELEASE i386
 >Environment:
 System: FreeBSD jordan.llnl.gov FreeBSD 4.6.2-RELEASE #0: i386
 
 >Description:
 	RE: PR kern/47982
 
 	This is an update of my previous submission. The previous
 	submitted patch should be discarded. I have added
 	inode hashing, and the ability to operate with data
 	zone sizes that can contain a power of two number of
 	contiguous data blocks. This is supposed to make disk reads
 	a little more efficient in Minix lore, but I put it in for
 	compatability reasons.
 
 	I have also fixed some bugs that either corrupted the FS or
 	in one case caused a random page-fault. This was traced to
 	the root inode pointer getting trashed when its vnode was
 	released and re-assigned through the vnode cache process.
 
 	Other bugs occured during renameing of files and directories
 	after a remove has been performed. (Boy! VOPS_RENAME(9) is
 	sure a subtle routine; I worked a long time on that one and
 	am still not 100% sure about it.)
 	
 >How-To-Repeat:
 	Not applicable
 >Fix:
 	Rather than submit one huge patch on /usr/src, I have submitted
 	two patches: one for /usr/src/sbin, and one for /usr/src/sys.
 	The patches are demarked by the line:
 
 	8><------------------------------------------------- Cut here
 
 	The sources have been diffed against FreeBSD 4.6.2.
 
 	To apply the /sbin patch:
 
 		cd /usr/src
 		patch -p0 < sbin.patch
 
 	To apply the /kernel patch:
 
 		cd /usr/src
 		patch -p0 < sys.patch
 
 				Ed Alley <wea@llnl.gov>
 
 
 	        FOLLOWING is the SBIN PATCH:
 8><------------------------------------------------- Cut here
 diff -ruN sbin.orig/Makefile sbin/Makefile
 --- sbin.orig/Makefile	Mon Mar 18 00:40:00 2002
 +++ sbin/Makefile	Fri Feb 28 13:44:16 2003
 @@ -78,7 +78,7 @@
  	vinum
  
  .if ${MACHINE_ARCH} == i386
 -SUBDIR+=	kget mount_nwfs mount_smbfs
 +SUBDIR+=	kget mount_nwfs mount_smbfs mount_minix newfs_minix
  .endif
  
  .if exists(${.CURDIR}/${MACHINE})
 diff -ruN sbin.orig/mount_minix/Makefile sbin/mount_minix/Makefile
 --- sbin.orig/mount_minix/Makefile	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minix/Makefile	Fri Feb 28 13:54:39 2003
 @@ -0,0 +1,10 @@
 +
 +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 sbin.orig/mount_minix/bsd-copyright sbin/mount_minix/bsd-copyright
 --- sbin.orig/mount_minix/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minix/bsd-copyright	Fri Feb 28 13:50:57 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sbin.orig/mount_minix/mount_minixfs.8 sbin/mount_minix/mount_minixfs.8
 --- sbin.orig/mount_minix/mount_minixfs.8	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minix/mount_minixfs.8	Fri Feb 28 13:55:27 2003
 @@ -0,0 +1,44 @@
 +
 +.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 sbin.orig/mount_minix/mount_minixfs.c sbin/mount_minix/mount_minixfs.c
 --- sbin.orig/mount_minix/mount_minixfs.c	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minix/mount_minixfs.c	Fri Feb 28 13:52:40 2003
 @@ -0,0 +1,118 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/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 sbin.orig/newfs_minix/Makefile sbin/newfs_minix/Makefile
 --- sbin.orig/newfs_minix/Makefile	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/Makefile	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,32 @@
 +#  Copyright (c) 2003 Ed Alley: wea@llnl.gov
 +#  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.
 +
 +PROG=	newfs_minix
 +SRCS=   main.c super.c blockio.c inodeio.c zoneio.c dirio.c misc.c
 +MAN=	newfs_minix.8
 +
 +LINKS= ${BINDIR}/newfs_minix ${BINDIR}/mount_minixfs
 +MLINKS= newfs_minix.8 mount_minixfs.8
 +
 +.include <bsd.prog.mk>
 diff -ruN sbin.orig/newfs_minix/blockio.c sbin/newfs_minix/blockio.c
 --- sbin.orig/newfs_minix/blockio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/blockio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,76 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +#include <string.h>
 +
 +void
 +insert_bit(block_t block, int bit)
 +{
 +	/* Inserts a bit in a bitmap */
 +     
 +	int w, s;
 +	short buf[BLOCK_SIZE / sizeof(short)];
 +
 +	get_block(block, (char*)buf);
 +  
 +	w = bit / (8 * sizeof(short));
 +	s = bit % (8 * sizeof(short));
 +  
 +	buf[w] |= (1 << s);
 +  
 +	put_block(block, (char*)buf);
 +}
 +
 +int
 +read_bit(block_t block, int bit)
 +{
 +	/* Returns a bit in a bitmap */
 +     
 +	int w, s;
 +	short buf[BLOCK_SIZE/sizeof(short)];
 +
 +	get_block(block, (char *)buf);
 +     
 +	w = bit / (8 * sizeof(short));
 +	s = bit % (8 * sizeof(short));
 +
 +	return ((int)((buf[w] >> s) & 0x1));
 +}
 +
 +void
 +get_block (block_t block, char *buf)
 +{
 +	bcopy (zone[block].store, buf, BLOCK_SIZE);
 +}
 +
 +void
 +put_block (block_t block, char *buf)
 +{
 +	bcopy (buf, zone[block].store, BLOCK_SIZE);
 +     
 +}
 diff -ruN sbin.orig/newfs_minix/bsd-copyright sbin/newfs_minix/bsd-copyright
 --- sbin.orig/newfs_minix/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/bsd-copyright	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sbin.orig/newfs_minix/dirio.c sbin/newfs_minix/dirio.c
 --- sbin.orig/newfs_minix/dirio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/dirio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,125 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/*
 + * The following code was slightly modified from Minix's mkfs.c with permission.
 + */
 +
 +#include "mkfs.h"
 +
 +void rootdir(mino_t inode)
 +{
 +	/*
 +	  Get a zone for the root directory and
 +	  add the two entries for '.' and '..'
 +	  The length of a single directory entry is
 +	  16 bytes; hence, 32 for two directories.
 +	*/
 +
 +	add_zone(inode, alloc_zone(), 32L, current_time);
 +
 +	/*
 +	  Add the directory entries for the root directory.
 +	  (note that the parent and child inodes are the same)
 +	*/
 +
 +	enter_dir(inode, ".", inode);
 +	enter_dir(inode, "..", inode);
 +
 +	/* increment the link counts */
 +
 +	incr_link(inode);
 +	incr_link(inode);
 +}
 +
 +
 +void
 +incr_link(mino_t n)
 +{
 +	/* Increment the link count to inode n */
 +	
 +	int off;
 +	block_t b;
 +	inode_t inode2[V2_INODES_PER_BLOCK];
 +
 +	b = ((n - 1) / inodes_per_block) + inode_offset;
 +	off = (n - 1) % inodes_per_block;
 +
 +	get_block(b, (char *) inode2);
 +	inode2[off].i_nlinks++;
 +	put_block(b, (char *) inode2);
 +}
 +
 +void
 +enter_dir(mino_t parent, char *name, mino_t child)
 +{
 +	/* Enter child in parent directory */
 +	/* Works for dir > 1 block and zone > block */
 +     
 +	int i, j, k, l, off;
 +	block_t b;
 +	zone_t z;
 +	char *p1, *p2;
 +	struct direct dir_entry[NR_DIR_ENTRIES];
 +	inode_t ino2[V2_INODES_PER_BLOCK];
 +	int nr_dzones;
 +
 +	b = ((parent - 1) / inodes_per_block) + inode_offset;
 +	off = (parent - 1) % inodes_per_block;
 +
 +	get_block(b, (char *) ino2);
 +	nr_dzones = V2_NR_DZONES;
 +  
 +	for (k = 0; k < nr_dzones; k++) {
 +		z = ino2[off].i_zone[k];
 +		if (z == 0) {
 +			z = alloc_zone();
 +			ino2[off].i_zone[k] = z;
 +		}
 +		for (l = 0; l < zone_size; l++) {
 +			get_block((z << zone_shift) + l, (char *) dir_entry);
 +			for (i = 0; i < NR_DIR_ENTRIES; i++) {
 +				if (dir_entry[i].d_ino == 0) {
 +					dir_entry[i].d_ino = child;
 +					p1 = name;
 +					p2 = dir_entry[i].d_name;
 +					j = 14;
 +					while (j--) {
 +						*p2++ = *p1;
 +						if (*p1 != 0) p1++;
 +					}
 +					put_block((z << zone_shift) + l, (char *) dir_entry);
 +					put_block(b, (char *) ino2);
 +					return;
 +				}
 +			}
 +		}
 +	}
 +
 +	printf("Directory-inode %d beyond direct blocks.  Could not enter %s\n",
 +	    parent, name);
 +	exit(1);
 +}
 diff -ruN sbin.orig/newfs_minix/inodeio.c sbin/newfs_minix/inodeio.c
 --- sbin.orig/newfs_minix/inodeio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/inodeio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,85 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +/*
 +  Returns a free inode number and marks it active
 +*/
 +
 +mino_t
 +get_inode(void)
 +{
 +	mino_t num;
 +	block_t inodemap;
 +	int bit;
 +
 +	/* loop over inodes */
 +
 +	for (num=1; num<nrinodes; num++) {
 +		inodemap = (num/BITS_PER_BLOCK) + INODE_MAP;
 +		bit = num % BITS_PER_BLOCK;
 +		if (read_bit(inodemap, bit) == 0) {
 +			insert_bit(inodemap, bit);
 +			return num;
 +		}
 +	}
 +
 +	printf("No inodes available!\n");
 +	exit(1);
 +}
 +
 +/*
 +  Get an inode, set its mode, usr and group ids.
 +*/
 +
 +mino_t
 +alloc_inode (int mode, int usrid, int grpid)
 +{
 +	mino_t num;
 +	int off;
 +	block_t b;
 +	inode_t inode2[V2_INODES_PER_BLOCK];
 +
 +	/* Get a free inode and mark it active */
 +  
 +	num = get_inode();
 +
 +	/* Get the inode block and its offset within the block */
 +  
 +	b   = ((num - 1) / inodes_per_block) + inode_offset;
 +	off = (num - 1) % inodes_per_block;
 +
 +	/* Set the mode, usr and group ids for this inode */
 +
 +	get_block(b, (char *) inode2);
 +	inode2[off].i_mode = mode;
 +	inode2[off].i_uid = usrid;
 +	inode2[off].i_gid = grpid;
 +	put_block(b, (char *) inode2);
 +  
 +	return (num);
 +}
 diff -ruN sbin.orig/newfs_minix/main.c sbin/newfs_minix/main.c
 --- sbin.orig/newfs_minix/main.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/main.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,242 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/* Some of this code was lifted from the Minix source with permission. */
 +
 +#include "mkfs.h"
 +
 +#include <string.h>
 +#include <stdlib.h>
 +#include <ctype.h>
 +
 +int zone_size, zone_map, zone_shift;
 +int zoff;
 +int inodes_per_block;
 +int inode_offset;
 +int nrblocks, nrnodes;
 +long current_time;
 +zone_t nrzones;
 +unsigned int nrinodes;
 +Zone_t *zone;
 +char zero[BLOCK_SIZE];
 +
 +#define MAX_BLOCKS (1024L * 1024)
 +#define MAX_INODES ((unsigned) 65535)
 +
 +void super(super_block_t*, zone_t, mino_t);
 +void usage(int,char*);
 +
 +int
 +main(int argc, char **argv)
 +{
 +	int c, i, s, fdo, no_output;
 +	super_block_t sp[1];
 +	struct timeval timestr;
 +	int mode;
 +	char *ch, *fname = (char*)NULL;
 +	char defname[] = {"flimage"};
 +
 +	/* The following default values are for a 1440K floppy fs */
 +     
 +	nrblocks = 1440;
 +	nrinodes = 0;
 +	zone_shift = 0;
 +	no_output = 0;
 +
 +	while(1) {
 +		if ((s = getopt(argc, argv, ":hNi:b:s:")) == -1) {
 +			fname = argv[optind++];
 +			break;
 +		}
 +		switch (s) {
 +		case 'h':
 +			usage(0,(char*)NULL);
 +			break;
 +		case 'i':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for nrinodes");
 +			}
 +			nrinodes = atoi(optarg);
 +			break;
 +		case 'b':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for nrblocks");
 +			}
 +			nrblocks  = atoi(optarg);
 +			break;
 +		case 's':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for zoneshift");
 +			}
 +			zone_shift  = atoi(optarg);
 +			break;
 +		case 'N':
 +			no_output = 1;
 +			break;
 +		case '?':
 +			usage(1,"Unknown option");
 +			break;
 +		case ':':
 +			usage(1,"Missing argument");
 +			break;
 +		default:
 +			usage(1,"Huh?");
 +			break;
 +		}
 +	}
 +
 +	inodes_per_block = V2_INODES_PER_BLOCK;
 +
 +	if (nrblocks < 5)
 +		usage(1, "nrblocks is too small");
 +
 +	if (nrblocks > MAX_BLOCKS)
 +		usage(1, "nrblocks greater than (1024 X 1024)");
 +
 +	if (nrinodes == 0) {
 +		/* The default for inodes is 3 blocks per inode, rounded up
 +		 * to fill an inode block.  Above 20M, the average files are
 +		 * sure to be larger because it is hard to fill up 20M with
 +		 * tiny files, so reduce the default number of inodes.  This
 +		 * default can always be overridden by using the -i option.
 +		 */
 +		nrinodes = nrblocks / 3;
 +		if (nrblocks >= 20000) nrinodes  = nrblocks / 4;
 +		if (nrblocks >= 40000) nrinodes  = nrblocks / 5;
 +		if (nrblocks >= 60000) nrinodes  = nrblocks / 6;
 +		if (nrblocks >= 80000) nrinodes  = nrblocks / 7;
 +		if (nrblocks >= 100000) nrinodes = nrblocks / 8;
 +		nrinodes += inodes_per_block - 1;
 +		nrinodes = nrinodes / inodes_per_block * inodes_per_block;
 +		if (nrinodes > MAX_INODES) nrinodes = MAX_INODES;
 +	}
 +
 +	if (nrinodes < 1)
 +		usage(1, "Inode count too small");
 +
 +	if (nrinodes > MAX_INODES)
 +		usage(1, "Inode count too large");
 +
 +	if (fname == NULL && !no_output) {
 +		usage(1, "No special file provided");
 +	}
 +
 +	zone_size = BLOCK_SIZE << zone_shift;
 +	nrzones = nrblocks >> zone_shift;
 +	if ((nrzones << zone_shift) < nrblocks)
 +		nrzones++;
 +
 +	printf("block size = 1024 bytes\n");
 +	printf("zone  size = %d bytes\n",(int)zone_size);
 +	printf("number of blocks = %d\n",(int)nrblocks);
 +	printf("number of zones  = %d\n",(int)nrzones);
 +	printf("zone shift = %d\n",(int)zone_shift);
 +
 +	zone = (Zone_t*)malloc(nrzones*zone_size);
 +
 +	bzero((char*)zone, nrzones*zone_size);
 +     
 +	gettimeofday(&timestr, NULL);
 +	current_time = timestr.tv_sec;  /* UNIX time in seconds */
 +
 +	/* Set up a block of zeros */
 +
 +	bzero(zero, BLOCK_SIZE);
 +
 +	/* Set up the superblock */
 +
 +	super(sp, nrzones, nrinodes);
 +
 +	/* The superblock goes into block[1]. */
 +     
 +	zone[1].sp = sp[0];
 +
 +	zone_map = INODE_MAP + sp->s_imap_blocks;
 +	zone_size  = 1 << zone_shift;         /* Re-define zone_size */
 +	zoff = sp->s_firstdatazone - 1;
 +
 +	insert_bit(INODE_MAP, 0);   /* inode zero is not used but must be allocated */
 +	insert_bit(zone_map,  0);   /* bit zero must always be allocated in zone map */
 +
 +	/*
 +	  Set the mode and rwx bits for the root directory
 +	*/
 +
 +	mode = 040755;  /* drwxr_xr_x */
 +
 +	rootdir(alloc_inode(mode, getuid(), getgid()));
 +
 +	if (!no_output) {
 +
 +		if ((fdo = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0644)) == -1) {
 +			printf("Can't open file: %s; Quitting!\n",fname);
 +			exit(1);
 +		}
 +
 +		/* Write out the blocks */
 +	
 +		for (i=0; i<nrblocks; i++)
 +			write(fdo, &(zone[i]), BLOCK_SIZE);
 +
 +		close(fdo);
 +	}
 +     
 +	exit (0);
 +}
 +
 +void
 +usage(int err, char *mess)
 +{
 +	if (mess != NULL)
 +		printf("\n     %s!\n",mess);
 +
 +	printf("\n");
 +	printf("usage: newfs_minix [-hN] [-i inodes] [-b blocks] [-s shift] special\n");
 +	printf("\n");
 +	printf("   -h,       gives this help message\n");
 +	printf("   -N        no special file is written\n");
 +	printf("   -i inodes is the number of inodes\n");
 +	printf("   -b blocks is the number of blocks\n");
 +	printf("   -s shift  is log2(blocks/zone)\n");
 +	printf("\n");
 +	printf("\"special\" is either a special file or a regular file.\n");
 +	printf("\n");
 +	printf(" If \"special\" is omitted, then this help message will be printed.\n");
 +	printf(" If \"inodes\" is omitted then the number is calculated\n");
 +	printf(" If \"blocks\" is omitted then 1440 is used\n");
 +	printf("\n");
 +
 +	exit(err);
 +}
 diff -ruN sbin.orig/newfs_minix/misc.c sbin/newfs_minix/misc.c
 --- sbin.orig/newfs_minix/misc.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/misc.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,67 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley; wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +/*
 + * Below was lifted directly from the Minix source with permission.
 + */
 +
 +/*
 + * The next routine is copied from Minix's fsck.c and mkfs.c...  (Re)define some
 + * things for consistency.  Some things should be done better.  The shifts
 + * should be replaced by multiplications and divisions by MAP_BITS_PER_BLOCK
 + * since log2 of this is too painful to get right.
 + */
 +
 +/* Convert from bit count to a block count. The usual expression
 + *
 + *	(nr_bits + (1 << BITMAPSHIFT) - 1) >> BITMAPSHIFT
 + *
 + * doesn't work because of overflow.
 + *
 + * Other overflow bugs, such as the expression for N_ILIST overflowing when
 + * s_inodes is just over INODES_PER_BLOCK less than the maximum+1, are not
 + * fixed yet, because that number of inodes is silly.
 + */
 +
 +/* The above comment doesn't all apply now bit_t is ulong.  Overflow is now
 + * unlikely, but negative bit counts are now possible (though unlikely)
 + * and give silly results.
 + */
 +
 +int
 +bitmapsize(bit_t nr_bits)
 +{
 +	int nr_blocks;
 +
 +	nr_blocks = (int) (nr_bits >> BITMAPSHIFT);
 +  
 +	if (((bit_t) nr_blocks << BITMAPSHIFT) < nr_bits)
 +		++nr_blocks;
 +  
 +	return nr_blocks;
 +}
 diff -ruN sbin.orig/newfs_minix/mkfs.h sbin/newfs_minix/mkfs.h
 --- sbin.orig/newfs_minix/mkfs.h	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/mkfs.h	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,178 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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                              *
 + *                                                                          *
 + *   The guide for these sources 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                          *
 + *                                                                          *
 + ****************************************************************************/
 +
 +#include <sys/types.h>
 +#include <sys/time.h>
 +#include <sys/uio.h>
 +
 +#include <fcntl.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <unistd.h>
 +
 +/*
 + * In the definitions that follow, a zone is a block.
 + * There exists the possiblity that a zone can be
 + * a power of two larger than a block; that is the
 + * function of the variable s_log_zone_size in the
 + * super block which represents the shift needed
 + * to go from blocks to zones and back again.
 + */
 +
 +#define BLOCK_SIZE 1024       /* # bytes in a disk block */
 +
 +#define BITS_PER_BLOCK 8192   /* 8*BLOCK_SIZE */
 +#define BITMAPSHIFT 13        /* log2(BITS_PER_BLOCK) */
 +
 +#define INODE_MAP 2           /* position of first inode bitmap */
 +
 +#define V2_NR_DZONES 7
 +#define V2_NR_TZONES 10
 +#define NR_INODES 64
 +#define V2_INODE_SIZE 64    /* sizeof(inode_t) */
 +#define V2_ZONE_NUM_SIZE 4  /* sizeof(zone_t) = 32 bits */
 +#define V2_INODES_PER_BLOCK (BLOCK_SIZE/V2_INODE_SIZE)
 +#define V2_INDIRECTS   (BLOCK_SIZE/V2_ZONE_NUM_SIZE)  /* # zones/indir block */
 +
 +/* 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 unsigned short mino_t; 	   /* i-node number */
 +typedef unsigned short mmode_t;	   /* file type and permissions bits */
 +typedef unsigned long  moff_t;	   /* offset within a file */
 +typedef unsigned short zone1_t;	   /* zone number for V1 file systems */
 +typedef unsigned long  zone_t;	   /* zone number */
 +typedef unsigned long  block_t;	   /* block number */
 +typedef unsigned long  bit_t;      /* bit number in a bitmap */
 +typedef unsigned short bitchunk_t; /* a collection of bits from a bitmap */
 +typedef long           mtime_t;    /* time in sec since 1 Jan 1970 0000 GMT */
 +
 +/* File system types. */
 +#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 */
 +
 +/* Directory layout */
 +#define DIRBLKSIZ 512
 +#define DIRSIZ    14
 +
 +struct direct {
 +	mino_t d_ino;           /*  2 */
 +	char d_name[DIRSIZ];    /* 14 */
 +};
 +
 +#define DIR_ENTRY_SIZE usizeof(struct direct)
 +#define NR_DIR_ENTRIES (BLOCK_SIZE/DIR_ENTRY_SIZE)
 +
 +/* Structure of the superblock on disk */
 +
 +typedef struct {
 +	mino_t s_ninodes;		/* # usable inodes on the minor device */
 +	zone1_t  s_nzones;		/* total device size, including bit maps etc */
 +	short s_imap_blocks;		/* # of blocks used by inode bit map */
 +	short s_zmap_blocks;		/* # of blocks used by zone bit map */
 +	zone1_t s_firstdatazone;	/* number of first data zone */
 +	short s_log_zone_size;	        /* log2 of blocks/zone */
 +	moff_t s_max_size;		/* maximum file size on this device */
 +	short s_magic;		        /* magic number to recognize super-blocks */
 +	short s_pad;			/* try to avoid compiler-dependent padding */
 +	zone_t s_zones;	        	/* number of zones (replaces s_nzones in V2) */
 +	char pad[1000];                 /* pad the block to 1024 bytes */
 +} super_block_t;
 +
 +/* Structure of an inode on disk. length = 64 bytes. */
 +
 +typedef struct {
 +	mmode_t  i_mode;		/* file type, protection, etc. 2 */
 +	short i_nlinks;		        /* how many links to this file 2 */
 +	short i_uid;		        /* user id of the file's owner 2 */
 +	short i_gid;	        	/* group number                2 */
 +	moff_t i_size;	       	        /* current file size in bytes  4 */
 +	mtime_t i_atime;		/* time of last access (V2 only) 4 */
 +	mtime_t i_mtime;		/* when was file data last changed 4 */
 +	mtime_t i_ctime;		/* when was inode itself changed (V2 only) 4 */
 +	zone_t i_zone[V2_NR_TZONES];	/* zone numbers for direct, ind, and dbl ind 40 */
 +} inode_t;
 +
 +/* A zone can be inodes, a bootblock, a superblock or data */
 +/* This definition really defines the possible structure of a block */
 +
 +typedef union {
 +	inode_t inode[16];
 +	super_block_t sp;
 +	struct boot_block_s {
 +		char bootblock[294];
 +		char pad[730];
 +	} boot_block;
 +	char store[1024];
 +} Zone_t;
 +
 +void insert_bit (block_t, int);
 +int read_bit (block_t, int);
 +mino_t get_inode(void);
 +void get_block (block_t, char*);
 +void put_block (block_t, char*);
 +void rootdir(mino_t);
 +zone_t alloc_zone(void);
 +mino_t alloc_inode(int , int, int);
 +void add_zone(mino_t, zone_t, long, long);
 +void incr_link(mino_t);
 +void enter_dir(mino_t, char*, mino_t);
 +
 +/* These variable are defined in main and used throughout */
 +
 +extern Zone_t *zone;
 +extern int zone_size, zone_map, zone_shift;
 +extern int zoff;
 +extern char zero[BLOCK_SIZE];
 +extern int inodes_per_block;
 +extern int inode_offset;
 +extern int nrblocks, nrnodes;
 +extern long current_time;
 +extern zone_t nrzones;
 +extern unsigned int nrinodes;
 diff -ruN sbin.orig/newfs_minix/newfs_minix.8 sbin/newfs_minix/newfs_minix.8
 --- sbin.orig/newfs_minix/newfs_minix.8	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/newfs_minix.8	Fri Feb 28 13:56:11 2003
 @@ -0,0 +1,59 @@
 +
 +.Dd February 28, 2003
 +.Dt NEWFS_MINIX 8
 +.Os
 +.Sh NAME
 +.Nm newfs_minix
 +.Nd construct a new Minix file system
 +.Sh SYNOPSIS
 +.Nm
 +.Op Fl N
 +.Op Fl b Ar number-of-blocks
 +.Op Fl i Ar number-of-inodes
 +.Op Fl s Ar zone-shift
 +.Ar special
 +.Sh DESCRIPTION
 +The
 +.Nm
 +utility is used to initialize and clear the Minix
 +filesystem on device
 +.Ar special.
 +(We often refer to the
 +.Dq special file
 +as the
 +.Dq disk ,
 +although the special file need not be a physical disk.
 +In fact, it need not even be special.)
 +Without options
 +.Nm
 +will make a file system that will fit on a 1.44MB
 +floppy disk, however,
 +.Nm
 +has some options to allow the defaults to be selectively overridden.
 +.Pp
 +The following options define the general layout policies:
 +.Bl -tag -width indent
 +.It Fl b Ar number-of-blocks
 +The total number of blocks of 1024 bytes each that the FS will contain.
 +If this option is not provided then 1440 blocks are chosen.
 +.It Fl i Ar number-of-inodes
 +The number of inode in the FS. If this option is not provided then
 +the number of inodes will be calculated to be roughly 1/3 of the
 +number of blocks.
 +.It Fl s Ar zone-shift
 +The log2 of the number of blocks/zone. The FS can be designed to be
 +made up of data zones that contain a power of two contiguous blocks
 +in them. The default for this parameter is zero.
 +.It Fl N
 +Cause the file system parameters to be printed out
 +without really creating the file system.
 +.Sh SEE ALSO
 +.Xr mount 8 ,
 +.Xr mount_minixfs 8
 +.Sh AUTHOR
 +.An Ed Alley <wea@llnl.gov>
 +.Sh HISTORY
 +The
 +.Nm
 +command first appeared in
 +.Bx 4.6.2  .
 diff -ruN sbin.orig/newfs_minix/super.c sbin/newfs_minix/super.c
 --- sbin.orig/newfs_minix/super.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/super.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,106 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/* The following code was lifted and modified from the minix source with permission. */
 +
 +#include "mkfs.h"
 +
 +int bitmapsize(bit_t);
 +
 +void
 +super(super_block_t *sp, zone_t nzones, mino_t ninodes)
 +{
 +	zone_t maxsize;
 +	zone_t fstblk, fstzon;
 +	short impsize, zmpsize;
 +	int i, inoblks;
 +     
 +	/*
 +	  The maximum file size is the cube of the number of indirects
 +	  plus the square of the number of indirects plus the number of
 +	  indirects plus the number of direct entries in the inode times
 +	  the number of bytes in a zone. This can be a very large number,
 +	  (more than 32 bits of addressing can manage).
 +	*/
 +
 +	/* With triple indirects just put the 2GB limit as the max size */
 +     
 +	maxsize = ~(1 << 31);  /* All bits are ones except the sign bit */
 +     
 +	/* maxsize is then (2^31 - 1) = 2,147,483,647 bytes */
 +     
 +	/*
 +	  bitmapsize() returns the number of blocks in a bitmap
 +	*/
 +     
 +	impsize = bitmapsize((bit_t) (1 + ninodes));
 +	zmpsize = bitmapsize((bit_t) nzones);
 +
 +	printf("blocks in imap = %d\n",impsize);
 +	printf("blocks in zmap = %d\n",zmpsize);
 +
 +	/*
 +	  The number of blocks to the beginning of the inodes
 +	*/
 +
 +	inode_offset = impsize + zmpsize + 2;
 +
 +	printf("inode offset = %d\n", inode_offset);
 +
 +	/* The number blocks that contain inodes */
 +     
 +	inoblks = (ninodes + inodes_per_block - 1)/inodes_per_block;
 +
 +	printf("inode blocks = %d\n",inoblks);
 +
 +	/*
 +	  The number of blocks to the beginning of the data
 +	  gives the location of the first data zone.
 +	*/
 +
 +	fstblk = inode_offset + inoblks;    /* Number of 1K blocks */
 +	fstzon = (fstblk + (1 << zone_shift) - 1) >> zone_shift;
 +
 +	printf("first data zone  = %d\n",(int)fstzon);
 +	printf("first data block = %d\n",(int)(fstzon << zone_shift));
 +
 +	/*
 +	  Construct the V2 superblock
 +	*/
 +     
 +	sp->s_ninodes = ninodes;      /* # of inodes */
 +	sp->s_nzones = 0;             /* Not used in V1; forces errors in V2 */
 +	sp->s_imap_blocks = impsize;  /* # of blocks used by inode bit map */
 +	sp->s_zmap_blocks = zmpsize;  /* # of blocks used by zone  bit map */
 +	sp->s_firstdatazone = fstzon; /* index of the first data zone */
 +	sp->s_log_zone_size = zone_shift; /* log2(blocks/zone) */
 +	sp->s_max_size = maxsize;     /* Maximum file system size */
 +	sp->s_magic = SUPER_V2;       /* Indicates a V2 filesystem */
 +	sp->s_pad = 0;                /* Just padding */
 +	sp->s_zones = nzones;         /* # of zones */
 +	for (i=0; i<1000; i++)        /* Pad the rest of the block with zeros */
 +		sp->pad[i] = '\0';
 +}
 diff -ruN sbin.orig/newfs_minix/zoneio.c sbin/newfs_minix/zoneio.c
 --- sbin.orig/newfs_minix/zoneio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/zoneio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,127 @@
 +/*-
 + * 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.
 + */
 +
 +/*
 + * The code below was lifted from Minix's mkfs.c with permission.
 + */
 +
 +#include "mkfs.h"
 +
 +/*
 + * Returns a free zone/block number and marks it active
 + */
 +
 +zone_t
 +get_zone(void)
 +{
 +	zone_t num;
 +	block_t znodemap;
 +	int bit;
 +
 +	/* loop over zones */
 +
 +	for (num=1; num<nrzones; num++) {
 +		znodemap = (num/BITS_PER_BLOCK) + zone_map;
 +		bit      = num % BITS_PER_BLOCK;
 +		if (read_bit(znodemap,bit) == 0) {
 +			insert_bit(znodemap, bit);
 +			return (num+zoff);
 +		}
 +	}
 +
 +	printf("No data blocks available!\n");
 +	exit(1);
 +}
 +
 +/*
 +  Returns the absolute zone number of a new zone
 +*/
 +
 +zone_t
 +alloc_zone(void)
 +{
 +	/* Allocate a new zone */
 +	/* Works for zone > block */
 +     
 +	block_t b;
 +	int i;
 +	zone_t z;
 +
 +	/* Get a free zone/block and mark it active */
 +  
 +	z = get_zone();
 +  
 +	b = z << zone_shift;
 +  
 +	for (i = 0; i < zone_size; i++)
 +		put_block(b + i, zero);	/* give an empty zone */
 +     
 +	return z;  /* Returns the absolute zone number in the file */
 +}
 +
 +void
 +add_zone(mino_t n, zone_t z, long bytes, long cur_time)
 +{
 +	/* Add zone z to inode n. The file has grown by 'bytes' bytes. */
 +
 +	int off, i;
 +	block_t b;
 +	zone_t indir;
 +	zone_t blk[V2_INDIRECTS];
 +	inode_t *p;
 +	inode_t inode[V2_INODES_PER_BLOCK];
 +
 +	b = ((n - 1) / V2_INODES_PER_BLOCK) + inode_offset;
 +	off = (n - 1) % V2_INODES_PER_BLOCK;
 +  
 +	get_block(b, (char *) inode);
 +	p = &inode[off];
 +	p->i_size += bytes;
 +	p->i_mtime = cur_time;
 +	for (i = 0; i < V2_NR_DZONES; i++)
 +		if (p->i_zone[i] == 0) {
 +			p->i_zone[i] = z;
 +			put_block(b, (char *) inode);
 +			return;
 +		}
 +	put_block(b, (char *) inode);
 +
 +  /* File has grown beyond a small file. */
 +
 +	if (p->i_zone[V2_NR_DZONES] == 0) p->i_zone[V2_NR_DZONES] = alloc_zone();
 +	indir = p->i_zone[V2_NR_DZONES];
 +	put_block(b, (char *) inode);
 +	b = indir << zone_shift;
 +	get_block(b, (char *) blk);
 +	for (i = 0; i < V2_INDIRECTS; i++)
 +		if (blk[i] == 0) {
 +			blk[i] = z;
 +			put_block(b, (char *) blk);
 +			return;
 +		}
 +	printf("File has grown beyond single indirect");
 +	exit(1);
 +}
 8><------------------------------------------------- Cut here
 
 
 	        FOLLOWING is the SYS PATCH:
 8><------------------------------------------------- Cut here
 diff -ruN sys.orig/fs/minixfs/README sys/fs/minixfs/README
 --- sys.orig/fs/minixfs/README	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/README	Fri Feb 28 16:02:15 2003
 @@ -0,0 +1,40 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 is the Minix FS. It was developed on FreeBSD-4.6.x.
 +
 +The fs should be located in: "/usr/src/sys/fs/minixfs". There are two other source
 +files located in /usr/src/sbin/: mount_minixfs(8) is located in the directory:
 +"mount_minix"; and newfs_minix(8) is located in the directory: newfs_minix. The first
 +source is needed to mount the FS and the second one will make the FS. 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 and newfs_minix.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 sys.orig/fs/minixfs/TODO sys/fs/minixfs/TODO
 --- sys.orig/fs/minixfs/TODO	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/TODO	Fri Feb 28 16:28:01 2003
 @@ -0,0 +1,69 @@
 +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.
 +			(DONE 030213 by Ed Alley)
 +
 +	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.
 +			(DONE 030228 by Ed Alley)
 +
 +	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.
 +
 +	7. Write a newfs_minix routine.
 +			(DONE 030210 by Ed Alley)
 +
 +	8. Page fault if module is loaded for days!
 +		I don't understand this one. It seems that
 +		the module will page fault if it is loaded
 +		for days even after being unmounted. If a
 +		fs is mounted, then a page fault will occur.
 +		This can be eliminated by unloading the
 +		module after the last fs is unmounted.
 +			(DONE 020220 by Ed Alley)
 +
 +	9. Clean up the directory manipulating routines:
 +	        Come up with a way to keep track of holes,
 +	  	so we don't have to search the directory
 +	 	each time we need to put an entry in.
 +	        Also, fix up the way that the directory
 +	        size is determined: right now we just count
 +	        the entries until the last is found.
 +	        Fix up the way directories are truncated
 +	        when they are shortened: right now we just
 +	        copy out the existing entries, clean the
 +	        directory out and then reload it with the
 +	        saved entries.
 diff -ruN sys.orig/fs/minixfs/bsd-copyright sys/fs/minixfs/bsd-copyright
 --- sys.orig/fs/minixfs/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/bsd-copyright	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix.h sys/fs/minixfs/minix.h
 --- sys.orig/fs/minixfs/minix.h	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix.h	Fri Feb 28 21:07:27 2003
 @@ -0,0 +1,373 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 = 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 NO_BLOCK 0            /* Flag indicating no block available */
 +#define NO_ZONE  0            /* Flag indicating no zone available */
 +#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 */
 +	mode_t    i_mode;               /* BSD style mode of file */
 +	u_int32_t i_flag;               /* flags */
 +	u_int32_t 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)
 +#define VTOMI(vp) ((struct minix_inode*)(vp)->v_data)
 +
 +/* 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_zshift;	         /* 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 vnode *s_devvp;   /* Vnode of device mounted on */
 +	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_zbmap;      /* Zone bit-map */
 +	u_int32_t imap_lock;     /* Inode bit-map lock variable */
 +	u_int32_t zmap_lock;     /* Zone 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_zoff;              /* Data block offset = s_firstdatazone - 1. */
 +	int s_bsize;             /* Block size */
 +	int s_zsize;             /* Zone 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 */
 +};
 +
 +#define MBLOCK(bp) ((union minix_block*)((bp)->b_data))
 +
 +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 */
 +
 +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_zalloc(struct minix_super_block*,u_daddr_t*,u_daddr_t*);
 +int minix_dzalloc(struct minix_super_block*,u_daddr_t);
 +int minix_makeinode(int,struct vnode*,struct vnode**,struct componentname*);
 +int minix_addzone(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_zone_count(struct minix_super_block*);
 +int minix_next_free_inode(struct minix_super_block*);
 +int minix_next_free_zone(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, short);
 +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 sys.orig/fs/minixfs/minix_alock.s sys/fs/minixfs/minix_alock.s
 --- sys.orig/fs/minixfs/minix_alock.s	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_alock.s	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,63 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix_bio.c sys/fs/minixfs/minix_bio.c
 --- sys.orig/fs/minixfs/minix_bio.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_bio.c	Fri Feb 28 21:27:15 2003
 @@ -0,0 +1,732 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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_zbit(struct minix_super_block*,u_int32_t);
 +void minix_write_zbit(struct minix_super_block*,u_int32_t);
 +void minix_delete_zbit(struct minix_super_block*,u_int32_t);
 +
 +/*
 + * Allocate new blocks from lbof0+1 to lbof1.
 + * If lbof0 doesn't exist, then allocate it first.
 + */
 +int
 +minix_balloc(struct vnode *vp, u_daddr_t lbof1, struct buf **bpp)
 +{
 +	struct buf *bp;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_daddr_t lbof0, lbn, pbn, lb, zone, boff, z0, z1, bsize;
 +	int error;
 +
 +	*bpp = NULL;
 +
 +	if (dip->i_size == 0) {  /* No zones so create a new one */
 +		zone = 0;
 +		if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +			return error;
 +		if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0)
 +			return error;
 +		if ((error = bread(devvp, pbn, sp->s_zsize, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)sp->s_zsize);
 +		bwrite(bp);
 +	}
 +
 +	if (dip->i_size > 0)
 +		lbof0 = (dip->i_size - 1)/BLOCK_SIZE;
 +	else
 +		lbof0 = 0;
 +
 +	z0 = lbof0 >> sp->s_zshift;
 +	z1 = lbof1 >> sp->s_zshift;
 +
 +	if (z1 <= z0) {  /* Zone already exists! just return the block */
 +		if ((error = minix_bmapfs(vp, lbof1, &pbn, NULL)) != 0)
 +			return error;
 +		if ((error = bread(devvp, pbn, BLOCK_SIZE, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)BLOCK_SIZE);
 +		*bpp = bp;
 +		return 0;
 +	}
 +
 +	/* The new zone extends beyond the file */
 +
 +	/* Allocate all the blocks between lbof0 and lbof1 and zero them out */
 +
 +	for (lb=lbof0+1; lb<lbof1; lb++) {
 +		zone = lb >> sp->s_zshift;
 +		boff = lb - (zone << sp->s_zshift);
 +		if (boff > 0) { /* Zone already exixts */
 +			bsize = BLOCK_SIZE;
 +			if ((error = minix_bmapfs(vp, lb, &pbn, NULL)) != 0)
 +				return error;
 +		} else {
 +			bsize = sp->s_zsize;
 +			if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +				return error;
 +			if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0) /* add the new zone to inode */
 +				return error;	
 +		}
 +		if ((error = bread(devvp, pbn, bsize, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)bsize);
 +		bwrite(bp);
 +	}
 +	/* Finally allocate lbof1, the required new block */
 +
 +	zone = lbof1 >> sp->s_zshift;
 +	boff = lbof1 - (zone << sp->s_zshift);
 +	if (boff > 0) {   /* Zone already exists, just get the block */
 +		bsize = BLOCK_SIZE;
 +		if ((error = minix_bmapfs(vp, lbof1, &pbn, NULL)) != 0)
 +			return error;
 +	} else {
 +		bsize = sp->s_zsize;
 +		if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +			return error;
 +		if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0) /* add the new zone to inode */
 +			return error;
 +	}
 +	if ((error = bread(devvp, pbn, bsize, NOCRED, &bp)) != 0)
 +		return error;
 +	bzero(bp->b_data, (size_t)bsize);
 +
 +	*bpp = bp;
 +	return 0;
 +
 +}
 +/*
 + * Returns the block number of the next free zone and sets
 + * the corresponding bit in the zone bitmap.
 + */
 +int
 +minix_zalloc(struct minix_super_block *sp, u_daddr_t *lblkp, u_daddr_t *pblkp)
 +{
 +	int ic, error;
 +	u_daddr_t zone, bblk;
 +	daddr_t off;
 +	struct buf *bp;
 +	struct vnode *devvp = sp->s_devvp;
 +	union minix_block *mbp;
 +	u_daddr_t pbn = 0, pzn = 0;
 +	u_int16_t *buf = sp->s_zbmap;
 +
 +	*lblkp = 0;
 +	*pblkp = 0;
 +
 +	minix_get_lock(&sp->zmap_lock);
 +
 +	if ((zone = minix_next_free_zone(sp)) == NO_ZONE) {
 +		minix_free_lock(&sp->zmap_lock);
 +		return ENOSPC;
 +	}
 +
 +	minix_write_zbit(sp, zone);
 +
 +	ic = zone >> 4;                 /* Chunk that bit resides in */
 +	bblk = zone / BITS_PER_BLOCK;   /* Block that bit is in */
 +	off  = zone % 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_zbit(sp, zone);
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	mbp = MBLOCK(bp);
 +	mbp->bitchunk[off] = buf[ic];
 +
 +	if ((error = minix_putblk(bp)) != 0) {
 +		minix_delete_zbit(sp, zone);
 +		if (bp != NULL) {
 +		     bp->b_flags |= B_INVAL;
 +		     brelse(bp);
 +		}
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	minix_free_lock(&sp->zmap_lock);
 +
 +	pzn = zone + sp->s_zoff;    /* Physical zone number */
 +	pbn = pzn << sp->s_zshift;  /* Physical block number */
 +	
 +	*lblkp = pbn;
 +	*pblkp = byte_to_blkn(blk_to_byte(pbn)); /* Suitable for bread */
 +
 +	return 0;
 +}
 +/*
 + * Deallocates a zone if the last block in it is released.
 + */
 +int
 +minix_dzalloc(struct minix_super_block *sp, u_daddr_t zone)
 +{
 +        int ic, error;
 +	u_daddr_t bblk;
 +	daddr_t off;
 +	struct buf *bp;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_int16_t *buf = sp->s_zbmap;
 +	union minix_block *mbp;
 +
 +	minix_get_lock(&sp->zmap_lock);
 +
 +	zone -= sp->s_zoff;              /* Convert to bit offset */
 +
 +	if (minix_read_zbit(sp, zone))
 +		minix_delete_zbit(sp, zone);
 +	else {
 +		minix_free_lock(&sp->zmap_lock);
 +		return 0;
 +	}
 +	
 +	ic = zone >> 4;                 /* Chunk that bit resides in */
 +	bblk = zone / BITS_PER_BLOCK;   /* Block that bit is in */
 +	off  = zone % 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_zbit(sp, zone);
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	mbp = MBLOCK(bp);
 +	mbp->bitchunk[off] = buf[ic];
 +
 +	if ((error = minix_putblk(bp)) != 0) {
 +		minix_write_zbit(sp, zone);
 +		if (bp != NULL) {
 +			bp->b_flags |= B_INVAL;
 +			brelse(bp);
 +		}
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	minix_free_lock(&sp->zmap_lock);
 +	
 +	return 0;
 +}
 +/*
 + * Add a zone to the end of a file. "zone" is the logical zone
 + * offset in the file and pbn is the physical zone offset in the
 + * file system. It is the responsibility of the calling
 + * routine to guarantee that the next zone to add is at the
 + * logical end of the file. This routine returns EIO otherwise.
 + */
 +int
 +minix_addzone(struct minix_inode *ip, u_daddr_t zone, u_daddr_t pbn)
 +{
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	union minix_block *mbp;
 +	struct buf *bp;
 +	u_daddr_t pind;
 +	u_daddr_t id0, id1, id2, id3, off, blk;
 +	int error, indirect = 0;
 +
 +	pind = zone + 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[zone] != 0)
 +			return EIO;
 +		dip->i_block[zone] = pbn;
 +		break;
 +	case 3:
 +		off = zone - 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_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			id0 = blk >> sp->s_zshift;
 +			dip->i_block[V2_NR_DBLOCKS+2] = id0;
 +			bwrite(bp);
 +		} else
 +			id0 = dip->i_block[V2_NR_DBLOCKS+2];
 +
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id3];
 +		if (id0 == 0) {
 +			/* Generate a double indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			mbp->ind[id3] = id0;
 +			bwrite(bp);
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			bwrite(bp);
 +		} else
 +			bqrelse(bp);
 +		goto rind2;
 +		break;
 +	case 2:
 +		off = zone - 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_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			id0 = blk >> sp->s_zshift;
 +			dip->i_block[V2_NR_DBLOCKS+1] = id0;
 +			bwrite(bp);
 +		} else
 +			id0 = dip->i_block[V2_NR_DBLOCKS+1];
 +	rind2:
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id2];
 +		if (id0 == 0) {
 +			/* Generate a single indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			mbp->ind[id2] = id0;
 +			bwrite(bp);
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			bwrite(bp);
 +		} else
 +			bqrelse(bp);
 +		goto rind1;
 +		break;
 +	case 1:
 +		id1 = zone - TOT_DIRECT;
 +		if (dip->i_block[V2_NR_DBLOCKS] == 0) {
 +			/* Generate a single indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			if ((error = minix_getblk(devvp, blk, &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:
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		mbp->ind[id1] = pbn;
 +		bwrite(bp);
 +		break;
 +	default:
 +		printf("minix_addzone: too large: zone = %d\n",(int)zone);
 +		return ENOSPC;
 +	}
 +
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +
 +	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 block number */
 +             u_daddr_t *pbk,          /* returned physical block number */
 +             int *runp)               /* number of dev chunks to add */
 +{
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	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;
 +	u_daddr_t id, idp, id0=0, id1, id2, id3, blk;
 +	daddr_t offset;
 +	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;
 +	}
 +
 +	id = lbk >> sp->s_zshift;            /* index by zones */
 +	offset = lbk - (id << sp->s_zshift); /* Block offset in zone */
 +	
 +	idp = id + 1;
 +
 +	if (idp > ndb)
 +		indirect++;
 +	if (idp > nsidb)
 +		indirect++;
 +	if (idp > ndidb)
 +		indirect++;
 +	if (idp > ntidb)
 +		indirect++;
 +
 +	*pbk = 0;
 +	switch (indirect) {
 +	case 0:                /* Direct */
 +		if ((*pbk = (dip->i_block[id] << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		*pbk += offset;
 +		*pbk = ((*pbk)*BLOCK_SIZE) >> DEV_BSHIFT;
 +		return 0;
 +	case 3:                /* Triple indirect */
 +		if ((blk = (dip->i_block[V2_NR_DBLOCKS+2] << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
 +			brelse(bp);
 +			return error;
 +		}
 +		mbp = MBLOCK(bp);
 +		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 ((blk = (id0 << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
 +			brelse(bp);
 +			return error;
 +		}
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id2];
 +		bqrelse(bp);
 +		goto rind1;
 +	case 1:                /* Single indirect */
 +		id1 = id - ndb;
 +		id0 = dip->i_block[V2_NR_DBLOCKS];
 +	rind1:
 +		if ((blk = (id0 << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		if ((*pbk = (mbp->ind[id1] << sp->s_zshift)) == 0) {
 +			brelse(bp);
 +			return EFAULT;
 +		}
 +		bqrelse(bp);
 +		*pbk += offset;
 +		*pbk = ((*pbk)*BLOCK_SIZE) >> DEV_BSHIFT;
 +		return 0;
 +	default:
 +		return ENOSPC;
 +	}
 +
 +	/* NOTREACHED */
 +}
 +/*
 + * 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 = VTOMI(vp);
 +	struct vnode *devvp = ip->i_su->s_devvp;
 +	int error;
 +	
 +	lbn = ((u_daddr_t)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 + (int)(offset % BLOCK_SIZE);
 +	*bpp = bp;
 +	return 0;
 +}
 +/*
 + * The next few routines read or alter the in-core
 + * zone bitmaps. Any locking that is required is
 + * assumed to occur in the calling programs.
 + */
 +
 +/*
 + * Return the number of free zones
 + */
 +int
 +minix_free_zone_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_zbmap;
 +     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 zone
 + */
 +int
 +minix_next_free_zone(struct minix_super_block *sp)
 +{
 +	int i, n, s, nb;
 +	u_int16_t *buf = sp->s_zbmap;
 +	register int bit;
 +
 +	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)) {
 +					bit = s + (i << 4);
 +					return((bit < nb) ? bit : NO_ZONE);
 +				}
 +	return NO_ZONE;
 +}
 +/*
 + * Read a bit from the in-core data block bitmap
 + */
 +int
 +minix_read_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap;     
 +	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_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap; 
 +	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_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap;
 +	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, short zshift)
 +{
 +     int nindirects, indirect = 0;
 +     int nzones, 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 set initially to one above. Add the extra blocks below.
 +      */
 +     nblocks += (size - 1) / BLOCK_SIZE;
 +     nzones = nblocks >> zshift;
 +     if (nblocks > (nzones << zshift))
 +	  nzones += 1;
 +
 +     /* Figure out whether we have indirect blocks */
 +
 +     if (nzones > TOT_DIRECT)
 +	  indirect++;         /* Single indirect */
 +
 +     if (nzones > TOT_SINDIRECT)
 +	  indirect++;         /* Double indirect */
 +
 +     if (nzones > 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 sys.orig/fs/minixfs/minix_ihash.c sys/fs/minixfs/minix_ihash.c
 --- sys.orig/fs/minixfs/minix_ihash.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_ihash.c	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,165 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +#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");
 +/*
 + * 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)
 +{
 +	mnxihashtb = hashinit(desiredvnodes, M_MNXIHASH, &minix_ihash);
 +	simple_lock_init(&minix_ihash_slock);
 +}
 +/*
 + * Free the inode hash table
 + */
 +void
 +minix_ihashuninit(void)
 +{
 +     if (mnxihashtb) {
 +	  free(mnxihashtb, M_MNXIHASH);
 +	  mnxihashtb = NULL;
 +     }
 +}
 +/*
 + * 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 sys.orig/fs/minixfs/minix_inode.c sys/fs/minixfs/minix_inode.c
 --- sys.orig/fs/minixfs/minix_inode.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_inode.c	Fri Feb 28 21:21:42 2003
 @@ -0,0 +1,757 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/stat.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,u_daddr_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 = VTOMI(dvp);
 +	*vpp = NULL;
 +	
 +	if ((mode & IFMT) == 0)
 +		mode |= IFREG;
 +
 +	if ((error = minix_valloc(dvp, mode, cnp->cn_cred, &tvp)) != 0)
 +		return error;
 +
 +	ip = VTOMI(tvp);
 +	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;
 +}
 +/*
 + * 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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	u_daddr_t lbn0, lbn1, len32;
 +	int error;
 +
 +	if (length > ~(1 << 31))   /* 32-bit offsets only! */
 +		return EFBIG;
 +
 +	len32 = (u_daddr_t)length;
 +
 +	/* 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 <= len32) {
 +	     ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	     return minix_update(vp);
 +	}
 +
 +	/* lbn1 is the last zone in the file */
 +
 +	lbn1 = (dip->i_size - 1)/sp->s_zsize;
 +
 +	/* lbn0 is the first zone to eliminate */
 +	
 +	if (len32 > 0)
 +		lbn0 = (len32 - 1)/sp->s_zsize + 1;
 +	else
 +		lbn0 = 0;    /* Eliminate all zones */
 +
 +	if (lbn0 > lbn1)     /* Silly but check anyway */
 +		return EIO;
 +
 +	/* truncatefs will eliminate all zones from lbn0 to EOF */
 +
 +	if ((error = minix_truncatefs(ip, lbn0, len32)) != 0)
 +		return error;
 +	
 +	dip->i_size  = len32;               /* The new length of the file */
 +	ip->i_blocks = minix_num_blocks(ip->i_mode, len32, sp->s_zshift);
 +	
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	
 +	return minix_update(vp);
 +}
 +/*
 + * Frees all zones from the logical offset lbn to the end of the file.
 + */
 +static int
 +minix_truncatefs(struct minix_inode *ip, u_daddr_t lbn, u_daddr_t length)
 +{
 +	struct buf *bp;
 +	union minix_block *mbp;
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_daddr_t lb0, lb, off = 0;
 +	u_daddr_t blk, id0, id1, id2, id3, zone;
 +	int error, indirect;
 +	int delete1, delete2, delete3;
 +
 +	if (dip->i_size == 0)
 +		return 0;
 +
 +	lb = lb0 = (dip->i_size - 1)/sp->s_zsize;
 +	
 +	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_dzalloc(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;
 +				}
 +			}
 +			blk = dip->i_block[V2_NR_DBLOCKS+2] << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return 0;
 +			mbp = MBLOCK(bp);
 +			id0 = mbp->ind[id3];
 +			if (delete3) {
 +				zone = dip->i_block[V2_NR_DBLOCKS+2];
 +				dip->i_block[V2_NR_DBLOCKS+2] = 0;
 +				if ((error = minix_dzalloc(sp, zone)) != 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:
 +			blk = id0 << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return error;
 +			if (delete2) {
 +				if ((error = minix_dzalloc(sp,id0)) != 0)
 +					return error;
 +			}
 +			mbp = MBLOCK(bp);
 +			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:
 +			blk = id0 << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			if ((error = minix_dzalloc(sp, mbp->ind[id1])) != 0)
 +				return error;
 +			if (delete1) {
 +				if ((error = minix_dzalloc(sp, id0)) != 0)
 +					return error;
 +				brelse(bp);
 +			} else {
 +				mbp->ind[id1] = 0;
 +				bwrite(bp);
 +			}
 +			break;
 +		default:
 +			return EIO;
 +		}
 +
 +	} while (lbn < lb--);
 +	
 +	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 = VTOMI(vp);
 +
 +	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;
 +
 +	return minix_iput(ip);
 +}
 +
 +void
 +minix_itimes(struct vnode *vp)
 +{
 +     	struct minix_inode *ip = VTOMI(vp);
 +	struct timespec ts;
 +	
 +	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);
 +}
 +/*
 + * The following access routine was lifted from the UFS code.
 + */
 +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);	
 +}
 +
 +/* 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 = VTOMI(vp);
 +
 +        return minix_dialloc(ip->i_su, 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;
 +     u_daddr_t off;
 +
 +     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 = MBLOCK(bp);
 +     off = (ino-1) % V2_INODES_PER_BLOCK;
 +     ip->i_dino = mbp->dinode[off];
 +     
 +     bqrelse(bp);
 +
 +     ip->i_number = ino;
 +     ip->i_parent = 0;
 +     ip->i_su = sp;
 +     ip->i_vnode = NULL;
 +     ip->i_mode = ip->i_dino.i_mode;
 +     ip->i_mmp = NULL;
 +     ip->i_dev = devvp->v_rdev;
 +     ip->i_flag = 0;
 +     
 +     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;
 +	u_daddr_t off;
 +	int error;
 +
 +	devvp = ip->i_su->s_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 = MBLOCK(bp);
 +	off = (ino-1) % V2_INODES_PER_BLOCK;
 +	mbp->dinode[off] = 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_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)) == NO_INODE) {
 +		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 = MBLOCK(bp);
 +	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_devvp;
 +	struct buf *bp;
 +	union minix_block *mbp;
 +
 +	minix_get_lock(&sp->imap_lock);
 +
 +	if (minix_read_ibit(sp, ino))
 +		minix_delete_ibit(sp, ino);
 +	else {
 +		minix_free_lock(&sp->imap_lock);
 +		return 0;
 +	}
 +
 +	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 = MBLOCK(bp);
 +	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;
 +	register int bit;
 +
 +	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)) {
 +					bit = s + (i << 4);
 +					return((bit < sp->s_ninodes) ? bit : NO_INODE);
 +				}
 +	return NO_INODE;
 +}
 +/*
 + * 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 sys.orig/fs/minixfs/minix_locks.c sys/fs/minixfs/minix_locks.c
 --- sys.orig/fs/minixfs/minix_locks.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_locks.c	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,52 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix_lookup.c sys/fs/minixfs/minix_lookup.c
 --- sys.orig/fs/minixfs/minix_lookup.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_lookup.c	Fri Feb 28 21:28:31 2003
 @@ -0,0 +1,1273 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/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 int minix_dircleanup(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  = VTOMI(dvp);
 +    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 = VTOMI(vp);
 +		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, ib, id, j, error, len, entry = 0;
 +	char ppath[256], *pname;
 +	struct minix_direct dir[NR_DIR_ENTRIES];
 +	unsigned long ind1[V2_INDIRECTS], blk;
 +	ino_t inum, inuml;
 +
 +	*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;
 +	ip->i_entry = -1;
 +     
 +	while(pname != NULL) {
 +
 +		ip->i_noent = -1;
 +		ip->i_entry = -1;
 +	  
 +		if (!(ip->i_mode & IFDIR))
 +			return ENOTDIR;
 +
 +		if (minix_dinode_access(dip,VEXEC,cred,sp) != 0)
 +			return EACCES;
 +
 +		/* Search directory */
 +
 +		entry = 0;
 +		for (i=0; i<V2_NR_DBLOCKS; i++) {
 +			if (dip->i_block[i] == 0) {
 +				if (ip->i_noent < 0)
 +					ip->i_noent = entry;
 +				return ENOENT;
 +			}
 +			ib  = 1 << sp->s_zshift;
 +			blk = dip->i_block[i] << sp->s_zshift;
 +			while(ib--) {
 +				if((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +					return error;
 +				bcopy(bp->b_data, dir, BLOCK_SIZE);
 +				bqrelse(bp);
 +				for (j=0; j<NR_DIR_ENTRIES; j++) {
 +					if (dir[j].d_ino == 0) {
 +						if (ip->i_noent < 0)
 +							ip->i_noent = entry;
 +						entry++;
 +						continue;
 +					}
 +					if (!strncmp(pname, dir[j].d_name,MINIX_NAME_MAX)) {
 +						ip->i_entry = entry;
 +						goto gotj;
 +					}
 +					entry++;
 +				}
 +		        }
 +		}
 +	  
 +		if (dip->i_block[V2_NR_DBLOCKS] == 0) {
 +			if (ip->i_noent < 0)
 +				ip->i_noent = entry;
 +			return ENOENT;
 +		}
 +		blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +			return error;
 +		bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +		bqrelse(bp);
 +		for (id=0; id<V2_NR_INDIRECTS; id++) {
 +			if (ind1[id] == 0) {
 +				if (ip->i_noent < 0)
 +					ip->i_noent = entry;
 +				return ENOENT;
 +			}
 +			ib  = 1 << sp->s_zshift;
 +			blk = ind1[id] << sp->s_zshift;
 +			while(ib--) {
 +				if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +					return error;
 +				bcopy(bp->b_data, dir, BLOCK_SIZE);
 +				bqrelse(bp);
 +				for (j=0; j<NR_DIR_ENTRIES; j++) {
 +					if (dir[j].d_ino == 0) {
 +						if (ip->i_noent < 0)
 +							ip->i_noent = entry;
 +						entry++;
 +						continue;
 +					}
 +					if (!strncmp(pname, dir[j].d_name,MINIX_NAME_MAX)) {
 +					        ip->i_entry = entry;
 +						goto gotj;
 +					}
 +					entry++;
 +				}
 +			}
 +		}
 +
 +		/*
 +		 * Dropped through:
 +		 *      Could not find an entry: return "no entry".
 +		 */
 +		
 +		if (ip->i_noent < 0)
 +			ip->i_noent = entry;
 +
 +		*ino  = 0;
 +		*pino = inuml;
 +		*entp = -1;
 +		
 +		return ENOENT;		
 +		
 +	gotj:
 +		/*
 +		 * Found an entry; get the inode and look for another token.
 +		 */
 +		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;
 +
 +		pname = minix_strtok((char*)NULL, "/");
 +	}
 +	
 +	/*
 +	 * Return the inode for the entry found, and related information.
 +	 */
 +	
 +	*ino  = inum;
 +	*pino = inuml;
 +	*entp = entry;
 +     
 +	return 0;
 +}
 +/*
 + * 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;
 +	u_int32_t bln, off, newsize;
 +	off_t offset;
 +	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_direct *ep;
 +	struct iovec aiov;
 +	struct uio auio;
 +	struct buf *bp;
 +	char *dirbuf;
 +	int error;
 +
 +	p  = curproc;
 +	cr = p->p_ucred;
 +
 +	newentrysize = DIR_ENTRY_SIZE;
 +
 +	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) { /* No Entries Available */
 +		if (!(ip->i_entry < MAX_DIR_ENTRIES))
 +			return ENOSPC;
 +		bln = ip->i_entry / NR_DIR_ENTRIES;
 +		auio.uio_offset = bln*BLOCK_SIZE;
 +		auio.uio_resid  = newentrysize;
 +		aiov.iov_len    = newentrysize;
 +		aiov.iov_base   = (caddr_t)dirp;
 +		auio.uio_iov    = &aiov;
 +		auio.uio_iovcnt = 1;
 +		auio.uio_rw     = UIO_WRITE;
 +		auio.uio_segflg = UIO_SYSSPACE;
 +		auio.uio_procp  = (struct proc*)0;
 +		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
 +		ip->i_flag |= IN_CHANGE;
 +		return error;
 +	}
 +
 +	if (ip->i_noent >= 0)
 +		newent = ip->i_noent;
 +	else
 +		newent = ip->i_entry;
 +
 +	bln = newent / NR_DIR_ENTRIES;
 +	off = newent % NR_DIR_ENTRIES;
 +	offset = (off_t)(bln*BLOCK_SIZE + off*newentrysize);
 +
 +	if ((error = minix_blkatoff(dvp, offset, &dirbuf, &bp)) != 0)
 +		return error;
 +	ep = (struct minix_direct*)dirbuf;
 +	*ep = *dirp;
 +	bwrite(bp);
 +
 +	newsize = (u_int32_t)offset + newentrysize;
 +	if (newsize > dip->i_size)
 +	     dip->i_size = newsize;
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	return minix_update(dvp);
 +}
 +static int
 +minix_dircleanup(struct vnode *dvp)
 +{
 +	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	u_daddr_t bln;
 +	u_long newsize;
 +	int error;
 +
 +	if ((error = minix_dirsize(dvp, &newsize)) != 0)
 +		return error;
 +
 +	bln = minix_num_blocks(ip->i_mode, (u_int32_t)newsize, sp->s_zshift);
 +
 +	if (ip->i_blocks > bln)
 +		if ((error = minix_truncate(dvp, (off_t)newsize, IO_SYNC, NOCRED, NULL)) != 0)
 +		     return error;
 +	
 +	if (bln > ip->i_blocks)
 +	     vnode_pager_setsize(dvp, (vm_ooffset_t)(bln*BLOCK_SIZE));
 +
 +	ip->i_blocks = bln;
 +	dip->i_size  = newsize;
 +
 +	return 0;
 +}
 +/*
 + * Removes an entry from a directory.
 + * NOTE: Will not remove . or .. but returns ENOENT
 + *       in that case.
 + */
 +int
 +minix_dirremove(struct vnode *dvp)
 +{
 +     struct buf *bp;
 +     struct minix_inode *dip = VTOMI(dvp);
 +     struct minix_dinode *dinp = &(dip->i_dino);
 +     union minix_block *mbp;
 +     struct minix_direct *dirp;
 +     struct minix_super_block *sp = dip->i_su;
 +     struct vnode *devvp = sp->s_devvp;
 +     u_daddr_t ind, bln, off, dsiz;
 +     u_daddr_t blks, blkc, zone, boff;
 +     u_long count, size;
 +     int entry, error;
 +
 +     /*
 +      * Entries 0 and 1 are '.' and '..' respectively.
 +      */
 +
 +     if ((entry = dip->i_entry) < 2)
 +	     return ENOENT;
 +     
 +     bln = entry / NR_DIR_ENTRIES;
 +     off = entry % NR_DIR_ENTRIES;
 +     zone = bln >> sp->s_zshift;
 +     boff = bln - (zone << sp->s_zshift);
 +
 +     if (zone < V2_NR_DBLOCKS) {
 +	  zone = dinp->i_block[zone];
 +	  bln  = (zone << sp->s_zshift) + boff;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  mbp->dir[off].d_ino = 0;
 +	  bwrite(bp);
 +     } else {
 +	  zone -= V2_NR_DBLOCKS;
 +	  bln = dinp->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  ind = mbp->ind[zone];
 +	  bqrelse(bp);
 +	  bln = (ind << sp->s_zshift) + boff;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  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 && count > 2) {
 +	     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);
 +
 +	     cache_purge(dvp);
 +
 +	     return 0;
 +     }
 +     
 +     return minix_dircleanup(dvp);
 +}
 +/*
 + * 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, because
 + * the available space is full, then the routine returns
 + * the next entry number and 0 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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	struct minix_direct dir[NR_DIR_ENTRIES];
 +	unsigned long ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	int ib, id, iz, error, entry = 0;
 +
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0) {
 +			ip->i_noent = -1;
 +			ip->i_entry = entry;
 +			return 0;
 +		}
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		ib = 1 << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&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 = entry;
 +					ip->i_entry = -1;
 +					return 0;
 +				}
 +				entry++;
 +			}
 +		}
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0) {
 +	     ip->i_noent = -1;
 +	     ip->i_entry = entry;
 +	     return 0;
 +	}
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0) {
 +			ip->i_noent = -1;
 +			ip->i_entry = entry;
 +			return 0;
 +		}
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&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 = entry;
 +					ip->i_entry = -1;
 +					return 0;
 +				}
 +				entry++;
 +			}
 +		}
 +	}
 +	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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	unsigned long ind1[V2_INDIRECTS];
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +	u_int32_t blk;
 +	u_long count = 0;
 +
 +	*last = count;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while (ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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 = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +
 +	*count = 0;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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 = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	unsigned long idc;
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +
 +	if (count < 2)
 +	     return EIO;
 +
 +	idc = 0;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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) {
 +					     bqrelse(bp);
 +					     return 0;
 +					}
 +				}
 +			}
 +			bqrelse(bp);
 +		}
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0)
 +		return 0;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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) {
 +						bqrelse(bp);
 +						return 0;
 +					}
 +				}
 +			}
 +			bqrelse(bp);
 +		}
 +	}
 +	return 0;	
 +}
 +/*
 + * Clean everything out of a directory except '.' and '..'.
 + */
 +static int
 +minix_dirclean(struct vnode *dvp)
 +{
 +     	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	union minix_block *mbp;
 +	int ib, ib0, id, iz, error;
 +
 +	if (dip->i_block[0] == 0) /* This zone must always exist */
 +		return EIO;
 +
 +	ip->i_blocks = 1 << sp->s_zshift;
 +	dip->i_size = 2*DIR_ENTRY_SIZE;
 +
 +	blk = dip->i_block[0] << sp->s_zshift;
 +	ib  = ip->i_blocks;
 +	ib0 = ib - 1;
 +
 +	while(ib--) {
 +		if ((error = minix_getblk(devvp,blk++, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		for (id=(ib==ib0)?2:0; id<NR_DIR_ENTRIES; id++)
 +			mbp->dir[id].d_ino = 0;
 +		bwrite(bp);
 +	}
 +
 +	for (iz=1; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		if ((error = minix_dzalloc(sp, dip->i_block[iz])) != 0)
 +			return error;
 +		dip->i_block[iz] = 0;
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0)
 +		return 0;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	brelse(bp);
 +
 +	if ((error = minix_dzalloc(sp, dip->i_block[V2_NR_DBLOCKS])) != 0)
 +	     return 0;
 +	dip->i_block[V2_NR_DBLOCKS] = 0;
 +
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		if ((error = minix_dzalloc(sp, ind1[iz])) != 0)
 +			return error;
 +	}
 +	return 0;	
 +}
 +/*
 + * Returns non-zero if the directory is empty, zero otherwise.
 + */
 +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<(off_t)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. NOTE: This only works
 + * for the first block.
 + */
 +int
 +minix_dirrewrite(struct minix_inode *dp, struct minix_inode *ip,
 +                 ino_t ino, int entry)
 +{
 +	struct buf *bp;
 +	union minix_block *mbp;
 +	int off, error;
 +	off_t offset;
 +	u_daddr_t bln;
 +
 +	bln = entry / NR_DIR_ENTRIES;
 +	off = entry % NR_DIR_ENTRIES;
 +
 +	offset = (off_t)(bln*BLOCK_SIZE);
 +
 +	if ((error = minix_blkatoff(dp->i_vnode, offset, NULL, &bp)) != 0)
 +	     return error;
 +
 +	mbp = MBLOCK(bp);
 +	mbp->dir[off].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 sys.orig/fs/minixfs/minix_subr.c sys/fs/minixfs/minix_subr.c
 --- sys.orig/fs/minixfs/minix_subr.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_subr.c	Fri Feb 28 21:04:32 2003
 @@ -0,0 +1,262 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 minix_inode *pip = VTOMI(pvp);
 +	struct minix_super_block *sp = pip->i_su;
 +	struct mount *mp = pip->i_mmp->mnx_mp;
 +	struct minix_inode *ip;
 +	struct minix_dinode *dip;
 +	int ino, error;
 +	
 +	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 = VTOMI(*vpp);
 +	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);
 +	
 +	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 = VTOMI(vp);
 +	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;
 +}
 +/*
 + * 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 = VTOMI(vp);
 +	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 = VTOMI(vp);
 +	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;
 +	     */
 +	     break;
 +	}
 +	return VBAD;
 +}
 diff -ruN sys.orig/fs/minixfs/minix_ufs.c sys/fs/minixfs/minix_ufs.c
 --- sys.orig/fs/minixfs/minix_ufs.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_ufs.c	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,60 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix_vfsops.c sys/fs/minixfs/minix_vfsops.c
 --- sys.orig/fs/minixfs/minix_vfsops.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_vfsops.c	Fri Feb 28 20:07:19 2003
 @@ -0,0 +1,716 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 modelel 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, zblkn;
 +	long isize, ssize, zsize;
 +	mode_t accessmode;
 +
 +	if (p->p_ucred->cr_uid != 0)
 +		return EACCES;
 +
 +	if (mp->mnt_flag & MNT_UPDATE)
 +		return EOPNOTSUPP;
 +
 +	/* no asynchronous updates and no soft updates */
 +
 +	mp->mnt_flag &= ~(MNT_ASYNC | MNT_SOFTDEP);
 +	
 +	/***  FORCE READ-ONLY FOR DEBUGGING  ***/
 +/*
 +	mp->mnt_flag |= MNT_RDONLY;
 +*/
 +	/***  FORCE NODEV and NOEXEC for safety ***/
 +
 +	mp->mnt_flag |= (MNT_NODEV | MNT_NOEXEC);
 +	
 +	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 the 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);
 +	}
 +	
 +	/*********************************/
 +
 +	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;
 +
 +	/* The superblock gets stored in 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;
 +
 +	/* Reset es to point to the stored super block */
 +
 +	es = mmp->mnx_su;
 +
 +	/* Fill the in-core super with stuff */
 +	
 +	es->s_firstinode = 2 + es->s_imap_blocks + es->s_zmap_blocks;
 +	es->s_zoff = es->s_firstdatazone - 1;
 +	es->s_version = 2;
 +	es->s_dev = devvp->v_rdev;
 +	es->s_devvp = devvp;
 +	es->s_rdonly = ronly;
 +	es->s_bsize = BLOCK_SIZE;
 +	es->s_zsize = BLOCK_SIZE << es->s_zshift;
 +	es->s_imnton = ufs_parent_ino(mp);
 +	es->s_max_size = ((es->s_zones - es->s_zoff) << es->s_zshift)*BLOCK_SIZE;
 +
 +	isize = blk_to_byte(es->s_imap_blocks);
 +	zsize = blk_to_byte(es->s_zmap_blocks);
 +	iblkn = byte_to_blkn(blk_to_byte(2));
 +	zblkn = byte_to_blkn(blk_to_byte(es->s_imap_blocks + 2));
 +
 +	/* Load the bitmaps into the in-core super */
 +
 +	es->s_ibmap = (u_int16_t*)malloc(isize, M_MINIXMNT, M_WAITOK);
 +	if (es->s_ibmap == NULL) {
 +		error = ENOMEM;
 +		goto out;
 +	}
 +	es->s_zbmap = (u_int16_t*)malloc(zsize, M_MINIXMNT, M_WAITOK);
 +	if (es->s_zbmap == NULL) {
 +		error = ENOMEM;
 +		free(es->s_ibmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bzero(es->s_ibmap, isize);
 +	bzero(es->s_zbmap, zsize);
 +	es->imap_lock = 0l;
 +	es->zmap_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_zbmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bcopy((caddr_t)bp->b_data, es->s_ibmap, isize);
 +	brelse(bp);
 +	bp = NULL;
 +
 +	if ((error = bread(devvp, zblkn, zsize, NOCRED, &bp)) != 0) {
 +		printf("bread error reading block bitmap\n");
 +		free(es->s_ibmap, M_MINIXMNT);
 +		free(es->s_zbmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bcopy((caddr_t)bp->b_data, es->s_zbmap, zsize);
 +	brelse(bp);
 +	bp = NULL;
 +
 +	/* Finish filling mmp */
 +
 +	mmp->mnx_devvp = devvp;
 +	mmp->mnx_dev   = devvp->v_rdev;
 +	mmp->mnx_mp    = mp;
 +       	mmp->mnx_malloctype = M_MINIXNOD;
 +
 +	/* Complete the mount */
 +
 +	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 */
 +
 +	es->s_root = (struct minix_inode*)NULL;
 +	
 +	if ((error = minix_root(mp, &vproot)) != 0) {
 +		free(es->s_ibmap, M_MINIXMNT);
 +		free(es->s_zbmap, M_MINIXMNT);
 +		goto out;
 +	}
 +
 +	ssize = sizeof(struct minix_inode);
 +
 +	es->s_root = (struct minix_inode*)malloc(ssize, M_MINIXMNT, M_WAITOK);
 +	if (es->s_root == NULL) {
 +	     free(es->s_ibmap, M_MINIXMNT);
 +	     free(es->s_zbmap, M_MINIXMNT);
 +	     goto out;
 +	}
 +	bcopy((char*)vproot->v_data, es->s_root, ssize);
 +	vput(vproot);
 +	
 +	es->s_root->i_parent = es->s_imnton;
 +
 +	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_zbmap, M_MINIXMNT);
 +	if (mmp->mnx_su->s_root)
 +	     free(mmp->mnx_su->s_root, 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_zoff) << sp->s_zshift;
 +	sbp->f_bfree  = (minix_free_zone_count(sp) << sp->s_zshift);
 +	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 vnode *vp;
 +	dev_t dev;
 +	int error;
 +
 +	*vpp = NULL;
 +	mmp  = (struct minixmount*)(mp->mnt_data);
 +	dev  = mmp->mnx_dev;
 +	su   = mmp->mnx_su;
 +
 +	/* Look for the vnode in the hash table first */
 +	
 +restart:
 +	if ((*vpp = minix_ihashget(dev, ino)) != NULL) {
 +
 +		vp = *vpp;
 +		ip = VTOMI(vp);
 +
 +		(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);
 +	
 +	if ((error = minix_iget(mmp->mnx_devvp, su, (mino_t)ino, ip)) != 0) {
 +		FREE(ip, M_MINIXNOD);
 +		*vpp = NULL;
 +		return error;
 +	}
 +
 +	/* 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 info */
 +	
 +	ip->i_vnode = vp;
 +	ip->i_dev = dev;       /* Specinfo pointer */
 +	ip->i_su  = su;
 +	ip->i_number = ino;
 +	ip->i_parent = 0;      /* Don't know this yet */
 +	ip->i_mmp    = mmp;
 +	ip->i_flag   = 0;
 +	ip->i_mode = ip->i_dino.i_mode;
 +	ip->i_blocks = minix_num_blocks(ip->i_mode, ip->i_dino.i_size, su->s_zshift);
 +	ip->i_count = 1;
 +	ip->i_entry = 0;
 +	ip->i_noent = 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;
 +
 +	/* 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(su->s_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);
 +                        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 sys.orig/fs/minixfs/minix_vnops.c sys/fs/minixfs/minix_vnops.c
 --- sys.orig/fs/minixfs/minix_vnops.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_vnops.c	Fri Feb 28 20:24:43 2003
 @@ -0,0 +1,1722 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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_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_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.
 + */
 +
 +static int
 +minix_inactive(struct vop_inactive_args *ap)
 +     /*
 +       struct vop_inactive_args {
 +          struct vnodeop_desc *a_desc;
 +	  struct vnode *a_vp;
 +	  struct proc  *a_p;
 +	  }
 +     */
 +{
 +	struct vnode *vp = ap->a_vp;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct proc *p = ap->a_p;
 +	int mode, error = 0;
 +     
 +	PRINTD("Entering: minix_inactive\n")
 +
 +	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;
 +		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 vop_reclaim_args {
 +             struct vnodeop_desc *a_desc;
 +	     struct vnode *a_vp;
 +	     struct proc *a_p;
 +	     };
 +     */
 +{
 +     struct vnode *vp = ap->a_vp;
 +     struct minix_inode *ip = VTOMI(vp);
 +     struct minix_super_block *sp = ip->i_su;
 +
 +     minix_ihashrem(ip);
 +     cache_purge(vp);
 +     vrele(sp->s_devvp);
 +
 +     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 minix_inode *ip = VTOMI(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);
 +
 +	ip->i_flag |= IN_UPDATE;
 +	return minix_update(vp);
 +}
 +
 +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  = VTOMI(vp);
 +	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 = VTOMI(vp);
 +     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 = VTOMI(vp);
 +	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 = VTOMI(vp);
 +	struct minix_super_block *sp;
 +	struct mount *mp = vp->v_mount;
 +	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;
 +
 +	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 = VTOMI(vp);
 +		bzero((char*)ip->i_shortlink, MINIX_MAXSYMLINKLEN);
 +		bcopy(ap->a_target, (char*)ip->i_shortlink, len);
 +		ip->i_dino.i_size = len;
 +		ip->i_blocks = 0;
 +		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 minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +
 +	struct buf *bp;
 +	off_t bytesinfile;
 +	u_daddr_t lbn, pbn;
 +	long size, xfersize, blkoffset;
 +	int error, orig_resid;
 +
 +	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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_daddr_t lbn, pbn;
 +	off_t osize;
 +	long size, resid, blkoffset, xfersize = 0;
 +	int flags, error;
 +	
 +	osize = (off_t)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, (vm_ooffset_t)(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;
 +		  ip->i_blocks = minix_num_blocks(ip->i_mode, dip->i_size, sp->s_zshift);
 +	     }
 +
 +	     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;
 +}
 +
 +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
 + * of the file, after a call to minix_namei().
 + */
 +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 = VTOMI(vp);
 +	int error;
 +	int prtrmv = 0;
 +	
 +	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 = VTOMI(vp);
 +	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 = VTOMI(dvp);
 +	struct vnode *tvp;
 +	struct minix_inode *ip = NULL;
 +	struct minix_dinode *dip;
 +	struct minix_super_block *sp;
 +	struct buf *bp;
 +	struct minix_dirtemplate dirtemplate, *dtp;
 +	struct minix_direct newdir;
 +	int error, dmode;
 +	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;
 +	sp  = ip->i_su;
 +	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_balloc(tvp, (u_daddr_t)0, &bp)) != 0)
 +		goto bad;
 +	dip->i_size = sizeof(dirtemplate);
 +	ip->i_blocks = 1 << sp->s_zshift;
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	vnode_pager_setsize(tvp, (vm_ooffset_t)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 proc *p = cnp->cn_proc;
 +	struct minix_inode *ip, *dp;
 +	int error, ioflag = IO_SYNC;
 +	int prmdir = 0;
 +
 +	ip = VTOMI(vp);
 +	dp = VTOMI(dvp);
 +
 +	/*
 +	 * 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;
 +	cache_purge(dvp);
 +	VOP_UNLOCK(dvp, 0, p);
 +	(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, p);
 +	cache_purge(vp);
 +	vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
 +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 = VTOMI(*vpp);
 +	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;
 +	  };
 +    */
 +{
 +        /*
 +	 * fdvp is the old parent directory
 +	 * fvp  is the file to be renamed
 +	 * fcnp is the path info about the current file: fvp
 +	 * tdvp is the new parent directory
 +	 * tvp  is the new 'target' file name (if it exists)
 +	 * tcnp is the path info about the file's new name
 +	 */
 +     	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 = VTOMI(fdvp);   /* From directory */
 +	ip = VTOMI(fvp);    /* 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 = VTOMI(tdvp);   /* dp is now the target directory */
 +	xp = NULL;       
 +	if (tvp)
 +		xp = VTOMI(tvp); /* xp is now the target file name */
 +	
 +	/*
 +	 * 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 = VTOMI(tdvp);
 +		xp = NULL;
 +		if (tvp)
 +			xp = VTOMI(tvp);
 +	}
 +
 +/*
 + * 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 (--xp->i_nlink != 0)
 +			     panic("minix_rename: linked directory");
 +		     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); /* Relookup gets a new vnode/inode */
 +	if (error == 0)
 +		vrele(fdvp);
 +	if (fvp != NULL) {
 +		xp = VTOMI(fvp);
 +		dp = VTOMI(fdvp);
 +	} 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) { /* Need to compare inode numbers here */
 +		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);
 +		}
 +		if((error = minix_dirremove(fdvp)) != 0)
 +		     printf("minix_dirremove: error = %d\n",error);
 +		if (!error) {
 +			cache_purge(fvp);
 +			ip->i_nlink--;
 +			ip->i_flag |=  IN_CHANGE;
 +			ip->i_flag &= ~IN_RENAME;
 +			(void)minix_update(fvp);
 +			dp->i_flag |= IN_CHANGE | IN_UPDATE;
 +			(void)minix_update(fdvp);
 +		}
 +	}
 +	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;
 +	     (void)minix_update(fvp);
 +	     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 = VTOMI(vp);
 +
 +	resid = uio->uio_resid;
 +	error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap);
 +	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 = VTOMI(vp);
 +
 +	resid = uio->uio_resid;
 +	error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap);
 +	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 sys.orig/modules/Makefile sys/modules/Makefile
 --- sys.orig/modules/Makefile	Sat May  4 01:23:52 2002
 +++ sys/modules/Makefile	Fri Feb 28 13:46:46 2003
 @@ -102,6 +102,7 @@
  	gnufpu \
  	ibcs2 \
  	linprocfs \
 +	minixfs \
  	mly \
  	ncv \
  	nsp \
 diff -ruN sys.orig/modules/minixfs/Makefile sys/modules/minixfs/Makefile
 --- sys.orig/modules/minixfs/Makefile	Wed Dec 31 16:00:00 1969
 +++ sys/modules/minixfs/Makefile	Fri Feb 28 13:46:44 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_ihash.c minix_alock.s
 +NOMAN=
 +
 +.include <bsd.kmod.mk>
 diff -ruN sys.orig/sys/vnode.h sys/sys/vnode.h
 --- sys.orig/sys/vnode.h	Mon Dec 24 17:44:44 2001
 +++ sys/sys/vnode.h	Fri Feb 28 13:46:55 2003
 @@ -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
  };
  
  /*

From: Ed Alley <wea@llnl.gov>
To: FreeBSD-gnats-submit@freebsd.org
Cc: wea@llnl.gov
Subject: kern/47982: Minix file-system offered
Date: Fri, 28 Feb 2003 22:42:58 -0800 (PST)

 >Submitter-Id:	current-users
 >Originator:	Ed Alley
 >Organization:	Lawrence Livermore National Laboratory
 >Confidential:	no
 >Synopsis:	kern/47982: Minix file-system offered
 >Severity:	non-critical
 >Priority:	low
 >Category:	kern
 >Class:		update
 >Release:	FreeBSD 4.6.2-RELEASE i386
 >Environment:
 System: FreeBSD jordan.llnl.gov FreeBSD 4.6.2-RELEASE #0: i386
 
 >Description:
 	RE: PR kern/47982
 
 	This is an update of my previous submission. The previous
 	submitted patch should be discarded. I have added
 	inode hashing, and the ability to operate with data
 	zone sizes that can contain a power of two number of
 	contiguous data blocks. This is supposed to make disk reads
 	a little more efficient in Minix lore, but I put it in for
 	compatability reasons.
 
 	I have also fixed some bugs that either corrupted the FS or
 	in one case caused a random page-fault. This was traced to
 	the root inode pointer getting trashed when its vnode was
 	released and re-assigned through the vnode cache process.
 
 	Other bugs occured during renameing of files and directories
 	after a remove has been performed. (Boy! VOPS_RENAME(9) is
 	sure a subtle routine; I worked a long time on that one and
 	am still not 100% sure about it.)
 	
 >How-To-Repeat:
 	Not applicable
 >Fix:
 	Rather than submit one huge patch on /usr/src, I have submitted
 	two patches: one for /usr/src/sbin, and one for /usr/src/sys.
 	The patches are demarked by the line:
 
 	8><------------------------------------------------- Cut here
 
 	The sources have been diffed against FreeBSD 4.6.2.
 
 	To apply the /sbin patch:
 
 		cd /usr/src
 		patch -p0 < sbin.patch
 
 	To apply the /kernel patch:
 
 		cd /usr/src
 		patch -p0 < sys.patch
 
 				Ed Alley <wea@llnl.gov>
 
 
 	        FOLLOWING is the SBIN PATCH:
 8><------------------------------------------------- Cut here
 diff -ruN sbin.orig/Makefile sbin/Makefile
 --- sbin.orig/Makefile	Mon Mar 18 00:40:00 2002
 +++ sbin/Makefile	Fri Feb 28 13:44:16 2003
 @@ -78,7 +78,7 @@
  	vinum
  
  .if ${MACHINE_ARCH} == i386
 -SUBDIR+=	kget mount_nwfs mount_smbfs
 +SUBDIR+=	kget mount_nwfs mount_smbfs mount_minix newfs_minix
  .endif
  
  .if exists(${.CURDIR}/${MACHINE})
 diff -ruN sbin.orig/mount_minix/Makefile sbin/mount_minix/Makefile
 --- sbin.orig/mount_minix/Makefile	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minix/Makefile	Fri Feb 28 13:54:39 2003
 @@ -0,0 +1,10 @@
 +
 +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 sbin.orig/mount_minix/bsd-copyright sbin/mount_minix/bsd-copyright
 --- sbin.orig/mount_minix/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minix/bsd-copyright	Fri Feb 28 13:50:57 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sbin.orig/mount_minix/mount_minixfs.8 sbin/mount_minix/mount_minixfs.8
 --- sbin.orig/mount_minix/mount_minixfs.8	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minix/mount_minixfs.8	Fri Feb 28 13:55:27 2003
 @@ -0,0 +1,44 @@
 +
 +.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 sbin.orig/mount_minix/mount_minixfs.c sbin/mount_minix/mount_minixfs.c
 --- sbin.orig/mount_minix/mount_minixfs.c	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minix/mount_minixfs.c	Fri Feb 28 13:52:40 2003
 @@ -0,0 +1,118 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/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 sbin.orig/newfs_minix/Makefile sbin/newfs_minix/Makefile
 --- sbin.orig/newfs_minix/Makefile	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/Makefile	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,32 @@
 +#  Copyright (c) 2003 Ed Alley: wea@llnl.gov
 +#  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.
 +
 +PROG=	newfs_minix
 +SRCS=   main.c super.c blockio.c inodeio.c zoneio.c dirio.c misc.c
 +MAN=	newfs_minix.8
 +
 +LINKS= ${BINDIR}/newfs_minix ${BINDIR}/mount_minixfs
 +MLINKS= newfs_minix.8 mount_minixfs.8
 +
 +.include <bsd.prog.mk>
 diff -ruN sbin.orig/newfs_minix/blockio.c sbin/newfs_minix/blockio.c
 --- sbin.orig/newfs_minix/blockio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/blockio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,76 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +#include <string.h>
 +
 +void
 +insert_bit(block_t block, int bit)
 +{
 +	/* Inserts a bit in a bitmap */
 +     
 +	int w, s;
 +	short buf[BLOCK_SIZE / sizeof(short)];
 +
 +	get_block(block, (char*)buf);
 +  
 +	w = bit / (8 * sizeof(short));
 +	s = bit % (8 * sizeof(short));
 +  
 +	buf[w] |= (1 << s);
 +  
 +	put_block(block, (char*)buf);
 +}
 +
 +int
 +read_bit(block_t block, int bit)
 +{
 +	/* Returns a bit in a bitmap */
 +     
 +	int w, s;
 +	short buf[BLOCK_SIZE/sizeof(short)];
 +
 +	get_block(block, (char *)buf);
 +     
 +	w = bit / (8 * sizeof(short));
 +	s = bit % (8 * sizeof(short));
 +
 +	return ((int)((buf[w] >> s) & 0x1));
 +}
 +
 +void
 +get_block (block_t block, char *buf)
 +{
 +	bcopy (zone[block].store, buf, BLOCK_SIZE);
 +}
 +
 +void
 +put_block (block_t block, char *buf)
 +{
 +	bcopy (buf, zone[block].store, BLOCK_SIZE);
 +     
 +}
 diff -ruN sbin.orig/newfs_minix/bsd-copyright sbin/newfs_minix/bsd-copyright
 --- sbin.orig/newfs_minix/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/bsd-copyright	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sbin.orig/newfs_minix/dirio.c sbin/newfs_minix/dirio.c
 --- sbin.orig/newfs_minix/dirio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/dirio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,125 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/*
 + * The following code was slightly modified from Minix's mkfs.c with permission.
 + */
 +
 +#include "mkfs.h"
 +
 +void rootdir(mino_t inode)
 +{
 +	/*
 +	  Get a zone for the root directory and
 +	  add the two entries for '.' and '..'
 +	  The length of a single directory entry is
 +	  16 bytes; hence, 32 for two directories.
 +	*/
 +
 +	add_zone(inode, alloc_zone(), 32L, current_time);
 +
 +	/*
 +	  Add the directory entries for the root directory.
 +	  (note that the parent and child inodes are the same)
 +	*/
 +
 +	enter_dir(inode, ".", inode);
 +	enter_dir(inode, "..", inode);
 +
 +	/* increment the link counts */
 +
 +	incr_link(inode);
 +	incr_link(inode);
 +}
 +
 +
 +void
 +incr_link(mino_t n)
 +{
 +	/* Increment the link count to inode n */
 +	
 +	int off;
 +	block_t b;
 +	inode_t inode2[V2_INODES_PER_BLOCK];
 +
 +	b = ((n - 1) / inodes_per_block) + inode_offset;
 +	off = (n - 1) % inodes_per_block;
 +
 +	get_block(b, (char *) inode2);
 +	inode2[off].i_nlinks++;
 +	put_block(b, (char *) inode2);
 +}
 +
 +void
 +enter_dir(mino_t parent, char *name, mino_t child)
 +{
 +	/* Enter child in parent directory */
 +	/* Works for dir > 1 block and zone > block */
 +     
 +	int i, j, k, l, off;
 +	block_t b;
 +	zone_t z;
 +	char *p1, *p2;
 +	struct direct dir_entry[NR_DIR_ENTRIES];
 +	inode_t ino2[V2_INODES_PER_BLOCK];
 +	int nr_dzones;
 +
 +	b = ((parent - 1) / inodes_per_block) + inode_offset;
 +	off = (parent - 1) % inodes_per_block;
 +
 +	get_block(b, (char *) ino2);
 +	nr_dzones = V2_NR_DZONES;
 +  
 +	for (k = 0; k < nr_dzones; k++) {
 +		z = ino2[off].i_zone[k];
 +		if (z == 0) {
 +			z = alloc_zone();
 +			ino2[off].i_zone[k] = z;
 +		}
 +		for (l = 0; l < zone_size; l++) {
 +			get_block((z << zone_shift) + l, (char *) dir_entry);
 +			for (i = 0; i < NR_DIR_ENTRIES; i++) {
 +				if (dir_entry[i].d_ino == 0) {
 +					dir_entry[i].d_ino = child;
 +					p1 = name;
 +					p2 = dir_entry[i].d_name;
 +					j = 14;
 +					while (j--) {
 +						*p2++ = *p1;
 +						if (*p1 != 0) p1++;
 +					}
 +					put_block((z << zone_shift) + l, (char *) dir_entry);
 +					put_block(b, (char *) ino2);
 +					return;
 +				}
 +			}
 +		}
 +	}
 +
 +	printf("Directory-inode %d beyond direct blocks.  Could not enter %s\n",
 +	    parent, name);
 +	exit(1);
 +}
 diff -ruN sbin.orig/newfs_minix/inodeio.c sbin/newfs_minix/inodeio.c
 --- sbin.orig/newfs_minix/inodeio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/inodeio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,85 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +/*
 +  Returns a free inode number and marks it active
 +*/
 +
 +mino_t
 +get_inode(void)
 +{
 +	mino_t num;
 +	block_t inodemap;
 +	int bit;
 +
 +	/* loop over inodes */
 +
 +	for (num=1; num<nrinodes; num++) {
 +		inodemap = (num/BITS_PER_BLOCK) + INODE_MAP;
 +		bit = num % BITS_PER_BLOCK;
 +		if (read_bit(inodemap, bit) == 0) {
 +			insert_bit(inodemap, bit);
 +			return num;
 +		}
 +	}
 +
 +	printf("No inodes available!\n");
 +	exit(1);
 +}
 +
 +/*
 +  Get an inode, set its mode, usr and group ids.
 +*/
 +
 +mino_t
 +alloc_inode (int mode, int usrid, int grpid)
 +{
 +	mino_t num;
 +	int off;
 +	block_t b;
 +	inode_t inode2[V2_INODES_PER_BLOCK];
 +
 +	/* Get a free inode and mark it active */
 +  
 +	num = get_inode();
 +
 +	/* Get the inode block and its offset within the block */
 +  
 +	b   = ((num - 1) / inodes_per_block) + inode_offset;
 +	off = (num - 1) % inodes_per_block;
 +
 +	/* Set the mode, usr and group ids for this inode */
 +
 +	get_block(b, (char *) inode2);
 +	inode2[off].i_mode = mode;
 +	inode2[off].i_uid = usrid;
 +	inode2[off].i_gid = grpid;
 +	put_block(b, (char *) inode2);
 +  
 +	return (num);
 +}
 diff -ruN sbin.orig/newfs_minix/main.c sbin/newfs_minix/main.c
 --- sbin.orig/newfs_minix/main.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/main.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,242 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/* Some of this code was lifted from the Minix source with permission. */
 +
 +#include "mkfs.h"
 +
 +#include <string.h>
 +#include <stdlib.h>
 +#include <ctype.h>
 +
 +int zone_size, zone_map, zone_shift;
 +int zoff;
 +int inodes_per_block;
 +int inode_offset;
 +int nrblocks, nrnodes;
 +long current_time;
 +zone_t nrzones;
 +unsigned int nrinodes;
 +Zone_t *zone;
 +char zero[BLOCK_SIZE];
 +
 +#define MAX_BLOCKS (1024L * 1024)
 +#define MAX_INODES ((unsigned) 65535)
 +
 +void super(super_block_t*, zone_t, mino_t);
 +void usage(int,char*);
 +
 +int
 +main(int argc, char **argv)
 +{
 +	int c, i, s, fdo, no_output;
 +	super_block_t sp[1];
 +	struct timeval timestr;
 +	int mode;
 +	char *ch, *fname = (char*)NULL;
 +	char defname[] = {"flimage"};
 +
 +	/* The following default values are for a 1440K floppy fs */
 +     
 +	nrblocks = 1440;
 +	nrinodes = 0;
 +	zone_shift = 0;
 +	no_output = 0;
 +
 +	while(1) {
 +		if ((s = getopt(argc, argv, ":hNi:b:s:")) == -1) {
 +			fname = argv[optind++];
 +			break;
 +		}
 +		switch (s) {
 +		case 'h':
 +			usage(0,(char*)NULL);
 +			break;
 +		case 'i':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for nrinodes");
 +			}
 +			nrinodes = atoi(optarg);
 +			break;
 +		case 'b':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for nrblocks");
 +			}
 +			nrblocks  = atoi(optarg);
 +			break;
 +		case 's':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for zoneshift");
 +			}
 +			zone_shift  = atoi(optarg);
 +			break;
 +		case 'N':
 +			no_output = 1;
 +			break;
 +		case '?':
 +			usage(1,"Unknown option");
 +			break;
 +		case ':':
 +			usage(1,"Missing argument");
 +			break;
 +		default:
 +			usage(1,"Huh?");
 +			break;
 +		}
 +	}
 +
 +	inodes_per_block = V2_INODES_PER_BLOCK;
 +
 +	if (nrblocks < 5)
 +		usage(1, "nrblocks is too small");
 +
 +	if (nrblocks > MAX_BLOCKS)
 +		usage(1, "nrblocks greater than (1024 X 1024)");
 +
 +	if (nrinodes == 0) {
 +		/* The default for inodes is 3 blocks per inode, rounded up
 +		 * to fill an inode block.  Above 20M, the average files are
 +		 * sure to be larger because it is hard to fill up 20M with
 +		 * tiny files, so reduce the default number of inodes.  This
 +		 * default can always be overridden by using the -i option.
 +		 */
 +		nrinodes = nrblocks / 3;
 +		if (nrblocks >= 20000) nrinodes  = nrblocks / 4;
 +		if (nrblocks >= 40000) nrinodes  = nrblocks / 5;
 +		if (nrblocks >= 60000) nrinodes  = nrblocks / 6;
 +		if (nrblocks >= 80000) nrinodes  = nrblocks / 7;
 +		if (nrblocks >= 100000) nrinodes = nrblocks / 8;
 +		nrinodes += inodes_per_block - 1;
 +		nrinodes = nrinodes / inodes_per_block * inodes_per_block;
 +		if (nrinodes > MAX_INODES) nrinodes = MAX_INODES;
 +	}
 +
 +	if (nrinodes < 1)
 +		usage(1, "Inode count too small");
 +
 +	if (nrinodes > MAX_INODES)
 +		usage(1, "Inode count too large");
 +
 +	if (fname == NULL && !no_output) {
 +		usage(1, "No special file provided");
 +	}
 +
 +	zone_size = BLOCK_SIZE << zone_shift;
 +	nrzones = nrblocks >> zone_shift;
 +	if ((nrzones << zone_shift) < nrblocks)
 +		nrzones++;
 +
 +	printf("block size = 1024 bytes\n");
 +	printf("zone  size = %d bytes\n",(int)zone_size);
 +	printf("number of blocks = %d\n",(int)nrblocks);
 +	printf("number of zones  = %d\n",(int)nrzones);
 +	printf("zone shift = %d\n",(int)zone_shift);
 +
 +	zone = (Zone_t*)malloc(nrzones*zone_size);
 +
 +	bzero((char*)zone, nrzones*zone_size);
 +     
 +	gettimeofday(&timestr, NULL);
 +	current_time = timestr.tv_sec;  /* UNIX time in seconds */
 +
 +	/* Set up a block of zeros */
 +
 +	bzero(zero, BLOCK_SIZE);
 +
 +	/* Set up the superblock */
 +
 +	super(sp, nrzones, nrinodes);
 +
 +	/* The superblock goes into block[1]. */
 +     
 +	zone[1].sp = sp[0];
 +
 +	zone_map = INODE_MAP + sp->s_imap_blocks;
 +	zone_size  = 1 << zone_shift;         /* Re-define zone_size */
 +	zoff = sp->s_firstdatazone - 1;
 +
 +	insert_bit(INODE_MAP, 0);   /* inode zero is not used but must be allocated */
 +	insert_bit(zone_map,  0);   /* bit zero must always be allocated in zone map */
 +
 +	/*
 +	  Set the mode and rwx bits for the root directory
 +	*/
 +
 +	mode = 040755;  /* drwxr_xr_x */
 +
 +	rootdir(alloc_inode(mode, getuid(), getgid()));
 +
 +	if (!no_output) {
 +
 +		if ((fdo = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0644)) == -1) {
 +			printf("Can't open file: %s; Quitting!\n",fname);
 +			exit(1);
 +		}
 +
 +		/* Write out the blocks */
 +	
 +		for (i=0; i<nrblocks; i++)
 +			write(fdo, &(zone[i]), BLOCK_SIZE);
 +
 +		close(fdo);
 +	}
 +     
 +	exit (0);
 +}
 +
 +void
 +usage(int err, char *mess)
 +{
 +	if (mess != NULL)
 +		printf("\n     %s!\n",mess);
 +
 +	printf("\n");
 +	printf("usage: newfs_minix [-hN] [-i inodes] [-b blocks] [-s shift] special\n");
 +	printf("\n");
 +	printf("   -h,       gives this help message\n");
 +	printf("   -N        no special file is written\n");
 +	printf("   -i inodes is the number of inodes\n");
 +	printf("   -b blocks is the number of blocks\n");
 +	printf("   -s shift  is log2(blocks/zone)\n");
 +	printf("\n");
 +	printf("\"special\" is either a special file or a regular file.\n");
 +	printf("\n");
 +	printf(" If \"special\" is omitted, then this help message will be printed.\n");
 +	printf(" If \"inodes\" is omitted then the number is calculated\n");
 +	printf(" If \"blocks\" is omitted then 1440 is used\n");
 +	printf("\n");
 +
 +	exit(err);
 +}
 diff -ruN sbin.orig/newfs_minix/misc.c sbin/newfs_minix/misc.c
 --- sbin.orig/newfs_minix/misc.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/misc.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,67 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley; wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +/*
 + * Below was lifted directly from the Minix source with permission.
 + */
 +
 +/*
 + * The next routine is copied from Minix's fsck.c and mkfs.c...  (Re)define some
 + * things for consistency.  Some things should be done better.  The shifts
 + * should be replaced by multiplications and divisions by MAP_BITS_PER_BLOCK
 + * since log2 of this is too painful to get right.
 + */
 +
 +/* Convert from bit count to a block count. The usual expression
 + *
 + *	(nr_bits + (1 << BITMAPSHIFT) - 1) >> BITMAPSHIFT
 + *
 + * doesn't work because of overflow.
 + *
 + * Other overflow bugs, such as the expression for N_ILIST overflowing when
 + * s_inodes is just over INODES_PER_BLOCK less than the maximum+1, are not
 + * fixed yet, because that number of inodes is silly.
 + */
 +
 +/* The above comment doesn't all apply now bit_t is ulong.  Overflow is now
 + * unlikely, but negative bit counts are now possible (though unlikely)
 + * and give silly results.
 + */
 +
 +int
 +bitmapsize(bit_t nr_bits)
 +{
 +	int nr_blocks;
 +
 +	nr_blocks = (int) (nr_bits >> BITMAPSHIFT);
 +  
 +	if (((bit_t) nr_blocks << BITMAPSHIFT) < nr_bits)
 +		++nr_blocks;
 +  
 +	return nr_blocks;
 +}
 diff -ruN sbin.orig/newfs_minix/mkfs.h sbin/newfs_minix/mkfs.h
 --- sbin.orig/newfs_minix/mkfs.h	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/mkfs.h	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,178 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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                              *
 + *                                                                          *
 + *   The guide for these sources 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                          *
 + *                                                                          *
 + ****************************************************************************/
 +
 +#include <sys/types.h>
 +#include <sys/time.h>
 +#include <sys/uio.h>
 +
 +#include <fcntl.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <unistd.h>
 +
 +/*
 + * In the definitions that follow, a zone is a block.
 + * There exists the possiblity that a zone can be
 + * a power of two larger than a block; that is the
 + * function of the variable s_log_zone_size in the
 + * super block which represents the shift needed
 + * to go from blocks to zones and back again.
 + */
 +
 +#define BLOCK_SIZE 1024       /* # bytes in a disk block */
 +
 +#define BITS_PER_BLOCK 8192   /* 8*BLOCK_SIZE */
 +#define BITMAPSHIFT 13        /* log2(BITS_PER_BLOCK) */
 +
 +#define INODE_MAP 2           /* position of first inode bitmap */
 +
 +#define V2_NR_DZONES 7
 +#define V2_NR_TZONES 10
 +#define NR_INODES 64
 +#define V2_INODE_SIZE 64    /* sizeof(inode_t) */
 +#define V2_ZONE_NUM_SIZE 4  /* sizeof(zone_t) = 32 bits */
 +#define V2_INODES_PER_BLOCK (BLOCK_SIZE/V2_INODE_SIZE)
 +#define V2_INDIRECTS   (BLOCK_SIZE/V2_ZONE_NUM_SIZE)  /* # zones/indir block */
 +
 +/* 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 unsigned short mino_t; 	   /* i-node number */
 +typedef unsigned short mmode_t;	   /* file type and permissions bits */
 +typedef unsigned long  moff_t;	   /* offset within a file */
 +typedef unsigned short zone1_t;	   /* zone number for V1 file systems */
 +typedef unsigned long  zone_t;	   /* zone number */
 +typedef unsigned long  block_t;	   /* block number */
 +typedef unsigned long  bit_t;      /* bit number in a bitmap */
 +typedef unsigned short bitchunk_t; /* a collection of bits from a bitmap */
 +typedef long           mtime_t;    /* time in sec since 1 Jan 1970 0000 GMT */
 +
 +/* File system types. */
 +#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 */
 +
 +/* Directory layout */
 +#define DIRBLKSIZ 512
 +#define DIRSIZ    14
 +
 +struct direct {
 +	mino_t d_ino;           /*  2 */
 +	char d_name[DIRSIZ];    /* 14 */
 +};
 +
 +#define DIR_ENTRY_SIZE usizeof(struct direct)
 +#define NR_DIR_ENTRIES (BLOCK_SIZE/DIR_ENTRY_SIZE)
 +
 +/* Structure of the superblock on disk */
 +
 +typedef struct {
 +	mino_t s_ninodes;		/* # usable inodes on the minor device */
 +	zone1_t  s_nzones;		/* total device size, including bit maps etc */
 +	short s_imap_blocks;		/* # of blocks used by inode bit map */
 +	short s_zmap_blocks;		/* # of blocks used by zone bit map */
 +	zone1_t s_firstdatazone;	/* number of first data zone */
 +	short s_log_zone_size;	        /* log2 of blocks/zone */
 +	moff_t s_max_size;		/* maximum file size on this device */
 +	short s_magic;		        /* magic number to recognize super-blocks */
 +	short s_pad;			/* try to avoid compiler-dependent padding */
 +	zone_t s_zones;	        	/* number of zones (replaces s_nzones in V2) */
 +	char pad[1000];                 /* pad the block to 1024 bytes */
 +} super_block_t;
 +
 +/* Structure of an inode on disk. length = 64 bytes. */
 +
 +typedef struct {
 +	mmode_t  i_mode;		/* file type, protection, etc. 2 */
 +	short i_nlinks;		        /* how many links to this file 2 */
 +	short i_uid;		        /* user id of the file's owner 2 */
 +	short i_gid;	        	/* group number                2 */
 +	moff_t i_size;	       	        /* current file size in bytes  4 */
 +	mtime_t i_atime;		/* time of last access (V2 only) 4 */
 +	mtime_t i_mtime;		/* when was file data last changed 4 */
 +	mtime_t i_ctime;		/* when was inode itself changed (V2 only) 4 */
 +	zone_t i_zone[V2_NR_TZONES];	/* zone numbers for direct, ind, and dbl ind 40 */
 +} inode_t;
 +
 +/* A zone can be inodes, a bootblock, a superblock or data */
 +/* This definition really defines the possible structure of a block */
 +
 +typedef union {
 +	inode_t inode[16];
 +	super_block_t sp;
 +	struct boot_block_s {
 +		char bootblock[294];
 +		char pad[730];
 +	} boot_block;
 +	char store[1024];
 +} Zone_t;
 +
 +void insert_bit (block_t, int);
 +int read_bit (block_t, int);
 +mino_t get_inode(void);
 +void get_block (block_t, char*);
 +void put_block (block_t, char*);
 +void rootdir(mino_t);
 +zone_t alloc_zone(void);
 +mino_t alloc_inode(int , int, int);
 +void add_zone(mino_t, zone_t, long, long);
 +void incr_link(mino_t);
 +void enter_dir(mino_t, char*, mino_t);
 +
 +/* These variable are defined in main and used throughout */
 +
 +extern Zone_t *zone;
 +extern int zone_size, zone_map, zone_shift;
 +extern int zoff;
 +extern char zero[BLOCK_SIZE];
 +extern int inodes_per_block;
 +extern int inode_offset;
 +extern int nrblocks, nrnodes;
 +extern long current_time;
 +extern zone_t nrzones;
 +extern unsigned int nrinodes;
 diff -ruN sbin.orig/newfs_minix/newfs_minix.8 sbin/newfs_minix/newfs_minix.8
 --- sbin.orig/newfs_minix/newfs_minix.8	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/newfs_minix.8	Fri Feb 28 13:56:11 2003
 @@ -0,0 +1,59 @@
 +
 +.Dd February 28, 2003
 +.Dt NEWFS_MINIX 8
 +.Os
 +.Sh NAME
 +.Nm newfs_minix
 +.Nd construct a new Minix file system
 +.Sh SYNOPSIS
 +.Nm
 +.Op Fl N
 +.Op Fl b Ar number-of-blocks
 +.Op Fl i Ar number-of-inodes
 +.Op Fl s Ar zone-shift
 +.Ar special
 +.Sh DESCRIPTION
 +The
 +.Nm
 +utility is used to initialize and clear the Minix
 +filesystem on device
 +.Ar special.
 +(We often refer to the
 +.Dq special file
 +as the
 +.Dq disk ,
 +although the special file need not be a physical disk.
 +In fact, it need not even be special.)
 +Without options
 +.Nm
 +will make a file system that will fit on a 1.44MB
 +floppy disk, however,
 +.Nm
 +has some options to allow the defaults to be selectively overridden.
 +.Pp
 +The following options define the general layout policies:
 +.Bl -tag -width indent
 +.It Fl b Ar number-of-blocks
 +The total number of blocks of 1024 bytes each that the FS will contain.
 +If this option is not provided then 1440 blocks are chosen.
 +.It Fl i Ar number-of-inodes
 +The number of inode in the FS. If this option is not provided then
 +the number of inodes will be calculated to be roughly 1/3 of the
 +number of blocks.
 +.It Fl s Ar zone-shift
 +The log2 of the number of blocks/zone. The FS can be designed to be
 +made up of data zones that contain a power of two contiguous blocks
 +in them. The default for this parameter is zero.
 +.It Fl N
 +Cause the file system parameters to be printed out
 +without really creating the file system.
 +.Sh SEE ALSO
 +.Xr mount 8 ,
 +.Xr mount_minixfs 8
 +.Sh AUTHOR
 +.An Ed Alley <wea@llnl.gov>
 +.Sh HISTORY
 +The
 +.Nm
 +command first appeared in
 +.Bx 4.6.2  .
 diff -ruN sbin.orig/newfs_minix/super.c sbin/newfs_minix/super.c
 --- sbin.orig/newfs_minix/super.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/super.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,106 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/* The following code was lifted and modified from the minix source with permission. */
 +
 +#include "mkfs.h"
 +
 +int bitmapsize(bit_t);
 +
 +void
 +super(super_block_t *sp, zone_t nzones, mino_t ninodes)
 +{
 +	zone_t maxsize;
 +	zone_t fstblk, fstzon;
 +	short impsize, zmpsize;
 +	int i, inoblks;
 +     
 +	/*
 +	  The maximum file size is the cube of the number of indirects
 +	  plus the square of the number of indirects plus the number of
 +	  indirects plus the number of direct entries in the inode times
 +	  the number of bytes in a zone. This can be a very large number,
 +	  (more than 32 bits of addressing can manage).
 +	*/
 +
 +	/* With triple indirects just put the 2GB limit as the max size */
 +     
 +	maxsize = ~(1 << 31);  /* All bits are ones except the sign bit */
 +     
 +	/* maxsize is then (2^31 - 1) = 2,147,483,647 bytes */
 +     
 +	/*
 +	  bitmapsize() returns the number of blocks in a bitmap
 +	*/
 +     
 +	impsize = bitmapsize((bit_t) (1 + ninodes));
 +	zmpsize = bitmapsize((bit_t) nzones);
 +
 +	printf("blocks in imap = %d\n",impsize);
 +	printf("blocks in zmap = %d\n",zmpsize);
 +
 +	/*
 +	  The number of blocks to the beginning of the inodes
 +	*/
 +
 +	inode_offset = impsize + zmpsize + 2;
 +
 +	printf("inode offset = %d\n", inode_offset);
 +
 +	/* The number blocks that contain inodes */
 +     
 +	inoblks = (ninodes + inodes_per_block - 1)/inodes_per_block;
 +
 +	printf("inode blocks = %d\n",inoblks);
 +
 +	/*
 +	  The number of blocks to the beginning of the data
 +	  gives the location of the first data zone.
 +	*/
 +
 +	fstblk = inode_offset + inoblks;    /* Number of 1K blocks */
 +	fstzon = (fstblk + (1 << zone_shift) - 1) >> zone_shift;
 +
 +	printf("first data zone  = %d\n",(int)fstzon);
 +	printf("first data block = %d\n",(int)(fstzon << zone_shift));
 +
 +	/*
 +	  Construct the V2 superblock
 +	*/
 +     
 +	sp->s_ninodes = ninodes;      /* # of inodes */
 +	sp->s_nzones = 0;             /* Not used in V1; forces errors in V2 */
 +	sp->s_imap_blocks = impsize;  /* # of blocks used by inode bit map */
 +	sp->s_zmap_blocks = zmpsize;  /* # of blocks used by zone  bit map */
 +	sp->s_firstdatazone = fstzon; /* index of the first data zone */
 +	sp->s_log_zone_size = zone_shift; /* log2(blocks/zone) */
 +	sp->s_max_size = maxsize;     /* Maximum file system size */
 +	sp->s_magic = SUPER_V2;       /* Indicates a V2 filesystem */
 +	sp->s_pad = 0;                /* Just padding */
 +	sp->s_zones = nzones;         /* # of zones */
 +	for (i=0; i<1000; i++)        /* Pad the rest of the block with zeros */
 +		sp->pad[i] = '\0';
 +}
 diff -ruN sbin.orig/newfs_minix/zoneio.c sbin/newfs_minix/zoneio.c
 --- sbin.orig/newfs_minix/zoneio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/zoneio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,127 @@
 +/*-
 + * 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.
 + */
 +
 +/*
 + * The code below was lifted from Minix's mkfs.c with permission.
 + */
 +
 +#include "mkfs.h"
 +
 +/*
 + * Returns a free zone/block number and marks it active
 + */
 +
 +zone_t
 +get_zone(void)
 +{
 +	zone_t num;
 +	block_t znodemap;
 +	int bit;
 +
 +	/* loop over zones */
 +
 +	for (num=1; num<nrzones; num++) {
 +		znodemap = (num/BITS_PER_BLOCK) + zone_map;
 +		bit      = num % BITS_PER_BLOCK;
 +		if (read_bit(znodemap,bit) == 0) {
 +			insert_bit(znodemap, bit);
 +			return (num+zoff);
 +		}
 +	}
 +
 +	printf("No data blocks available!\n");
 +	exit(1);
 +}
 +
 +/*
 +  Returns the absolute zone number of a new zone
 +*/
 +
 +zone_t
 +alloc_zone(void)
 +{
 +	/* Allocate a new zone */
 +	/* Works for zone > block */
 +     
 +	block_t b;
 +	int i;
 +	zone_t z;
 +
 +	/* Get a free zone/block and mark it active */
 +  
 +	z = get_zone();
 +  
 +	b = z << zone_shift;
 +  
 +	for (i = 0; i < zone_size; i++)
 +		put_block(b + i, zero);	/* give an empty zone */
 +     
 +	return z;  /* Returns the absolute zone number in the file */
 +}
 +
 +void
 +add_zone(mino_t n, zone_t z, long bytes, long cur_time)
 +{
 +	/* Add zone z to inode n. The file has grown by 'bytes' bytes. */
 +
 +	int off, i;
 +	block_t b;
 +	zone_t indir;
 +	zone_t blk[V2_INDIRECTS];
 +	inode_t *p;
 +	inode_t inode[V2_INODES_PER_BLOCK];
 +
 +	b = ((n - 1) / V2_INODES_PER_BLOCK) + inode_offset;
 +	off = (n - 1) % V2_INODES_PER_BLOCK;
 +  
 +	get_block(b, (char *) inode);
 +	p = &inode[off];
 +	p->i_size += bytes;
 +	p->i_mtime = cur_time;
 +	for (i = 0; i < V2_NR_DZONES; i++)
 +		if (p->i_zone[i] == 0) {
 +			p->i_zone[i] = z;
 +			put_block(b, (char *) inode);
 +			return;
 +		}
 +	put_block(b, (char *) inode);
 +
 +  /* File has grown beyond a small file. */
 +
 +	if (p->i_zone[V2_NR_DZONES] == 0) p->i_zone[V2_NR_DZONES] = alloc_zone();
 +	indir = p->i_zone[V2_NR_DZONES];
 +	put_block(b, (char *) inode);
 +	b = indir << zone_shift;
 +	get_block(b, (char *) blk);
 +	for (i = 0; i < V2_INDIRECTS; i++)
 +		if (blk[i] == 0) {
 +			blk[i] = z;
 +			put_block(b, (char *) blk);
 +			return;
 +		}
 +	printf("File has grown beyond single indirect");
 +	exit(1);
 +}
 8><------------------------------------------------- Cut here
 
 
 	        FOLLOWING is the SYS PATCH:
 8><------------------------------------------------- Cut here
 diff -ruN sys.orig/fs/minixfs/README sys/fs/minixfs/README
 --- sys.orig/fs/minixfs/README	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/README	Fri Feb 28 16:02:15 2003
 @@ -0,0 +1,40 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 is the Minix FS. It was developed on FreeBSD-4.6.x.
 +
 +The fs should be located in: "/usr/src/sys/fs/minixfs". There are two other source
 +files located in /usr/src/sbin/: mount_minixfs(8) is located in the directory:
 +"mount_minix"; and newfs_minix(8) is located in the directory: newfs_minix. The first
 +source is needed to mount the FS and the second one will make the FS. 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 and newfs_minix.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 sys.orig/fs/minixfs/TODO sys/fs/minixfs/TODO
 --- sys.orig/fs/minixfs/TODO	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/TODO	Fri Feb 28 16:28:01 2003
 @@ -0,0 +1,69 @@
 +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.
 +			(DONE 030213 by Ed Alley)
 +
 +	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.
 +			(DONE 030228 by Ed Alley)
 +
 +	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.
 +
 +	7. Write a newfs_minix routine.
 +			(DONE 030210 by Ed Alley)
 +
 +	8. Page fault if module is loaded for days!
 +		I don't understand this one. It seems that
 +		the module will page fault if it is loaded
 +		for days even after being unmounted. If a
 +		fs is mounted, then a page fault will occur.
 +		This can be eliminated by unloading the
 +		module after the last fs is unmounted.
 +			(DONE 020220 by Ed Alley)
 +
 +	9. Clean up the directory manipulating routines:
 +	        Come up with a way to keep track of holes,
 +	  	so we don't have to search the directory
 +	 	each time we need to put an entry in.
 +	        Also, fix up the way that the directory
 +	        size is determined: right now we just count
 +	        the entries until the last is found.
 +	        Fix up the way directories are truncated
 +	        when they are shortened: right now we just
 +	        copy out the existing entries, clean the
 +	        directory out and then reload it with the
 +	        saved entries.
 diff -ruN sys.orig/fs/minixfs/bsd-copyright sys/fs/minixfs/bsd-copyright
 --- sys.orig/fs/minixfs/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/bsd-copyright	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix.h sys/fs/minixfs/minix.h
 --- sys.orig/fs/minixfs/minix.h	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix.h	Fri Feb 28 21:07:27 2003
 @@ -0,0 +1,373 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 = 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 NO_BLOCK 0            /* Flag indicating no block available */
 +#define NO_ZONE  0            /* Flag indicating no zone available */
 +#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 */
 +	mode_t    i_mode;               /* BSD style mode of file */
 +	u_int32_t i_flag;               /* flags */
 +	u_int32_t 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)
 +#define VTOMI(vp) ((struct minix_inode*)(vp)->v_data)
 +
 +/* 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_zshift;	         /* 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 vnode *s_devvp;   /* Vnode of device mounted on */
 +	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_zbmap;      /* Zone bit-map */
 +	u_int32_t imap_lock;     /* Inode bit-map lock variable */
 +	u_int32_t zmap_lock;     /* Zone 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_zoff;              /* Data block offset = s_firstdatazone - 1. */
 +	int s_bsize;             /* Block size */
 +	int s_zsize;             /* Zone 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 */
 +};
 +
 +#define MBLOCK(bp) ((union minix_block*)((bp)->b_data))
 +
 +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 */
 +
 +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_zalloc(struct minix_super_block*,u_daddr_t*,u_daddr_t*);
 +int minix_dzalloc(struct minix_super_block*,u_daddr_t);
 +int minix_makeinode(int,struct vnode*,struct vnode**,struct componentname*);
 +int minix_addzone(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_zone_count(struct minix_super_block*);
 +int minix_next_free_inode(struct minix_super_block*);
 +int minix_next_free_zone(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, short);
 +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 sys.orig/fs/minixfs/minix_alock.s sys/fs/minixfs/minix_alock.s
 --- sys.orig/fs/minixfs/minix_alock.s	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_alock.s	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,63 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix_bio.c sys/fs/minixfs/minix_bio.c
 --- sys.orig/fs/minixfs/minix_bio.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_bio.c	Fri Feb 28 21:27:15 2003
 @@ -0,0 +1,732 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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_zbit(struct minix_super_block*,u_int32_t);
 +void minix_write_zbit(struct minix_super_block*,u_int32_t);
 +void minix_delete_zbit(struct minix_super_block*,u_int32_t);
 +
 +/*
 + * Allocate new blocks from lbof0+1 to lbof1.
 + * If lbof0 doesn't exist, then allocate it first.
 + */
 +int
 +minix_balloc(struct vnode *vp, u_daddr_t lbof1, struct buf **bpp)
 +{
 +	struct buf *bp;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_daddr_t lbof0, lbn, pbn, lb, zone, boff, z0, z1, bsize;
 +	int error;
 +
 +	*bpp = NULL;
 +
 +	if (dip->i_size == 0) {  /* No zones so create a new one */
 +		zone = 0;
 +		if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +			return error;
 +		if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0)
 +			return error;
 +		if ((error = bread(devvp, pbn, sp->s_zsize, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)sp->s_zsize);
 +		bwrite(bp);
 +	}
 +
 +	if (dip->i_size > 0)
 +		lbof0 = (dip->i_size - 1)/BLOCK_SIZE;
 +	else
 +		lbof0 = 0;
 +
 +	z0 = lbof0 >> sp->s_zshift;
 +	z1 = lbof1 >> sp->s_zshift;
 +
 +	if (z1 <= z0) {  /* Zone already exists! just return the block */
 +		if ((error = minix_bmapfs(vp, lbof1, &pbn, NULL)) != 0)
 +			return error;
 +		if ((error = bread(devvp, pbn, BLOCK_SIZE, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)BLOCK_SIZE);
 +		*bpp = bp;
 +		return 0;
 +	}
 +
 +	/* The new zone extends beyond the file */
 +
 +	/* Allocate all the blocks between lbof0 and lbof1 and zero them out */
 +
 +	for (lb=lbof0+1; lb<lbof1; lb++) {
 +		zone = lb >> sp->s_zshift;
 +		boff = lb - (zone << sp->s_zshift);
 +		if (boff > 0) { /* Zone already exixts */
 +			bsize = BLOCK_SIZE;
 +			if ((error = minix_bmapfs(vp, lb, &pbn, NULL)) != 0)
 +				return error;
 +		} else {
 +			bsize = sp->s_zsize;
 +			if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +				return error;
 +			if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0) /* add the new zone to inode */
 +				return error;	
 +		}
 +		if ((error = bread(devvp, pbn, bsize, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)bsize);
 +		bwrite(bp);
 +	}
 +	/* Finally allocate lbof1, the required new block */
 +
 +	zone = lbof1 >> sp->s_zshift;
 +	boff = lbof1 - (zone << sp->s_zshift);
 +	if (boff > 0) {   /* Zone already exists, just get the block */
 +		bsize = BLOCK_SIZE;
 +		if ((error = minix_bmapfs(vp, lbof1, &pbn, NULL)) != 0)
 +			return error;
 +	} else {
 +		bsize = sp->s_zsize;
 +		if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +			return error;
 +		if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0) /* add the new zone to inode */
 +			return error;
 +	}
 +	if ((error = bread(devvp, pbn, bsize, NOCRED, &bp)) != 0)
 +		return error;
 +	bzero(bp->b_data, (size_t)bsize);
 +
 +	*bpp = bp;
 +	return 0;
 +
 +}
 +/*
 + * Returns the block number of the next free zone and sets
 + * the corresponding bit in the zone bitmap.
 + */
 +int
 +minix_zalloc(struct minix_super_block *sp, u_daddr_t *lblkp, u_daddr_t *pblkp)
 +{
 +	int ic, error;
 +	u_daddr_t zone, bblk;
 +	daddr_t off;
 +	struct buf *bp;
 +	struct vnode *devvp = sp->s_devvp;
 +	union minix_block *mbp;
 +	u_daddr_t pbn = 0, pzn = 0;
 +	u_int16_t *buf = sp->s_zbmap;
 +
 +	*lblkp = 0;
 +	*pblkp = 0;
 +
 +	minix_get_lock(&sp->zmap_lock);
 +
 +	if ((zone = minix_next_free_zone(sp)) == NO_ZONE) {
 +		minix_free_lock(&sp->zmap_lock);
 +		return ENOSPC;
 +	}
 +
 +	minix_write_zbit(sp, zone);
 +
 +	ic = zone >> 4;                 /* Chunk that bit resides in */
 +	bblk = zone / BITS_PER_BLOCK;   /* Block that bit is in */
 +	off  = zone % 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_zbit(sp, zone);
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	mbp = MBLOCK(bp);
 +	mbp->bitchunk[off] = buf[ic];
 +
 +	if ((error = minix_putblk(bp)) != 0) {
 +		minix_delete_zbit(sp, zone);
 +		if (bp != NULL) {
 +		     bp->b_flags |= B_INVAL;
 +		     brelse(bp);
 +		}
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	minix_free_lock(&sp->zmap_lock);
 +
 +	pzn = zone + sp->s_zoff;    /* Physical zone number */
 +	pbn = pzn << sp->s_zshift;  /* Physical block number */
 +	
 +	*lblkp = pbn;
 +	*pblkp = byte_to_blkn(blk_to_byte(pbn)); /* Suitable for bread */
 +
 +	return 0;
 +}
 +/*
 + * Deallocates a zone if the last block in it is released.
 + */
 +int
 +minix_dzalloc(struct minix_super_block *sp, u_daddr_t zone)
 +{
 +        int ic, error;
 +	u_daddr_t bblk;
 +	daddr_t off;
 +	struct buf *bp;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_int16_t *buf = sp->s_zbmap;
 +	union minix_block *mbp;
 +
 +	minix_get_lock(&sp->zmap_lock);
 +
 +	zone -= sp->s_zoff;              /* Convert to bit offset */
 +
 +	if (minix_read_zbit(sp, zone))
 +		minix_delete_zbit(sp, zone);
 +	else {
 +		minix_free_lock(&sp->zmap_lock);
 +		return 0;
 +	}
 +	
 +	ic = zone >> 4;                 /* Chunk that bit resides in */
 +	bblk = zone / BITS_PER_BLOCK;   /* Block that bit is in */
 +	off  = zone % 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_zbit(sp, zone);
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	mbp = MBLOCK(bp);
 +	mbp->bitchunk[off] = buf[ic];
 +
 +	if ((error = minix_putblk(bp)) != 0) {
 +		minix_write_zbit(sp, zone);
 +		if (bp != NULL) {
 +			bp->b_flags |= B_INVAL;
 +			brelse(bp);
 +		}
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	minix_free_lock(&sp->zmap_lock);
 +	
 +	return 0;
 +}
 +/*
 + * Add a zone to the end of a file. "zone" is the logical zone
 + * offset in the file and pbn is the physical zone offset in the
 + * file system. It is the responsibility of the calling
 + * routine to guarantee that the next zone to add is at the
 + * logical end of the file. This routine returns EIO otherwise.
 + */
 +int
 +minix_addzone(struct minix_inode *ip, u_daddr_t zone, u_daddr_t pbn)
 +{
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	union minix_block *mbp;
 +	struct buf *bp;
 +	u_daddr_t pind;
 +	u_daddr_t id0, id1, id2, id3, off, blk;
 +	int error, indirect = 0;
 +
 +	pind = zone + 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[zone] != 0)
 +			return EIO;
 +		dip->i_block[zone] = pbn;
 +		break;
 +	case 3:
 +		off = zone - 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_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			id0 = blk >> sp->s_zshift;
 +			dip->i_block[V2_NR_DBLOCKS+2] = id0;
 +			bwrite(bp);
 +		} else
 +			id0 = dip->i_block[V2_NR_DBLOCKS+2];
 +
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id3];
 +		if (id0 == 0) {
 +			/* Generate a double indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			mbp->ind[id3] = id0;
 +			bwrite(bp);
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			bwrite(bp);
 +		} else
 +			bqrelse(bp);
 +		goto rind2;
 +		break;
 +	case 2:
 +		off = zone - 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_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			id0 = blk >> sp->s_zshift;
 +			dip->i_block[V2_NR_DBLOCKS+1] = id0;
 +			bwrite(bp);
 +		} else
 +			id0 = dip->i_block[V2_NR_DBLOCKS+1];
 +	rind2:
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id2];
 +		if (id0 == 0) {
 +			/* Generate a single indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			mbp->ind[id2] = id0;
 +			bwrite(bp);
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			bwrite(bp);
 +		} else
 +			bqrelse(bp);
 +		goto rind1;
 +		break;
 +	case 1:
 +		id1 = zone - TOT_DIRECT;
 +		if (dip->i_block[V2_NR_DBLOCKS] == 0) {
 +			/* Generate a single indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			if ((error = minix_getblk(devvp, blk, &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:
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		mbp->ind[id1] = pbn;
 +		bwrite(bp);
 +		break;
 +	default:
 +		printf("minix_addzone: too large: zone = %d\n",(int)zone);
 +		return ENOSPC;
 +	}
 +
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +
 +	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 block number */
 +             u_daddr_t *pbk,          /* returned physical block number */
 +             int *runp)               /* number of dev chunks to add */
 +{
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	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;
 +	u_daddr_t id, idp, id0=0, id1, id2, id3, blk;
 +	daddr_t offset;
 +	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;
 +	}
 +
 +	id = lbk >> sp->s_zshift;            /* index by zones */
 +	offset = lbk - (id << sp->s_zshift); /* Block offset in zone */
 +	
 +	idp = id + 1;
 +
 +	if (idp > ndb)
 +		indirect++;
 +	if (idp > nsidb)
 +		indirect++;
 +	if (idp > ndidb)
 +		indirect++;
 +	if (idp > ntidb)
 +		indirect++;
 +
 +	*pbk = 0;
 +	switch (indirect) {
 +	case 0:                /* Direct */
 +		if ((*pbk = (dip->i_block[id] << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		*pbk += offset;
 +		*pbk = ((*pbk)*BLOCK_SIZE) >> DEV_BSHIFT;
 +		return 0;
 +	case 3:                /* Triple indirect */
 +		if ((blk = (dip->i_block[V2_NR_DBLOCKS+2] << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
 +			brelse(bp);
 +			return error;
 +		}
 +		mbp = MBLOCK(bp);
 +		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 ((blk = (id0 << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
 +			brelse(bp);
 +			return error;
 +		}
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id2];
 +		bqrelse(bp);
 +		goto rind1;
 +	case 1:                /* Single indirect */
 +		id1 = id - ndb;
 +		id0 = dip->i_block[V2_NR_DBLOCKS];
 +	rind1:
 +		if ((blk = (id0 << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		if ((*pbk = (mbp->ind[id1] << sp->s_zshift)) == 0) {
 +			brelse(bp);
 +			return EFAULT;
 +		}
 +		bqrelse(bp);
 +		*pbk += offset;
 +		*pbk = ((*pbk)*BLOCK_SIZE) >> DEV_BSHIFT;
 +		return 0;
 +	default:
 +		return ENOSPC;
 +	}
 +
 +	/* NOTREACHED */
 +}
 +/*
 + * 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 = VTOMI(vp);
 +	struct vnode *devvp = ip->i_su->s_devvp;
 +	int error;
 +	
 +	lbn = ((u_daddr_t)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 + (int)(offset % BLOCK_SIZE);
 +	*bpp = bp;
 +	return 0;
 +}
 +/*
 + * The next few routines read or alter the in-core
 + * zone bitmaps. Any locking that is required is
 + * assumed to occur in the calling programs.
 + */
 +
 +/*
 + * Return the number of free zones
 + */
 +int
 +minix_free_zone_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_zbmap;
 +     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 zone
 + */
 +int
 +minix_next_free_zone(struct minix_super_block *sp)
 +{
 +	int i, n, s, nb;
 +	u_int16_t *buf = sp->s_zbmap;
 +	register int bit;
 +
 +	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)) {
 +					bit = s + (i << 4);
 +					return((bit < nb) ? bit : NO_ZONE);
 +				}
 +	return NO_ZONE;
 +}
 +/*
 + * Read a bit from the in-core data block bitmap
 + */
 +int
 +minix_read_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap;     
 +	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_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap; 
 +	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_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap;
 +	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, short zshift)
 +{
 +     int nindirects, indirect = 0;
 +     int nzones, 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 set initially to one above. Add the extra blocks below.
 +      */
 +     nblocks += (size - 1) / BLOCK_SIZE;
 +     nzones = nblocks >> zshift;
 +     if (nblocks > (nzones << zshift))
 +	  nzones += 1;
 +
 +     /* Figure out whether we have indirect blocks */
 +
 +     if (nzones > TOT_DIRECT)
 +	  indirect++;         /* Single indirect */
 +
 +     if (nzones > TOT_SINDIRECT)
 +	  indirect++;         /* Double indirect */
 +
 +     if (nzones > 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 sys.orig/fs/minixfs/minix_ihash.c sys/fs/minixfs/minix_ihash.c
 --- sys.orig/fs/minixfs/minix_ihash.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_ihash.c	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,165 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +#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");
 +/*
 + * 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)
 +{
 +	mnxihashtb = hashinit(desiredvnodes, M_MNXIHASH, &minix_ihash);
 +	simple_lock_init(&minix_ihash_slock);
 +}
 +/*
 + * Free the inode hash table
 + */
 +void
 +minix_ihashuninit(void)
 +{
 +     if (mnxihashtb) {
 +	  free(mnxihashtb, M_MNXIHASH);
 +	  mnxihashtb = NULL;
 +     }
 +}
 +/*
 + * 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 sys.orig/fs/minixfs/minix_inode.c sys/fs/minixfs/minix_inode.c
 --- sys.orig/fs/minixfs/minix_inode.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_inode.c	Fri Feb 28 21:21:42 2003
 @@ -0,0 +1,757 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/stat.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,u_daddr_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 = VTOMI(dvp);
 +	*vpp = NULL;
 +	
 +	if ((mode & IFMT) == 0)
 +		mode |= IFREG;
 +
 +	if ((error = minix_valloc(dvp, mode, cnp->cn_cred, &tvp)) != 0)
 +		return error;
 +
 +	ip = VTOMI(tvp);
 +	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;
 +}
 +/*
 + * 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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	u_daddr_t lbn0, lbn1, len32;
 +	int error;
 +
 +	if (length > ~(1 << 31))   /* 32-bit offsets only! */
 +		return EFBIG;
 +
 +	len32 = (u_daddr_t)length;
 +
 +	/* 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 <= len32) {
 +	     ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	     return minix_update(vp);
 +	}
 +
 +	/* lbn1 is the last zone in the file */
 +
 +	lbn1 = (dip->i_size - 1)/sp->s_zsize;
 +
 +	/* lbn0 is the first zone to eliminate */
 +	
 +	if (len32 > 0)
 +		lbn0 = (len32 - 1)/sp->s_zsize + 1;
 +	else
 +		lbn0 = 0;    /* Eliminate all zones */
 +
 +	if (lbn0 > lbn1)     /* Silly but check anyway */
 +		return EIO;
 +
 +	/* truncatefs will eliminate all zones from lbn0 to EOF */
 +
 +	if ((error = minix_truncatefs(ip, lbn0, len32)) != 0)
 +		return error;
 +	
 +	dip->i_size  = len32;               /* The new length of the file */
 +	ip->i_blocks = minix_num_blocks(ip->i_mode, len32, sp->s_zshift);
 +	
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	
 +	return minix_update(vp);
 +}
 +/*
 + * Frees all zones from the logical offset lbn to the end of the file.
 + */
 +static int
 +minix_truncatefs(struct minix_inode *ip, u_daddr_t lbn, u_daddr_t length)
 +{
 +	struct buf *bp;
 +	union minix_block *mbp;
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_daddr_t lb0, lb, off = 0;
 +	u_daddr_t blk, id0, id1, id2, id3, zone;
 +	int error, indirect;
 +	int delete1, delete2, delete3;
 +
 +	if (dip->i_size == 0)
 +		return 0;
 +
 +	lb = lb0 = (dip->i_size - 1)/sp->s_zsize;
 +	
 +	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_dzalloc(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;
 +				}
 +			}
 +			blk = dip->i_block[V2_NR_DBLOCKS+2] << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return 0;
 +			mbp = MBLOCK(bp);
 +			id0 = mbp->ind[id3];
 +			if (delete3) {
 +				zone = dip->i_block[V2_NR_DBLOCKS+2];
 +				dip->i_block[V2_NR_DBLOCKS+2] = 0;
 +				if ((error = minix_dzalloc(sp, zone)) != 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:
 +			blk = id0 << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return error;
 +			if (delete2) {
 +				if ((error = minix_dzalloc(sp,id0)) != 0)
 +					return error;
 +			}
 +			mbp = MBLOCK(bp);
 +			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:
 +			blk = id0 << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			if ((error = minix_dzalloc(sp, mbp->ind[id1])) != 0)
 +				return error;
 +			if (delete1) {
 +				if ((error = minix_dzalloc(sp, id0)) != 0)
 +					return error;
 +				brelse(bp);
 +			} else {
 +				mbp->ind[id1] = 0;
 +				bwrite(bp);
 +			}
 +			break;
 +		default:
 +			return EIO;
 +		}
 +
 +	} while (lbn < lb--);
 +	
 +	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 = VTOMI(vp);
 +
 +	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;
 +
 +	return minix_iput(ip);
 +}
 +
 +void
 +minix_itimes(struct vnode *vp)
 +{
 +     	struct minix_inode *ip = VTOMI(vp);
 +	struct timespec ts;
 +	
 +	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);
 +}
 +/*
 + * The following access routine was lifted from the UFS code.
 + */
 +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);	
 +}
 +
 +/* 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 = VTOMI(vp);
 +
 +        return minix_dialloc(ip->i_su, 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;
 +     u_daddr_t off;
 +
 +     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 = MBLOCK(bp);
 +     off = (ino-1) % V2_INODES_PER_BLOCK;
 +     ip->i_dino = mbp->dinode[off];
 +     
 +     bqrelse(bp);
 +
 +     ip->i_number = ino;
 +     ip->i_parent = 0;
 +     ip->i_su = sp;
 +     ip->i_vnode = NULL;
 +     ip->i_mode = ip->i_dino.i_mode;
 +     ip->i_mmp = NULL;
 +     ip->i_dev = devvp->v_rdev;
 +     ip->i_flag = 0;
 +     
 +     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;
 +	u_daddr_t off;
 +	int error;
 +
 +	devvp = ip->i_su->s_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 = MBLOCK(bp);
 +	off = (ino-1) % V2_INODES_PER_BLOCK;
 +	mbp->dinode[off] = 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_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)) == NO_INODE) {
 +		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 = MBLOCK(bp);
 +	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_devvp;
 +	struct buf *bp;
 +	union minix_block *mbp;
 +
 +	minix_get_lock(&sp->imap_lock);
 +
 +	if (minix_read_ibit(sp, ino))
 +		minix_delete_ibit(sp, ino);
 +	else {
 +		minix_free_lock(&sp->imap_lock);
 +		return 0;
 +	}
 +
 +	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 = MBLOCK(bp);
 +	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;
 +	register int bit;
 +
 +	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)) {
 +					bit = s + (i << 4);
 +					return((bit < sp->s_ninodes) ? bit : NO_INODE);
 +				}
 +	return NO_INODE;
 +}
 +/*
 + * 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 sys.orig/fs/minixfs/minix_locks.c sys/fs/minixfs/minix_locks.c
 --- sys.orig/fs/minixfs/minix_locks.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_locks.c	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,52 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix_lookup.c sys/fs/minixfs/minix_lookup.c
 --- sys.orig/fs/minixfs/minix_lookup.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_lookup.c	Fri Feb 28 21:28:31 2003
 @@ -0,0 +1,1273 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/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 int minix_dircleanup(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  = VTOMI(dvp);
 +    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 = VTOMI(vp);
 +		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, ib, id, j, error, len, entry = 0;
 +	char ppath[256], *pname;
 +	struct minix_direct dir[NR_DIR_ENTRIES];
 +	unsigned long ind1[V2_INDIRECTS], blk;
 +	ino_t inum, inuml;
 +
 +	*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;
 +	ip->i_entry = -1;
 +     
 +	while(pname != NULL) {
 +
 +		ip->i_noent = -1;
 +		ip->i_entry = -1;
 +	  
 +		if (!(ip->i_mode & IFDIR))
 +			return ENOTDIR;
 +
 +		if (minix_dinode_access(dip,VEXEC,cred,sp) != 0)
 +			return EACCES;
 +
 +		/* Search directory */
 +
 +		entry = 0;
 +		for (i=0; i<V2_NR_DBLOCKS; i++) {
 +			if (dip->i_block[i] == 0) {
 +				if (ip->i_noent < 0)
 +					ip->i_noent = entry;
 +				return ENOENT;
 +			}
 +			ib  = 1 << sp->s_zshift;
 +			blk = dip->i_block[i] << sp->s_zshift;
 +			while(ib--) {
 +				if((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +					return error;
 +				bcopy(bp->b_data, dir, BLOCK_SIZE);
 +				bqrelse(bp);
 +				for (j=0; j<NR_DIR_ENTRIES; j++) {
 +					if (dir[j].d_ino == 0) {
 +						if (ip->i_noent < 0)
 +							ip->i_noent = entry;
 +						entry++;
 +						continue;
 +					}
 +					if (!strncmp(pname, dir[j].d_name,MINIX_NAME_MAX)) {
 +						ip->i_entry = entry;
 +						goto gotj;
 +					}
 +					entry++;
 +				}
 +		        }
 +		}
 +	  
 +		if (dip->i_block[V2_NR_DBLOCKS] == 0) {
 +			if (ip->i_noent < 0)
 +				ip->i_noent = entry;
 +			return ENOENT;
 +		}
 +		blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +			return error;
 +		bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +		bqrelse(bp);
 +		for (id=0; id<V2_NR_INDIRECTS; id++) {
 +			if (ind1[id] == 0) {
 +				if (ip->i_noent < 0)
 +					ip->i_noent = entry;
 +				return ENOENT;
 +			}
 +			ib  = 1 << sp->s_zshift;
 +			blk = ind1[id] << sp->s_zshift;
 +			while(ib--) {
 +				if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +					return error;
 +				bcopy(bp->b_data, dir, BLOCK_SIZE);
 +				bqrelse(bp);
 +				for (j=0; j<NR_DIR_ENTRIES; j++) {
 +					if (dir[j].d_ino == 0) {
 +						if (ip->i_noent < 0)
 +							ip->i_noent = entry;
 +						entry++;
 +						continue;
 +					}
 +					if (!strncmp(pname, dir[j].d_name,MINIX_NAME_MAX)) {
 +					        ip->i_entry = entry;
 +						goto gotj;
 +					}
 +					entry++;
 +				}
 +			}
 +		}
 +
 +		/*
 +		 * Dropped through:
 +		 *      Could not find an entry: return "no entry".
 +		 */
 +		
 +		if (ip->i_noent < 0)
 +			ip->i_noent = entry;
 +
 +		*ino  = 0;
 +		*pino = inuml;
 +		*entp = -1;
 +		
 +		return ENOENT;		
 +		
 +	gotj:
 +		/*
 +		 * Found an entry; get the inode and look for another token.
 +		 */
 +		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;
 +
 +		pname = minix_strtok((char*)NULL, "/");
 +	}
 +	
 +	/*
 +	 * Return the inode for the entry found, and related information.
 +	 */
 +	
 +	*ino  = inum;
 +	*pino = inuml;
 +	*entp = entry;
 +     
 +	return 0;
 +}
 +/*
 + * 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;
 +	u_int32_t bln, off, newsize;
 +	off_t offset;
 +	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_direct *ep;
 +	struct iovec aiov;
 +	struct uio auio;
 +	struct buf *bp;
 +	char *dirbuf;
 +	int error;
 +
 +	p  = curproc;
 +	cr = p->p_ucred;
 +
 +	newentrysize = DIR_ENTRY_SIZE;
 +
 +	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) { /* No Entries Available */
 +		if (!(ip->i_entry < MAX_DIR_ENTRIES))
 +			return ENOSPC;
 +		bln = ip->i_entry / NR_DIR_ENTRIES;
 +		auio.uio_offset = bln*BLOCK_SIZE;
 +		auio.uio_resid  = newentrysize;
 +		aiov.iov_len    = newentrysize;
 +		aiov.iov_base   = (caddr_t)dirp;
 +		auio.uio_iov    = &aiov;
 +		auio.uio_iovcnt = 1;
 +		auio.uio_rw     = UIO_WRITE;
 +		auio.uio_segflg = UIO_SYSSPACE;
 +		auio.uio_procp  = (struct proc*)0;
 +		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
 +		ip->i_flag |= IN_CHANGE;
 +		return error;
 +	}
 +
 +	if (ip->i_noent >= 0)
 +		newent = ip->i_noent;
 +	else
 +		newent = ip->i_entry;
 +
 +	bln = newent / NR_DIR_ENTRIES;
 +	off = newent % NR_DIR_ENTRIES;
 +	offset = (off_t)(bln*BLOCK_SIZE + off*newentrysize);
 +
 +	if ((error = minix_blkatoff(dvp, offset, &dirbuf, &bp)) != 0)
 +		return error;
 +	ep = (struct minix_direct*)dirbuf;
 +	*ep = *dirp;
 +	bwrite(bp);
 +
 +	newsize = (u_int32_t)offset + newentrysize;
 +	if (newsize > dip->i_size)
 +	     dip->i_size = newsize;
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	return minix_update(dvp);
 +}
 +static int
 +minix_dircleanup(struct vnode *dvp)
 +{
 +	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	u_daddr_t bln;
 +	u_long newsize;
 +	int error;
 +
 +	if ((error = minix_dirsize(dvp, &newsize)) != 0)
 +		return error;
 +
 +	bln = minix_num_blocks(ip->i_mode, (u_int32_t)newsize, sp->s_zshift);
 +
 +	if (ip->i_blocks > bln)
 +		if ((error = minix_truncate(dvp, (off_t)newsize, IO_SYNC, NOCRED, NULL)) != 0)
 +		     return error;
 +	
 +	if (bln > ip->i_blocks)
 +	     vnode_pager_setsize(dvp, (vm_ooffset_t)(bln*BLOCK_SIZE));
 +
 +	ip->i_blocks = bln;
 +	dip->i_size  = newsize;
 +
 +	return 0;
 +}
 +/*
 + * Removes an entry from a directory.
 + * NOTE: Will not remove . or .. but returns ENOENT
 + *       in that case.
 + */
 +int
 +minix_dirremove(struct vnode *dvp)
 +{
 +     struct buf *bp;
 +     struct minix_inode *dip = VTOMI(dvp);
 +     struct minix_dinode *dinp = &(dip->i_dino);
 +     union minix_block *mbp;
 +     struct minix_direct *dirp;
 +     struct minix_super_block *sp = dip->i_su;
 +     struct vnode *devvp = sp->s_devvp;
 +     u_daddr_t ind, bln, off, dsiz;
 +     u_daddr_t blks, blkc, zone, boff;
 +     u_long count, size;
 +     int entry, error;
 +
 +     /*
 +      * Entries 0 and 1 are '.' and '..' respectively.
 +      */
 +
 +     if ((entry = dip->i_entry) < 2)
 +	     return ENOENT;
 +     
 +     bln = entry / NR_DIR_ENTRIES;
 +     off = entry % NR_DIR_ENTRIES;
 +     zone = bln >> sp->s_zshift;
 +     boff = bln - (zone << sp->s_zshift);
 +
 +     if (zone < V2_NR_DBLOCKS) {
 +	  zone = dinp->i_block[zone];
 +	  bln  = (zone << sp->s_zshift) + boff;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  mbp->dir[off].d_ino = 0;
 +	  bwrite(bp);
 +     } else {
 +	  zone -= V2_NR_DBLOCKS;
 +	  bln = dinp->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  ind = mbp->ind[zone];
 +	  bqrelse(bp);
 +	  bln = (ind << sp->s_zshift) + boff;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  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 && count > 2) {
 +	     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);
 +
 +	     cache_purge(dvp);
 +
 +	     return 0;
 +     }
 +     
 +     return minix_dircleanup(dvp);
 +}
 +/*
 + * 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, because
 + * the available space is full, then the routine returns
 + * the next entry number and 0 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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	struct minix_direct dir[NR_DIR_ENTRIES];
 +	unsigned long ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	int ib, id, iz, error, entry = 0;
 +
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0) {
 +			ip->i_noent = -1;
 +			ip->i_entry = entry;
 +			return 0;
 +		}
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		ib = 1 << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&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 = entry;
 +					ip->i_entry = -1;
 +					return 0;
 +				}
 +				entry++;
 +			}
 +		}
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0) {
 +	     ip->i_noent = -1;
 +	     ip->i_entry = entry;
 +	     return 0;
 +	}
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0) {
 +			ip->i_noent = -1;
 +			ip->i_entry = entry;
 +			return 0;
 +		}
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&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 = entry;
 +					ip->i_entry = -1;
 +					return 0;
 +				}
 +				entry++;
 +			}
 +		}
 +	}
 +	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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	unsigned long ind1[V2_INDIRECTS];
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +	u_int32_t blk;
 +	u_long count = 0;
 +
 +	*last = count;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while (ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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 = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +
 +	*count = 0;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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 = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	unsigned long idc;
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +
 +	if (count < 2)
 +	     return EIO;
 +
 +	idc = 0;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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) {
 +					     bqrelse(bp);
 +					     return 0;
 +					}
 +				}
 +			}
 +			bqrelse(bp);
 +		}
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0)
 +		return 0;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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) {
 +						bqrelse(bp);
 +						return 0;
 +					}
 +				}
 +			}
 +			bqrelse(bp);
 +		}
 +	}
 +	return 0;	
 +}
 +/*
 + * Clean everything out of a directory except '.' and '..'.
 + */
 +static int
 +minix_dirclean(struct vnode *dvp)
 +{
 +     	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	union minix_block *mbp;
 +	int ib, ib0, id, iz, error;
 +
 +	if (dip->i_block[0] == 0) /* This zone must always exist */
 +		return EIO;
 +
 +	ip->i_blocks = 1 << sp->s_zshift;
 +	dip->i_size = 2*DIR_ENTRY_SIZE;
 +
 +	blk = dip->i_block[0] << sp->s_zshift;
 +	ib  = ip->i_blocks;
 +	ib0 = ib - 1;
 +
 +	while(ib--) {
 +		if ((error = minix_getblk(devvp,blk++, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		for (id=(ib==ib0)?2:0; id<NR_DIR_ENTRIES; id++)
 +			mbp->dir[id].d_ino = 0;
 +		bwrite(bp);
 +	}
 +
 +	for (iz=1; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		if ((error = minix_dzalloc(sp, dip->i_block[iz])) != 0)
 +			return error;
 +		dip->i_block[iz] = 0;
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0)
 +		return 0;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	brelse(bp);
 +
 +	if ((error = minix_dzalloc(sp, dip->i_block[V2_NR_DBLOCKS])) != 0)
 +	     return 0;
 +	dip->i_block[V2_NR_DBLOCKS] = 0;
 +
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		if ((error = minix_dzalloc(sp, ind1[iz])) != 0)
 +			return error;
 +	}
 +	return 0;	
 +}
 +/*
 + * Returns non-zero if the directory is empty, zero otherwise.
 + */
 +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<(off_t)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. NOTE: This only works
 + * for the first block.
 + */
 +int
 +minix_dirrewrite(struct minix_inode *dp, struct minix_inode *ip,
 +                 ino_t ino, int entry)
 +{
 +	struct buf *bp;
 +	union minix_block *mbp;
 +	int off, error;
 +	off_t offset;
 +	u_daddr_t bln;
 +
 +	bln = entry / NR_DIR_ENTRIES;
 +	off = entry % NR_DIR_ENTRIES;
 +
 +	offset = (off_t)(bln*BLOCK_SIZE);
 +
 +	if ((error = minix_blkatoff(dp->i_vnode, offset, NULL, &bp)) != 0)
 +	     return error;
 +
 +	mbp = MBLOCK(bp);
 +	mbp->dir[off].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 sys.orig/fs/minixfs/minix_subr.c sys/fs/minixfs/minix_subr.c
 --- sys.orig/fs/minixfs/minix_subr.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_subr.c	Fri Feb 28 21:04:32 2003
 @@ -0,0 +1,262 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 minix_inode *pip = VTOMI(pvp);
 +	struct minix_super_block *sp = pip->i_su;
 +	struct mount *mp = pip->i_mmp->mnx_mp;
 +	struct minix_inode *ip;
 +	struct minix_dinode *dip;
 +	int ino, error;
 +	
 +	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 = VTOMI(*vpp);
 +	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);
 +	
 +	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 = VTOMI(vp);
 +	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;
 +}
 +/*
 + * 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 = VTOMI(vp);
 +	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 = VTOMI(vp);
 +	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;
 +	     */
 +	     break;
 +	}
 +	return VBAD;
 +}
 diff -ruN sys.orig/fs/minixfs/minix_ufs.c sys/fs/minixfs/minix_ufs.c
 --- sys.orig/fs/minixfs/minix_ufs.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_ufs.c	Fri Feb 28 13:46:38 2003
 @@ -0,0 +1,60 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix_vfsops.c sys/fs/minixfs/minix_vfsops.c
 --- sys.orig/fs/minixfs/minix_vfsops.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_vfsops.c	Fri Feb 28 20:07:19 2003
 @@ -0,0 +1,716 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 modelel 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, zblkn;
 +	long isize, ssize, zsize;
 +	mode_t accessmode;
 +
 +	if (p->p_ucred->cr_uid != 0)
 +		return EACCES;
 +
 +	if (mp->mnt_flag & MNT_UPDATE)
 +		return EOPNOTSUPP;
 +
 +	/* no asynchronous updates and no soft updates */
 +
 +	mp->mnt_flag &= ~(MNT_ASYNC | MNT_SOFTDEP);
 +	
 +	/***  FORCE READ-ONLY FOR DEBUGGING  ***/
 +/*
 +	mp->mnt_flag |= MNT_RDONLY;
 +*/
 +	/***  FORCE NODEV and NOEXEC for safety ***/
 +
 +	mp->mnt_flag |= (MNT_NODEV | MNT_NOEXEC);
 +	
 +	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 the 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);
 +	}
 +	
 +	/*********************************/
 +
 +	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;
 +
 +	/* The superblock gets stored in 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;
 +
 +	/* Reset es to point to the stored super block */
 +
 +	es = mmp->mnx_su;
 +
 +	/* Fill the in-core super with stuff */
 +	
 +	es->s_firstinode = 2 + es->s_imap_blocks + es->s_zmap_blocks;
 +	es->s_zoff = es->s_firstdatazone - 1;
 +	es->s_version = 2;
 +	es->s_dev = devvp->v_rdev;
 +	es->s_devvp = devvp;
 +	es->s_rdonly = ronly;
 +	es->s_bsize = BLOCK_SIZE;
 +	es->s_zsize = BLOCK_SIZE << es->s_zshift;
 +	es->s_imnton = ufs_parent_ino(mp);
 +	es->s_max_size = ((es->s_zones - es->s_zoff) << es->s_zshift)*BLOCK_SIZE;
 +
 +	isize = blk_to_byte(es->s_imap_blocks);
 +	zsize = blk_to_byte(es->s_zmap_blocks);
 +	iblkn = byte_to_blkn(blk_to_byte(2));
 +	zblkn = byte_to_blkn(blk_to_byte(es->s_imap_blocks + 2));
 +
 +	/* Load the bitmaps into the in-core super */
 +
 +	es->s_ibmap = (u_int16_t*)malloc(isize, M_MINIXMNT, M_WAITOK);
 +	if (es->s_ibmap == NULL) {
 +		error = ENOMEM;
 +		goto out;
 +	}
 +	es->s_zbmap = (u_int16_t*)malloc(zsize, M_MINIXMNT, M_WAITOK);
 +	if (es->s_zbmap == NULL) {
 +		error = ENOMEM;
 +		free(es->s_ibmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bzero(es->s_ibmap, isize);
 +	bzero(es->s_zbmap, zsize);
 +	es->imap_lock = 0l;
 +	es->zmap_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_zbmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bcopy((caddr_t)bp->b_data, es->s_ibmap, isize);
 +	brelse(bp);
 +	bp = NULL;
 +
 +	if ((error = bread(devvp, zblkn, zsize, NOCRED, &bp)) != 0) {
 +		printf("bread error reading block bitmap\n");
 +		free(es->s_ibmap, M_MINIXMNT);
 +		free(es->s_zbmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bcopy((caddr_t)bp->b_data, es->s_zbmap, zsize);
 +	brelse(bp);
 +	bp = NULL;
 +
 +	/* Finish filling mmp */
 +
 +	mmp->mnx_devvp = devvp;
 +	mmp->mnx_dev   = devvp->v_rdev;
 +	mmp->mnx_mp    = mp;
 +       	mmp->mnx_malloctype = M_MINIXNOD;
 +
 +	/* Complete the mount */
 +
 +	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 */
 +
 +	es->s_root = (struct minix_inode*)NULL;
 +	
 +	if ((error = minix_root(mp, &vproot)) != 0) {
 +		free(es->s_ibmap, M_MINIXMNT);
 +		free(es->s_zbmap, M_MINIXMNT);
 +		goto out;
 +	}
 +
 +	ssize = sizeof(struct minix_inode);
 +
 +	es->s_root = (struct minix_inode*)malloc(ssize, M_MINIXMNT, M_WAITOK);
 +	if (es->s_root == NULL) {
 +	     free(es->s_ibmap, M_MINIXMNT);
 +	     free(es->s_zbmap, M_MINIXMNT);
 +	     goto out;
 +	}
 +	bcopy((char*)vproot->v_data, es->s_root, ssize);
 +	vput(vproot);
 +	
 +	es->s_root->i_parent = es->s_imnton;
 +
 +	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_zbmap, M_MINIXMNT);
 +	if (mmp->mnx_su->s_root)
 +	     free(mmp->mnx_su->s_root, 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_zoff) << sp->s_zshift;
 +	sbp->f_bfree  = (minix_free_zone_count(sp) << sp->s_zshift);
 +	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 vnode *vp;
 +	dev_t dev;
 +	int error;
 +
 +	*vpp = NULL;
 +	mmp  = (struct minixmount*)(mp->mnt_data);
 +	dev  = mmp->mnx_dev;
 +	su   = mmp->mnx_su;
 +
 +	/* Look for the vnode in the hash table first */
 +	
 +restart:
 +	if ((*vpp = minix_ihashget(dev, ino)) != NULL) {
 +
 +		vp = *vpp;
 +		ip = VTOMI(vp);
 +
 +		(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);
 +	
 +	if ((error = minix_iget(mmp->mnx_devvp, su, (mino_t)ino, ip)) != 0) {
 +		FREE(ip, M_MINIXNOD);
 +		*vpp = NULL;
 +		return error;
 +	}
 +
 +	/* 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 info */
 +	
 +	ip->i_vnode = vp;
 +	ip->i_dev = dev;       /* Specinfo pointer */
 +	ip->i_su  = su;
 +	ip->i_number = ino;
 +	ip->i_parent = 0;      /* Don't know this yet */
 +	ip->i_mmp    = mmp;
 +	ip->i_flag   = 0;
 +	ip->i_mode = ip->i_dino.i_mode;
 +	ip->i_blocks = minix_num_blocks(ip->i_mode, ip->i_dino.i_size, su->s_zshift);
 +	ip->i_count = 1;
 +	ip->i_entry = 0;
 +	ip->i_noent = 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;
 +
 +	/* 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(su->s_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);
 +                        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 sys.orig/fs/minixfs/minix_vnops.c sys/fs/minixfs/minix_vnops.c
 --- sys.orig/fs/minixfs/minix_vnops.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_vnops.c	Fri Feb 28 20:24:43 2003
 @@ -0,0 +1,1722 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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_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_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.
 + */
 +
 +static int
 +minix_inactive(struct vop_inactive_args *ap)
 +     /*
 +       struct vop_inactive_args {
 +          struct vnodeop_desc *a_desc;
 +	  struct vnode *a_vp;
 +	  struct proc  *a_p;
 +	  }
 +     */
 +{
 +	struct vnode *vp = ap->a_vp;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct proc *p = ap->a_p;
 +	int mode, error = 0;
 +     
 +	PRINTD("Entering: minix_inactive\n")
 +
 +	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;
 +		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 vop_reclaim_args {
 +             struct vnodeop_desc *a_desc;
 +	     struct vnode *a_vp;
 +	     struct proc *a_p;
 +	     };
 +     */
 +{
 +     struct vnode *vp = ap->a_vp;
 +     struct minix_inode *ip = VTOMI(vp);
 +     struct minix_super_block *sp = ip->i_su;
 +
 +     minix_ihashrem(ip);
 +     cache_purge(vp);
 +     vrele(sp->s_devvp);
 +
 +     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 minix_inode *ip = VTOMI(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);
 +
 +	ip->i_flag |= IN_UPDATE;
 +	return minix_update(vp);
 +}
 +
 +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  = VTOMI(vp);
 +	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 = VTOMI(vp);
 +     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 = VTOMI(vp);
 +	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 = VTOMI(vp);
 +	struct minix_super_block *sp;
 +	struct mount *mp = vp->v_mount;
 +	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;
 +
 +	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 = VTOMI(vp);
 +		bzero((char*)ip->i_shortlink, MINIX_MAXSYMLINKLEN);
 +		bcopy(ap->a_target, (char*)ip->i_shortlink, len);
 +		ip->i_dino.i_size = len;
 +		ip->i_blocks = 0;
 +		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 minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +
 +	struct buf *bp;
 +	off_t bytesinfile;
 +	u_daddr_t lbn, pbn;
 +	long size, xfersize, blkoffset;
 +	int error, orig_resid;
 +
 +	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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_daddr_t lbn, pbn;
 +	off_t osize;
 +	long size, resid, blkoffset, xfersize = 0;
 +	int flags, error;
 +	
 +	osize = (off_t)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, (vm_ooffset_t)(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;
 +		  ip->i_blocks = minix_num_blocks(ip->i_mode, dip->i_size, sp->s_zshift);
 +	     }
 +
 +	     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;
 +}
 +
 +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
 + * of the file, after a call to minix_namei().
 + */
 +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 = VTOMI(vp);
 +	int error;
 +	int prtrmv = 0;
 +	
 +	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 = VTOMI(vp);
 +	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 = VTOMI(dvp);
 +	struct vnode *tvp;
 +	struct minix_inode *ip = NULL;
 +	struct minix_dinode *dip;
 +	struct minix_super_block *sp;
 +	struct buf *bp;
 +	struct minix_dirtemplate dirtemplate, *dtp;
 +	struct minix_direct newdir;
 +	int error, dmode;
 +	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;
 +	sp  = ip->i_su;
 +	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_balloc(tvp, (u_daddr_t)0, &bp)) != 0)
 +		goto bad;
 +	dip->i_size = sizeof(dirtemplate);
 +	ip->i_blocks = 1 << sp->s_zshift;
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	vnode_pager_setsize(tvp, (vm_ooffset_t)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 proc *p = cnp->cn_proc;
 +	struct minix_inode *ip, *dp;
 +	int error, ioflag = IO_SYNC;
 +	int prmdir = 0;
 +
 +	ip = VTOMI(vp);
 +	dp = VTOMI(dvp);
 +
 +	/*
 +	 * 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;
 +	cache_purge(dvp);
 +	VOP_UNLOCK(dvp, 0, p);
 +	(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, p);
 +	cache_purge(vp);
 +	vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, p);
 +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 = VTOMI(*vpp);
 +	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;
 +	  };
 +    */
 +{
 +        /*
 +	 * fdvp is the old parent directory
 +	 * fvp  is the file to be renamed
 +	 * fcnp is the path info about the current file: fvp
 +	 * tdvp is the new parent directory
 +	 * tvp  is the new 'target' file name (if it exists)
 +	 * tcnp is the path info about the file's new name
 +	 */
 +     	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 = VTOMI(fdvp);   /* From directory */
 +	ip = VTOMI(fvp);    /* 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 = VTOMI(tdvp);   /* dp is now the target directory */
 +	xp = NULL;       
 +	if (tvp)
 +		xp = VTOMI(tvp); /* xp is now the target file name */
 +	
 +	/*
 +	 * 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 = VTOMI(tdvp);
 +		xp = NULL;
 +		if (tvp)
 +			xp = VTOMI(tvp);
 +	}
 +
 +/*
 + * 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 (--xp->i_nlink != 0)
 +			     panic("minix_rename: linked directory");
 +		     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); /* Relookup gets a new vnode/inode */
 +	if (error == 0)
 +		vrele(fdvp);
 +	if (fvp != NULL) {
 +		xp = VTOMI(fvp);
 +		dp = VTOMI(fdvp);
 +	} 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) { /* Need to compare inode numbers here */
 +		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);
 +		}
 +		if((error = minix_dirremove(fdvp)) != 0)
 +		     printf("minix_dirremove: error = %d\n",error);
 +		if (!error) {
 +			cache_purge(fvp);
 +			ip->i_nlink--;
 +			ip->i_flag |=  IN_CHANGE;
 +			ip->i_flag &= ~IN_RENAME;
 +			(void)minix_update(fvp);
 +			dp->i_flag |= IN_CHANGE | IN_UPDATE;
 +			(void)minix_update(fdvp);
 +		}
 +	}
 +	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;
 +	     (void)minix_update(fvp);
 +	     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 = VTOMI(vp);
 +
 +	resid = uio->uio_resid;
 +	error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap);
 +	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 = VTOMI(vp);
 +
 +	resid = uio->uio_resid;
 +	error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap);
 +	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 sys.orig/modules/Makefile sys/modules/Makefile
 --- sys.orig/modules/Makefile	Sat May  4 01:23:52 2002
 +++ sys/modules/Makefile	Fri Feb 28 13:46:46 2003
 @@ -102,6 +102,7 @@
  	gnufpu \
  	ibcs2 \
  	linprocfs \
 +	minixfs \
  	mly \
  	ncv \
  	nsp \
 diff -ruN sys.orig/modules/minixfs/Makefile sys/modules/minixfs/Makefile
 --- sys.orig/modules/minixfs/Makefile	Wed Dec 31 16:00:00 1969
 +++ sys/modules/minixfs/Makefile	Fri Feb 28 13:46:44 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_ihash.c minix_alock.s
 +NOMAN=
 +
 +.include <bsd.kmod.mk>
 diff -ruN sys.orig/sys/vnode.h sys/sys/vnode.h
 --- sys.orig/sys/vnode.h	Mon Dec 24 17:44:44 2001
 +++ sys/sys/vnode.h	Fri Feb 28 13:46:55 2003
 @@ -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
  };
  
  /*
State-Changed-From-To: open->feedback 
State-Changed-By: dwmalone 
State-Changed-When: Tue Mar 4 14:03:39 PST 2003 
State-Changed-Why:  
The minix support looks quite fun - I guess Bruce might get a kick 
out of it anyway! The main complication is that it is quite late in 
the life of the 4.X branch to introduce this and we'd really need a 
version for 5.X before we can bring it in. Do you think that you'd 
have time to produce a version for 5.X? 

David. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=47982 

From: Peter Pentchev <roam@ringlet.net>
To: Ed Alley <alley1@llnl.gov>
Cc: dwmalone@FreeBSD.org, bug-followup@FreeBSD.org
Subject: Re: kern/47982: Minix file-system offered
Date: Wed, 5 Mar 2003 08:58:24 +0200

 On Tue, Mar 04, 2003 at 04:46:14PM -0800, Ed Alley wrote:
 > To: "David Malone" <dwmalone@FreeBSD.org>
 > Cc: <freebsd-bugs@FreeBSD.org>, <wea@llnl.gov>
 > Date: Tue, 4 Mar 2003 14:21:07 -0800 (PST)
 > From: "Ed Alley" <alley1@llnl.gov>
 > Subject: Re: kern/47982: Minix file-system offered
 > 
 > >On Tue, 4 Mar 2003, David Malone wrote:
 > 
 > >>  Do you think that you'd have time to produce a version for 5.X?
 > >>
 > >> 	David.
 > >>
 > >> http://www.freebsd.org/cgi/query-pr.cgi?pr=47982
 > 
 > >On Tue, 4 Mar 2003, Ed Alley wrote:
 > 
 > >At this time I don't have 5.0 installed anywhere. Would it
 > >be OK if I diffed the Minix FS against the 5.0 source
 > >only, or do you anticipate that the vfs/vnode structure
 > >has changed significantly? (Maybe I'd better install
 > >5.0 somewhere first and do it right. ???? (:-S) )
 > 
 > >			Ed
 > 
 > I just looked at the 5.0 source. I can see that there are
 > non-trivial changes between it and 4.x. I'll take on the
 > job of converting to 5.0; that will involve me installing
 > the 5.0 source somewhere and starting there. After, I've
 > got it working, should I submit a new send-pr or submit
 > it under the old pr: kern/47982?
 
 I think a follow-up would be fine.
 
 G'luck,
 Peter
 
 -- 
 Peter Pentchev	roam@ringlet.net    roam@sbnd.net    roam@FreeBSD.org
 PGP key:	http://people.FreeBSD.org/~roam/roam.key.asc
 Key fingerprint	FDBA FD79 C26F 3C51 C95E  DF9E ED18 B68D 1619 4553
 You have, of course, just begun reading the sentence that you have just finished reading.

From: Ed Alley <wea@llnl.gov>
To: FreeBSD-gnats-submit@freebsd.org
Cc: wea@llnl.gov
Subject: kern/47982: Minix fs offered for RELEASE 5.0
Date: Mon, 17 Mar 2003 11:50:10 -0800 (PST)

 >Submitter-Id:	current-users
 >Originator:	Ed Alley
 >Organization:	Lawrence Livermore National Laboratory
 >Confidential:	no
 >Synopsis:	kern/47982: Minix fs offered for RELEASE 5.0
 >Severity:	non-critical
 >Priority:	low
 >Category:	kern
 >Class:		update
 >Release:	FreeBSD 5.0-RELEASE i386
 >Environment:
 System: FreeBSD jordan.llnl.gov 5.0-RELEASE i386
 
 >Description:
 
 	Re: PR kern/47982
 
 	This is an update of my previous submission. The previous submissions
 	should be discarded. This submission is diffed against FreeBSD 5.0-RELEASE.
 	There are two diffs included below:
 
 		The first diff is done against: /usr/src/sbin.
 		Expand it as follows:
 
 			cd /usr/src
 			patch -p0 < sbin.patch
 
 		The second diff is done against: /usr/src/sys.
 		Expand it as follows:
 
 			cd /usr/src
 			patch -p0 < sys.patch
 
 	The first diff (sbin.patch) introduces two new user commands:
 
 		    1)  mount_minixfs(8) used to mount the Minix fs.
 			There is a man page also provided.
 
 		    2)  newfs_minix(8) used to make a Minix fs.
 			There is also a man page provided. Also,
 			the command will print a usage message
 			if executed with no agruments.
 
 	The second diff (sys.patch) is the new Minix fs. It runs only
 	as a loadable module. The souce will go into /usr/src/fs/minixfs
 	and some small modifications will occur in /usr/src/modules:
 	the Makefile will be modified in /usr/src/modules and a new
 	make file will be introduced into /usr/src/modules/minixfs.
 
 	As is already apparent, the FS was originally developed in
 	the 4.x RELEASE branch. As there were some very non-trivial
 	changes between the 4.x and 5.x branches, the conversion took
 	me more time than I expected, with some suprise panics along
 	the way. I think that I have cleaned them up sufficiently
 	to submit this work, however, I suggest giving the fs a
 	thorough going over. I have encluded a README and a TODO list
 	in /usr/src/sys/fs/minixfs which will give you an idea of the
 	things that I have done and think still need to be done in the
 	future. On thing that I have not tested is how the OS behaves
 	under multiple user interactions.
 
 	The two patches to follow are separated by the line:
 
 	8><------------------------ Cut Here ------------------------------
 
 >How-To-Repeat:
 	Not applicable
 >Fix:
 	Following is the sbin.patch:
 
 	8><------------------------ Cut Here ------------------------------
 diff -ruN sbin.orig/Makefile sbin/Makefile
 --- sbin.orig/Makefile	Tue Oct 22 21:50:34 2002
 +++ sbin/Makefile	Wed Mar  5 21:53:10 2003
 @@ -55,6 +55,7 @@
  	natd \
  	newfs \
  	newfs_msdos \
 +	newfs_minix \
  	nfsiod \
  	nologin \
  	nos-tun \
 @@ -90,7 +91,8 @@
  .if ${MACHINE_ARCH} == "i386"
  SUBDIR+=cxconfig \
  	mount_nwfs \
 -	mount_smbfs
 +	mount_smbfs \
 +	mount_minixfs
  .if ${MACHINE} == "pc98"
  SUBDIR+=fdisk_pc98
  .else
 diff -ruN sbin.orig/mount_minixfs/Makefile sbin/mount_minixfs/Makefile
 --- sbin.orig/mount_minixfs/Makefile	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minixfs/Makefile	Wed Mar  5 22:10:42 2003
 @@ -0,0 +1,11 @@
 +
 +PROG=	mount_minixfs
 +SRCS=	mount_minixfs.c getmntopts.c
 +MAN=	mount_minixfs.8
 +WARNS=  0
 +
 +MOUNT=	${.CURDIR}/../mount
 +CFLAGS+= -I${MOUNT}
 +.PATH:	${MOUNT}
 +
 +.include <bsd.prog.mk>
 diff -ruN sbin.orig/mount_minixfs/bsd-copyright sbin/mount_minixfs/bsd-copyright
 --- sbin.orig/mount_minixfs/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minixfs/bsd-copyright	Fri Feb 28 13:50:57 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sbin.orig/mount_minixfs/mount_minixfs.8 sbin/mount_minixfs/mount_minixfs.8
 --- sbin.orig/mount_minixfs/mount_minixfs.8	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minixfs/mount_minixfs.8	Fri Feb 28 13:55:27 2003
 @@ -0,0 +1,44 @@
 +
 +.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 sbin.orig/mount_minixfs/mount_minixfs.c sbin/mount_minixfs/mount_minixfs.c
 --- sbin.orig/mount_minixfs/mount_minixfs.c	Wed Dec 31 16:00:00 1969
 +++ sbin/mount_minixfs/mount_minixfs.c	Fri Feb 28 13:52:40 2003
 @@ -0,0 +1,118 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/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 sbin.orig/newfs_minix/Makefile sbin/newfs_minix/Makefile
 --- sbin.orig/newfs_minix/Makefile	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/Makefile	Wed Mar  5 21:50:51 2003
 @@ -0,0 +1,30 @@
 +#  Copyright (c) 2003 Ed Alley: wea@llnl.gov
 +#  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.
 +
 +PROG=	newfs_minix
 +SRCS=   main.c super.c blockio.c inodeio.c zoneio.c dirio.c misc.c
 +MAN=	newfs_minix.8
 +WARNS=  0
 +
 +.include <bsd.prog.mk>
 diff -ruN sbin.orig/newfs_minix/blockio.c sbin/newfs_minix/blockio.c
 --- sbin.orig/newfs_minix/blockio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/blockio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,76 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +#include <string.h>
 +
 +void
 +insert_bit(block_t block, int bit)
 +{
 +	/* Inserts a bit in a bitmap */
 +     
 +	int w, s;
 +	short buf[BLOCK_SIZE / sizeof(short)];
 +
 +	get_block(block, (char*)buf);
 +  
 +	w = bit / (8 * sizeof(short));
 +	s = bit % (8 * sizeof(short));
 +  
 +	buf[w] |= (1 << s);
 +  
 +	put_block(block, (char*)buf);
 +}
 +
 +int
 +read_bit(block_t block, int bit)
 +{
 +	/* Returns a bit in a bitmap */
 +     
 +	int w, s;
 +	short buf[BLOCK_SIZE/sizeof(short)];
 +
 +	get_block(block, (char *)buf);
 +     
 +	w = bit / (8 * sizeof(short));
 +	s = bit % (8 * sizeof(short));
 +
 +	return ((int)((buf[w] >> s) & 0x1));
 +}
 +
 +void
 +get_block (block_t block, char *buf)
 +{
 +	bcopy (zone[block].store, buf, BLOCK_SIZE);
 +}
 +
 +void
 +put_block (block_t block, char *buf)
 +{
 +	bcopy (buf, zone[block].store, BLOCK_SIZE);
 +     
 +}
 diff -ruN sbin.orig/newfs_minix/bsd-copyright sbin/newfs_minix/bsd-copyright
 --- sbin.orig/newfs_minix/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/bsd-copyright	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sbin.orig/newfs_minix/dirio.c sbin/newfs_minix/dirio.c
 --- sbin.orig/newfs_minix/dirio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/dirio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,125 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/*
 + * The following code was slightly modified from Minix's mkfs.c with permission.
 + */
 +
 +#include "mkfs.h"
 +
 +void rootdir(mino_t inode)
 +{
 +	/*
 +	  Get a zone for the root directory and
 +	  add the two entries for '.' and '..'
 +	  The length of a single directory entry is
 +	  16 bytes; hence, 32 for two directories.
 +	*/
 +
 +	add_zone(inode, alloc_zone(), 32L, current_time);
 +
 +	/*
 +	  Add the directory entries for the root directory.
 +	  (note that the parent and child inodes are the same)
 +	*/
 +
 +	enter_dir(inode, ".", inode);
 +	enter_dir(inode, "..", inode);
 +
 +	/* increment the link counts */
 +
 +	incr_link(inode);
 +	incr_link(inode);
 +}
 +
 +
 +void
 +incr_link(mino_t n)
 +{
 +	/* Increment the link count to inode n */
 +	
 +	int off;
 +	block_t b;
 +	inode_t inode2[V2_INODES_PER_BLOCK];
 +
 +	b = ((n - 1) / inodes_per_block) + inode_offset;
 +	off = (n - 1) % inodes_per_block;
 +
 +	get_block(b, (char *) inode2);
 +	inode2[off].i_nlinks++;
 +	put_block(b, (char *) inode2);
 +}
 +
 +void
 +enter_dir(mino_t parent, char *name, mino_t child)
 +{
 +	/* Enter child in parent directory */
 +	/* Works for dir > 1 block and zone > block */
 +     
 +	int i, j, k, l, off;
 +	block_t b;
 +	zone_t z;
 +	char *p1, *p2;
 +	struct direct dir_entry[NR_DIR_ENTRIES];
 +	inode_t ino2[V2_INODES_PER_BLOCK];
 +	int nr_dzones;
 +
 +	b = ((parent - 1) / inodes_per_block) + inode_offset;
 +	off = (parent - 1) % inodes_per_block;
 +
 +	get_block(b, (char *) ino2);
 +	nr_dzones = V2_NR_DZONES;
 +  
 +	for (k = 0; k < nr_dzones; k++) {
 +		z = ino2[off].i_zone[k];
 +		if (z == 0) {
 +			z = alloc_zone();
 +			ino2[off].i_zone[k] = z;
 +		}
 +		for (l = 0; l < zone_size; l++) {
 +			get_block((z << zone_shift) + l, (char *) dir_entry);
 +			for (i = 0; i < NR_DIR_ENTRIES; i++) {
 +				if (dir_entry[i].d_ino == 0) {
 +					dir_entry[i].d_ino = child;
 +					p1 = name;
 +					p2 = dir_entry[i].d_name;
 +					j = 14;
 +					while (j--) {
 +						*p2++ = *p1;
 +						if (*p1 != 0) p1++;
 +					}
 +					put_block((z << zone_shift) + l, (char *) dir_entry);
 +					put_block(b, (char *) ino2);
 +					return;
 +				}
 +			}
 +		}
 +	}
 +
 +	printf("Directory-inode %d beyond direct blocks.  Could not enter %s\n",
 +	    parent, name);
 +	exit(1);
 +}
 diff -ruN sbin.orig/newfs_minix/inodeio.c sbin/newfs_minix/inodeio.c
 --- sbin.orig/newfs_minix/inodeio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/inodeio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,85 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +/*
 +  Returns a free inode number and marks it active
 +*/
 +
 +mino_t
 +get_inode(void)
 +{
 +	mino_t num;
 +	block_t inodemap;
 +	int bit;
 +
 +	/* loop over inodes */
 +
 +	for (num=1; num<nrinodes; num++) {
 +		inodemap = (num/BITS_PER_BLOCK) + INODE_MAP;
 +		bit = num % BITS_PER_BLOCK;
 +		if (read_bit(inodemap, bit) == 0) {
 +			insert_bit(inodemap, bit);
 +			return num;
 +		}
 +	}
 +
 +	printf("No inodes available!\n");
 +	exit(1);
 +}
 +
 +/*
 +  Get an inode, set its mode, usr and group ids.
 +*/
 +
 +mino_t
 +alloc_inode (int mode, int usrid, int grpid)
 +{
 +	mino_t num;
 +	int off;
 +	block_t b;
 +	inode_t inode2[V2_INODES_PER_BLOCK];
 +
 +	/* Get a free inode and mark it active */
 +  
 +	num = get_inode();
 +
 +	/* Get the inode block and its offset within the block */
 +  
 +	b   = ((num - 1) / inodes_per_block) + inode_offset;
 +	off = (num - 1) % inodes_per_block;
 +
 +	/* Set the mode, usr and group ids for this inode */
 +
 +	get_block(b, (char *) inode2);
 +	inode2[off].i_mode = mode;
 +	inode2[off].i_uid = usrid;
 +	inode2[off].i_gid = grpid;
 +	put_block(b, (char *) inode2);
 +  
 +	return (num);
 +}
 diff -ruN sbin.orig/newfs_minix/main.c sbin/newfs_minix/main.c
 --- sbin.orig/newfs_minix/main.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/main.c	Fri Mar 14 22:39:25 2003
 @@ -0,0 +1,243 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/* Some of this code was lifted from the Minix source with permission. */
 +
 +#include "mkfs.h"
 +
 +#include <string.h>
 +#include <stdlib.h>
 +#include <ctype.h>
 +
 +int zone_size, zone_map, zone_shift;
 +int zoff;
 +int inodes_per_block;
 +int inode_offset;
 +int nrblocks, nrnodes;
 +long current_time;
 +zone_t nrzones;
 +unsigned int nrinodes;
 +Zone_t *zone;
 +char zero[BLOCK_SIZE];
 +
 +#define MAX_BLOCKS (1024L * 1024)
 +#define MAX_INODES ((unsigned) 65535)
 +
 +void super(super_block_t*, zone_t, mino_t);
 +void usage(int,char*);
 +
 +int
 +main(int argc, char **argv)
 +{
 +	int c, i, s, fdo, no_output;
 +	super_block_t sp[1];
 +	struct timeval timestr;
 +	int mode;
 +	char *ch, *fname = (char*)NULL;
 +	char defname[] = {"flimage"};
 +
 +	/* The following default values are for a 1440K floppy fs */
 +     
 +	nrblocks = 1440;
 +	nrinodes = 0;
 +	zone_shift = 0;
 +	no_output = 0;
 +
 +	while(1) {
 +		if ((s = getopt(argc, argv, ":hNi:b:s:")) == -1) {
 +			fname = argv[optind++];
 +			break;
 +		}
 +		switch (s) {
 +		case 'h':
 +			usage(0,(char*)NULL);
 +			break;
 +		case 'i':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for nrinodes");
 +			}
 +			nrinodes = atoi(optarg);
 +			break;
 +		case 'b':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for nrblocks");
 +			}
 +			nrblocks  = atoi(optarg);
 +			break;
 +		case 's':
 +			ch = optarg;
 +			for (i=0; i<strlen(ch); i++) {
 +				c = ch[i];
 +				if (!isdigit(c))
 +					usage(1,"Bad digit for zoneshift");
 +			}
 +			zone_shift  = atoi(optarg);
 +			break;
 +		case 'N':
 +			no_output = 1;
 +			break;
 +		case '?':
 +			usage(1,"Unknown option");
 +			break;
 +		case ':':
 +			usage(1,"Missing argument");
 +			break;
 +		default:
 +			usage(1,"Huh?");
 +			break;
 +		}
 +	}
 +
 +	inodes_per_block = V2_INODES_PER_BLOCK;
 +
 +	if (nrblocks < 5)
 +		usage(1, "nrblocks is too small");
 +
 +	if (nrblocks > MAX_BLOCKS)
 +		usage(1, "nrblocks greater than (1024 X 1024)");
 +
 +	if (nrinodes == 0) {
 +		/* The default for inodes is 3 blocks per inode, rounded up
 +		 * to fill an inode block.  Above 20M, the average files are
 +		 * sure to be larger because it is hard to fill up 20M with
 +		 * tiny files, so reduce the default number of inodes.  This
 +		 * default can always be overridden by using the -i option.
 +		 */
 +		nrinodes = nrblocks / 3;
 +		if (nrblocks >= 20000) nrinodes  = nrblocks / 4;
 +		if (nrblocks >= 40000) nrinodes  = nrblocks / 5;
 +		if (nrblocks >= 60000) nrinodes  = nrblocks / 6;
 +		if (nrblocks >= 80000) nrinodes  = nrblocks / 7;
 +		if (nrblocks >= 100000) nrinodes = nrblocks / 8;
 +		nrinodes += inodes_per_block - 1;
 +		nrinodes = nrinodes / inodes_per_block * inodes_per_block;
 +		if (nrinodes > MAX_INODES) nrinodes = MAX_INODES;
 +	}
 +
 +	if (nrinodes < 1)
 +		usage(1, "Inode count too small");
 +
 +	if (nrinodes > MAX_INODES)
 +		usage(1, "Inode count too large");
 +
 +	if (fname == NULL && !no_output) {
 +		usage(1, "No special file provided");
 +	}
 +
 +	zone_size = BLOCK_SIZE << zone_shift;
 +	nrzones = nrblocks >> zone_shift;
 +	if ((nrzones << zone_shift) < nrblocks)
 +		nrzones++;
 +
 +	printf("block size = 1024 bytes\n");
 +	printf("zone  size = %d bytes\n",(int)zone_size);
 +        printf("zone shift = %d\n",(int)zone_shift);
 +	printf("number of blocks = %d\n",(int)nrblocks);
 +	printf("number of zones  = %d\n",(int)nrzones);
 +	printf("number of inodes = %d\n",(int)nrinodes);
 +
 +	zone = (Zone_t*)malloc(nrzones*zone_size);
 +
 +	bzero((char*)zone, nrzones*zone_size);
 +     
 +	gettimeofday(&timestr, NULL);
 +	current_time = timestr.tv_sec;  /* UNIX time in seconds */
 +
 +	/* Set up a block of zeros */
 +
 +	bzero(zero, BLOCK_SIZE);
 +
 +	/* Set up the superblock */
 +
 +	super(sp, nrzones, nrinodes);
 +
 +	/* The superblock goes into block[1]. */
 +     
 +	zone[1].sp = sp[0];
 +
 +	zone_map = INODE_MAP + sp->s_imap_blocks;
 +	zone_size  = 1 << zone_shift;         /* Re-define zone_size */
 +	zoff = sp->s_firstdatazone - 1;
 +
 +	insert_bit(INODE_MAP, 0);   /* inode zero is not used but must be allocated */
 +	insert_bit(zone_map,  0);   /* bit zero must always be allocated in zone map */
 +
 +	/*
 +	  Set the mode and rwx bits for the root directory
 +	*/
 +
 +	mode = 040755;  /* drwxr_xr_x */
 +
 +	rootdir(alloc_inode(mode, getuid(), getgid()));
 +
 +	if (!no_output) {
 +
 +		if ((fdo = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0644)) == -1) {
 +			printf("Can't open file: %s; Quitting!\n",fname);
 +			exit(1);
 +		}
 +
 +		/* Write out the blocks */
 +	
 +		for (i=0; i<nrblocks; i++)
 +			write(fdo, &(zone[i]), BLOCK_SIZE);
 +
 +		close(fdo);
 +	}
 +     
 +	exit (0);
 +}
 +
 +void
 +usage(int err, char *mess)
 +{
 +	if (mess != NULL)
 +		printf("\n     %s!\n",mess);
 +
 +	printf("\n");
 +	printf("usage: newfs_minix [-hN] [-i inodes] [-b blocks] [-s shift] special\n");
 +	printf("\n");
 +	printf("   -h,       gives this help message\n");
 +	printf("   -N        no special file is written\n");
 +	printf("   -i inodes is the number of inodes\n");
 +	printf("   -b blocks is the number of blocks\n");
 +	printf("   -s shift  is log2(blocks/zone)\n");
 +	printf("\n");
 +	printf("\"special\" is either a special file or a regular file.\n");
 +	printf("\n");
 +	printf(" If \"special\" is omitted, then this help message will be printed.\n");
 +	printf(" If \"inodes\" is omitted then the number is calculated\n");
 +	printf(" If \"blocks\" is omitted then 1440 is used\n");
 +	printf("\n");
 +
 +	exit(err);
 +}
 diff -ruN sbin.orig/newfs_minix/misc.c sbin/newfs_minix/misc.c
 --- sbin.orig/newfs_minix/misc.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/misc.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,67 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley; wea@llnl.gov
 + * 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 "mkfs.h"
 +
 +/*
 + * Below was lifted directly from the Minix source with permission.
 + */
 +
 +/*
 + * The next routine is copied from Minix's fsck.c and mkfs.c...  (Re)define some
 + * things for consistency.  Some things should be done better.  The shifts
 + * should be replaced by multiplications and divisions by MAP_BITS_PER_BLOCK
 + * since log2 of this is too painful to get right.
 + */
 +
 +/* Convert from bit count to a block count. The usual expression
 + *
 + *	(nr_bits + (1 << BITMAPSHIFT) - 1) >> BITMAPSHIFT
 + *
 + * doesn't work because of overflow.
 + *
 + * Other overflow bugs, such as the expression for N_ILIST overflowing when
 + * s_inodes is just over INODES_PER_BLOCK less than the maximum+1, are not
 + * fixed yet, because that number of inodes is silly.
 + */
 +
 +/* The above comment doesn't all apply now bit_t is ulong.  Overflow is now
 + * unlikely, but negative bit counts are now possible (though unlikely)
 + * and give silly results.
 + */
 +
 +int
 +bitmapsize(bit_t nr_bits)
 +{
 +	int nr_blocks;
 +
 +	nr_blocks = (int) (nr_bits >> BITMAPSHIFT);
 +  
 +	if (((bit_t) nr_blocks << BITMAPSHIFT) < nr_bits)
 +		++nr_blocks;
 +  
 +	return nr_blocks;
 +}
 diff -ruN sbin.orig/newfs_minix/mkfs.h sbin/newfs_minix/mkfs.h
 --- sbin.orig/newfs_minix/mkfs.h	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/mkfs.h	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,178 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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                              *
 + *                                                                          *
 + *   The guide for these sources 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                          *
 + *                                                                          *
 + ****************************************************************************/
 +
 +#include <sys/types.h>
 +#include <sys/time.h>
 +#include <sys/uio.h>
 +
 +#include <fcntl.h>
 +#include <stdio.h>
 +#include <stdlib.h>
 +#include <unistd.h>
 +
 +/*
 + * In the definitions that follow, a zone is a block.
 + * There exists the possiblity that a zone can be
 + * a power of two larger than a block; that is the
 + * function of the variable s_log_zone_size in the
 + * super block which represents the shift needed
 + * to go from blocks to zones and back again.
 + */
 +
 +#define BLOCK_SIZE 1024       /* # bytes in a disk block */
 +
 +#define BITS_PER_BLOCK 8192   /* 8*BLOCK_SIZE */
 +#define BITMAPSHIFT 13        /* log2(BITS_PER_BLOCK) */
 +
 +#define INODE_MAP 2           /* position of first inode bitmap */
 +
 +#define V2_NR_DZONES 7
 +#define V2_NR_TZONES 10
 +#define NR_INODES 64
 +#define V2_INODE_SIZE 64    /* sizeof(inode_t) */
 +#define V2_ZONE_NUM_SIZE 4  /* sizeof(zone_t) = 32 bits */
 +#define V2_INODES_PER_BLOCK (BLOCK_SIZE/V2_INODE_SIZE)
 +#define V2_INDIRECTS   (BLOCK_SIZE/V2_ZONE_NUM_SIZE)  /* # zones/indir block */
 +
 +/* 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 unsigned short mino_t; 	   /* i-node number */
 +typedef unsigned short mmode_t;	   /* file type and permissions bits */
 +typedef unsigned long  moff_t;	   /* offset within a file */
 +typedef unsigned short zone1_t;	   /* zone number for V1 file systems */
 +typedef unsigned long  zone_t;	   /* zone number */
 +typedef unsigned long  block_t;	   /* block number */
 +typedef unsigned long  bit_t;      /* bit number in a bitmap */
 +typedef unsigned short bitchunk_t; /* a collection of bits from a bitmap */
 +typedef long           mtime_t;    /* time in sec since 1 Jan 1970 0000 GMT */
 +
 +/* File system types. */
 +#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 */
 +
 +/* Directory layout */
 +#define DIRBLKSIZ 512
 +#define DIRSIZ    14
 +
 +struct direct {
 +	mino_t d_ino;           /*  2 */
 +	char d_name[DIRSIZ];    /* 14 */
 +};
 +
 +#define DIR_ENTRY_SIZE usizeof(struct direct)
 +#define NR_DIR_ENTRIES (BLOCK_SIZE/DIR_ENTRY_SIZE)
 +
 +/* Structure of the superblock on disk */
 +
 +typedef struct {
 +	mino_t s_ninodes;		/* # usable inodes on the minor device */
 +	zone1_t  s_nzones;		/* total device size, including bit maps etc */
 +	short s_imap_blocks;		/* # of blocks used by inode bit map */
 +	short s_zmap_blocks;		/* # of blocks used by zone bit map */
 +	zone1_t s_firstdatazone;	/* number of first data zone */
 +	short s_log_zone_size;	        /* log2 of blocks/zone */
 +	moff_t s_max_size;		/* maximum file size on this device */
 +	short s_magic;		        /* magic number to recognize super-blocks */
 +	short s_pad;			/* try to avoid compiler-dependent padding */
 +	zone_t s_zones;	        	/* number of zones (replaces s_nzones in V2) */
 +	char pad[1000];                 /* pad the block to 1024 bytes */
 +} super_block_t;
 +
 +/* Structure of an inode on disk. length = 64 bytes. */
 +
 +typedef struct {
 +	mmode_t  i_mode;		/* file type, protection, etc. 2 */
 +	short i_nlinks;		        /* how many links to this file 2 */
 +	short i_uid;		        /* user id of the file's owner 2 */
 +	short i_gid;	        	/* group number                2 */
 +	moff_t i_size;	       	        /* current file size in bytes  4 */
 +	mtime_t i_atime;		/* time of last access (V2 only) 4 */
 +	mtime_t i_mtime;		/* when was file data last changed 4 */
 +	mtime_t i_ctime;		/* when was inode itself changed (V2 only) 4 */
 +	zone_t i_zone[V2_NR_TZONES];	/* zone numbers for direct, ind, and dbl ind 40 */
 +} inode_t;
 +
 +/* A zone can be inodes, a bootblock, a superblock or data */
 +/* This definition really defines the possible structure of a block */
 +
 +typedef union {
 +	inode_t inode[16];
 +	super_block_t sp;
 +	struct boot_block_s {
 +		char bootblock[294];
 +		char pad[730];
 +	} boot_block;
 +	char store[1024];
 +} Zone_t;
 +
 +void insert_bit (block_t, int);
 +int read_bit (block_t, int);
 +mino_t get_inode(void);
 +void get_block (block_t, char*);
 +void put_block (block_t, char*);
 +void rootdir(mino_t);
 +zone_t alloc_zone(void);
 +mino_t alloc_inode(int , int, int);
 +void add_zone(mino_t, zone_t, long, long);
 +void incr_link(mino_t);
 +void enter_dir(mino_t, char*, mino_t);
 +
 +/* These variable are defined in main and used throughout */
 +
 +extern Zone_t *zone;
 +extern int zone_size, zone_map, zone_shift;
 +extern int zoff;
 +extern char zero[BLOCK_SIZE];
 +extern int inodes_per_block;
 +extern int inode_offset;
 +extern int nrblocks, nrnodes;
 +extern long current_time;
 +extern zone_t nrzones;
 +extern unsigned int nrinodes;
 diff -ruN sbin.orig/newfs_minix/newfs_minix.8 sbin/newfs_minix/newfs_minix.8
 --- sbin.orig/newfs_minix/newfs_minix.8	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/newfs_minix.8	Fri Feb 28 13:56:11 2003
 @@ -0,0 +1,59 @@
 +
 +.Dd February 28, 2003
 +.Dt NEWFS_MINIX 8
 +.Os
 +.Sh NAME
 +.Nm newfs_minix
 +.Nd construct a new Minix file system
 +.Sh SYNOPSIS
 +.Nm
 +.Op Fl N
 +.Op Fl b Ar number-of-blocks
 +.Op Fl i Ar number-of-inodes
 +.Op Fl s Ar zone-shift
 +.Ar special
 +.Sh DESCRIPTION
 +The
 +.Nm
 +utility is used to initialize and clear the Minix
 +filesystem on device
 +.Ar special.
 +(We often refer to the
 +.Dq special file
 +as the
 +.Dq disk ,
 +although the special file need not be a physical disk.
 +In fact, it need not even be special.)
 +Without options
 +.Nm
 +will make a file system that will fit on a 1.44MB
 +floppy disk, however,
 +.Nm
 +has some options to allow the defaults to be selectively overridden.
 +.Pp
 +The following options define the general layout policies:
 +.Bl -tag -width indent
 +.It Fl b Ar number-of-blocks
 +The total number of blocks of 1024 bytes each that the FS will contain.
 +If this option is not provided then 1440 blocks are chosen.
 +.It Fl i Ar number-of-inodes
 +The number of inode in the FS. If this option is not provided then
 +the number of inodes will be calculated to be roughly 1/3 of the
 +number of blocks.
 +.It Fl s Ar zone-shift
 +The log2 of the number of blocks/zone. The FS can be designed to be
 +made up of data zones that contain a power of two contiguous blocks
 +in them. The default for this parameter is zero.
 +.It Fl N
 +Cause the file system parameters to be printed out
 +without really creating the file system.
 +.Sh SEE ALSO
 +.Xr mount 8 ,
 +.Xr mount_minixfs 8
 +.Sh AUTHOR
 +.An Ed Alley <wea@llnl.gov>
 +.Sh HISTORY
 +The
 +.Nm
 +command first appeared in
 +.Bx 4.6.2  .
 diff -ruN sbin.orig/newfs_minix/super.c sbin/newfs_minix/super.c
 --- sbin.orig/newfs_minix/super.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/super.c	Fri Mar 14 22:58:35 2003
 @@ -0,0 +1,110 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +/* The following code was lifted and modified from the minix source with permission. */
 +
 +#include "mkfs.h"
 +
 +int bitmapsize(bit_t);
 +
 +#define DEBUG 0
 +
 +void
 +super(super_block_t *sp, zone_t nzones, mino_t ninodes)
 +{
 +	zone_t maxsize;
 +	zone_t fstblk, fstzon;
 +	short impsize, zmpsize;
 +	int i, inoblks;
 +     
 +	/*
 +	  The maximum file size is the cube of the number of indirects
 +	  plus the square of the number of indirects plus the number of
 +	  indirects plus the number of direct entries in the inode times
 +	  the number of bytes in a zone. This can be a very large number,
 +	  (more than 32 bits of addressing can manage).
 +	*/
 +
 +	/* With triple indirects just put the 2GB limit as the max size */
 +     
 +	maxsize = ~(1 << 31);  /* All bits are ones except the sign bit */
 +     
 +	/* maxsize is then (2^31 - 1) = 2,147,483,647 bytes */
 +     
 +	/*
 +	  bitmapsize() returns the number of blocks in a bitmap
 +	*/
 +     
 +	impsize = bitmapsize((bit_t) (1 + ninodes));
 +	zmpsize = bitmapsize((bit_t) nzones);
 +#if DEBUG > 0
 +	printf("blocks in imap = %d\n",impsize);
 +	printf("blocks in zmap = %d\n",zmpsize);
 +#endif
 +	/*
 +	  The number of blocks to the beginning of the inodes
 +	*/
 +
 +	inode_offset = impsize + zmpsize + 2;
 +#if DEBUG > 0
 +	printf("inode offset = %d\n", inode_offset);
 +#endif
 +
 +	/* The number blocks that contain inodes */
 +     
 +	inoblks = (ninodes + inodes_per_block - 1)/inodes_per_block;
 +#if DEBUG > 0
 +	printf("inode blocks = %d\n",inoblks);
 +#endif
 +	/*
 +	  The number of blocks to the beginning of the data
 +	  gives the location of the first data zone.
 +	*/
 +
 +	fstblk = inode_offset + inoblks;    /* Number of 1K blocks */
 +	fstzon = (fstblk + (1 << zone_shift) - 1) >> zone_shift;
 +#if DEBUG > 0
 +	printf("first data zone  = %d\n",(int)fstzon);
 +	printf("first data block = %d\n",(int)(fstzon << zone_shift));
 +#endif
 +	/*
 +	  Construct the V2 superblock
 +	*/
 +     
 +	sp->s_ninodes = ninodes;      /* # of inodes */
 +	sp->s_nzones = 0;             /* Not used in V1; forces errors in V2 */
 +	sp->s_imap_blocks = impsize;  /* # of blocks used by inode bit map */
 +	sp->s_zmap_blocks = zmpsize;  /* # of blocks used by zone  bit map */
 +	sp->s_firstdatazone = fstzon; /* index of the first data zone */
 +	sp->s_log_zone_size = zone_shift; /* log2(blocks/zone) */
 +	sp->s_max_size = maxsize;     /* Maximum file system size */
 +	sp->s_magic = SUPER_V2;       /* Indicates a V2 filesystem */
 +	sp->s_pad = 0;                /* Just padding */
 +	sp->s_zones = nzones;         /* # of zones */
 +	
 +	for (i=0; i<1000; i++)        /* Pad the rest of the block with zeros */
 +		sp->pad[i] = '\0';
 +}
 diff -ruN sbin.orig/newfs_minix/zoneio.c sbin/newfs_minix/zoneio.c
 --- sbin.orig/newfs_minix/zoneio.c	Wed Dec 31 16:00:00 1969
 +++ sbin/newfs_minix/zoneio.c	Fri Feb 28 13:44:15 2003
 @@ -0,0 +1,127 @@
 +/*-
 + * 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.
 + */
 +
 +/*
 + * The code below was lifted from Minix's mkfs.c with permission.
 + */
 +
 +#include "mkfs.h"
 +
 +/*
 + * Returns a free zone/block number and marks it active
 + */
 +
 +zone_t
 +get_zone(void)
 +{
 +	zone_t num;
 +	block_t znodemap;
 +	int bit;
 +
 +	/* loop over zones */
 +
 +	for (num=1; num<nrzones; num++) {
 +		znodemap = (num/BITS_PER_BLOCK) + zone_map;
 +		bit      = num % BITS_PER_BLOCK;
 +		if (read_bit(znodemap,bit) == 0) {
 +			insert_bit(znodemap, bit);
 +			return (num+zoff);
 +		}
 +	}
 +
 +	printf("No data blocks available!\n");
 +	exit(1);
 +}
 +
 +/*
 +  Returns the absolute zone number of a new zone
 +*/
 +
 +zone_t
 +alloc_zone(void)
 +{
 +	/* Allocate a new zone */
 +	/* Works for zone > block */
 +     
 +	block_t b;
 +	int i;
 +	zone_t z;
 +
 +	/* Get a free zone/block and mark it active */
 +  
 +	z = get_zone();
 +  
 +	b = z << zone_shift;
 +  
 +	for (i = 0; i < zone_size; i++)
 +		put_block(b + i, zero);	/* give an empty zone */
 +     
 +	return z;  /* Returns the absolute zone number in the file */
 +}
 +
 +void
 +add_zone(mino_t n, zone_t z, long bytes, long cur_time)
 +{
 +	/* Add zone z to inode n. The file has grown by 'bytes' bytes. */
 +
 +	int off, i;
 +	block_t b;
 +	zone_t indir;
 +	zone_t blk[V2_INDIRECTS];
 +	inode_t *p;
 +	inode_t inode[V2_INODES_PER_BLOCK];
 +
 +	b = ((n - 1) / V2_INODES_PER_BLOCK) + inode_offset;
 +	off = (n - 1) % V2_INODES_PER_BLOCK;
 +  
 +	get_block(b, (char *) inode);
 +	p = &inode[off];
 +	p->i_size += bytes;
 +	p->i_mtime = cur_time;
 +	for (i = 0; i < V2_NR_DZONES; i++)
 +		if (p->i_zone[i] == 0) {
 +			p->i_zone[i] = z;
 +			put_block(b, (char *) inode);
 +			return;
 +		}
 +	put_block(b, (char *) inode);
 +
 +  /* File has grown beyond a small file. */
 +
 +	if (p->i_zone[V2_NR_DZONES] == 0) p->i_zone[V2_NR_DZONES] = alloc_zone();
 +	indir = p->i_zone[V2_NR_DZONES];
 +	put_block(b, (char *) inode);
 +	b = indir << zone_shift;
 +	get_block(b, (char *) blk);
 +	for (i = 0; i < V2_INDIRECTS; i++)
 +		if (blk[i] == 0) {
 +			blk[i] = z;
 +			put_block(b, (char *) blk);
 +			return;
 +		}
 +	printf("File has grown beyond single indirect");
 +	exit(1);
 +}
 8><------------------------ Cut Here ------------------------------
 
 
 Following is the sys.patch:
 
 8><------------------------ Cut Here ------------------------------
 diff -ruN sys.orig/fs/minixfs/README sys/fs/minixfs/README
 --- sys.orig/fs/minixfs/README	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/README	Mon Mar 17 10:23:24 2003
 @@ -0,0 +1,45 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 is the Minix FS. It was developed on FreeBSD-4.6.x.
 +
 +The fs is located in: "/usr/src/sys/fs/minixfs". There are two other source
 +files located in /usr/src/sbin/: mount_minixfs(8) is located in the directory:
 +"mount_minix"; and newfs_minix(8) is located in the directory: newfs_minix. The first
 +source is needed to mount the FS and the second one will make the FS.  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 and newfs_minix.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. The Minixfs was originally
 +developed to compile statically into the kernel with the option MINIXFS included
 +in the configure file. Although, this was useful during the debugging phase in
 +the development of the FS (in terms of reading the inevitable panic dumps that
 +occur) I have taken out this feature in this version of the FS. The Minix fs
 +is not a "main-line" production FS but only a simple "research" FS designed
 +to be studied by a student. As such I thought that it made more sense to present
 +it as a loadable module only.
 +
 +					Ed
 diff -ruN sys.orig/fs/minixfs/TODO sys/fs/minixfs/TODO
 --- sys.orig/fs/minixfs/TODO	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/TODO	Mon Mar 17 10:38:48 2003
 @@ -0,0 +1,113 @@
 +Things to do and Things done:    (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.
 +			(DONE 030213)
 +
 +	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.
 +			(Fixed 030228)
 +		I believe that the problem was in the minix_fsync() routine.
 +		I have made every effort to copy the way that ffs_fsync()
 +		does the job. This helped the above problem, and I haven't
 +		generated any recent panics since.
 +
 +	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 metadata
 +		updates. I believe that now is the time to start relaxing
 +		that conservative approach.
 +
 +	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.
 +
 +	7. Write a newfs_minix routine.
 +			(DONE 030210)
 +
 +	8. Page fault if module is loaded for days!
 +		I don't understand this one. It seems that
 +		the module will page fault if it is loaded
 +		for days even after being unmounted. If a
 +		fs is mounted, then a page fault will occur.
 +		This can be eliminated by unloading the
 +		module after the last fs is unmounted.
 +			(Fixed 030220)
 +		This problem had to do with a resource not
 +		getting released properly after the unmount.
 +
 +	9. Clean up the directory manipulating routines:
 +	        Come up with a way to keep track of holes,
 +	  	so we don't have to search the directory
 +	 	each time we need to put an entry in.
 +	        Also, fix up the way that the directory
 +	        size is determined: right now we just count
 +	        the entries until the last is found.
 +	        Fix up the way directories are truncated
 +	        when they are shortened: right now we just
 +	        copy out the existing entries, clean the
 +	        directory out and then reload it with the
 +	        saved entries.
 +
 +			(First iteration on this completed:
 +			 030304 HBDTM)
 +
 +		We now use stuff that minix_namei() leaves
 +		behind before minix_direnter() is called.
 +		However, minix_dirremove() still re-packs
 +		the directory in a ponderous way to eliminate
 +		extra blocks when possible; there may be
 +		faster ways to do this that don't involve
 +		copying back and forth. (For instance: by moving
 +		tailing entries up into recently vacated spots.)
 +
 +	10. Convert to 5.x RELEASE.
 +			(Done 030314)
 +
 +		I have tried to break the fs in a variety of
 +		ways:
 +		      
 +                 1.  Exhaust the inode list.
 +		 2.  Exhaust the block list.
 +                 3.  Make strings of directories, one
 +		     within the other and then move
 +		     them in and out of one another.
 +	         4.  Copy large files in and out, and
 +		     across mount points.
 +
 +		With all these exercises the fs worked correctly.
 +		However, I have not adequately tested the fs
 +		in a multi-user environment: multiple/simultaneous
 +		reads/writes to the same fs, etc.
 +
 +		Also, when I switched to a moduled-loaded fs
 +		I began experiencing the problem in #2. above.
 +		This prompted me to look at minix_fsync() again.
 +		I noticed that ffs_fsync() had changed between
 +		4.x and 5.x. I re-wrote minix_fsync() to more
 +		closely follow 5.x's fsync() and that seemed to
 +		clear up the panics after a remount. Time will
 +		tell of course.
 +			(Done 030317)
 diff -ruN sys.orig/fs/minixfs/bsd-copyright sys/fs/minixfs/bsd-copyright
 --- sys.orig/fs/minixfs/bsd-copyright	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/bsd-copyright	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,25 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix.h sys/fs/minixfs/minix.h
 --- sys.orig/fs/minixfs/minix.h	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix.h	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,373 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/bio.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 = 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 NO_BLOCK 0            /* Flag indicating no block available */
 +#define NO_ZONE  0            /* Flag indicating no zone available */
 +#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) */
 +#define NIADDR 3              /* Total number of indirects in an inode */
 +
 +/* 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 {
 +	LIST_ENTRY(minix_inode) i_hash; /* Hash chain */
 +	struct minixmount *i_mmp;
 +	struct vnode *i_vnode;          /* Vnode associated with this inode */
 +	mode_t    i_mode;               /* BSD style mode of file */
 +	u_int32_t i_flag;               /* flags */
 +	u_int32_t 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) ((daddr_t)((byte) >> DEV_BSHIFT))
 +#define ino_off(ino) (((ino-1) % V2_INODES_PER_BLOCK) * V2_INODE_SIZE)
 +#define VTOMI(vp) ((struct minix_inode*)(vp)->v_data)
 +
 +/* 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 ((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_zshift;	         /* 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 vnode *s_devvp;   /* Vnode of device mounted on */
 +	ino_t s_imnton;          /* UFS inode number mounted on */
 +	u_int16_t *s_ibmap;      /* Inode bit-map */
 +	u_int16_t *s_zbmap;      /* Zone bit-map */
 +	u_int32_t imap_lock;     /* Inode bit-map lock variable */
 +	u_int32_t zmap_lock;     /* Zone 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_zoff;              /* Data block offset = s_firstdatazone - 1. */
 +	int s_bsize;             /* Block size */
 +	int s_zsize;             /* Zone 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 */
 +};
 +
 +#define MBLOCK(bp) ((union minix_block*)((bp)->b_data))
 +
 +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 */
 +
 +void minix_ihashinit(void);
 +void minix_ihashuninit(void);
 +struct vnode *minix_ihashlookup(dev_t, ino_t);
 +int minix_ihashget(dev_t, ino_t, int, struct vnode**);
 +int minix_ihashins(struct minix_inode*,int,struct vnode**);
 +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, int, 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*,daddr_t,struct buf**);
 +int minix_blkatoff(struct vnode*,off_t,char**,struct buf**);
 +int minix_zalloc(struct minix_super_block*,daddr_t*,daddr_t*);
 +int minix_dzalloc(struct minix_super_block*,daddr_t);
 +int minix_makeinode(int,struct vnode*,struct vnode**,struct componentname*);
 +int minix_addzone(struct minix_inode*,daddr_t,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 thread*);
 +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_zone_count(struct minix_super_block*);
 +int minix_next_free_inode(struct minix_super_block*);
 +int minix_next_free_zone(struct minix_super_block*);
 +int minix_bmapfs(struct vnode*,daddr_t,daddr_t*,int*);
 +int minix_get_vtype(struct minix_inode*);
 +int minix_num_blocks(u_int16_t, u_int32_t, short);
 +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*, struct componentname*);
 +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 sys.orig/fs/minixfs/minix_alock.s sys/fs/minixfs/minix_alock.s
 --- sys.orig/fs/minixfs/minix_alock.s	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_alock.s	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,63 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix_bio.c sys/fs/minixfs/minix_bio.c
 --- sys.orig/fs/minixfs/minix_bio.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_bio.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,735 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/bio.h>
 +#include <sys/buf.h>
 +
 +#include <fs/minixfs/minix.h>
 +
 +int  minix_read_zbit(struct minix_super_block*,u_int32_t);
 +void minix_write_zbit(struct minix_super_block*,u_int32_t);
 +void minix_delete_zbit(struct minix_super_block*,u_int32_t);
 +
 +/*
 + * Allocate new blocks from lbof0+1 to lbof1.
 + * If lbof0 doesn't exist, then allocate it first.
 + */
 +int
 +minix_balloc(struct vnode *vp, daddr_t lbof1, struct buf **bpp)
 +{
 +	struct buf *bp;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	daddr_t lbof0, lbn, pbn, lb, zone, boff, z0, z1, bsize;
 +	int error;
 +
 +	*bpp = NULL;
 +
 +	if (dip->i_size == 0) {  /* No zones so create a new one */
 +		zone = 0;
 +		if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +			return error;
 +		if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0)
 +			return error;
 +		if ((error = bread(devvp, pbn, sp->s_zsize, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)sp->s_zsize);
 +		bwrite(bp);
 +	}
 +
 +	if (dip->i_size > 0)
 +		lbof0 = (dip->i_size - 1)/BLOCK_SIZE;
 +	else
 +		lbof0 = 0;
 +
 +	z0 = lbof0 >> sp->s_zshift;
 +	z1 = lbof1 >> sp->s_zshift;
 +
 +	if (z1 <= z0) {  /* Zone already exists! just return the block */
 +		if ((error = minix_bmapfs(vp, lbof1, &pbn, NULL)) != 0)
 +			return error;
 +		if ((error = bread(devvp, pbn, BLOCK_SIZE, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)BLOCK_SIZE);
 +		*bpp = bp;
 +		return 0;
 +	}
 +
 +	/* The new zone extends beyond the file */
 +
 +	/* Allocate all the blocks between lbof0 and lbof1 and zero them out */
 +
 +	for (lb=lbof0+1; lb<lbof1; lb++) {
 +		zone = lb >> sp->s_zshift;
 +		boff = lb - (zone << sp->s_zshift);
 +		if (boff > 0) { /* Zone already exixts */
 +			bsize = BLOCK_SIZE;
 +			if ((error = minix_bmapfs(vp, lb, &pbn, NULL)) != 0)
 +				return error;
 +		} else {
 +			bsize = sp->s_zsize;
 +			if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +				return error;
 +			if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0) /* add the new zone to inode */
 +				return error;	
 +		}
 +		if ((error = bread(devvp, pbn, bsize, NOCRED, &bp)) != 0)
 +			return error;
 +		bzero(bp->b_data, (size_t)bsize);
 +		bwrite(bp);
 +	}
 +	/* Finally allocate lbof1, the required new block */
 +
 +	zone = lbof1 >> sp->s_zshift;
 +	boff = lbof1 - (zone << sp->s_zshift);
 +	if (boff > 0) {   /* Zone already exists, just get the block */
 +		bsize = BLOCK_SIZE;
 +		if ((error = minix_bmapfs(vp, lbof1, &pbn, NULL)) != 0)
 +			return error;
 +	} else {
 +		bsize = sp->s_zsize;
 +		if ((error = minix_zalloc(sp, &lbn, &pbn)) != 0)
 +			return error;
 +		if ((error = minix_addzone(ip, zone, lbn >> sp->s_zshift)) != 0) /* add the new zone to inode */
 +			return error;
 +	}
 +	if ((error = bread(devvp, pbn, bsize, NOCRED, &bp)) != 0)
 +		return error;
 +	bzero(bp->b_data, (size_t)bsize);
 +
 +	*bpp = bp;
 +	return 0;
 +
 +}
 +/*
 + * Returns the block number of the next free zone and sets
 + * the corresponding bit in the zone bitmap.
 + */
 +int
 +minix_zalloc(struct minix_super_block *sp, daddr_t *lblkp, daddr_t *pblkp)
 +{
 +	int ic, error;
 +	daddr_t zone, bblk;
 +	daddr_t off;
 +	struct buf *bp;
 +	struct vnode *devvp = sp->s_devvp;
 +	union minix_block *mbp;
 +	daddr_t pbn = 0, pzn = 0;
 +	u_int16_t *buf = sp->s_zbmap;
 +
 +	*lblkp = 0;
 +	*pblkp = 0;
 +
 +	minix_get_lock(&sp->zmap_lock);
 +
 +	if ((zone = minix_next_free_zone(sp)) == NO_ZONE) {
 +		minix_free_lock(&sp->zmap_lock);
 +		return ENOSPC;
 +	}
 +
 +	minix_write_zbit(sp, zone);
 +
 +	ic = zone >> 4;                 /* Chunk that bit resides in */
 +	bblk = zone / BITS_PER_BLOCK;   /* Block that bit is in */
 +	off  = zone % 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_zbit(sp, zone);
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	mbp = MBLOCK(bp);
 +	mbp->bitchunk[off] = buf[ic];
 +
 +	if ((error = minix_putblk(bp)) != 0) {
 +		minix_delete_zbit(sp, zone);
 +		if (bp != NULL) {
 +		     bp->b_flags |= B_INVAL;
 +		     brelse(bp);
 +		}
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	minix_free_lock(&sp->zmap_lock);
 +
 +	pzn = zone + sp->s_zoff;    /* Physical zone number */
 +	pbn = pzn << sp->s_zshift;  /* Physical block number */
 +	
 +	*lblkp = pbn;
 +	*pblkp = byte_to_blkn(blk_to_byte(pbn)); /* Suitable for bread */
 +
 +	return 0;
 +}
 +/*
 + * Deallocates a zone if the last block in it is released.
 + */
 +int
 +minix_dzalloc(struct minix_super_block *sp, daddr_t zone)
 +{
 +        int ic, error;
 +	daddr_t bblk;
 +	daddr_t off;
 +	struct buf *bp;
 +	struct vnode *devvp = sp->s_devvp;
 +	u_int16_t *buf = sp->s_zbmap;
 +	union minix_block *mbp;
 +
 +	minix_get_lock(&sp->zmap_lock);
 +
 +	zone -= sp->s_zoff;              /* Convert to bit offset */
 +
 +	if (minix_read_zbit(sp, zone))
 +		minix_delete_zbit(sp, zone);
 +	else {
 +		minix_free_lock(&sp->zmap_lock);
 +		return 0;
 +	}
 +	
 +	ic = zone >> 4;                 /* Chunk that bit resides in */
 +	bblk = zone / BITS_PER_BLOCK;   /* Block that bit is in */
 +	off  = zone % 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_zbit(sp, zone);
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	mbp = MBLOCK(bp);
 +	mbp->bitchunk[off] = buf[ic];
 +
 +	if ((error = minix_putblk(bp)) != 0) {
 +		minix_write_zbit(sp, zone);
 +		if (bp != NULL) {
 +			bp->b_flags |= B_INVAL;
 +			brelse(bp);
 +		}
 +		minix_free_lock(&sp->zmap_lock);
 +		return error;
 +	}
 +
 +	minix_free_lock(&sp->zmap_lock);
 +	
 +	return 0;
 +}
 +/*
 + * Add a zone to the end of a file. "zone" is the logical zone
 + * offset in the file and pbn is the physical zone offset in the
 + * file system. It is the responsibility of the calling
 + * routine to guarantee that the next zone to add is at the
 + * logical end of the file. This routine returns EIO otherwise.
 + */
 +int
 +minix_addzone(struct minix_inode *ip, daddr_t zone, daddr_t pbn)
 +{
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	union minix_block *mbp;
 +	struct buf *bp;
 +	daddr_t pind;
 +	daddr_t id0, id1, id2, id3, off, blk;
 +	int error, indirect = 0;
 +
 +	pind = zone + 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[zone] != 0)
 +			return EIO;
 +		dip->i_block[zone] = pbn;
 +		break;
 +	case 3:
 +		off = zone - 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_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			id0 = blk >> sp->s_zshift;
 +			dip->i_block[V2_NR_DBLOCKS+2] = id0;
 +			bwrite(bp);
 +		} else
 +			id0 = dip->i_block[V2_NR_DBLOCKS+2];
 +
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id3];
 +		if (id0 == 0) {
 +			/* Generate a double indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			mbp->ind[id3] = id0;
 +			bwrite(bp);
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			bwrite(bp);
 +		} else
 +			bqrelse(bp);
 +		goto rind2;
 +		break;
 +	case 2:
 +		off = zone - 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_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			id0 = blk >> sp->s_zshift;
 +			dip->i_block[V2_NR_DBLOCKS+1] = id0;
 +			bwrite(bp);
 +		} else
 +			id0 = dip->i_block[V2_NR_DBLOCKS+1];
 +	rind2:
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id2];
 +		if (id0 == 0) {
 +			/* Generate a single indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			mbp->ind[id2] = id0;
 +			bwrite(bp);
 +			if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +				return error;
 +			bzero(bp->b_data, BLOCK_SIZE);
 +			bwrite(bp);
 +		} else
 +			bqrelse(bp);
 +		goto rind1;
 +		break;
 +	case 1:
 +		id1 = zone - TOT_DIRECT;
 +		if (dip->i_block[V2_NR_DBLOCKS] == 0) {
 +			/* Generate a single indirect block */
 +			if ((error = minix_zalloc(sp, &blk, &pind)) != 0)
 +				return error;
 +			id0 = blk >> sp->s_zshift;
 +			if ((error = minix_getblk(devvp, blk, &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:
 +		blk = id0 << sp->s_zshift;
 +		if ((error = minix_getblk(devvp, blk, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		mbp->ind[id1] = pbn;
 +		bwrite(bp);
 +		break;
 +	default:
 +		printf("minix_addzone: too large: zone = %d\n",(int)zone);
 +		return ENOSPC;
 +	}
 +
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +
 +	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 */
 +	     daddr_t lbk,           /* logical block number */
 +             daddr_t *pbk,          /* returned physical block number */
 +             int *runp)               /* number of dev chunks to add */
 +{
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	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;
 +	daddr_t id, idp, id0=0, id1, id2, id3, blk;
 +	daddr_t offset;
 +	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;
 +	}
 +
 +	id = lbk >> sp->s_zshift;            /* index by zones */
 +	offset = lbk - (id << sp->s_zshift); /* Block offset in zone */
 +	
 +	idp = id + 1;
 +
 +	if (idp > ndb)
 +		indirect++;
 +	if (idp > nsidb)
 +		indirect++;
 +	if (idp > ndidb)
 +		indirect++;
 +	if (idp > ntidb)
 +		indirect++;
 +
 +	*pbk = 0;
 +	switch (indirect) {
 +	case 0:                /* Direct */
 +		if ((*pbk = (dip->i_block[id] << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		*pbk += offset;
 +		*pbk = ((*pbk)*BLOCK_SIZE) >> DEV_BSHIFT;
 +		return 0;
 +	case 3:                /* Triple indirect */
 +		if ((blk = (dip->i_block[V2_NR_DBLOCKS+2] << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
 +			brelse(bp);
 +			return error;
 +		}
 +		mbp = MBLOCK(bp);
 +		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 ((blk = (id0 << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0) {
 +			brelse(bp);
 +			return error;
 +		}
 +		mbp = MBLOCK(bp);
 +		id0 = mbp->ind[id2];
 +		bqrelse(bp);
 +		goto rind1;
 +	case 1:                /* Single indirect */
 +		id1 = id - ndb;
 +		id0 = dip->i_block[V2_NR_DBLOCKS];
 +	rind1:
 +		if ((blk = (id0 << sp->s_zshift)) == 0)
 +			return EFAULT;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		if ((*pbk = (mbp->ind[id1] << sp->s_zshift)) == 0) {
 +			brelse(bp);
 +			return EFAULT;
 +		}
 +		bqrelse(bp);
 +		*pbk += offset;
 +		*pbk = ((*pbk)*BLOCK_SIZE) >> DEV_BSHIFT;
 +		return 0;
 +	default:
 +		return ENOSPC;
 +	}
 +
 +	/* NOTREACHED */
 +}
 +/*
 + * Returns a logical block in a buffer
 + */
 +int
 +minix_getblk(struct vnode *devvp, block_t blk, struct buf **bpp)
 +{
 +	daddr_t offset;
 +	int error;
 +
 +	offset = (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_RELBUF;
 +	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;
 +	daddr_t lbn, pbn;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct vnode *devvp = ip->i_su->s_devvp;
 +	int error;
 +	
 +	lbn = ((daddr_t)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 + (int)(offset % BLOCK_SIZE);
 +	*bpp = bp;
 +	return 0;
 +}
 +/*
 + * The next few routines read or alter the in-core
 + * zone bitmaps. Any locking that is required is
 + * assumed to occur in the calling programs.
 + */
 +
 +/*
 + * Return the number of free zones
 + */
 +int
 +minix_free_zone_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_zbmap;
 +     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 zone
 + */
 +int
 +minix_next_free_zone(struct minix_super_block *sp)
 +{
 +	int i, n, s, nb;
 +	u_int16_t *buf = sp->s_zbmap;
 +	register int bit;
 +
 +	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)) {
 +					bit = s + (i << 4);
 +					return((bit < nb) ? bit : NO_ZONE);
 +				}
 +	return NO_ZONE;
 +}
 +/*
 + * Read a bit from the in-core data block bitmap
 + */
 +int
 +minix_read_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap;     
 +	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_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap; 
 +	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_zbit(struct minix_super_block *sp, u_int32_t bit)
 +{
 +	u_int16_t *buf = sp->s_zbmap;
 +	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, short zshift)
 +{
 +     int nindirects, indirect = 0;
 +     int nzones, 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;
 +     case I_DIRECTORY:
 +	  break;
 +     default:     /* This includes Sock,B_Spec,C_Spec, and Fifo. */
 +	  return 0;
 +     }
 +     if (size == 0)
 +	  return 0;
 +     /*
 +      * nblocks set initially to one above. Add the extra blocks below.
 +      */
 +     nblocks += (size - 1) / BLOCK_SIZE;
 +     nzones = nblocks >> zshift;
 +     if (nblocks > (nzones << zshift))
 +	  nzones += 1;
 +
 +     /* Figure out whether we have indirect blocks */
 +
 +     if (nzones > TOT_DIRECT)
 +	  indirect++;         /* Single indirect */
 +
 +     if (nzones > TOT_SINDIRECT)
 +	  indirect++;         /* Double indirect */
 +
 +     if (nzones > 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 sys.orig/fs/minixfs/minix_ihash.c sys/fs/minixfs/minix_ihash.c
 --- sys.orig/fs/minixfs/minix_ihash.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_ihash.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,187 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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.
 + */
 +
 +#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 <sys/mutex.h>
 +#include <sys/mount.h>
 +
 +#include <fs/minixfs/minix.h>
 +
 +static MALLOC_DEFINE(M_MNXIHASH, "Minix ihash", "Minix Inode hash tables");
 +/*
 + * Structures associated with inode cacheing.
 + */
 +static LIST_HEAD(mnxihashhead, minix_inode) *mnxihashtb;
 +static u_long	minix_ihash;		/* size of hash table - 1 */
 +#define	MNXINOHASH(device, inum) (&mnxihashtb[(minor(device) + (inum)) & minix_ihash])
 +static struct mtx minix_ihash_mtx;
 +
 +/*
 + * Initialize inode hash table.
 + */
 +void
 +minix_ihashinit()
 +{
 +
 +	mnxihashtb = hashinit(desiredvnodes, M_MNXIHASH, &minix_ihash);
 +	mtx_init(&minix_ihash_mtx, "minix ihash", NULL, MTX_DEF);
 +}
 +
 +/*
 + * Destroy the inode hash table.
 + */
 +void
 +minix_ihashuninit()
 +{
 +
 +	hashdestroy(mnxihashtb, M_MNXIHASH, minix_ihash);
 +	mtx_destroy(&minix_ihash_mtx);
 +}
 +
 +/*
 + * 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, inum)
 +	dev_t dev;
 +	ino_t inum;
 +{
 +	struct minix_inode *ip;
 +
 +	mtx_lock(&minix_ihash_mtx);
 +	LIST_FOREACH(ip, MNXINOHASH(dev, inum), i_hash)
 +		if (inum == ip->i_number && dev == ip->i_dev)
 +			break;
 +	mtx_unlock(&minix_ihash_mtx);
 +
 +	if (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.
 + */
 +int
 +minix_ihashget(dev, inum, flags, vpp)
 +	dev_t dev;
 +	ino_t inum;
 +	int flags;
 +	struct vnode **vpp;
 +{
 +	struct thread *td = curthread;	/* XXX */
 +	struct minix_inode *ip;
 +	struct vnode *vp;
 +	int error;
 +
 +	*vpp = NULL;
 +loop:
 +	mtx_lock(&minix_ihash_mtx);
 +	LIST_FOREACH(ip, MNXINOHASH(dev, inum), i_hash) {
 +		if (inum == ip->i_number && dev == ip->i_dev) {
 +		        vp = ip->i_vnode;
 +			mtx_lock(&vp->v_interlock);
 +			mtx_unlock(&minix_ihash_mtx);
 +			error = vget(vp, flags | LK_INTERLOCK, td);
 +			if (error == ENOENT)
 +				goto loop;
 +			if (error)
 +				return (error);
 +			*vpp = vp;
 +			return (0);
 +		}
 +	}
 +	mtx_unlock(&minix_ihash_mtx);
 +	return (0);
 +}
 +
 +/*
 + * Check hash for duplicate of passed inode, and add if there is no one.
 + * if there is a duplicate, vget() it and return to the caller.
 + */
 +int
 +minix_ihashins(ip, flags, ovpp)
 +	struct minix_inode *ip;
 +	int flags;
 +	struct vnode **ovpp;
 +{
 +	struct thread *td = curthread;		/* XXX */
 +	struct mnxihashhead *ipp;
 +	struct minix_inode *oip;
 +	struct vnode *ovp;
 +	int error;
 +
 +loop:
 +	mtx_lock(&minix_ihash_mtx);
 +	ipp = MNXINOHASH(ip->i_dev, ip->i_number);
 +	LIST_FOREACH(oip, ipp, i_hash) {
 +		if (ip->i_number == oip->i_number && ip->i_dev == oip->i_dev) {
 +			ovp = oip->i_vnode;
 +			mtx_lock(&ovp->v_interlock);
 +			mtx_unlock(&minix_ihash_mtx);
 +			error = vget(ovp, flags | LK_INTERLOCK, td);
 +			if (error == ENOENT)
 +				goto loop;
 +			if (error)
 +				return (error);
 +			*ovpp = ovp;
 +			return (0);
 +		}
 +	}
 +	LIST_INSERT_HEAD(ipp, ip, i_hash);
 +	ip->i_flag |= IN_HASHED;
 +	mtx_unlock(&minix_ihash_mtx);
 +	*ovpp = NULL;
 +	return (0);
 +}
 +
 +/*
 + * Remove the inode from the hash table.
 + */
 +void
 +minix_ihashrem(ip)
 +	struct minix_inode *ip;
 +{
 +	mtx_lock(&minix_ihash_mtx);
 +	if (ip->i_flag & IN_HASHED) {
 +		ip->i_flag &= ~IN_HASHED;
 +		LIST_REMOVE(ip, i_hash);
 +	}
 +	mtx_unlock(&minix_ihash_mtx);
 +}
 diff -ruN sys.orig/fs/minixfs/minix_inode.c sys/fs/minixfs/minix_inode.c
 --- sys.orig/fs/minixfs/minix_inode.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_inode.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,762 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/stat.h>
 +#include <sys/bio.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*,daddr_t,daddr_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 = VTOMI(dvp);
 +	*vpp = NULL;
 +	
 +	if ((mode & IFMT) == 0)
 +		mode |= IFREG;
 +
 +	if ((error = minix_valloc(dvp, mode, cnp->cn_cred, &tvp)) != 0)
 +		return error;
 +
 +	ip = VTOMI(tvp);
 +	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_cred(cnp->cn_cred, 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;
 +}
 +/*
 + * 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 thread  *td)
 +{
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	daddr_t lbn0, lbn1, len32;
 +	int error;
 +
 +	if (length > ~(1 << 31))   /* 32-bit offsets only! */
 +		return EFBIG;
 +
 +	len32 = (daddr_t)length;
 +
 +	/* 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 <= len32) {
 +	     ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	     return minix_update(vp);
 +	}
 +
 +	/* lbn1 is the last zone in the file */
 +
 +	lbn1 = (dip->i_size - 1)/sp->s_zsize;
 +
 +	/* lbn0 is the first zone to eliminate */
 +	
 +	if (len32 > 0)
 +		lbn0 = (len32 - 1)/sp->s_zsize + 1;
 +	else
 +		lbn0 = 0;    /* Eliminate all zones */
 +
 +	if (lbn0 > lbn1)     /* Silly but check anyway */
 +		return EIO;
 +
 +	/* truncatefs will eliminate all zones from lbn0 to EOF */
 +
 +	if ((error = minix_truncatefs(ip, lbn0, len32)) != 0)
 +		return error;
 +	
 +	dip->i_size  = len32;               /* The new length of the file */
 +	ip->i_blocks = minix_num_blocks(ip->i_mode, len32, sp->s_zshift);
 +	
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	
 +	return minix_update(vp);
 +}
 +/*
 + * Frees all zones from the logical offset lbn to the end of the file.
 + */
 +static int
 +minix_truncatefs(struct minix_inode *ip, daddr_t lbn, daddr_t length)
 +{
 +	struct buf *bp;
 +	union minix_block *mbp;
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	daddr_t lb0, lb, off = 0;
 +	daddr_t blk, id0, id1, id2, id3, zone;
 +	int error, indirect;
 +	int delete1, delete2, delete3;
 +
 +	if (dip->i_size == 0)
 +		return 0;
 +
 +	lb = lb0 = (dip->i_size - 1)/sp->s_zsize;
 +	
 +	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_dzalloc(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;
 +				}
 +			}
 +			blk = dip->i_block[V2_NR_DBLOCKS+2] << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return 0;
 +			mbp = MBLOCK(bp);
 +			id0 = mbp->ind[id3];
 +			if (delete3) {
 +				zone = dip->i_block[V2_NR_DBLOCKS+2];
 +				dip->i_block[V2_NR_DBLOCKS+2] = 0;
 +				if ((error = minix_dzalloc(sp, zone)) != 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:
 +			blk = id0 << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return error;
 +			if (delete2) {
 +				if ((error = minix_dzalloc(sp,id0)) != 0)
 +					return error;
 +			}
 +			mbp = MBLOCK(bp);
 +			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:
 +			blk = id0 << sp->s_zshift;
 +			if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			if ((error = minix_dzalloc(sp, mbp->ind[id1])) != 0)
 +				return error;
 +			if (delete1) {
 +				if ((error = minix_dzalloc(sp, id0)) != 0)
 +					return error;
 +				brelse(bp);
 +			} else {
 +				mbp->ind[id1] = 0;
 +				bwrite(bp);
 +			}
 +			break;
 +		default:
 +			return EIO;
 +		}
 +
 +	} while (lbn < lb--);
 +	
 +	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 = VTOMI(vp);
 +
 +	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;
 +
 +	return minix_iput(ip);
 +}
 +
 +void
 +minix_itimes(struct vnode *vp)
 +{
 +     	struct minix_inode *ip = VTOMI(vp);
 +	struct timespec ts;
 +	
 +	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);
 +}
 +/*
 + * The following access routine was lifted from the UFS code.
 + */
 +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);	
 +}
 +
 +/* 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 = VTOMI(vp);
 +
 +        return minix_dialloc(ip->i_su, 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;
 +     daddr_t off;
 +
 +     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 = MBLOCK(bp);
 +     off = (ino-1) % V2_INODES_PER_BLOCK;
 +     ip->i_dino = mbp->dinode[off];
 +     
 +     bqrelse(bp);
 +
 +     ip->i_number = ino;
 +     ip->i_parent = 0;
 +     ip->i_su = sp;
 +     ip->i_vnode = NULL;
 +     ip->i_mode = ip->i_dino.i_mode;
 +     ip->i_mmp = NULL;
 +     ip->i_dev = devvp->v_rdev;
 +     ip->i_flag = 0;
 +     
 +     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;
 +	daddr_t off;
 +	int error;
 +
 +	devvp = ip->i_su->s_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 = MBLOCK(bp);
 +	off = (ino-1) % V2_INODES_PER_BLOCK;
 +	mbp->dinode[off] = 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_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)) == NO_INODE) {
 +		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 = MBLOCK(bp);
 +	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_devvp;
 +	struct buf *bp;
 +	union minix_block *mbp;
 +
 +	minix_get_lock(&sp->imap_lock);
 +
 +	if (minix_read_ibit(sp, ino))
 +		minix_delete_ibit(sp, ino);
 +	else {
 +		minix_free_lock(&sp->imap_lock);
 +		return 0;
 +	}
 +
 +	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 = MBLOCK(bp);
 +	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);
 +     
 +     if (((sp->s_ninodes+1) % 16) > 0)
 +	  n += 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 */
 +}
 +/*
 + * Return the next free inode (pretty simple)
 + */
 +int
 +minix_next_free_inode(struct minix_super_block *sp)
 +{
 +	int i, n, s;
 +	u_int16_t *buf = sp->s_ibmap;
 +	register int bit;
 +
 +	n = (sp->s_ninodes+1) >> 4;
 +	if (((sp->s_ninodes+1) % 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)) {
 +					bit = s + (i << 4);
 +					return((bit <= sp->s_ninodes) ? bit : NO_INODE);
 +				}
 +	return NO_INODE;
 +}
 +/*
 + * 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 sys.orig/fs/minixfs/minix_locks.c sys/fs/minixfs/minix_locks.c
 --- sys.orig/fs/minixfs/minix_locks.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_locks.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,51 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/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 sys.orig/fs/minixfs/minix_lookup.c sys/fs/minixfs/minix_lookup.c
 --- sys.orig/fs/minixfs/minix_lookup.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_lookup.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,1280 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/bio.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 thread *a_td;
 +};
 +
 +int minix_lookup(struct vop_cachedlookup_args*);
 +
 +static int minix_namei(struct minix_namei_args*,char*,ino_t*,ino_t*,int*);
 +static int minix_newdirentry(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 int minix_dircleanup(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, dirfull;
 +    u_long flags, islastcn, lockparent, nameiop, wantparent;
 +    struct thread *td;
 +    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);
 +
 +    td   = cnp->cn_thread;
 +    cred = cnp->cn_cred;
 +    dip  = VTOMI(dvp);
 +    mmp  = dip->i_mmp;
 +    mp   = mmp->mnx_mp;
 +    msp  = mmp->mnx_su;
 +
 +    root_inode    = (ino_t)ROOT_INO;
 +    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, td)) != 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_td    = td;
 +
 +    /* 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 == -1) { /* This is a hack, but it gets the job done */
 +	 dirfull = 1;  /* error = -1 only occurs if we found no free slots */
 +	 error = ENOENT;
 +    } else
 +	 dirfull = 0;
 +
 +    if (error != 0) {
 +
 +	 dip->i_entry = -1;
 +	 dip->i_noent = -1;
 +
 +	 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, td)) != 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.
 +	     */
 +	      if (dirfull) { /* Ran off the end of the directory */
 +		      dip->i_entry = entry;
 +		      dip->i_noent = -1;
 +	      } else {        /* Found an empty entry */
 +		      dip->i_entry = -1;
 +		      dip->i_noent = 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, td);
 +
 +	      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, td)) != 0)
 +			return error;
 +		
 +		dip->i_entry = entry;
 +		dip->i_noent = -1;
 +
 +		if (mino == dip->i_number || isdot) {
 +			VREF(dvp);
 +			*vpp = dvp;
 +			return 0;
 +		}
 +
 +		if ((error = VFS_VGET(dvp->v_mount, mino,
 +			              LK_EXCLUSIVE, &vp)) != 0)
 +			return error;
 +
 +		ip = VTOMI(vp);
 +		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, td);
 +
 +		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, td);
 +	    if (error)
 +		return (error);
 +
 +	    dip->i_entry = entry;
 +	    dip->i_noent = -1;
 +
 +	    /*
 +	     * Check for "."
 +	     */
 +	    if (mino == dip->i_number || isdot)
 +		return EISDIR;
 +
 +	    error = VFS_VGET(dvp->v_mount, mino, LK_EXCLUSIVE, &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, td);
 +
 +	    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, td);	/* race to get the inode */
 +	    error = VFS_VGET(dvp->v_mount, mino, LK_EXCLUSIVE, &vp);
 +	    if (error) {
 +		vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
 +		return (error);
 +	    }
 +	    if (lockparent && islastcn) {
 +		error = vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
 +		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, LK_EXCLUSIVE, &vp);
 +	    if (error)
 +		return (error);
 +	    if (!lockparent || !islastcn)
 +		VOP_UNLOCK(dvp, 0, td);
 +	    *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 along with its entry number. If     *
 + *   there is no path then zero entry is returned along with -1 for    *
 + *   the entry number and EIO is returned as an error. If the path     *
 + *   leads to no inode then zero inode is returned, the lowest empty   *
 + *   entry number is returned and ENOENT is returned as an error.      *
 + *   Finally, if the path leads to no inode and no empty entry is      *
 + *   found, then no inode is returned and the error is set to -1.      *
 + *                                     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, ib, id, j, error, len, entry = 0, noent = 0;
 +	char ppath[256], *pname;
 +	struct minix_direct dir[NR_DIR_ENTRIES];
 +	unsigned long ind1[V2_INDIRECTS], blk;
 +	ino_t inum, inuml;
 +
 +	*ino = 0;
 +	*pino = current_inode;
 +	*entp = -1;
 +
 +	if ((len = strlen(path)) > 255)
 +		return ENAMETOOLONG;
 +
 +	if (path == NULL || len == 0)
 +		return EIO;
 +
 +	bzero(ppath, 256);
 +     
 +	if (path[0] == '/')
 +		inum = root_inode;
 +	else
 +		inum = current_inode;
 +
 +	strncpy(ppath, path, len+1);
 +
 +	/* 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;
 +     
 +	while(pname != NULL) {
 +	  
 +		if (!(ip->i_mode & IFDIR))
 +			return ENOTDIR;
 +
 +		if (minix_dinode_access(dip,VEXEC,cred,sp) != 0)
 +			return EACCES;
 +
 +		/* Search directory */
 +
 +		entry =  0;
 +		noent = -1;
 +		for (i=0; i<V2_NR_DBLOCKS; i++) {
 +			if (dip->i_block[i] == 0)
 +				goto notj;
 +			ib  = 1 << sp->s_zshift;
 +			blk = dip->i_block[i] << sp->s_zshift;
 +			while(ib--) {
 +				if((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +					return error;
 +				bcopy(bp->b_data, dir, BLOCK_SIZE);
 +				bqrelse(bp);
 +				for (j=0; j<NR_DIR_ENTRIES; j++) {
 +					if (dir[j].d_ino == 0) {
 +						if (noent < 0)
 +							noent = entry;
 +						entry++;
 +						continue;
 +					}
 +					if (!strncmp(pname, dir[j].d_name,MINIX_NAME_MAX))
 +						goto gotj;
 +					entry++;
 +				}
 +		        }
 +		}
 +	  
 +		if (dip->i_block[V2_NR_DBLOCKS] == 0)
 +			goto notj;
 +		
 +		blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +		if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +			return error;
 +		bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +		bqrelse(bp);
 +		for (id=0; id<V2_NR_INDIRECTS; id++) {
 +			if (ind1[id] == 0)
 +				goto notj;
 +			ib  = 1 << sp->s_zshift;
 +			blk = ind1[id] << sp->s_zshift;
 +			while(ib--) {
 +				if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +					return error;
 +				bcopy(bp->b_data, dir, BLOCK_SIZE);
 +				bqrelse(bp);
 +				for (j=0; j<NR_DIR_ENTRIES; j++) {
 +					if (dir[j].d_ino == 0) {
 +						if (noent < 0)
 +							noent = entry;
 +						entry++;
 +						continue;
 +					}
 +					if (!strncmp(pname, dir[j].d_name,MINIX_NAME_MAX))
 +						goto gotj;
 +					entry++;
 +				}
 +			}
 +		}
 +		/*
 +		 * Dropped through:
 +		 *      Could not find an entry.
 +		 */
 +	notj:
 +		*ino  = 0;
 +		*pino = inuml;
 +		if (noent < 0) {
 +			*entp  = entry;
 +			return (-1);
 +		}
 +		
 +		*entp = noent;
 +		
 +		return ENOENT;		
 +		
 +	gotj:
 +		/*
 +		 * Found an entry; get the inode and look for another token.
 +		 */
 +		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;
 +
 +		pname = minix_strtok((char*)NULL, "/");
 +	}
 +	
 +	/*
 +	 * Return the inode for the entry found, and related information.
 +	 */
 +	
 +	*ino  = inum;
 +	*pino = inuml;
 +	*entp = entry;
 +     
 +	return 0;
 +}
 +/*
 + * 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, ip->i_entry is positive if a
 + * new block is needed. Finally, ip->i_entry < 0 and ip->i_noent < 0
 + * if direnter was called without first calling namei, then we need
 + * a search of the directory with a call to newdirentry().
 + */
 +int
 +minix_direnter(dvp, tvp, dirp, cnp)
 +	struct vnode *dvp;
 +	struct vnode *tvp;
 +	struct minix_direct *dirp;
 +	struct componentname *cnp;
 +{
 +	struct ucred *cr;
 +	struct thread *td;
 +	int newentrysize, newent;
 +	u_int32_t bln, off, newsize;
 +	off_t offset;
 +	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_direct *ep;
 +	struct iovec aiov;
 +	struct uio auio;
 +	struct buf *bp;
 +	char *dirbuf;
 +	int error;
 +
 +	td  = curthread;
 +	cr = td->td_ucred;
 +
 +	newentrysize = DIR_ENTRY_SIZE;
 +
 +	if (ip->i_noent < 0 && ip->i_entry < 0)  /* No namei */
 +		if((error = minix_newdirentry(dvp)) != 0)
 +			return error;
 +	
 +	if (ip->i_noent < 0 && ip->i_entry >= 0) { /* No empty entries available */
 +		if (!(ip->i_entry < MAX_DIR_ENTRIES))
 +			return ENOSPC;
 +		bln = ip->i_entry / NR_DIR_ENTRIES;
 +		auio.uio_offset = bln*BLOCK_SIZE;
 +		auio.uio_resid  = newentrysize;
 +		aiov.iov_len    = newentrysize;
 +		aiov.iov_base   = (caddr_t)dirp;
 +		auio.uio_iov    = &aiov;
 +		auio.uio_iovcnt = 1;
 +		auio.uio_rw     = UIO_WRITE;
 +		auio.uio_segflg = UIO_SYSSPACE;
 +		auio.uio_td  = (struct thread*)0;
 +		error = VOP_WRITE(dvp, &auio, IO_SYNC, cnp->cn_cred);
 +		ip->i_flag |= IN_CHANGE;
 +		return error;
 +	}
 +
 +	if ((newent = ip->i_noent) < 0)
 +		panic("minix_direnter: newent < 0");
 +
 +	bln = newent / NR_DIR_ENTRIES;
 +	off = newent % NR_DIR_ENTRIES;
 +	offset = (off_t)(bln*BLOCK_SIZE + off*newentrysize);
 +
 +	if ((error = minix_blkatoff(dvp, offset, &dirbuf, &bp)) != 0)
 +		return error;
 +	ep = (struct minix_direct*)dirbuf;
 +	*ep = *dirp;
 +	bwrite(bp);
 +
 +	newsize = (u_int32_t)offset + newentrysize;
 +	if (newsize > dip->i_size)
 +	     dip->i_size = newsize;
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	return minix_update(dvp);
 +}
 +/*
 + * 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, because
 + * the available space is full, then the routine returns
 + * the next entry number and 0 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_newdirentry(struct vnode *vp)
 +{
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	struct minix_direct dir[NR_DIR_ENTRIES];
 +	unsigned long ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	int ib, id, iz, error, entry = 0;
 +
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0) {
 +			ip->i_noent = -1;
 +			ip->i_entry = entry;
 +			return 0;
 +		}
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		ib = 1 << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&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 = entry;
 +					ip->i_entry = -1;
 +					return 0;
 +				}
 +				entry++;
 +			}
 +		}
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0) {
 +	     ip->i_noent = -1;
 +	     ip->i_entry = entry;
 +	     return 0;
 +	}
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0) {
 +			ip->i_noent = -1;
 +			ip->i_entry = entry;
 +			return 0;
 +		}
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&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 = entry;
 +					ip->i_entry = -1;
 +					return 0;
 +				}
 +				entry++;
 +			}
 +		}
 +	}
 +	ip->i_noent = -1;
 +	ip->i_entry = -1;
 +	return ENOSPC;
 +}
 +
 +static int
 +minix_dircleanup(struct vnode *dvp)
 +{
 +	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	daddr_t bln;
 +	u_long newsize;
 +	int error;
 +
 +	if ((error = minix_dirsize(dvp, &newsize)) != 0)
 +		return error;
 +
 +	bln = minix_num_blocks(ip->i_mode, (u_int32_t)newsize, sp->s_zshift);
 +
 +	if (ip->i_blocks > bln) {
 +		if ((error = minix_truncate(dvp, (off_t)newsize, IO_SYNC, NOCRED, NULL)) != 0)
 +		     return error;
 +	}
 +	
 +	if (bln > ip->i_blocks)
 +		vnode_pager_setsize(dvp, (vm_ooffset_t)(bln*BLOCK_SIZE));
 +
 +	ip->i_blocks = bln;
 +	dip->i_size  = newsize;
 +
 +	return 0;
 +}
 +/*
 + * Removes an entry from a directory.
 + * NOTE: Will not remove . or .. but returns ENOENT
 + *       in that case.
 + */
 +int
 +minix_dirremove(struct vnode *dvp, struct componentname *cnp)
 +{
 +     struct buf *bp;
 +     struct minix_inode *dip = VTOMI(dvp);
 +     struct minix_dinode *dinp = &(dip->i_dino);
 +     union minix_block *mbp;
 +     struct minix_direct *dirp;
 +     struct minix_super_block *sp = dip->i_su;
 +     struct vnode *devvp = sp->s_devvp;
 +     daddr_t ind, bln, off, dsiz;
 +     daddr_t blks, blkc, zone, boff;
 +     u_long count, size;
 +     int entry, error;
 +
 +     /*
 +      * Entries 0 and 1 are '.' and '..' respectively.
 +      */
 +
 +     if ((entry = dip->i_entry) < 2)
 +	     return EIO;
 +     
 +     bln = entry / NR_DIR_ENTRIES;
 +     off = entry % NR_DIR_ENTRIES;
 +     zone = bln >> sp->s_zshift;
 +     boff = bln - (zone << sp->s_zshift);
 +
 +     if (zone < V2_NR_DBLOCKS) {
 +	  zone = dinp->i_block[zone];
 +	  bln  = (zone << sp->s_zshift) + boff;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  mbp->dir[off].d_ino = 0;
 +	  bwrite(bp);
 +     } else {
 +	  zone -= V2_NR_DBLOCKS;
 +	  bln = dinp->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  ind = mbp->ind[zone];
 +	  bqrelse(bp);
 +	  bln = (ind << sp->s_zshift) + boff;
 +	  if ((error = minix_getblk(devvp,bln,&bp)) != 0)
 +	       return error;
 +	  mbp = MBLOCK(bp);
 +	  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 && count > 2) {
 +	     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++) {
 +		     /* Setting entry < 0 and noent < 0 indicates: no namei() call. */
 +		     dip->i_entry = -1;
 +		     dip->i_noent = -1;
 +		     if ((error = minix_direnter(dvp, NULL, &(dirp[ind]), cnp)) != 0)
 +			     return error;
 +	     }
 +
 +	     free(dirp, M_MINIXNOD);
 +
 +	     cache_purge(dvp);
 +
 +	     return 0;
 +     }
 +     
 +     return minix_dircleanup(dvp);
 +}
 +/*
 + * 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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	unsigned long ind1[V2_INDIRECTS];
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +	u_int32_t blk;
 +	u_long count = 0;
 +
 +	*last = count;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while (ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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 = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +
 +	*count = 0;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			for (id=0; id<NR_DIR_ENTRIES; id++) {
 +				if (mbp->dir[id].d_ino > 0)
 +					(*count) += 1;
 +			}
 +			bqrelse(bp);
 +		}
 +	}
 +	return 0;
 +}
 +/*
 + * Move a directory from dvp to dirp given a count of the number of
 + * valid entries to move.
 + */
 +static int
 +minix_dirmove(struct vnode *dvp, struct minix_direct *dirp, unsigned long count)
 +{
 +     	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	unsigned long idc;
 +	union minix_block *mbp;
 +	int ib, id, iz, error;
 +
 +	if (count < 2)
 +	     return EIO;
 +
 +	idc = 0;
 +	for (iz=0; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = dip->i_block[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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) {
 +					     bqrelse(bp);
 +					     return 0;
 +					}
 +				}
 +			}
 +			bqrelse(bp);
 +		}
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0)
 +		return 0;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	bqrelse(bp);
 +	
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		ib = 1 << sp->s_zshift;
 +		blk = ind1[iz] << sp->s_zshift;
 +		while(ib--) {
 +			if ((error = minix_getblk(devvp,blk++,&bp)) != 0)
 +				return error;
 +			mbp = MBLOCK(bp);
 +			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) {
 +						bqrelse(bp);
 +						return 0;
 +					}
 +				}
 +			}
 +			bqrelse(bp);
 +		}
 +	}
 +	return 0;	
 +}
 +/*
 + * Clean everything out of a directory except '.' and '..'.
 + */
 +static int
 +minix_dirclean(struct vnode *dvp)
 +{
 +     	struct minix_inode *ip = VTOMI(dvp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	struct buf *bp;
 +	u_int32_t ind1[V2_INDIRECTS];
 +	u_int32_t blk;
 +	union minix_block *mbp;
 +	int ib, ib0, id, iz, error;
 +
 +	if (dip->i_block[0] == 0) /* This zone must always exist */
 +		return EIO;
 +
 +	ip->i_blocks = 1 << sp->s_zshift;
 +	dip->i_size = 2*DIR_ENTRY_SIZE;
 +
 +	blk = dip->i_block[0] << sp->s_zshift;
 +	ib  = ip->i_blocks;
 +	ib0 = ib - 1;
 +
 +	while(ib--) {
 +		if ((error = minix_getblk(devvp,blk++, &bp)) != 0)
 +			return error;
 +		mbp = MBLOCK(bp);
 +		for (id=(ib==ib0)?2:0; id<NR_DIR_ENTRIES; id++)
 +			mbp->dir[id].d_ino = 0;
 +		bwrite(bp);
 +	}
 +
 +	for (iz=1; iz<V2_NR_DBLOCKS; iz++) {
 +		if (dip->i_block[iz] == 0)
 +			return 0;
 +		if ((error = minix_dzalloc(sp, dip->i_block[iz])) != 0)
 +			return error;
 +		dip->i_block[iz] = 0;
 +	}
 +
 +	if (dip->i_block[V2_NR_DBLOCKS] == 0)
 +		return 0;
 +
 +	blk = dip->i_block[V2_NR_DBLOCKS] << sp->s_zshift;
 +	if ((error = minix_getblk(devvp,blk,&bp)) != 0)
 +		return error;
 +	bcopy(bp->b_data, ind1, BLOCK_SIZE);
 +	brelse(bp);
 +
 +	if ((error = minix_dzalloc(sp, dip->i_block[V2_NR_DBLOCKS])) != 0)
 +	     return 0;
 +	dip->i_block[V2_NR_DBLOCKS] = 0;
 +
 +	for (iz=0; iz<V2_NR_INDIRECTS; iz++) {
 +		if (ind1[iz] == 0)
 +			return 0;
 +		if ((error = minix_dzalloc(sp, ind1[iz])) != 0)
 +			return error;
 +	}
 +	return 0;	
 +}
 +/*
 + * Returns non-zero if the directory is empty, zero otherwise.
 + */
 +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<(off_t)dip->i_size; off+=mindirsiz) {
 +		error = vn_rdwr(UIO_READ,ip->i_vnode,(caddr_t)dp,
 +		    mindirsiz, off, UIO_SYSSPACE, IO_NODELOCKED,
 +		    cred, NOCRED, &count, (struct thread*)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, NOCRED, (int*)0, (struct thread*)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,
 +		                 LK_EXCLUSIVE, &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 off, error;
 +	off_t offset;
 +	daddr_t bln;
 +
 +	bln = entry / NR_DIR_ENTRIES;
 +	off = entry % NR_DIR_ENTRIES;
 +
 +	offset = (off_t)(bln*BLOCK_SIZE);
 +
 +	if ((error = minix_blkatoff(dp->i_vnode, offset, NULL, &bp)) != 0)
 +	     return error;
 +
 +	mbp = MBLOCK(bp);
 +	mbp->dir[off].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 sys.orig/fs/minixfs/minix_subr.c sys/fs/minixfs/minix_subr.c
 --- sys.orig/fs/minixfs/minix_subr.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_subr.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,263 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/bio.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 thread*);
 +int minix_chmod(struct vnode*,int,struct ucred*,struct thread*);
 +
 +/*
 + * 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 minix_inode *pip = VTOMI(pvp);
 +	struct minix_super_block *sp = pip->i_su;
 +	struct mount *mp = pip->i_mmp->mnx_mp;
 +	struct minix_inode *ip;
 +	struct minix_dinode *dip;
 +	int ino, error;
 +	
 +	if ((error = minix_ialloc(sp, &ino)) != 0)
 +		return error;
 +
 +	if ((error = minix_vget(mp, ino, LK_EXCLUSIVE, vpp)) != 0) {
 +		(void)minix_dialloc(sp, ino);
 +		return error;
 +	}
 +
 +	ip = VTOMI(*vpp);
 +	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);
 +	
 +	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 = VTOMI(vp);
 +	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_vflag |= VV_ROOT;
 +
 +	*vpp = vp;
 +	
 +	return 0;
 +}
 +/*
 + * 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 thread *td)
 +{
 +	struct minix_inode *ip = VTOMI(vp);
 +	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_cred(cred, 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 thread *td)
 +{
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	int error;
 +
 +     	if (cred->cr_uid != dip->i_uid) {
 +		error = suser_cred(cred, 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;
 +	     */
 +	     break;
 +	}
 +	return VBAD;
 +}
 diff -ruN sys.orig/fs/minixfs/minix_ufs.c sys/fs/minixfs/minix_ufs.c
 --- sys.orig/fs/minixfs/minix_ufs.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_ufs.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,60 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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 sys.orig/fs/minixfs/minix_vfsops.c sys/fs/minixfs/minix_vfsops.c
 --- sys.orig/fs/minixfs/minix_vfsops.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_vfsops.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,666 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * All rights reserved.
 + *
 + * Redistribution and use in surce 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 modelel 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/bio.h>
 +#include <sys/buf.h>
 +
 +#include <fs/minixfs/minix.h>
 +
 +static int minix_mount(struct mount*, char*, caddr_t,
 +    struct nameidata*, struct thread*);
 +static int minix_unmount(struct mount*, int, struct thread*);
 +static int minix_root(struct mount*, struct vnode**);
 +static int minix_statfs(struct mount*, struct statfs*, struct thread*);
 +static int minix_sync(struct mount*, int, struct ucred*, struct thread*);
 +static int minix_start(struct mount*, int, struct thread*);
 +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 thread *td)
 +{
 +	struct minix_args args;
 +	struct minixmount *mmp = NULL;
 +	struct vnode *devvp;
 +	struct buf *bp;
 +	struct minix_super_block *es;
 +	struct ucred *cred;
 +	int error = 0, ronly = 0, size;
 +	daddr_t iblkn, zblkn;
 +	long isize, zsize;
 +	mode_t accessmode;
 +
 +	cred = td->td_ucred;
 +
 +	if (cred->cr_uid != 0)
 +		return EACCES;
 +
 +	if (mp->mnt_flag & MNT_UPDATE)
 +		return EOPNOTSUPP;
 +
 +	/* no asynchronous updates and no soft updates */
 +
 +	mp->mnt_flag &= ~(MNT_ASYNC | MNT_SOFTDEP);
 +	
 +	/***  FORCE READ-ONLY FOR DEBUGGING  ***/
 +/*
 +	mp->mnt_flag |= MNT_RDONLY;
 +*/
 +	/***  FORCE NODEV for safety's sake ***/
 +
 +	mp->mnt_flag |= MNT_NODEV;
 +	
 +	error = copyin(data, (caddr_t)&args, sizeof(struct minix_args));
 +	if (error)
 +		return error;
 +
 +	NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, td);
 +	ndp->ni_vp = NULL;
 +	if ((error = namei(ndp)) != 0)
 +		return error;	
 +	NDFREE(ndp, NDF_ONLY_PNBUF);
 +
 +	if ((devvp = ndp->ni_vp) == NULL) {
 +		printf("devvp is NULL!\n");
 +		return ENXIO;
 +	}
 +	
 +	if (!vn_isdisk(devvp, &error)) {
 +		printf("vn_isdisk error = %d\n",error);
 +		goto out;
 +	}
 +
 +	/*
 +	 * 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, td);
 +	error = vinvalbuf(devvp, V_SAVE, cred, td, 0, 0);
 +	VOP_UNLOCK(devvp, 0, td);
 +
 +	if (error) {
 +		printf("vinvalbuf error\n");
 +		goto out;
 +	}
 +
 +	/*
 +	 * If mount by non-root, then verify that the 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, td);
 +		if ((error = VOP_ACCESS(devvp, accessmode, cred, td)) != 0) {
 +			vput(devvp);
 +			return (error);
 +		}
 +		VOP_UNLOCK(devvp, 0, td);
 +	}
 +
 +	/*
 +	 * Only VMIO the backing device if the backing device is a real
 +	 * block device.
 +	 */
 +	if (vn_isdisk(devvp, NULL)) {
 +		vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
 +		vfs_object_create(devvp, td, cred);
 +		/* XXX Why lock only to release immediately?? */
 +		/* XXX I'm doing it because ufs does it. */
 +		mtx_lock(&devvp->v_interlock);
 +		VOP_UNLOCK(devvp, LK_INTERLOCK, td);
 +	}
 +	
 +	/*********************************/
 +
 +	ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
 +	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
 +	error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, td);
 +	VOP_UNLOCK(devvp, 0, td);
 +	
 +	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;
 +
 +	/* The superblock gets stored in 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;
 +
 +	/* Reset es to point to the stored super block */
 +
 +	es = mmp->mnx_su;
 +
 +	/* Fill the in-core super with stuff */
 +	
 +	es->s_firstinode = 2 + es->s_imap_blocks + es->s_zmap_blocks;
 +	es->s_zoff = es->s_firstdatazone - 1;
 +	es->s_version = 2;
 +	es->s_dev = devvp->v_rdev;
 +	es->s_devvp = devvp;
 +	es->s_rdonly = ronly;
 +	es->s_bsize = BLOCK_SIZE;
 +	es->s_zsize = BLOCK_SIZE << es->s_zshift;
 +	es->s_imnton = ufs_parent_ino(mp);
 +	es->s_max_size = ((es->s_zones - es->s_zoff) << es->s_zshift)*BLOCK_SIZE;
 +
 +	isize = blk_to_byte(es->s_imap_blocks);
 +	zsize = blk_to_byte(es->s_zmap_blocks);
 +	iblkn = byte_to_blkn(blk_to_byte(2));
 +	zblkn = byte_to_blkn(blk_to_byte(es->s_imap_blocks + 2));
 +
 +	/* Load the bitmaps into the in-core super */
 +
 +	es->s_ibmap = (u_int16_t*)malloc(isize, M_MINIXMNT, M_WAITOK);
 +	if (es->s_ibmap == NULL) {
 +		error = ENOMEM;
 +		goto out;
 +	}
 +	es->s_zbmap = (u_int16_t*)malloc(zsize, M_MINIXMNT, M_WAITOK);
 +	if (es->s_zbmap == NULL) {
 +		error = ENOMEM;
 +		free(es->s_ibmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bzero(es->s_ibmap, isize);
 +	bzero(es->s_zbmap, zsize);
 +	es->imap_lock = 0l;
 +	es->zmap_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_zbmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bcopy((caddr_t)bp->b_data, es->s_ibmap, isize);
 +	brelse(bp);
 +	bp = NULL;
 +
 +	if ((error = bread(devvp, zblkn, zsize, NOCRED, &bp)) != 0) {
 +		printf("bread error reading block bitmap\n");
 +		free(es->s_ibmap, M_MINIXMNT);
 +		free(es->s_zbmap, M_MINIXMNT);
 +		goto out;
 +	}
 +	bcopy((caddr_t)bp->b_data, es->s_zbmap, zsize);
 +	brelse(bp);
 +	bp = NULL;
 +
 +	/* Finish filling mmp */
 +
 +	mmp->mnx_devvp = devvp;
 +	mmp->mnx_dev   = devvp->v_rdev;
 +	mmp->mnx_mp    = mp;
 +       	mmp->mnx_malloctype = M_MINIXNOD;
 +
 +	/* Complete the mount */
 +
 +	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_rdev->si_mountpoint = mp;
 +
 +	/**************/
 +
 +	/* Mounted "on" info is now done in vfs_mount() */
 +
 +	/* 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 = cred->cr_uid;
 +	mp->mnt_stat.f_flags = mp->mnt_flag;
 +
 +	minix_statfs(mp, &mp->mnt_stat, td);
 +
 +	return 0;
 +
 +out:
 +	devvp->v_rdev->si_mountpoint = NULL;
 +	if (bp)
 +		brelse(bp);
 +	(void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, td);
 +	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 thread *td)
 +{
 +	struct minixmount *mmp = (struct minixmount*)(mp->mnt_data);
 +	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;
 +	
 +	devvp = mmp->mnx_devvp;
 +
 +	/* Sync up metadata */
 +
 +	vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY, td);
 +	error = VOP_FSYNC(devvp, td->td_ucred, MNT_WAIT, td);
 +	VOP_UNLOCK(devvp, 0, td);
 +
 +	devvp->v_rdev->si_mountpoint = NULL;
 +	vinvalbuf(devvp, V_SAVE, NOCRED, td, 0, 0);
 +	ronly =(mp->mnt_flag & MNT_RDONLY) != 0;
 +	error = VOP_CLOSE(devvp,ronly ? FREAD : FREAD|FWRITE,NOCRED,td);
 +	vrele(devvp);
 +
 +	free(mmp->mnx_su->s_ibmap, M_MINIXMNT);
 +	free(mmp->mnx_su->s_zbmap, 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, LK_EXCLUSIVE, vpp);
 +}
 +
 +static int
 +minix_statfs(struct mount *mp, struct statfs *sbp, struct thread *td)
 +{
 +	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_zoff) << sp->s_zshift;
 +	sbp->f_bfree  = (minix_free_zone_count(sp) << sp->s_zshift);
 +	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, int flags, struct vnode **vpp)
 +{
 +	struct minix_super_block *su;
 +	struct minix_inode *ip;
 +	struct minixmount *mmp;
 +	struct thread *td = curthread;
 +	struct vnode *vp;
 +	dev_t dev;
 +	int error;
 +
 +	*vpp = NULL;
 +	mmp  = (struct minixmount*)(mp->mnt_data);
 +	dev  = mmp->mnx_dev;
 +	su   = mmp->mnx_su;
 +
 +	/* The following was pulled from ffs_vget() */
 +
 +	/*
 +	 * We do not lock vnode creation as it is believed to be too
 +	 * expensive for such rare case as simultaneous creation of vnode
 +	 * for same ino by different processes. We just allow them to race
 +	 * and check later to decide who wins. Let the race begin!
 +	 */
 +	if ((error = minix_ihashget(dev, ino, flags, vpp)) != 0)
 +		return (error);
 +	if (*vpp != NULL)
 +		return (0);
 +	
 +
 +	/* 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);
 +	
 +	if ((error = minix_iget(mmp->mnx_devvp, su, (mino_t)ino, ip)) != 0) {
 +		FREE(ip, M_MINIXNOD);
 +		*vpp = NULL;
 +		return error;
 +	}
 +
 +	/* Allocate a new vnode */
 +	
 +	error = getnewvnode("minixfs", mp, minix_vnodeop_p, &vp);
 +     	if (error) {
 +		*vpp = NULL;
 +		FREE(ip, M_MINIXNOD);
 +		return (error);
 +	}
 +
 +	vp->v_data = ip;     /* connect vnode to inode */
 +
 +	/*
 +	 * Try supporting recursve locking as in ufs
 +	 */
 +	vp->v_vnlock->lk_flags |= LK_CANRECURSE;
 +
 +	/* Load up the inode with info */
 +	
 +	ip->i_vnode = vp;
 +	ip->i_dev = dev;       /* Specinfo pointer */
 +	ip->i_su  = su;
 +	ip->i_number = ino;
 +	ip->i_parent = 0;      /* Don't know this yet */
 +	ip->i_mmp    = mmp;
 +	ip->i_flag   = 0;
 +	ip->i_mode = ip->i_dino.i_mode;
 +	ip->i_blocks = minix_num_blocks(ip->i_mode, ip->i_dino.i_size, su->s_zshift);
 +	ip->i_count = 1;
 +	ip->i_entry = 0;
 +	ip->i_noent = 0;
 +
 +	/*
 +	 * Exclusively lock the vnode before adding to hash. Note, that we
 +	 * must not release nor downgrade the lock (despite flags argument
 +	 * says) till it is fully initialized.
 +	 */
 +
 +	lockmgr(vp->v_vnlock, LK_EXCLUSIVE, (struct mtx *)0, td);
 +
 +	/*
 +	 * Atomicaly (in terms of minix_hash operations) check the hash for
 +	 * duplicate of vnode being created and add it to the hash. If a
 +	 * duplicate vnode was found, it will be vget()ed from hash for us.
 +	 */
 +	if ((error = minix_ihashins(ip, flags, vpp)) != 0) {
 +		vput(vp);
 +		*vpp = NULL;
 +		return (error);
 +	}
 +
 +	/* We lost the race, then throw away our vnode and return existing */
 +	if (*vpp != NULL) {
 +		vput(vp);
 +		return (0);
 +	}
 +
 +	/* Put more good stuff into the vnode */
 +
 +	vp->v_type = minix_get_vtype(ip);
 +	
 +	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(su->s_devvp);
 +  
 +	*vpp = vp;
 +     
 +	return 0;
 +}
 +
 +static int
 +minix_sync(struct mount *mp, int waitfor, struct ucred *cred, struct thread *td)
 +{
 +	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.
 +	 */
 +	mtx_lock(&mntvnode_mtx);
 +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) {
 +			mtx_unlock(&mntvnode_mtx);
 +			error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT, td);
 +			if (error) {
 +				mtx_lock(&mntvnode_mtx);
 +				if (error == ENOENT)
 +					goto loop;
 +			} else {
 +				if ((error = VOP_FSYNC(vp, cred, waitfor, td)) != 0)
 +				     allerror = error;
 +				VOP_UNLOCK(vp, 0, td);
 +				vrele(vp);
 +				mtx_lock(&mntvnode_mtx);
 +			}
 +			
 +		} 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);
 +			mtx_unlock(&mntvnode_mtx);
 +                        minix_update(vp);
 +			vrele(vp);
 +			mtx_lock(&mntvnode_mtx);
 +		}
 +		if (TAILQ_NEXT(vp, v_nmntvnodes) != nvp)
 +			goto loop;
 +	}
 +	mtx_unlock(&mntvnode_mtx);
 +	/*
 +	 * 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, td);
 +		if ((error = VOP_FSYNC(mmp->mnx_devvp, cred, waitfor, td)) != 0)
 +			allerror = error;
 +		VOP_UNLOCK(mmp->mnx_devvp, 0, td);
 +	}
 +	return 0;
 +}
 +
 +static int
 +minix_start(struct mount *mp, int flags, struct thread *td)
 +{
 +	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_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 sys.orig/fs/minixfs/minix_vnops.c sys/fs/minixfs/minix_vnops.c
 --- sys.orig/fs/minixfs/minix_vnops.c	Wed Dec 31 16:00:00 1969
 +++ sys/fs/minixfs/minix_vnops.c	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,1870 @@
 +/*-
 + * Copyright (c) 2003 Ed Alley: wea@llnl.gov
 + * 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/bio.h>
 +#include <sys/buf.h>
 +#include <sys/namei.h>
 +#include <sys/unistd.h>
 +
 +#include <machine/limits.h>
 +
 +#include <vm/vm.h>
 +#include <vm/vm_extern.h>
 +#include <vm/vm_object.h>
 +#include <vm/vm_page.h>
 +#include <vm/vm_pager.h>
 +#include <vm/vnode_pager.h>
 +
 +#include <fs/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 thread*);
 +int minix_chmod(struct vnode*,int,struct ucred*,struct thread*);
 +
 +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_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*);
 +
 +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_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.
 + */
 +
 +/*
 + * This routine has changed somewhat from 4.x to 5.x:
 + * There are now some calls to vn_write_suspend_wait().
 + */
 +static int
 +minix_inactive(struct vop_inactive_args *ap)
 +     /*
 +       struct vop_inactive_args {
 +          struct vnodeop_desc *a_desc;
 +	  struct vnode *a_vp;
 +	  struct thread  *a_td;
 +	  }
 +     */
 +{
 +	struct vnode *vp = ap->a_vp;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct thread *td = ap->a_td;
 +	int mode, error = 0;
 +
 +	if (ip->i_mode == 0)
 +		goto out;
 +
 +	if (ip->i_nlink <= 0) {
 +	        (void)vn_write_suspend_wait(vp, NULL, V_WAIT);
 +		error = minix_truncate(vp, (off_t)0, IO_SYNC, NOCRED, td);
 +		ip->i_dev = 0;
 +		mode = ip->i_mode;
 +		ip->i_mode = 0;
 +		ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +		minix_vfree(vp, ip->i_number, mode);
 +	}
 +	if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
 +	       if ((ip->i_flag & (IN_CHANGE | IN_UPDATE | IN_MODIFIED)) == 0 &&
 +		    vn_write_suspend_wait(vp, NULL, V_NOWAIT)) {
 +		        ip->i_flag &= ~IN_ACCESS;
 +	       } else {
 +		    (void)vn_write_suspend_wait(vp, NULL, V_WAIT);
 +		    (void)minix_update(vp);
 +	       }
 +	}
 +out:
 +	VOP_UNLOCK(vp, 0, td);
 +	/*
 +	 * If we are done with the inode, reclaim it
 +	 * so that it can be reused immediately.
 +	 */
 +	if (ip->i_mode == 0)
 +		vrecycle(vp, NULL, td);
 +	
 +	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 vop_reclaim_args {
 +             struct vnodeop_desc *a_desc;
 +	     struct vnode *a_vp;
 +	     struct thread *a_td;
 +	     };
 +     */
 +{
 +     struct vnode *vp = ap->a_vp;
 +     struct minix_inode *ip = VTOMI(vp);
 +     struct minix_super_block *sp = ip->i_su;
 +
 +     minix_ihashrem(ip);
 +     cache_purge(vp);
 +     vrele(sp->s_devvp);
 +
 +     FREE(ip, M_MINIXNOD); /* release inode space */
 +     vp->v_data = 0;
 +     
 +     return 0;
 +}
 +
 +/*
 + * Sync an open file
 + */
 +
 +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 thread *a_td;
 +	 };
 +    */
 +{
 +	struct vnode *vp = ap->a_vp;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct buf *bp, *nbp;
 +	int s, error, wait, passes, skipmeta;
 +	daddr_t lbn;
 +
 +	wait = (ap->a_waitfor == MNT_WAIT);
 +	if (vn_isdisk(vp, NULL)) {
 +		lbn = INT_MAX;
 +	} else {
 +	        if (dip->i_size > 0)
 +		     lbn = (dip->i_size-1) / BLOCK_SIZE + 1;
 +		else
 +		     lbn = 0;
 +	}
 +
 +	/*
 +	 * Flush all dirty buffers associated with a vnode.
 +	 */
 +	passes = NIADDR + 1;
 +	skipmeta = 0;
 +	if (wait)
 +		skipmeta = 1;
 +	s = splbio();
 +	VI_LOCK(vp);
 +loop:
 +	TAILQ_FOREACH(bp, &vp->v_dirtyblkhd, b_vnbufs)
 +		bp->b_flags &= ~B_SCANNED;
 +	for (bp = TAILQ_FIRST(&vp->v_dirtyblkhd); bp; bp = nbp) {
 +		nbp = TAILQ_NEXT(bp, b_vnbufs);
 +		/* 
 +		 * Reasons to skip this buffer: it has already been considered
 +		 * on this pass, this pass is the first time through on a
 +		 * synchronous flush request and the buffer being considered
 +		 * is metadata, the buffer has dependencies that will cause
 +		 * it to be redirtied and it has not already been deferred,
 +		 * or it is already being written.
 +		 */
 +		if ((bp->b_flags & B_SCANNED) != 0)
 +			continue;
 +		bp->b_flags |= B_SCANNED;
 +		if ((skipmeta == 1 && bp->b_lblkno < 0))
 +			continue;
 +		if (!wait && LIST_FIRST(&bp->b_dep) != NULL &&
 +		    (bp->b_flags & B_DEFERRED) == 0 &&
 +		    buf_countdeps(bp, 0)) {
 +			bp->b_flags |= B_DEFERRED;
 +			continue;
 +		}
 +		VI_UNLOCK(vp);
 +		if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT)) {
 +			VI_LOCK(vp);
 +			continue;
 +		}
 +		if ((bp->b_flags & B_DELWRI) == 0)
 +			panic("minix_fsync: not dirty");
 +		if (vp != bp->b_vp)
 +			panic("minix_fsync: vp != vp->b_vp");
 +		/*
 +		 * If this is a synchronous flush request, or it is not a
 +		 * file or device, start the write on this buffer immediatly.
 +		 */
 +		if (wait || (vp->v_type != VREG && vp->v_type != VBLK)) {
 +
 +			/*
 +			 * On our final pass through, do all I/O synchronously
 +			 * so that we can find out if our flush is failing
 +			 * because of write errors.
 +			 */
 +			if (passes > 0 || !wait) {
 +				if ((bp->b_flags & B_CLUSTEROK) && !wait) {
 +					BUF_UNLOCK(bp);
 +					(void) vfs_bio_awrite(bp);
 +				} else {
 +					bremfree(bp);
 +					splx(s);
 +					(void) bawrite(bp);
 +					s = splbio();
 +				}
 +			} else {
 +				bremfree(bp);
 +				splx(s);
 +				if ((error = bwrite(bp)) != 0)
 +					return (error);
 +				s = splbio();
 +			}
 +		} else if ((vp->v_type == VREG) && (bp->b_lblkno >= lbn)) {
 +			/* 
 +			 * If the buffer is for data that has been truncated
 +			 * off the file, then throw it away.
 +			 */
 +			bremfree(bp);
 +			bp->b_flags |= B_INVAL | B_NOCACHE;
 +			splx(s);
 +			brelse(bp);
 +			s = splbio();
 +		} else {
 +			BUF_UNLOCK(bp);
 +			vfs_bio_awrite(bp);
 +		}
 +		/*
 +		 * Since we may have slept during the I/O, we need 
 +		 * to start from a known point.
 +		 */
 +		VI_LOCK(vp);
 +		nbp = TAILQ_FIRST(&vp->v_dirtyblkhd);
 +	}
 +	/*
 +	 * If we were asked to do this synchronously, then go back for
 +	 * another pass, this time doing the metadata.
 +	 */
 +	if (skipmeta) {
 +		skipmeta = 0;
 +		goto loop;
 +	}
 +
 +	if (wait) {
 +		while (vp->v_numoutput) {
 +			vp->v_iflag |= VI_BWAIT;
 +			msleep((caddr_t)&vp->v_numoutput, VI_MTX(vp),
 +			    PRIBIO + 4, "ffsfsn", 0);
 +  		}
 +		VI_UNLOCK(vp);
 +
 +		/* 
 +		 * Ensure that any filesystem metatdata associated
 +		 * with the vnode has been written.
 +		 */
 +		splx(s);
 +		/* NOOP */
 +		s = splbio();
 +
 +		VI_LOCK(vp);
 +		if (!TAILQ_EMPTY(&vp->v_dirtyblkhd)) {
 +			/*
 +			 * Block devices associated with filesystems may
 +			 * have new I/O requests posted for them even if
 +			 * the vnode is locked, so no amount of trying will
 +			 * get them clean. Thus we give block devices a
 +			 * good effort, then just give up. For all other file
 +			 * types, go around and try again until it is clean.
 +			 */
 +			if (passes > 0) {
 +				passes -= 1;
 +				goto loop;
 +			}
 +#ifdef DIAGNOSTIC
 +			if (!vn_isdisk(vp, NULL))
 +				vprint("minix_fsync: dirty", vp);
 +#endif
 +		}
 +	}
 +	VI_UNLOCK(vp);
 +	splx(s);
 +
 +	ip->i_flag |= IN_UPDATE;
 +	return minix_update(vp);
 +}
 +
 +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 thread *a_td;
 +	  };
 +*/
 +{
 +	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  = VTOMI(vp);
 +	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 thread  *a_td;
 +	       }
 +     */
 +{
 +     struct vnode *vp = ap->a_vp;
 +     struct minix_inode *ip = VTOMI(vp);
 +     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 thread  *a_td;
 +	       }
 +     */
 +{
 +	struct vattr *vap = ap->a_vap;
 +	struct vnode *vp = ap->a_vp;
 +	struct minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct ucred *cred = ap->a_cred;
 +	struct thread *td = ap->a_td;
 +	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, td)) != 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, td)) != 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_cred(cred, PRISON_ROOT)) &&
 +		    ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
 +			(error = VOP_ACCESS(vp, VWRITE, cred, td))))
 +			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, td);
 +	}
 +
 +	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 = VTOMI(vp);
 +	struct minix_super_block *sp;
 +	struct mount *mp = vp->v_mount;
 +	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;
 +
 +	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 = VTOMI(vp);
 +		bzero((char*)ip->i_shortlink, MINIX_MAXSYMLINKLEN);
 +		bcopy(ap->a_target, (char*)ip->i_shortlink, len);
 +		ip->i_dino.i_size = len;
 +		ip->i_blocks = 0;
 +		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, NOCRED,
 +		    (int*)0, (struct thread*)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;
 +	daddr_t blkno;
 +	int error;
 +     
 +	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;
 +
 +	error = minix_bmapfs(ap->a_vp, (daddr_t)ap->a_bn,
 +	                       &blkno, ap->a_runp);
 +	*ap->a_bnp = (daddr_t)blkno;
 +
 +	return error;
 +}
 +static int
 +minix_open(struct vop_open_args *ap)
 +{
 +	return 0;
 +}
 +/*
 + * According to ufs we need to add an else to the
 + * if for the case where the usecount = 0 in going
 + * from release 4.x to 5.x
 + */
 +static int
 +minix_close(struct vop_close_args *ap)
 +{
 +	struct vnode *vp = ap->a_vp;
 +	struct mount *mp;
 +	
 +	VI_LOCK(vp);
 +	if (vp->v_usecount > 1) {
 +		minix_itimes(vp);
 +		VI_UNLOCK(vp);
 +	} else {
 +	        VI_UNLOCK(vp);
 +	     	/*
 +		 * If we are closing the last reference to an unlinked
 +		 * file, then it will be freed by the inactive routine.
 +		 * Because the freeing causes the filesystem to be
 +		 * modified, it must be held up during periods when the
 +		 * filesystem is suspended.
 +		 *
 +		 * XXX - EAGAIN is returned to prevent vn_close from
 +		 * repeating the vrele operation.
 +		 */
 +		if (vp->v_type == VREG && VTOMI(vp)->i_nlink == 0) {
 +			(void) vn_start_write(vp, &mp, V_WAIT);
 +			vrele(vp);
 +			vn_finished_write(mp);
 +			return EAGAIN;
 +		}
 +	}
 +	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 minix_inode *ip = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +
 +	struct buf *bp;
 +	off_t bytesinfile;
 +	daddr_t lbn, pbn;
 +	long size, xfersize, blkoffset;
 +	int error, orig_resid;
 +
 +	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 = VTOMI(vp);
 +	struct minix_dinode *dip = &(ip->i_dino);
 +	struct minix_super_block *sp = ip->i_su;
 +	struct vnode *devvp = sp->s_devvp;
 +	daddr_t lbn, pbn;
 +	off_t osize;
 +	long size, resid, blkoffset, xfersize = 0;
 +	int flags, error;
 +	
 +	osize = (off_t)dip->i_size;
 +	size = BLOCK_SIZE;
 +	resid = uio->uio_resid;
 +	
 +	if (ioflag & IO_SYNC)
 +		flags = IO_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;
 +
 +	     if (uio->uio_offset + xfersize > dip->i_size) {
 +		  vnode_pager_setsize(vp, (vm_ooffset_t)(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;
 +		  ip->i_blocks = minix_num_blocks(ip->i_mode, dip->i_size, sp->s_zshift);
 +	     }
 +
 +	     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_td);
 +		  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;
 +}
 +
 +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
 + * of the file, after a call to minix_namei().
 + */
 +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 = VTOMI(vp);
 +	struct componentname *cnp = ap->a_cnp;
 +	int error;
 +	
 +	if ((error = minix_dirremove(dvp,cnp)) == 0) {
 +	     ip->i_nlink--;
 +	     ip->i_flag |= IN_CHANGE;
 +	}
 +
 +	if (ip->i_nlink <= 0)
 +	     vp->v_vflag |= VV_NOSYNC;
 +
 +	if (error)
 +	     return error;
 +	
 +	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 thread *td = cnp->cn_thread;
 +	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, td)))
 +		return error;
 +
 +	ip = VTOMI(vp);
 +	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, td);
 +     
 +	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 = VTOMI(dvp);
 +	struct vnode *tvp;
 +	struct minix_inode *ip = NULL;
 +	struct minix_dinode *dip;
 +	struct minix_super_block *sp;
 +	struct buf *bp;
 +	struct minix_dirtemplate dirtemplate, *dtp;
 +	struct minix_direct newdir;
 +	int error, dmode;
 +	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;
 +	sp  = ip->i_su;
 +	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_balloc(tvp, (daddr_t)0, &bp)) != 0)
 +		goto bad;
 +	dip->i_size = sizeof(dirtemplate);
 +	ip->i_blocks = 1 << sp->s_zshift;
 +	ip->i_flag |= IN_CHANGE | IN_UPDATE;
 +	vnode_pager_setsize(tvp, (vm_ooffset_t)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 thread *td = cnp->cn_thread;
 +	struct minix_inode *ip, *dp;
 +	int error, ioflag = IO_SYNC;
 +
 +	ip = VTOMI(vp);
 +	dp = VTOMI(dvp);
 +
 +	/*
 +	 * 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,cnp)) != 0)
 +		goto out;
 +	dp->i_nlink--;
 +	dp->i_flag |= IN_CHANGE;
 +	cache_purge(dvp);
 +	VOP_UNLOCK(dvp, 0, td);
 +	(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, td);
 +	cache_purge(vp);
 +	vn_lock(dvp, LK_EXCLUSIVE | LK_RETRY, td);
 +out:
 +	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 = VTOMI(*vpp);
 +	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, LK_EXCLUSIVE,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;
 +	  };
 +    */
 +{
 +        /*
 +	 * fdvp is the old parent directory
 +	 * fvp  is the file to be renamed
 +	 * fcnp is the path info about the current file: fvp
 +	 * tdvp is the new parent directory
 +	 * tvp  is the new 'target' file name (if it exists)
 +	 * tcnp is the path info about the file's new name
 +	 */
 +     	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 thread *td = fcnp->cn_thread;
 +	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, td)) != 0)
 +		goto abortit;
 +
 +	dp = VTOMI(fdvp);   /* From directory */
 +	ip = VTOMI(fvp);    /* From inode */
 +
 +	if (ip->i_nlink >= MINIX_LINK_MAX) {
 +		VOP_UNLOCK(fvp, 0, td);
 +		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, td);
 +			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 = VTOMI(tdvp);   /* dp is now the target directory */
 +	xp = NULL;       
 +	if (tvp)
 +		xp = VTOMI(tvp); /* xp is now the target file name */
 +	
 +	/*
 +	 * 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, td);
 +		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_thread);
 +	VOP_UNLOCK(fvp, 0, td);
 +	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 = VTOMI(tdvp);
 +		xp = NULL;
 +		if (tvp)
 +			xp = VTOMI(tvp);
 +	}
 +
 +/*
 + * 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 (--xp->i_nlink != 0)
 +			     panic("minix_rename: linked directory");
 +		     if ((error = minix_truncate(tvp, (off_t)0, ioflag,
 +			tcnp->cn_cred, tcnp->cn_thread)) != 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); /* Relookup gets a new vnode/inode */
 +	if (error == 0)
 +		vrele(fdvp);
 +	if (fvp != NULL) {
 +		xp = VTOMI(fvp);
 +		dp = VTOMI(fdvp);
 +	} 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) { /* Need to compare inode numbers here */
 +		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);
 +		}
 +		if((error = minix_dirremove(fdvp,fcnp)) != 0)
 +		     printf("rename, dirremove: error = %d\n",error);
 +		if (!error) {
 +			cache_purge(fvp);
 +			ip->i_nlink--;
 +			ip->i_flag |=  IN_CHANGE;
 +			ip->i_flag &= ~IN_RENAME;
 +			(void)minix_update(fvp);
 +			dp->i_flag |= IN_CHANGE | IN_UPDATE;
 +			(void)minix_update(fdvp);
 +		}
 +	}
 +	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, td) == 0) {
 +	     ip->i_nlink--;
 +	     ip->i_flag |= IN_CHANGE;
 +	     ip->i_flag &= ~IN_RENAME;
 +	     (void)minix_update(fvp);
 +	     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;
 +
 +	VI_LOCK(vp);
 +	if (vp->v_usecount > 1)
 +		minix_itimes(vp);
 +	VI_UNLOCK(vp);
 +	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 = VTOMI(vp);
 +
 +	resid = uio->uio_resid;
 +	error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), ap);
 +	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 = VTOMI(vp);
 +
 +	resid = uio->uio_resid;
 +	error = VOCALL(fifo_vnodeop_p, VOFFSET(vop_write), ap);
 +	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 sys.orig/modules/Makefile sys/modules/Makefile
 --- sys.orig/modules/Makefile	Thu Dec 12 16:32:29 2002
 +++ sys/modules/Makefile	Mon Mar 17 10:14:28 2003
 @@ -174,6 +174,7 @@
  	linprocfs \
  	linux \
  	lnc \
 +	minixfs \
  	ncv \
  	netgraph \
  	nsp \
 diff -ruN sys.orig/modules/minixfs/Makefile sys/modules/minixfs/Makefile
 --- sys.orig/modules/minixfs/Makefile	Wed Dec 31 16:00:00 1969
 +++ sys/modules/minixfs/Makefile	Mon Mar 17 10:14:28 2003
 @@ -0,0 +1,11 @@
 +# $FreeBSD: src/sys/modules/minixfs/Makefile,v 1.0 2002/11/05 00:00:05 peter 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_ihash.c minix_alock.s 
 +
 +.include <bsd.kmod.mk>

From: des@ofug.org (Dag-Erling =?iso-8859-1?q?Sm=F8rgrav?=)
To: freebsd-gnats-submit@freebsd.org
Cc:  
Subject: Re: kern/47982: Minix fs offered for RELEASE 5.0
Date: Mon, 17 Mar 2003 22:44:55 +0100

 This is not very likely to float unless you can argue convincingly
 that it will have a sufficiently large user base to not simply rot in
 the tree.  Honestly, I think you'd have a very hard time of it; you'd
 be better off turning your code into a port.  Take a look at the
 audio/aureal-kmod port for an example of a port that installs a
 loadable module.
 
 Also,
 
  - Andrew Tanenbaum spells his name "Tanenbaum", not "Tannenbaum", no
    matter how dearly you might like to see him dressed up as a
    Christmas tree;
 
  - next time, you may want to post the patch on the web and just
    provide a URL in the PR rather than the entire patch.  30,000-line
    PRs aren't much fun.
 
 DES
 -- 
 Dag-Erling Smorgrav - des@ofug.org

From: Ed Alley <wea@llnl.gov>
To: FreeBSD-gnats-submit@freebsd.org
Cc: wea@llnl.gov
Subject: kern/47982: Response to Dag-Erling
Date: Mon, 17 Mar 2003 15:36:48 -0800 (PST)

 >Submitter-Id:	current-users
 >Originator:	Ed Alley
 >Organization:	LLNL
 >Confidential:	no
 >Synopsis:	kern/47982: Response to Dag-Erling
 >Severity:	non-critical
 >Priority:	low
 >Category:	kern
 >Class:		update
 >Release:	FreeBSD 5.0-RELEASE i386
 >Environment:   FreeBSD 4.6.2-RELEASE i386
 
 >Description:
 	Dag-Erling Smorgrav wrote:
 
 >you'd be better off turning your code into a port.
 
 That's a silly idea. Either the FS should fly as a kernel module
 as submitted or else forget it. A port exists in user-land NOT
 kernel-land. The VFS/VNODE interface changes from RELEASE to
 RELEASE. This would mean that the port would need to be upgraded
 for each release of FreeBSD.
 
 >Andrew Tanenbaum spells his name "Tanenbaum", not "Tannenbaum",
 >no matter how dearly you might like to see him dressed up as
 >a Christmas tree;
 
 I apologize to Andrew Tanenbaum for misspelling his name; I meant
 no disrespect to him and I will correct the error in the appropriate
 places. Thank-you for pointing that out to me; however, the remark
 that you made that I want to see him dressed up as a Christmas tree,
 seems a little desparate to me: Is that the extent of your criticism
 of this submittal?
 
 >next time, you may want to post the patch on the web and just provide
 >a URL in the PR rather than the entire patch. 30,000-line PRs aren't
 >much fun.
 
 Firstly, I posted the question in question@freebsd.org. It was suggested
 to me (by more than one response) that I should submit the diff as
 a PR.
 
 Secondly, It says in the handbook to submit large code as a PR.
 
 Thirdly, You, Dag-Erling Smorgrav, don't have to read the PR!
 
 			Ed
 			wea@llnl.gov
 
 
 
 
State-Changed-From-To: feedback->closed 
State-Changed-By: hmp 
State-Changed-When: Fri May 16 00:55:34 PDT 2003 
State-Changed-Why:  
There seems to be no interest in porting minixfs to 5.0. 
So close the PR for now.  If someone in the future has 
interest in this, they can reopen the PR.  Thanks. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=47982 
>Unformatted:
