summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Downs <downsj@cvs.openbsd.org>1997-05-30 05:18:52 +0000
committerJason Downs <downsj@cvs.openbsd.org>1997-05-30 05:18:52 +0000
commitd908a83a81adfc346b70e30ce4fc7853f62e0df6 (patch)
treedb8ae64ff36c9b327a35d028e87f85c91ea87565
parent033264ad4b94e60bb5c7599637dfd6ef0cfe6bd5 (diff)
Non-GPLd ext2fs code from Manuel, unmodified.
-rw-r--r--sys/ufs/ext2fs/ext2fs.h236
-rw-r--r--sys/ufs/ext2fs/ext2fs_alloc.c650
-rw-r--r--sys/ufs/ext2fs/ext2fs_balloc.c247
-rw-r--r--sys/ufs/ext2fs/ext2fs_bmap.c222
-rw-r--r--sys/ufs/ext2fs/ext2fs_dinode.h132
-rw-r--r--sys/ufs/ext2fs/ext2fs_dir.h114
-rw-r--r--sys/ufs/ext2fs/ext2fs_extern.h154
-rw-r--r--sys/ufs/ext2fs/ext2fs_inode.c528
-rw-r--r--sys/ufs/ext2fs/ext2fs_lookup.c1066
-rw-r--r--sys/ufs/ext2fs/ext2fs_readwrite.c298
-rw-r--r--sys/ufs/ext2fs/ext2fs_subr.c120
-rw-r--r--sys/ufs/ext2fs/ext2fs_vfsops.c996
-rw-r--r--sys/ufs/ext2fs/ext2fs_vnops.c1541
13 files changed, 6304 insertions, 0 deletions
diff --git a/sys/ufs/ext2fs/ext2fs.h b/sys/ufs/ext2fs/ext2fs.h
new file mode 100644
index 00000000000..136267f4df3
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs.h
@@ -0,0 +1,236 @@
+/* $NetBSD: fs.h,v 1.6 1995/04/12 21:21:02 mycroft Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * 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.
+ *
+ * @(#)fs.h 8.10 (Berkeley) 10/27/94
+ */
+
+/*
+ * Each disk drive contains some number of file systems.
+ * A file system consists of a number of cylinder groups.
+ * Each cylinder group has inodes and data.
+ *
+ * A file system is described by its super-block, which in turn
+ * describes the cylinder groups. The super-block is critical
+ * data and is replicated in each cylinder group to protect against
+ * catastrophic loss. This is done at `newfs' time and the critical
+ * super-block data does not change, so the copies need not be
+ * referenced further unless disaster strikes.
+ *
+ * The first boot and super blocks are given in absolute disk addresses.
+ * The byte-offset forms are preferred, as they don't imply a sector size.
+ */
+#define BBSIZE 1024
+#define SBSIZE 1024
+#define BBOFF ((off_t)(0))
+#define SBOFF ((off_t)(BBOFF + BBSIZE))
+#define BBLOCK ((daddr_t)(0))
+#define SBLOCK ((daddr_t)(BBLOCK + BBSIZE / DEV_BSIZE))
+
+/*
+ * Addresses stored in inodes are capable of addressing blocks
+ * XXX
+ */
+
+/*
+ * MINBSIZE is the smallest allowable block size.
+ * MINBSIZE must be big enough to hold a cylinder group block,
+ * thus changes to (struct cg) must keep its size within MINBSIZE.
+ * Note that super blocks are always of size SBSIZE,
+ * and that both SBSIZE and MAXBSIZE must be >= MINBSIZE.
+ */
+#define LOG_MINBSIZE 10
+#define MINBSIZE (1 << LOG_MINBSIZE)
+
+/*
+ * The path name on which the file system is mounted is maintained
+ * in fs_fsmnt. MAXMNTLEN defines the amount of space allocated in
+ * the super block for this name.
+ */
+#define MAXMNTLEN 512
+
+/*
+ * MINFREE gives the minimum acceptable percentage of file system
+ * blocks which may be free. If the freelist drops below this level
+ * only the superuser may continue to allocate blocks. This may
+ * be set to 0 if no reserve of free blocks is deemed necessary,
+ * however throughput drops by fifty percent if the file system
+ * is run at between 95% and 100% full; thus the minimum default
+ * value of fs_minfree is 5%. However, to get good clustering
+ * performance, 10% is a better choice. hence we use 10% as our
+ * default value. With 10% free space, fragmentation is not a
+ * problem, so we choose to optimize for time.
+ */
+#define MINFREE 5
+
+/*
+ * Super block for an ext2fs file system.
+ */
+struct ext2fs {
+ u_int32_t e2fs_icount; /* Inode count */
+ u_int32_t e2fs_bcount; /* blocks count */
+ u_int32_t e2fs_rbcount; /* reserved blocks count */
+ u_int32_t e2fs_fbcount; /* free blocks count */
+ u_int32_t e2fs_ficount; /* free inodes count */
+ u_int32_t e2fs_first_dblock; /* first data block */
+ u_int32_t e2fs_log_bsize; /* block size = 1024*(2^e2fs_log_bsize) */
+ u_int32_t e2fs_fsize; /* fragment size */
+ u_int32_t e2fs_bpg; /* blocks per group */
+ u_int32_t e2fs_fpg; /* frags per group */
+ u_int32_t e2fs_ipg; /* inodes per group */
+ u_int32_t e2fs_mtime; /* mount time */
+ u_int32_t e2fs_wtime; /* write time */
+ u_int16_t e2fs_mnt_count; /* mount count */
+ u_int16_t e2fs_max_mnt_count; /* max mount count */
+ u_int16_t e2fs_magic; /* magic number */
+ u_int16_t e2fs_state; /* file system state */
+ u_int16_t e2fs_beh; /* behavior on errors */
+ u_int16_t reserved;
+ u_int32_t e2fs_lastfsck; /* time of last fsck */
+ u_int32_t e2fs_fsckintv; /* max time between fscks */
+ u_int32_t e2fs_creator; /* creator OS */
+ u_int32_t e2fs_rev; /* revision level */
+ u_int16_t e2fs_ruid; /* default uid for reserved blocks */
+ u_int16_t e2fs_rgid; /* default gid for reserved blocks */
+ u_int32_t reserved2[235];
+};
+
+
+/* in-memory data for ext2fs */
+struct m_ext2fs {
+ struct ext2fs e2fs;
+ u_char e2fs_fsmnt[MAXMNTLEN]; /* name mounted on */
+ int8_t e2fs_ronly; /* mounted read-only flag */
+ int8_t e2fs_fmod; /* super block modified flag */
+ int32_t e2fs_bsize; /* block size */
+ int32_t e2fs_bshift; /* ``lblkno'' calc of logical blkno */
+ int32_t e2fs_bmask; /* ``blkoff'' calc of blk offsets */
+ int64_t e2fs_qbmask; /* ~fs_bmask - for use with quad size */
+ int32_t e2fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */
+ int32_t e2fs_ncg; /* number of cylinder groups */
+ int32_t e2fs_ngdb; /* number of group descriptor block */
+ int32_t e2fs_ipb; /* number of inodes per block */
+ int32_t e2fs_itpg; /* number of inode table per group */
+ struct ext2_gd *e2fs_gd; /* group descripors */
+};
+
+
+
+/*
+ * Filesystem identification
+ */
+#define E2FS_MAGIC 0xef53 /* the ext2fs magic number */
+#define E2FS_REV 0 /* revision level */
+
+/*
+ * OS identification
+ */
+#define E2FS_OS_LINUX 0
+#define E2FS_OS_HURD 1
+#define E2FS_OS_MASIX 2
+
+/*
+ * Filesystem clean flags
+ */
+#define E2FS_ISCLEAN 0x01
+#define E2FS_ERRORS 0x02
+
+/* ext2 file system block group descriptor */
+
+struct ext2_gd {
+ u_int32_t ext2bgd_b_bitmap; /* blocks bitmap block */
+ u_int32_t ext2bgd_i_bitmap; /* inodes bitmap block */
+ u_int32_t ext2bgd_i_tables; /* inodes table block */
+ u_int16_t ext2bgd_nbfree; /* number of free blocks */
+ u_int16_t ext2bgd_nifree; /* number of free inodes */
+ u_int16_t ext2bgd_ndirs; /* number of directories */
+ u_int16_t reserved;
+ u_int32_t reserved2[3];
+
+};
+
+/*
+ * Turn file system block numbers into disk block addresses.
+ * This maps file system blocks to device size blocks.
+ */
+#define fsbtodb(fs, b) ((b) << (fs)->e2fs_fsbtodb)
+#define dbtofsb(fs, b) ((b) >> (fs)->e2fs_fsbtodb)
+
+/*
+ * Macros for handling inode numbers:
+ * inode number to file system block offset.
+ * inode number to cylinder group number.
+ * inode number to file system block address.
+ */
+#define ino_to_cg(fs, x) (((x) - 1) / (fs)->e2fs.e2fs_ipg)
+#define ino_to_fsba(fs, x) \
+ ((fs)->e2fs_gd[ino_to_cg(fs, x)].ext2bgd_i_tables + \
+ (((x)-1) % (fs)->e2fs.e2fs_ipg)/(fs)->e2fs_ipb)
+#define ino_to_fsbo(fs, x) (((x)-1) % (fs)->e2fs_ipb)
+
+/*
+ * Give cylinder group number for a file system block.
+ * Give cylinder group block number for a file system block.
+ */
+#define dtog(fs, d) (((d) - (fs)->e2fs.e2fs_first_dblock) / (fs)->e2fs.e2fs_fpg)
+#define dtogd(fs, d) \
+ (((d) - (fs)->e2fs.e2fs_first_dblock) % (fs)->e2fs.e2fs_fpg)
+
+/*
+ * The following macros optimize certain frequently calculated
+ * quantities by using shifts and masks in place of divisions
+ * modulos and multiplications.
+ */
+#define blkoff(fs, loc) /* calculates (loc % fs->e2fs_bsize) */ \
+ ((loc) & (fs)->e2fs_qbmask)
+#define lblktosize(fs, blk) /* calculates (blk * fs->e2fs_bsize) */ \
+ ((blk) << (fs)->e2fs_bshift)
+#define lblkno(fs, loc) /* calculates (loc / fs->e2fs_bsize) */ \
+ ((loc) >> (fs)->e2fs_bshift)
+#define blkroundup(fs, size) /* calculates roundup(size, fs->e2fs_bsize) */ \
+ (((size) + (fs)->e2fs_qbmask) & (fs)->e2fs_bmask)
+#define fragroundup(fs, size) /* calculates roundup(size, fs->e2fs_bsize) */ \
+ (((size) + (fs)->e2fs_qbmask) & (fs)->e2fs_bmask)
+/*
+ * Determine the number of available frags given a
+ * percentage to hold in reserve.
+ */
+#define freespace(fs) \
+ ((fs)->e2fs.e2fs_fbcount - (fs)->e2fs.e2fs_rbcount)
+
+/*
+ * Number of indirects in a file system block.
+ */
+#define NINDIR(fs) ((fs)->e2fs_bsize / sizeof(u_int32_t))
diff --git a/sys/ufs/ext2fs/ext2fs_alloc.c b/sys/ufs/ext2fs/ext2fs_alloc.c
new file mode 100644
index 00000000000..14e93c7bf79
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_alloc.c
@@ -0,0 +1,650 @@
+/* $NetBSD: ffs_alloc.c,v 1.13 1996/10/12 21:58:44 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * 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.
+ *
+ * @(#)ffs_alloc.c 8.11 (Berkeley) 10/27/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+
+u_long ext2gennumber;
+
+static daddr_t ext2fs_alloccg __P((struct inode *, int, daddr_t, int));
+static u_long ext2fs_dirpref __P((struct m_ext2fs *));
+static void ext2fs_fserr __P((struct m_ext2fs *, u_int, char *));
+static u_long ext2fs_hashalloc __P((struct inode *, int, long, int,
+ daddr_t (*)(struct inode *, int, daddr_t,
+ int)));
+static daddr_t ext2fs_nodealloccg __P((struct inode *, int, daddr_t, int));
+static daddr_t ext2fs_mapsearch __P((struct m_ext2fs *, char *, daddr_t));
+
+/*
+ * Allocate a block in the file system.
+ *
+ * A preference may be optionally specified. If a preference is given
+ * the following hierarchy is used to allocate a block:
+ * 1) allocate the requested block.
+ * 2) allocate a rotationally optimal block in the same cylinder.
+ * 3) allocate a block in the same cylinder group.
+ * 4) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ * If no block preference is given the following heirarchy is used
+ * to allocate a block:
+ * 1) allocate a block in the cylinder group that contains the
+ * inode for the file.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ */
+int
+ext2fs_alloc(ip, lbn, bpref, cred, bnp)
+ register struct inode *ip;
+ daddr_t lbn, bpref;
+ struct ucred *cred;
+ daddr_t *bnp;
+{
+ register struct m_ext2fs *fs;
+ daddr_t bno;
+ int cg;
+
+ *bnp = 0;
+ fs = ip->i_e2fs;
+#ifdef DIAGNOSTIC
+ if (cred == NOCRED)
+ panic("ext2fs_alloc: missing credential\n");
+#endif /* DIAGNOSTIC */
+ if (fs->e2fs.e2fs_fbcount == 0)
+ goto nospace;
+ if (cred->cr_uid != 0 && freespace(fs) <= 0)
+ goto nospace;
+ if (bpref >= fs->e2fs.e2fs_bcount)
+ bpref = 0;
+ if (bpref == 0)
+ cg = ino_to_cg(fs, ip->i_number);
+ else
+ cg = dtog(fs, bpref);
+ bno = (daddr_t)ext2fs_hashalloc(ip, cg, (long)bpref, fs->e2fs_bsize,
+ ext2fs_alloccg);
+ if (bno > 0) {
+ ip->i_e2fs_nblock += btodb(fs->e2fs_bsize);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ *bnp = bno;
+ return (0);
+ }
+nospace:
+ ext2fs_fserr(fs, cred->cr_uid, "file system full");
+ uprintf("\n%s: write failed, file system is full\n", fs->e2fs_fsmnt);
+ return (ENOSPC);
+}
+
+/*
+ * Allocate an inode in the file system.
+ *
+ * If allocating a directory, use ext2fs_dirpref to select the inode.
+ * If allocating in a directory, the following hierarchy is followed:
+ * 1) allocate the preferred inode.
+ * 2) allocate an inode in the same cylinder group.
+ * 3) quadradically rehash into other cylinder groups, until an
+ * available inode is located.
+ * If no inode preference is given the following heirarchy is used
+ * to allocate an inode:
+ * 1) allocate an inode in cylinder group 0.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available inode is located.
+ */
+int
+ext2fs_valloc(v)
+ void *v;
+{
+ struct vop_valloc_args /* {
+ struct vnode *a_pvp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct vnode **a_vpp;
+ } */ *ap = v;
+ register struct vnode *pvp = ap->a_pvp;
+ register struct inode *pip;
+ register struct m_ext2fs *fs;
+ register struct inode *ip;
+ mode_t mode = ap->a_mode;
+ ino_t ino, ipref;
+ int cg, error;
+
+ *ap->a_vpp = NULL;
+ pip = VTOI(pvp);
+ fs = pip->i_e2fs;
+ if (fs->e2fs.e2fs_ficount == 0)
+ goto noinodes;
+
+ if ((mode & IFMT) == IFDIR)
+ cg = ext2fs_dirpref(fs);
+ else
+ cg = ino_to_cg(fs, pip->i_number);
+ ipref = cg * fs->e2fs.e2fs_ipg;
+ ino = (ino_t)ext2fs_hashalloc(pip, cg, (long)ipref, mode, ext2fs_nodealloccg);
+ if (ino == 0)
+ goto noinodes;
+ error = VFS_VGET(pvp->v_mount, ino, ap->a_vpp);
+ if (error) {
+ VOP_VFREE(pvp, ino, mode);
+ return (error);
+ }
+ ip = VTOI(*ap->a_vpp);
+ if (ip->i_e2fs_mode && ip->i_e2fs_nlink != 0) {
+ printf("mode = 0%o, nlinks %d, inum = %d, fs = %s\n",
+ ip->i_e2fs_mode, ip->i_e2fs_nlink, ip->i_number, fs->e2fs_fsmnt);
+ panic("ext2fs_valloc: dup alloc");
+ }
+
+ bzero(&(ip->i_din.e2fs_din), sizeof(struct ext2fs_dinode));
+
+ /*
+ * Set up a new generation number for this inode.
+ */
+ if (++ext2gennumber < (u_long)time.tv_sec)
+ ext2gennumber = time.tv_sec;
+ ip->i_e2fs_gen = ext2gennumber;
+ return (0);
+noinodes:
+ ext2fs_fserr(fs, ap->a_cred->cr_uid, "out of inodes");
+ uprintf("\n%s: create/symlink failed, no inodes free\n", fs->e2fs_fsmnt);
+ return (ENOSPC);
+}
+
+/*
+ * Find a cylinder to place a directory.
+ *
+ * The policy implemented by this algorithm is to select from
+ * among those cylinder groups with above the average number of
+ * free inodes, the one with the smallest number of directories.
+ */
+static u_long
+ext2fs_dirpref(fs)
+ register struct m_ext2fs *fs;
+{
+ int cg, maxspace, mincg, avgifree;
+
+ avgifree = fs->e2fs.e2fs_ficount / fs->e2fs_ncg;
+ maxspace = 0;
+ mincg = -1;
+ for (cg = 0; cg < fs->e2fs_ncg; cg++)
+ if ( fs->e2fs_gd[cg].ext2bgd_nifree >= avgifree) {
+ if (mincg == -1 || fs->e2fs_gd[cg].ext2bgd_nbfree > maxspace) {
+ mincg = cg;
+ maxspace = fs->e2fs_gd[cg].ext2bgd_nbfree;
+ }
+ }
+ return mincg;
+}
+
+/*
+ * Select the desired position for the next block in a file. The file is
+ * logically divided into sections. The first section is composed of the
+ * direct blocks. Each additional section contains fs_maxbpg blocks.
+ *
+ * If no blocks have been allocated in the first section, the policy is to
+ * request a block in the same cylinder group as the inode that describes
+ * the file. Otherwise, the policy is to try to allocate the blocks
+ * contigously. The two fields of the ext2 inode extention (see
+ * ufs/ufs/inode.h) help this.
+ */
+daddr_t
+ext2fs_blkpref(ip, lbn, indx, bap)
+ struct inode *ip;
+ daddr_t lbn;
+ int indx;
+ daddr_t *bap;
+{
+ register struct m_ext2fs *fs;
+ register int cg, i;
+
+ fs = ip->i_e2fs;
+ /*
+ * if we are doing contigous lbn allocation, try to alloc blocks
+ * contigously on disk
+ */
+
+ if ( ip->i_e2fs_last_blk && lbn == ip->i_e2fs_last_lblk + 1) {
+ return ip->i_e2fs_last_blk + 1;
+ }
+
+ /*
+ * bap, if provided, gives us a list of blocks to which we want to
+ * stay close
+ */
+
+ if (bap) {
+ for (i = indx; i >= 0 ; i--) {
+ if (bap[i]) {
+ return bap[i] + 1;
+ }
+ }
+ }
+
+ /* fall back to the first block of the cylinder containing the inode */
+
+ cg = ino_to_cg(fs, ip->i_number);
+ return fs->e2fs.e2fs_bpg * cg + fs->e2fs.e2fs_first_dblock + 1;
+}
+
+/*
+ * Implement the cylinder overflow algorithm.
+ *
+ * The policy implemented by this algorithm is:
+ * 1) allocate the block in its requested cylinder group.
+ * 2) quadradically rehash on the cylinder group number.
+ * 3) brute force search for a free block.
+ */
+static u_long
+ext2fs_hashalloc(ip, cg, pref, size, allocator)
+ struct inode *ip;
+ int cg;
+ long pref;
+ int size; /* size for data blocks, mode for inodes */
+ daddr_t (*allocator) __P((struct inode *, int, daddr_t, int));
+{
+ register struct m_ext2fs *fs;
+ long result;
+ int i, icg = cg;
+
+ fs = ip->i_e2fs;
+ /*
+ * 1: preferred cylinder group
+ */
+ result = (*allocator)(ip, cg, pref, size);
+ if (result)
+ return (result);
+ /*
+ * 2: quadratic rehash
+ */
+ for (i = 1; i < fs->e2fs_ncg; i *= 2) {
+ cg += i;
+ if (cg >= fs->e2fs_ncg)
+ cg -= fs->e2fs_ncg;
+ result = (*allocator)(ip, cg, 0, size);
+ if (result)
+ return (result);
+ }
+ /*
+ * 3: brute force search
+ * Note that we start at i == 2, since 0 was checked initially,
+ * and 1 is always checked in the quadratic rehash.
+ */
+ cg = (icg + 2) % fs->e2fs_ncg;
+ for (i = 2; i < fs->e2fs_ncg; i++) {
+ result = (*allocator)(ip, cg, 0, size);
+ if (result)
+ return (result);
+ cg++;
+ if (cg == fs->e2fs_ncg)
+ cg = 0;
+ }
+ return (NULL);
+}
+
+/*
+ * Determine whether a block can be allocated.
+ *
+ * Check to see if a block of the appropriate size is available,
+ * and if it is, allocate it.
+ */
+
+static daddr_t
+ext2fs_alloccg(ip, cg, bpref, size)
+ struct inode *ip;
+ int cg;
+ daddr_t bpref;
+ int size;
+{
+ register struct m_ext2fs *fs;
+ register char *bbp;
+ struct buf *bp;
+ int error, bno, start, end, loc;
+
+ fs = ip->i_e2fs;
+ if (fs->e2fs_gd[cg].ext2bgd_nbfree == 0)
+ return (NULL);
+ error = bread(ip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_b_bitmap),
+ (int)fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (NULL);
+ }
+ bbp = (char *)bp->b_data;
+
+ if (dtog(fs, bpref) != cg)
+ bpref = 0;
+ if (bpref != 0) {
+ bpref = dtogd(fs, bpref);
+ /*
+ * if the requested block is available, use it
+ */
+ if (isclr(bbp, bpref)) {
+ bno = bpref;
+ goto gotit;
+ }
+ }
+ /*
+ * no blocks in the requested cylinder, so take next
+ * available one in this cylinder group.
+ * first try to get 8 contigous blocks, then fall back to a single
+ * block.
+ */
+ if (bpref)
+ start = dtogd(fs, bpref) / NBBY;
+ else
+ start = 0;
+ end = howmany(fs->e2fs.e2fs_fpg, NBBY) - start;
+ for (loc = start; loc < end; loc++) {
+ if (bbp[loc] == 0) {
+ bno = loc * NBBY;
+ goto gotit;
+ }
+ }
+ for (loc = 0; loc < start; loc++) {
+ if (bbp[loc] == 0) {
+ bno = loc * NBBY;
+ goto gotit;
+ }
+ }
+
+ bno = ext2fs_mapsearch(fs, bbp, bpref);
+ if (bno < 0)
+ return (NULL);
+gotit:
+#ifdef DIAGNOSTIC
+ if (isset(bbp, (long)bno)) {
+ printf("ext2fs_alloccgblk: cg=%d bno=%d fs=%s\n",
+ cg, bno, fs->e2fs_fsmnt);
+ panic("ext2fs_valloc: dup alloc");
+ }
+#endif
+ setbit(bbp, (long)bno);
+ fs->e2fs.e2fs_fbcount--;
+ fs->e2fs_gd[cg].ext2bgd_nbfree--;
+ fs->e2fs_fmod = 1;
+ bdwrite(bp);
+ return (cg * fs->e2fs.e2fs_fpg + fs->e2fs.e2fs_first_dblock + bno);
+}
+
+/*
+ * Determine whether an inode can be allocated.
+ *
+ * Check to see if an inode is available, and if it is,
+ * allocate it using the following policy:
+ * 1) allocate the requested inode.
+ * 2) allocate the next available inode after the requested
+ * inode in the specified cylinder group.
+ */
+static daddr_t
+ext2fs_nodealloccg(ip, cg, ipref, mode)
+ struct inode *ip;
+ int cg;
+ daddr_t ipref;
+ int mode;
+{
+ register struct m_ext2fs *fs;
+ register char *ibp;
+ struct buf *bp;
+ int error, start, len, loc, map, i;
+
+ ipref--; /* to avoid a lot of (ipref -1) */
+ fs = ip->i_e2fs;
+ if (fs->e2fs_gd[cg].ext2bgd_nifree == 0)
+ return (NULL);
+ error = bread(ip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_i_bitmap),
+ (int)fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (NULL);
+ }
+ ibp = (char *)bp->b_data;
+ if (ipref) {
+ ipref %= fs->e2fs.e2fs_ipg;
+ if (isclr(ibp, ipref))
+ goto gotit;
+ }
+ start = ipref / NBBY;
+ len = howmany(fs->e2fs.e2fs_ipg - ipref, NBBY);
+ loc = skpc(0xff, len, &ibp[start]);
+ if (loc == 0) {
+ len = start + 1;
+ start = 0;
+ loc = skpc(0xff, len, &ibp[0]);
+ if (loc == 0) {
+ printf("cg = %d, ipref = %d, fs = %s\n",
+ cg, ipref, fs->e2fs_fsmnt);
+ panic("ext2fs_nodealloccg: map corrupted");
+ /* NOTREACHED */
+ }
+ }
+ i = start + len - loc;
+ map = ibp[i];
+ ipref = i * NBBY;
+ for (i = 1; i < (1 << NBBY); i <<= 1, ipref++) {
+ if ((map & i) == 0) {
+ goto gotit;
+ }
+ }
+ printf("fs = %s\n", fs->e2fs_fsmnt);
+ panic("ext2fs_nodealloccg: block not in map");
+ /* NOTREACHED */
+gotit:
+ setbit(ibp, ipref);
+ fs->e2fs.e2fs_ficount--;
+ fs->e2fs_gd[cg].ext2bgd_nifree--;
+ fs->e2fs_fmod = 1;
+ if ((mode & IFMT) == IFDIR) {
+ fs->e2fs_gd[cg].ext2bgd_ndirs++;
+ }
+ bdwrite(bp);
+ return (cg * fs->e2fs.e2fs_ipg + ipref +1);
+}
+
+/*
+ * Free a block.
+ *
+ * The specified block is placed back in the
+ * free map.
+ */
+void
+ext2fs_blkfree(ip, bno)
+ register struct inode *ip;
+ daddr_t bno;
+{
+ register struct m_ext2fs *fs;
+ register char *bbp;
+ struct buf *bp;
+ int error, cg;
+
+ fs = ip->i_e2fs;
+ cg = dtog(fs, bno);
+ if ((u_int)bno >= fs->e2fs.e2fs_bcount) {
+ printf("bad block %d, ino %d\n", bno, ip->i_number);
+ ext2fs_fserr(fs, ip->i_e2fs_uid, "bad block");
+ return;
+ }
+ error = bread(ip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_b_bitmap),
+ (int)fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return;
+ }
+ bbp = (char *)bp->b_data;
+ bno = dtogd(fs, bno);
+ if (isclr(bbp, bno)) {
+ printf("dev = 0x%x, block = %d, fs = %s\n",
+ ip->i_dev, bno, fs->e2fs_fsmnt);
+ panic("blkfree: freeing free block");
+ }
+ clrbit(bbp, bno);
+ fs->e2fs.e2fs_fbcount++;
+ fs->e2fs_gd[cg].ext2bgd_nbfree++;
+
+ fs->e2fs_fmod = 1;
+ bdwrite(bp);
+}
+
+/*
+ * Free an inode.
+ *
+ * The specified inode is placed back in the free map.
+ */
+int
+ext2fs_vfree(v)
+ void *v;
+{
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap = v;
+ register struct m_ext2fs *fs;
+ register char *ibp;
+ register struct inode *pip;
+ ino_t ino = ap->a_ino;
+ struct buf *bp;
+ int error, cg;
+
+ pip = VTOI(ap->a_pvp);
+ fs = pip->i_e2fs;
+ if ((u_int)ino >= fs->e2fs.e2fs_icount || (u_int)ino < EXT2_FIRSTINO)
+ panic("ifree: range: dev = 0x%x, ino = %d, fs = %s\n",
+ pip->i_dev, ino, fs->e2fs_fsmnt);
+ cg = ino_to_cg(fs, ino);
+ error = bread(pip->i_devvp, fsbtodb(fs, fs->e2fs_gd[cg].ext2bgd_i_bitmap),
+ (int)fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (0);
+ }
+ ibp = (char *)bp->b_data;
+ ino = (ino - 1) % fs->e2fs.e2fs_ipg;
+ if (isclr(ibp, ino)) {
+ printf("dev = 0x%x, ino = %d, fs = %s\n",
+ pip->i_dev, ino, fs->e2fs_fsmnt);
+ if (fs->e2fs_ronly == 0)
+ panic("ifree: freeing free inode");
+ }
+ clrbit(ibp, ino);
+ fs->e2fs.e2fs_ficount++;
+ fs->e2fs_gd[cg].ext2bgd_nifree++;
+ if ((ap->a_mode & IFMT) == IFDIR) {
+ fs->e2fs_gd[cg].ext2bgd_ndirs--;
+ }
+ fs->e2fs_fmod = 1;
+ bdwrite(bp);
+ return (0);
+}
+
+/*
+ * Find a block in the specified cylinder group.
+ *
+ * It is a panic if a request is made to find a block if none are
+ * available.
+ */
+
+static daddr_t
+ext2fs_mapsearch(fs, bbp, bpref)
+ register struct m_ext2fs *fs;
+ register char *bbp;
+ daddr_t bpref;
+{
+ daddr_t bno;
+ int start, len, loc, i, map;
+
+ /*
+ * find the fragment by searching through the free block
+ * map for an appropriate bit pattern
+ */
+ if (bpref)
+ start = dtogd(fs, bpref) / NBBY;
+ else
+ start = 0;
+ len = howmany(fs->e2fs.e2fs_fpg, NBBY) - start;
+ loc = skpc(0xff, len, &bbp[start]);
+ if (loc == 0) {
+ len = start + 1;
+ start = 0;
+ loc = skpc(0xff, len, &bbp[start]);
+ if (loc == 0) {
+ printf("start = %d, len = %d, fs = %s\n",
+ start, len, fs->e2fs_fsmnt);
+ panic("ext2fs_alloccg: map corrupted");
+ /* NOTREACHED */
+ }
+ }
+ i = start + len - loc;
+ map = bbp[i];
+ bno = i * NBBY;
+ for (i = 1; i < (1 << NBBY); i <<= 1, bno++) {
+ if ((map & i) == 0)
+ return (bno);
+ }
+ printf("fs = %s\n", fs->e2fs_fsmnt);
+ panic("ext2fs_mapsearch: block not in map");
+ /* NOTREACHED */
+}
+
+/*
+ * Fserr prints the name of a file system with an error diagnostic.
+ *
+ * The form of the error message is:
+ * fs: error message
+ */
+static void
+ext2fs_fserr(fs, uid, cp)
+ struct m_ext2fs *fs;
+ u_int uid;
+ char *cp;
+{
+
+ log(LOG_ERR, "uid %d on %s: %s\n", uid, fs->e2fs_fsmnt, cp);
+}
diff --git a/sys/ufs/ext2fs/ext2fs_balloc.c b/sys/ufs/ext2fs/ext2fs_balloc.c
new file mode 100644
index 00000000000..af1fe8c6803
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_balloc.c
@@ -0,0 +1,247 @@
+/* $NetBSD: ffs_balloc.c,v 1.3 1996/02/09 22:22:21 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * 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.
+ *
+ * @(#)ffs_balloc.c 8.4 (Berkeley) 9/23/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+
+/*
+ * Balloc defines the structure of file system storage
+ * by allocating the physical blocks on a device given
+ * the inode and the logical block number in a file.
+ */
+int
+ext2fs_balloc(ip, bn, size, cred, bpp, flags)
+ register struct inode *ip;
+ register daddr_t bn;
+ int size;
+ struct ucred *cred;
+ struct buf **bpp;
+ int flags;
+{
+ register struct m_ext2fs *fs;
+ register daddr_t nb;
+ struct buf *bp, *nbp;
+ struct vnode *vp = ITOV(ip);
+ struct indir indirs[NIADDR + 2];
+ daddr_t newb, lbn, *bap, pref;
+ int num, i, error;
+
+ *bpp = NULL;
+ if (bn < 0)
+ return (EFBIG);
+ fs = ip->i_e2fs;
+ lbn = bn;
+
+ /*
+ * The first NDADDR blocks are direct blocks
+ */
+ if (bn < NDADDR) {
+ nb = ip->i_e2fs_blocks[bn];
+ if (nb != 0) {
+ error = bread(vp, bn, fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ *bpp = bp;
+ return (0);
+ } else {
+ error = ext2fs_alloc(ip, bn,
+ ext2fs_blkpref(ip, bn, (int)bn, &ip->i_e2fs_blocks[0]),
+ cred, &newb);
+ if (error)
+ return (error);
+ ip->i_e2fs_last_lblk = lbn;
+ ip->i_e2fs_last_blk = newb;
+ bp = getblk(vp, bn, fs->e2fs_bsize, 0, 0);
+ bp->b_blkno = fsbtodb(fs, newb);
+ if (flags & B_CLRBUF)
+ clrbuf(bp);
+ }
+ ip->i_e2fs_blocks[bn] = dbtofsb(fs, bp->b_blkno);
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ *bpp = bp;
+ return (0);
+ }
+ /*
+ * Determine the number of levels of indirection.
+ */
+ pref = 0;
+ if ((error = ufs_getlbns(vp, bn, indirs, &num)) != 0)
+ return(error);
+#ifdef DIAGNOSTIC
+ if (num < 1)
+ panic ("ext2fs_balloc: ufs_getlbns returned indirect block\n");
+#endif
+ /*
+ * Fetch the first indirect block allocating if necessary.
+ */
+ --num;
+ nb = ip->i_e2fs_blocks[NDADDR + indirs[0].in_off];
+ if (nb == 0) {
+ pref = ext2fs_blkpref(ip, lbn, 0, (daddr_t *)0);
+ error = ext2fs_alloc(ip, lbn, pref,
+ cred, &newb);
+ if (error)
+ return (error);
+ nb = newb;
+ ip->i_e2fs_last_blk = newb;
+ bp = getblk(vp, indirs[1].in_lbn, fs->e2fs_bsize, 0, 0);
+ bp->b_blkno = fsbtodb(fs, newb);
+ clrbuf(bp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if ((error = bwrite(bp)) != 0) {
+ ext2fs_blkfree(ip, nb);
+ return (error);
+ }
+ ip->i_e2fs_blocks[NDADDR + indirs[0].in_off] = newb;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ }
+ /*
+ * Fetch through the indirect blocks, allocating as necessary.
+ */
+ for (i = 1;;) {
+ error = bread(vp,
+ indirs[i].in_lbn, (int)fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ bap = (daddr_t *)bp->b_data;
+ nb = bap[indirs[i].in_off];
+ if (i == num)
+ break;
+ i += 1;
+ if (nb != 0) {
+ brelse(bp);
+ continue;
+ }
+ pref = ext2fs_blkpref(ip, lbn, 0, (daddr_t *)0);
+ error = ext2fs_alloc(ip, lbn, pref, cred,
+ &newb);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ nb = newb;
+ ip->i_e2fs_last_blk = newb;
+ nbp = getblk(vp, indirs[i].in_lbn, fs->e2fs_bsize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ clrbuf(nbp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if ((error = bwrite(nbp)) != 0) {
+ ext2fs_blkfree(ip, nb);
+ brelse(bp);
+ return (error);
+ }
+ bap[indirs[i - 1].in_off] = nb;
+ /*
+ * If required, write synchronously, otherwise use
+ * delayed write.
+ */
+ if (flags & B_SYNC) {
+ bwrite(bp);
+ } else {
+ bdwrite(bp);
+ }
+ }
+ /*
+ * Get the data block, allocating if necessary.
+ */
+ if (nb == 0) {
+ pref = ext2fs_blkpref(ip, lbn, indirs[i].in_off, &bap[0]);
+ error = ext2fs_alloc(ip, lbn, pref, cred,
+ &newb);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ nb = newb;
+ ip->i_e2fs_last_lblk = lbn;
+ ip->i_e2fs_last_blk = newb;
+ nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ if (flags & B_CLRBUF)
+ clrbuf(nbp);
+ bap[indirs[i].in_off] = nb;
+ /*
+ * If required, write synchronously, otherwise use
+ * delayed write.
+ */
+ if (flags & B_SYNC) {
+ bwrite(bp);
+ } else {
+ bdwrite(bp);
+ }
+ *bpp = nbp;
+ return (0);
+ }
+ brelse(bp);
+ if (flags & B_CLRBUF) {
+ error = bread(vp, lbn, (int)fs->e2fs_bsize, NOCRED, &nbp);
+ if (error) {
+ brelse(nbp);
+ return (error);
+ }
+ } else {
+ nbp = getblk(vp, lbn, fs->e2fs_bsize, 0, 0);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ }
+ *bpp = nbp;
+ return (0);
+}
diff --git a/sys/ufs/ext2fs/ext2fs_bmap.c b/sys/ufs/ext2fs/ext2fs_bmap.c
new file mode 100644
index 00000000000..fb6bef9b8d2
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_bmap.c
@@ -0,0 +1,222 @@
+/* $NetBSD: ufs_bmap.c,v 1.3 1996/02/09 22:36:00 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ * @(#)ufs_bmap.c 8.6 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/resourcevar.h>
+#include <sys/trace.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+
+static int ext2fs_bmaparray __P((struct vnode *, daddr_t, daddr_t *,
+ struct indir *, int *, int *));
+
+/*
+ * Bmap converts a the logical block number of a file to its physical block
+ * number on the disk. The conversion is done by using the logical block
+ * number to index into the array of block pointers described by the dinode.
+ */
+int
+ext2fs_bmap(v)
+ void *v;
+{
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap = v;
+ /*
+ * Check for underlying vnode requests and ensure that logical
+ * to physical mapping is requested.
+ */
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = VTOI(ap->a_vp)->i_devvp;
+ if (ap->a_bnp == NULL)
+ return (0);
+
+ return (ext2fs_bmaparray(ap->a_vp, ap->a_bn, ap->a_bnp, NULL, NULL,
+ ap->a_runp));
+}
+
+/*
+ * Indirect blocks are now on the vnode for the file. They are given negative
+ * logical block numbers. Indirect blocks are addressed by the negative
+ * address of the first data block to which they point. Double indirect blocks
+ * are addressed by one less than the address of the first indirect block to
+ * which they point. Triple indirect blocks are addressed by one less than
+ * the address of the first double indirect block to which they point.
+ *
+ * ext2fs_bmaparray does the bmap conversion, and if requested returns the
+ * array of logical blocks which must be traversed to get to a block.
+ * Each entry contains the offset into that block that gets you to the
+ * next block and the disk address of the block (if it is assigned).
+ */
+
+int
+ext2fs_bmaparray(vp, bn, bnp, ap, nump, runp)
+ struct vnode *vp;
+ register daddr_t bn;
+ daddr_t *bnp;
+ struct indir *ap;
+ int *nump;
+ int *runp;
+{
+ register struct inode *ip;
+ struct buf *bp;
+ struct ufsmount *ump;
+ struct mount *mp;
+ struct vnode *devvp;
+ struct indir a[NIADDR], *xap;
+ daddr_t daddr;
+ long metalbn;
+ int error, maxrun = 0, num;
+
+ ip = VTOI(vp);
+ mp = vp->v_mount;
+ ump = VFSTOUFS(mp);
+#ifdef DIAGNOSTIC
+ if ((ap != NULL && nump == NULL) || (ap == NULL && nump != NULL))
+ panic("ext2fs_bmaparray: invalid arguments");
+#endif
+
+ if (runp) {
+ /*
+ * XXX
+ * If MAXBSIZE is the largest transfer the disks can handle,
+ * we probably want maxrun to be 1 block less so that we
+ * don't create a block larger than the device can handle.
+ */
+ *runp = 0;
+ maxrun = MAXBSIZE / mp->mnt_stat.f_iosize - 1;
+ }
+
+ xap = ap == NULL ? a : ap;
+ if (!nump)
+ nump = &num;
+ if ((error = ufs_getlbns(vp, bn, xap, nump)) != 0)
+ return (error);
+
+ num = *nump;
+ if (num == 0) {
+ *bnp = blkptrtodb(ump, ip->i_e2fs_blocks[bn]);
+ if (*bnp == 0)
+ *bnp = -1;
+ else if (runp)
+ for (++bn; bn < NDADDR && *runp < maxrun &&
+ is_sequential(ump, ip->i_e2fs_blocks[bn - 1],
+ ip->i_e2fs_blocks[bn]);
+ ++bn, ++*runp);
+ return (0);
+ }
+
+
+ /* Get disk address out of indirect block array */
+ daddr = ip->i_e2fs_blocks[NDADDR + xap->in_off];
+
+ devvp = VFSTOUFS(vp->v_mount)->um_devvp;
+ for (bp = NULL, ++xap; --num; ++xap) {
+ /*
+ * Exit the loop if there is no disk address assigned yet and
+ * the indirect block isn't in the cache, or if we were
+ * looking for an indirect block and we've found it.
+ */
+
+ metalbn = xap->in_lbn;
+ if ((daddr == 0 && !incore(vp, metalbn)) || metalbn == bn)
+ break;
+ /*
+ * If we get here, we've either got the block in the cache
+ * or we have a disk address for it, go fetch it.
+ */
+ if (bp)
+ brelse(bp);
+
+ xap->in_exists = 1;
+ bp = getblk(vp, metalbn, mp->mnt_stat.f_iosize, 0, 0);
+ if (bp->b_flags & (B_DONE | B_DELWRI)) {
+ trace(TR_BREADHIT, pack(vp, size), metalbn);
+ }
+#ifdef DIAGNOSTIC
+ else if (!daddr)
+ panic("ext2fs_bmaparry: indirect block not in cache");
+#endif
+ else {
+ trace(TR_BREADMISS, pack(vp, size), metalbn);
+ bp->b_blkno = blkptrtodb(ump, daddr);
+ bp->b_flags |= B_READ;
+ VOP_STRATEGY(bp);
+ curproc->p_stats->p_ru.ru_inblock++; /* XXX */
+ if ((error = biowait(bp)) != 0) {
+ brelse(bp);
+ return (error);
+ }
+ }
+
+ daddr = ((daddr_t *)bp->b_data)[xap->in_off];
+ if (num == 1 && daddr && runp)
+ for (bn = xap->in_off + 1;
+ bn < MNINDIR(ump) && *runp < maxrun &&
+ is_sequential(ump, ((daddr_t *)bp->b_data)[bn - 1],
+ ((daddr_t *)bp->b_data)[bn]);
+ ++bn, ++*runp);
+ }
+ if (bp)
+ brelse(bp);
+
+ daddr = blkptrtodb(ump, daddr);
+ *bnp = daddr == 0 ? -1 : daddr;
+ return (0);
+}
diff --git a/sys/ufs/ext2fs/ext2fs_dinode.h b/sys/ufs/ext2fs/ext2fs_dinode.h
new file mode 100644
index 00000000000..db0ce26ec92
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_dinode.h
@@ -0,0 +1,132 @@
+/* $NetBSD: dinode.h,v 1.7 1995/06/15 23:22:48 cgd Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1982, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ * @(#)dinode.h 8.6 (Berkeley) 9/13/94
+ */
+
+#include <sys/stat.h>
+
+/*
+ * The root inode is the root of the file system. Inode 0 can't be used for
+ * normal purposes and bad blocks are normally linked to inode 1, thus
+ * the root inode is 2.
+ * Inode 3 to 10 are reserved in ext2fs.
+ */
+#define EXT2_ROOTINO ((ino_t)2)
+#define EXT2_FIRSTINO ((ino_t)11)
+
+/*
+ * A dinode contains all the meta-data associated with a UFS file.
+ * This structure defines the on-disk format of a dinode. Since
+ * this structure describes an on-disk structure, all its fields
+ * are defined by types with precise widths.
+ */
+
+#define NDADDR 12 /* Direct addresses in inode. */
+#define NIADDR 3 /* Indirect addresses in inode. */
+
+#define EXT2_MAXSYMLINKLEN ((NDADDR+NIADDR) * sizeof (u_int32_t))
+
+struct ext2fs_dinode {
+ u_int16_t e2di_mode; /* 0: IFMT, permissions; see below. */
+ u_int16_t e2di_uid; /* 2: Owner UID */
+ u_int32_t e2di_size; /* 4: Size (in bytes) */
+ u_int32_t e2di_atime; /* 8: Acces time */
+ u_int32_t e2di_ctime; /* 12: Create time */
+ u_int32_t e2di_mtime; /* 16: Modification time */
+ u_int32_t e2di_dtime; /* 20: Deletion time */
+ u_int16_t e2di_gid; /* 24: Owner GID */
+ u_int16_t e2di_nlink; /* 26: File link count */
+ u_int32_t e2di_nblock;/* 28: Blocks count */
+ u_int32_t e2di_flags; /* 32: Status flags (chflags) */
+ u_int32_t e2di_linux_reserved1; /* 36 */
+ u_int32_t e2di_blocks[NDADDR+NIADDR]; /* 40: disk blocks */
+ u_int32_t e2di_gen; /* 100: generation number (file version) */
+ u_int32_t e2di_facl; /* 104: file ACL (not implemented) */
+ u_int32_t e2di_dacl; /* 108: dir ACL (not implemented) */
+ u_int32_t e2di_faddr; /* 112: fragment address */
+ u_int8_t e2di_nfrag; /* 116: fragment number */
+ u_int8_t e2di_fsize; /* 117: fragment size */
+ u_int16_t e2di_linux_reserved2; /* 118 */
+ u_int32_t e2di_linux_reserved3[2]; /* 120 */
+};
+
+
+
+#define E2MAXSYMLINKLEN ((NDADDR + NIADDR) * sizeof(u_int32_t))
+
+/* File permissions. */
+#define EXT2_IEXEC 0000100 /* Executable. */
+#define EXT2_IWRITE 0000200 /* Writeable. */
+#define EXT2_IREAD 0000400 /* Readable. */
+#define EXT2_ISVTX 0001000 /* Sticky bit. */
+#define EXT2_ISGID 0002000 /* Set-gid. */
+#define EXT2_ISUID 0004000 /* Set-uid. */
+
+/* File types. */
+#define EXT2_IFMT 0170000 /* Mask of file type. */
+#define EXT2_IFIFO 0010000 /* Named pipe (fifo). */
+#define EXT2_IFCHR 0020000 /* Character device. */
+#define EXT2_IFDIR 0040000 /* Directory file. */
+#define EXT2_IFBLK 0060000 /* Block device. */
+#define EXT2_IFREG 0100000 /* Regular file. */
+#define EXT2_IFLNK 0120000 /* Symbolic link. */
+#define EXT2_IFSOCK 0140000 /* UNIX domain socket. */
+
+/* file flags */
+#define EXT2_SECRM 0x00000001 /* Secure deletion */
+#define EXT2_UNRM 0x00000002 /* Undelete */
+#define EXT2_COMPR 0x00000004 /* Compress file */
+#define EXT2_SYNC 0x00000008 /* Synchronous updates */
+#define EXT2_IMMUTABLE 0x00000010 /* Immutable file */
+#define EXT2_APPEND 0x00000020 /* writes to file may only append */
+#define EXT2_NODUMP 0x00000040 /* do not dump file */
+
+/*
+ * The e2di_blocks fields may be overlaid with other information for
+ * file types that do not have associated disk storage. Block
+ * and character devices overlay the first data block with their
+ * dev_t value. Short symbolic links place their path in the
+ * di_db area.
+ */
+
+#define e2di_rdev e2di_blocks[0]
+#define e2di_shortlink e2di_blocks
diff --git a/sys/ufs/ext2fs/ext2fs_dir.h b/sys/ufs/ext2fs/ext2fs_dir.h
new file mode 100644
index 00000000000..341dd5ca21d
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_dir.h
@@ -0,0 +1,114 @@
+/* $NetBSD: dir.h,v 1.8 1996/03/09 19:42:41 scottr Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ * @(#)dir.h 8.4 (Berkeley) 8/10/94
+ */
+
+#ifndef _EXT2FS_DIR_H_
+#define _EXT2FS_DIR_H_
+
+/*
+ * Theoretically, directories can be more than 2Gb in length, however, in
+ * practice this seems unlikely. So, we define the type doff_t as a 32-bit
+ * quantity to keep down the cost of doing lookup on a 32-bit machine.
+*/
+#define doff_t int32_t
+#define EXT2FS_MAXDIRSIZE (0x7fffffff)
+
+/*
+ * A directory consists of some number of blocks of e2fs_bsize bytes.
+ *
+ * Each block contains some number of directory entry
+ * structures, which are of variable length. Each directory entry has
+ * a struct direct at the front of it, containing its inode number,
+ * the length of the entry, and the length of the name contained in
+ * the entry. These are followed by the name padded to a 4 byte boundary
+ * with null bytes. All names are guaranteed null terminated.
+ * The maximum length of a name in a directory is EXT2FS_MAXNAMLEN.
+ *
+ * The macro EXT2FS_DIRSIZ(fmt, dp) gives the amount of space required to
+ * represent a directory entry. Free space in a directory is represented by
+ * entries which have dp->e2d_reclen > DIRSIZ(fmt, dp). All d2fs_bsize bytes
+ * in a directory block are claimed by the directory entries. This
+ * usually results in the last entry in a directory having a large
+ * dp->e2d_reclen. When entries are deleted from a directory, the
+ * space is returned to the previous entry in the same directory
+ * block by increasing its dp->e2d_reclen. If the first entry of
+ * a directory block is free, then its dp->e2d_ino is set to 0.
+ * Entries other than the first in a directory do not normally have
+ * dp->e2d_ino set to 0.
+ */
+
+#define EXT2FS_MAXNAMLEN 255
+
+struct ext2fs_direct {
+ u_int32_t e2d_ino; /* inode number of entry */
+ u_int16_t e2d_reclen; /* length of this record */
+ u_int16_t e2d_namlen; /* length of string in d_name */
+ char e2d_name[EXT2FS_MAXNAMLEN];/* name with length <= EXT2FS_MAXNAMLEN */
+};
+
+/*
+ * The EXT2FS_DIRSIZ macro gives the minimum record length which will hold
+ * the directory entryfor a name len "len" (without the terminating null byte).
+ * This requires the amount of space in struct direct
+ * without the d_name field, plus enough space for the name without a
+ * terminating null byte, rounded up to a 4 byte boundary.
+ */
+#define EXT2FS_DIRSIZ(len) \
+ (( 8 + len + 3) &~ 3)
+
+/*
+ * Template for manipulating directories. Should use struct direct's,
+ * but the name field is EXT2FS_MAXNAMLEN - 1, and this just won't do.
+ */
+struct ext2fs_dirtemplate {
+ u_int32_t dot_ino;
+ int16_t dot_reclen;
+ u_int16_t dot_namlen;
+ char dot_name[4]; /* must be multiple of 4 */
+ u_int32_t dotdot_ino;
+ int16_t dotdot_reclen;
+ u_int16_t dotdot_namlen;
+ char dotdot_name[4]; /* ditto */
+};
+
+#endif /* !_EXT2FS_DIR_H_ */
diff --git a/sys/ufs/ext2fs/ext2fs_extern.h b/sys/ufs/ext2fs/ext2fs_extern.h
new file mode 100644
index 00000000000..10681c180ce
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_extern.h
@@ -0,0 +1,154 @@
+/* $NetBSD: ffs_extern.h,v 1.5 1996/09/01 23:49:18 mycroft Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*-
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)ffs_extern.h 8.3 (Berkeley) 4/16/94
+ */
+
+struct buf;
+struct fid;
+struct m_ext2fs;
+struct inode;
+struct mount;
+struct nameidata;
+struct proc;
+struct statfs;
+struct timeval;
+struct ucred;
+struct ufsmount;
+struct uio;
+struct vnode;
+struct mbuf;
+struct componentname;
+
+__BEGIN_DECLS
+
+/* ext2fs_alloc.c */
+int ext2fs_alloc __P((struct inode *, daddr_t, daddr_t , struct ucred *,
+ daddr_t *));
+int ext2fs_realloccg __P((struct inode *, daddr_t, daddr_t, int, int ,
+ struct ucred *, struct buf **));
+int ext2fs_reallocblks __P((void *));
+int ext2fs_valloc __P((void *));
+daddr_t ext2fs_blkpref __P((struct inode *, daddr_t, int, daddr_t *));
+void ext2fs_blkfree __P((struct inode *, daddr_t));
+int ext2fs_vfree __P((void *));
+
+/* ext2fs_balloc.c */
+int ext2fs_balloc __P((struct inode *, daddr_t, int, struct ucred *,
+ struct buf **, int));
+
+/* ext2fs_bmap.c */
+int ext2fs_bmap __P((void *));
+
+/* ext2fs_inode.c */
+void ext2fs_init __P((void));
+int ext2fs_update __P((void *));
+int ext2fs_truncate __P((void *));
+int ext2fs_inactive __P((void *));
+
+/* ext2fs_lookup.c */
+int ext2fs_readdir __P((void *));
+int ext2fs_lookup __P((void *));
+int ext2fs_direnter __P((struct inode *, struct vnode *,
+ struct componentname *));
+int ext2fs_dirremove __P((struct vnode *, struct componentname *));
+int ext2fs_dirrewrite __P((struct inode *, struct inode *,
+ struct componentname *));
+int ext2fs_dirempty __P((struct inode *, ino_t, struct ucred *));
+int ext2fs_checkpath __P((struct inode *, struct inode *, struct ucred *));
+
+/* ext2fs_subr.c */
+int ext2fs_blkatoff __P((void *));
+void ext2fs_fragacct __P((struct m_ext2fs *, int, int32_t[], int));
+#ifdef DIAGNOSTIC
+void ext2fs_checkoverlap __P((struct buf *, struct inode *));
+#endif
+
+/* ext2fs_vfsops.c */
+int ext2fs_mountroot __P((void));
+int ext2fs_mount __P((struct mount *, const char *, void *, struct nameidata *,
+ struct proc *));
+int ext2fs_reload __P((struct mount *, struct ucred *, struct proc *));
+int ext2fs_mountfs __P((struct vnode *, struct mount *, struct proc *));
+int ext2fs_unmount __P((struct mount *, int, struct proc *));
+int ext2fs_flushfiles __P((struct mount *, int, struct proc *));
+int ext2fs_statfs __P((struct mount *, struct statfs *, struct proc *));
+int ext2fs_sync __P((struct mount *, int, struct ucred *, struct proc *));
+int ext2fs_vget __P((struct mount *, ino_t, struct vnode **));
+int ext2fs_fhtovp __P((struct mount *, struct fid *, struct mbuf *,
+ struct vnode **, int *, struct ucred **));
+int ext2fs_vptofh __P((struct vnode *, struct fid *));
+int ext2fs_sbupdate __P((struct ufsmount *, int));
+int ext2fs_cgupdate __P((struct ufsmount *, int));
+
+/* ext2fs_readwrite.c */
+int ext2fs_read __P((void *));
+int ext2fs_write __P((void *));
+
+/* ext2fs_vnops.c */
+int ext2fs_create __P((void *));
+int ext2fs_mknod __P((void *));
+int ext2fs_open __P((void *));
+int ext2fs_access __P((void *));
+int ext2fs_getattr __P((void *));
+int ext2fs_setattr __P((void *));
+int ext2fs_remove __P((void *));
+int ext2fs_link __P((void *));
+int ext2fs_rename __P((void *));
+int ext2fs_mkdir __P((void *));
+int ext2fs_rmdir __P((void *));
+int ext2fs_symlink __P((void *));
+int ext2fs_readlink __P((void *));
+int ext2fs_advlock __P((void *));
+int ext2fs_vinit __P(( struct mount *, int (**specops) __P((void *)),
+ int (**fifoops) __P((void *)), struct vnode **));
+int ext2fs_makeinode __P((int, struct vnode *, struct vnode **,
+ struct componentname *cnp));
+int ext2fs_reclaim __P((void *));
+
+#define ext2fs_fsync genfs_fsync
+__END_DECLS
+
+#define IS_EXT2_VNODE(vp) (vp->v_tag == VT_EXT2FS)
+
+extern int (**ext2fs_vnodeop_p) __P((void *));
+extern int (**ext2fs_specop_p) __P((void *));
+#ifdef FIFO
+extern int (**ext2fs_fifoop_p) __P((void *));
+#define EXT2FS_FIFOOPS ext2fs_fifoop_p
+#else
+#define EXT2FS_FIFOOPS NULL
+#endif
diff --git a/sys/ufs/ext2fs/ext2fs_inode.c b/sys/ufs/ext2fs/ext2fs_inode.c
new file mode 100644
index 00000000000..0609c3c13a6
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_inode.c
@@ -0,0 +1,528 @@
+/* $NetBSD: ffs_inode.c,v 1.12 1996/11/06 03:02:59 thorpej Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * 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.
+ *
+ * @(#)ffs_inode.c 8.8 (Berkeley) 10/19/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mount.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/buf.h>
+#include <sys/vnode.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/trace.h>
+#include <sys/resourcevar.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+
+static int ext2fs_indirtrunc __P((struct inode *, daddr_t, daddr_t,
+ daddr_t, int, long *));
+
+void
+ext2fs_init()
+{
+ static int done = 0;
+
+ if (done)
+ return;
+ done = 1;
+ ufs_ihashinit();
+ return;
+}
+
+/*
+ * Last reference to an inode. If necessary, write or delete it.
+ */
+int
+ext2fs_inactive(v)
+ void *v;
+{
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap = v;
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ struct timespec ts;
+ int error;
+ extern int prtactive;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("ffs_inactive: pushing active", vp);
+ /* Get rid of inodes related to stale file handles. */
+ if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0) {
+ if ((vp->v_flag & VXLOCK) == 0)
+ vgone(vp);
+ return (0);
+ }
+
+ error = 0;
+#ifdef DIAGNOSTIC
+ if (VOP_ISLOCKED(vp))
+ panic("ffs_inactive: locked inode");
+ if (curproc)
+ ip->i_lockholder = curproc->p_pid;
+ else
+ ip->i_lockholder = -1;
+#endif
+ ip->i_flag |= IN_LOCKED;
+ if (ip->i_e2fs_nlink == 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
+ error = VOP_TRUNCATE(vp, (off_t)0, 0, NOCRED, NULL);
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ ip->i_e2fs_dtime = ts.tv_sec;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ VOP_VFREE(vp, ip->i_number, ip->i_e2fs_mode);
+ }
+ if (ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) {
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ VOP_UPDATE(vp, &ts, &ts, 0);
+ }
+ VOP_UNLOCK(vp);
+ /*
+ * If we are done with the inode, reclaim it
+ * so that it can be reused immediately.
+ */
+ if (vp->v_usecount == 0 && ip->i_e2fs_dtime != 0)
+ vgone(vp);
+ return (error);
+}
+
+
+/*
+ * Update the access, modified, and inode change times as specified by the
+ * IACCESS, IUPDATE, and ICHANGE flags respectively. The IMODIFIED flag is
+ * used to specify that the inode needs to be updated but that the times have
+ * already been set. The access and modified times are taken from the second
+ * and third parameters; the inode change time is always taken from the current
+ * time. If waitfor is set, then wait for the disk write of the inode to
+ * complete.
+ */
+int
+ext2fs_update(v)
+ void *v;
+{
+ struct vop_update_args /* {
+ struct vnode *a_vp;
+ struct timespec *a_access;
+ struct timespec *a_modify;
+ int a_waitfor;
+ } */ *ap = v;
+ register struct m_ext2fs *fs;
+ struct buf *bp;
+ struct inode *ip;
+ int error;
+ struct timespec ts;
+
+ if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY)
+ return (0);
+ ip = VTOI(ap->a_vp);
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ EXT2FS_ITIMES(ip, ap->a_access, ap->a_modify, &ts);
+ if ((ip->i_flag & IN_MODIFIED) == 0)
+ return (0);
+ ip->i_flag &= ~IN_MODIFIED;
+ fs = ip->i_e2fs;
+ error = bread(ip->i_devvp,
+ fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
+ (int)fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ bcopy(&ip->i_din.e2fs_din,
+ ((struct ext2fs_dinode *)bp->b_data + ino_to_fsbo(fs, ip->i_number)),
+ sizeof(struct ext2fs_dinode));
+ if (ap->a_waitfor)
+ return (bwrite(bp));
+ else {
+ bdwrite(bp);
+ return (0);
+ }
+}
+
+#define SINGLE 0 /* index of single indirect block */
+#define DOUBLE 1 /* index of double indirect block */
+#define TRIPLE 2 /* index of triple indirect block */
+/*
+ * Truncate the inode oip to at most length size, freeing the
+ * disk blocks.
+ */
+int
+ext2fs_truncate(v)
+ void *v;
+{
+ struct vop_truncate_args /* {
+ struct vnode *a_vp;
+ off_t a_length;
+ int a_flags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap = v;
+ register struct vnode *ovp = ap->a_vp;
+ register daddr_t lastblock;
+ register struct inode *oip;
+ daddr_t bn, lbn, lastiblock[NIADDR], indir_lbn[NIADDR];
+ daddr_t oldblks[NDADDR + NIADDR], newblks[NDADDR + NIADDR];
+ off_t length = ap->a_length;
+ register struct m_ext2fs *fs;
+ struct buf *bp;
+ int offset, size, level;
+ long count, nblocks, vflags, blocksreleased = 0;
+ struct timespec ts;
+ register int i;
+ int aflags, error, allerror;
+ off_t osize;
+
+ if (length < 0)
+ return (EINVAL);
+
+ oip = VTOI(ovp);
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ if (ovp->v_type == VLNK &&
+ (oip->i_e2fs_size < ovp->v_mount->mnt_maxsymlinklen ||
+ (ovp->v_mount->mnt_maxsymlinklen == 0 &&
+ oip->i_e2fs_nblock == 0))) {
+#ifdef DIAGNOSTIC
+ if (length != 0)
+ panic("ext2fs_truncate: partial truncate of symlink");
+#endif
+ bzero((char *)&oip->i_din.e2fs_din.e2di_shortlink,
+ (u_int)oip->i_e2fs_size);
+ oip->i_e2fs_size = 0;
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(ovp, &ts, &ts, 1));
+ }
+ if (oip->i_e2fs_size == length) {
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(ovp, &ts, &ts, 0));
+ }
+ fs = oip->i_e2fs;
+ osize = oip->i_e2fs_size;
+ /*
+ * Lengthen the size of the file. We must ensure that the
+ * last byte of the file is allocated. Since the smallest
+ * value of osize is 0, length will be at least 1.
+ */
+ if (osize < length) {
+#if 0 /* XXX */
+ if (length > fs->fs_maxfilesize)
+ return (EFBIG);
+#endif
+ offset = blkoff(fs, length - 1);
+ lbn = lblkno(fs, length - 1);
+ aflags = B_CLRBUF;
+ if (ap->a_flags & IO_SYNC)
+ aflags |= B_SYNC;
+ error = ext2fs_balloc(oip, lbn, offset + 1, ap->a_cred, &bp,
+ aflags);
+ if (error)
+ return (error);
+ oip->i_e2fs_size = length;
+ vnode_pager_setsize(ovp, (u_long)length);
+ (void) vnode_pager_uncache(ovp);
+ if (aflags & B_SYNC)
+ bwrite(bp);
+ else
+ bawrite(bp);
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (VOP_UPDATE(ovp, &ts, &ts, 1));
+ }
+ /*
+ * Shorten the size of the file. If the file is not being
+ * truncated to a block boundry, the contents of the
+ * partial block following the end of the file must be
+ * zero'ed in case it ever become accessable again because
+ * of subsequent file growth.
+ */
+ offset = blkoff(fs, length);
+ if (offset == 0) {
+ oip->i_e2fs_size = length;
+ } else {
+ lbn = lblkno(fs, length);
+ aflags = B_CLRBUF;
+ if (ap->a_flags & IO_SYNC)
+ aflags |= B_SYNC;
+ error = ext2fs_balloc(oip, lbn, offset, ap->a_cred, &bp, aflags);
+ if (error)
+ return (error);
+ oip->i_e2fs_size = length;
+ size = fs->e2fs_bsize;
+ vnode_pager_setsize(ovp, (u_long)length);
+ (void) vnode_pager_uncache(ovp);
+ bzero((char *)bp->b_data + offset, (u_int)(size - offset));
+ allocbuf(bp, size);
+ if (aflags & B_SYNC)
+ bwrite(bp);
+ else
+ bawrite(bp);
+ }
+ /*
+ * Calculate index into inode's block list of
+ * last direct and indirect blocks (if any)
+ * which we want to keep. Lastblock is -1 when
+ * the file is truncated to 0.
+ */
+ lastblock = lblkno(fs, length + fs->e2fs_bsize - 1) - 1;
+ lastiblock[SINGLE] = lastblock - NDADDR;
+ lastiblock[DOUBLE] = lastiblock[SINGLE] - NINDIR(fs);
+ lastiblock[TRIPLE] = lastiblock[DOUBLE] - NINDIR(fs) * NINDIR(fs);
+ nblocks = btodb(fs->e2fs_bsize);
+ /*
+ * Update file and block pointers on disk before we start freeing
+ * blocks. If we crash before free'ing blocks below, the blocks
+ * will be returned to the free list. lastiblock values are also
+ * normalized to -1 for calls to ext2fs_indirtrunc below.
+ */
+ bcopy((caddr_t)&oip->i_e2fs_blocks[0], (caddr_t)oldblks, sizeof oldblks);
+ for (level = TRIPLE; level >= SINGLE; level--)
+ if (lastiblock[level] < 0) {
+ oip->i_e2fs_blocks[NDADDR + level] = 0;
+ lastiblock[level] = -1;
+ }
+ for (i = NDADDR - 1; i > lastblock; i--)
+ oip->i_e2fs_blocks[i] = 0;
+ oip->i_flag |= IN_CHANGE | IN_UPDATE;
+ if ((error = VOP_UPDATE(ovp, &ts, &ts, 1)) != 0)
+ allerror = error;
+ /*
+ * Having written the new inode to disk, save its new configuration
+ * and put back the old block pointers long enough to process them.
+ * Note that we save the new block configuration so we can check it
+ * when we are done.
+ */
+ bcopy((caddr_t)&oip->i_e2fs_blocks[0], (caddr_t)newblks, sizeof newblks);
+ bcopy((caddr_t)oldblks, (caddr_t)&oip->i_e2fs_blocks[0], sizeof oldblks);
+ oip->i_e2fs_size = osize;
+ vflags = ((length > 0) ? V_SAVE : 0) | V_SAVEMETA;
+ allerror = vinvalbuf(ovp, vflags, ap->a_cred, ap->a_p, 0, 0);
+
+ /*
+ * Indirect blocks first.
+ */
+ indir_lbn[SINGLE] = -NDADDR;
+ indir_lbn[DOUBLE] = indir_lbn[SINGLE] - NINDIR(fs) -1;
+ indir_lbn[TRIPLE] = indir_lbn[DOUBLE] - NINDIR(fs) * NINDIR(fs) - 1;
+ for (level = TRIPLE; level >= SINGLE; level--) {
+ bn = oip->i_e2fs_blocks[NDADDR + level];
+ if (bn != 0) {
+ error = ext2fs_indirtrunc(oip, indir_lbn[level],
+ fsbtodb(fs, bn), lastiblock[level], level, &count);
+ if (error)
+ allerror = error;
+ blocksreleased += count;
+ if (lastiblock[level] < 0) {
+ oip->i_e2fs_blocks[NDADDR + level] = 0;
+ ext2fs_blkfree(oip, bn);
+ blocksreleased += nblocks;
+ }
+ }
+ if (lastiblock[level] >= 0)
+ goto done;
+ }
+
+ /*
+ * All whole direct blocks or frags.
+ */
+ for (i = NDADDR - 1; i > lastblock; i--) {
+ bn = oip->i_e2fs_blocks[i];
+ if (bn == 0)
+ continue;
+ oip->i_e2fs_blocks[i] = 0;
+ ext2fs_blkfree(oip, bn);
+ blocksreleased += btodb(fs->e2fs_bsize);
+ }
+ if (lastblock < 0)
+ goto done;
+
+done:
+#ifdef DIAGNOSTIC
+ for (level = SINGLE; level <= TRIPLE; level++)
+ if (newblks[NDADDR + level] != oip->i_e2fs_blocks[NDADDR + level])
+ panic("itrunc1");
+ for (i = 0; i < NDADDR; i++)
+ if (newblks[i] != oip->i_e2fs_blocks[i])
+ panic("itrunc2");
+ if (length == 0 &&
+ (ovp->v_dirtyblkhd.lh_first || ovp->v_cleanblkhd.lh_first))
+ panic("itrunc3");
+#endif /* DIAGNOSTIC */
+ /*
+ * Put back the real size.
+ */
+ oip->i_e2fs_size = length;
+ oip->i_e2fs_nblock -= blocksreleased;
+ if (oip->i_e2fs_nblock < 0) /* sanity */
+ oip->i_e2fs_nblock = 0;
+ oip->i_flag |= IN_CHANGE;
+ return (allerror);
+}
+
+/*
+ * Release blocks associated with the inode ip and stored in the indirect
+ * block bn. Blocks are free'd in LIFO order up to (but not including)
+ * lastbn. If level is greater than SINGLE, the block is an indirect block
+ * and recursive calls to indirtrunc must be used to cleanse other indirect
+ * blocks.
+ *
+ * NB: triple indirect blocks are untested.
+ */
+static int
+ext2fs_indirtrunc(ip, lbn, dbn, lastbn, level, countp)
+ register struct inode *ip;
+ daddr_t lbn, lastbn;
+ daddr_t dbn;
+ int level;
+ long *countp;
+{
+ register int i;
+ struct buf *bp;
+ register struct m_ext2fs *fs = ip->i_e2fs;
+ register daddr_t *bap;
+ struct vnode *vp;
+ daddr_t *copy = NULL, nb, nlbn, last;
+ long blkcount, factor;
+ int nblocks, blocksreleased = 0;
+ int error = 0, allerror = 0;
+
+ /*
+ * Calculate index in current block of last
+ * block to be kept. -1 indicates the entire
+ * block so we need not calculate the index.
+ */
+ factor = 1;
+ for (i = SINGLE; i < level; i++)
+ factor *= NINDIR(fs);
+ last = lastbn;
+ if (lastbn > 0)
+ last /= factor;
+ nblocks = btodb(fs->e2fs_bsize);
+ /*
+ * Get buffer of block pointers, zero those entries corresponding
+ * to blocks to be free'd, and update on disk copy first. Since
+ * double(triple) indirect before single(double) indirect, calls
+ * to bmap on these blocks will fail. However, we already have
+ * the on disk address, so we have to set the b_blkno field
+ * explicitly instead of letting bread do everything for us.
+ */
+ vp = ITOV(ip);
+ bp = getblk(vp, lbn, (int)fs->e2fs_bsize, 0, 0);
+ if (bp->b_flags & (B_DONE | B_DELWRI)) {
+ /* Braces must be here in case trace evaluates to nothing. */
+ trace(TR_BREADHIT, pack(vp, fs->e2fs_bsize), lbn);
+ } else {
+ trace(TR_BREADMISS, pack(vp, fs->e2fs_bsize), lbn);
+ curproc->p_stats->p_ru.ru_inblock++; /* pay for read */
+ bp->b_flags |= B_READ;
+ if (bp->b_bcount > bp->b_bufsize)
+ panic("ext2fs_indirtrunc: bad buffer size");
+ bp->b_blkno = dbn;
+ VOP_STRATEGY(bp);
+ error = biowait(bp);
+ }
+ if (error) {
+ brelse(bp);
+ *countp = 0;
+ return (error);
+ }
+
+ bap = (daddr_t *)bp->b_data;
+ if (lastbn != -1) {
+ MALLOC(copy, daddr_t *, fs->e2fs_bsize, M_TEMP, M_WAITOK);
+ bcopy((caddr_t)bap, (caddr_t)copy, (u_int)fs->e2fs_bsize);
+ bzero((caddr_t)&bap[last + 1],
+ (u_int)(NINDIR(fs) - (last + 1)) * sizeof (u_int32_t));
+ error = bwrite(bp);
+ if (error)
+ allerror = error;
+ bap = copy;
+ }
+
+ /*
+ * Recursively free totally unused blocks.
+ */
+ for (i = NINDIR(fs) - 1,
+ nlbn = lbn + 1 - i * factor; i > last;
+ i--, nlbn += factor) {
+ nb = bap[i];
+ if (nb == 0)
+ continue;
+ if (level > SINGLE) {
+ error = ext2fs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
+ (daddr_t)-1, level - 1,
+ &blkcount);
+ if (error)
+ allerror = error;
+ blocksreleased += blkcount;
+ }
+ ext2fs_blkfree(ip, nb);
+ blocksreleased += nblocks;
+ }
+
+ /*
+ * Recursively free last partial block.
+ */
+ if (level > SINGLE && lastbn >= 0) {
+ last = lastbn % factor;
+ nb = bap[i];
+ if (nb != 0) {
+ error = ext2fs_indirtrunc(ip, nlbn, fsbtodb(fs, nb),
+ last, level - 1, &blkcount);
+ if (error)
+ allerror = error;
+ blocksreleased += blkcount;
+ }
+ }
+
+ if (copy != NULL) {
+ FREE(copy, M_TEMP);
+ } else {
+ bp->b_flags |= B_INVAL;
+ brelse(bp);
+ }
+
+ *countp = blocksreleased;
+ return (allerror);
+}
diff --git a/sys/ufs/ext2fs/ext2fs_lookup.c b/sys/ufs/ext2fs/ext2fs_lookup.c
new file mode 100644
index 00000000000..1af31ab1869
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_lookup.c
@@ -0,0 +1,1066 @@
+/* $OpenBSD: ext2fs_lookup.c,v 1.1 1997/05/30 05:18:51 downsj Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * modified for Lites 1.1
+ *
+ * Aug 1995, Godmar Back (gback@cs.utah.edu)
+ * University of Utah, Department of Computer Science
+ */
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ * @(#)ufs_lookup.c 8.6 (Berkeley) 4/1/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ext2fs/ext2fs_extern.h>
+#include <ufs/ext2fs/ext2fs_dir.h>
+#include <ufs/ext2fs/ext2fs.h>
+
+extern int dirchk;
+
+static void ext2fs_dirconv2ffs __P((struct ext2fs_direct *e2dir,
+ struct dirent *ffsdir));
+static int ext2fs_dirbadentry __P((struct vnode *dp,
+ struct ext2fs_direct *de,
+ int entryoffsetinblock));
+
+/*
+ * the problem that is tackled below is the fact that FFS
+ * includes the terminating zero on disk while EXT2FS doesn't
+ * this implies that we need to introduce some padding.
+ * For instance, a filename "sbin" has normally a reclen 12
+ * in EXT2, but 16 in FFS.
+ * This reminds me of that Pepsi commercial: 'Kid saved a lousy nine cents...'
+ * If it wasn't for that, the complete ufs code for directories would
+ * have worked w/o changes (except for the difference in DIRBLKSIZ)
+ */
+static void
+ext2fs_dirconv2ffs( e2dir, ffsdir)
+ struct ext2fs_direct *e2dir;
+ struct dirent *ffsdir;
+{
+ bzero(ffsdir, sizeof(struct dirent));
+ ffsdir->d_fileno = e2dir->e2d_ino;
+ ffsdir->d_namlen = e2dir->e2d_namlen;
+
+ ffsdir->d_type = DT_UNKNOWN; /* don't know more here */
+#ifdef DIAGNOSTIC
+ /*
+ * XXX Rigth now this can't happen, but if one day
+ * MAXNAMLEN != E2FS_MAXNAMLEN we should handle this more gracefully !
+ */
+ if (e2dir->e2d_namlen > MAXNAMLEN) panic("ext2fs: e2dir->e2d_namlen\n");
+#endif
+ strncpy(ffsdir->d_name, e2dir->e2d_name, ffsdir->d_namlen);
+
+ /* Godmar thinks: since e2dir->e2d_reclen can be big and means
+ nothing anyway, we compute our own reclen according to what
+ we think is right
+ */
+ ffsdir->d_reclen = DIRENT_SIZE(ffsdir);
+}
+
+/*
+ * Vnode op for reading directories.
+ *
+ * Convert the on-disk entries to <sys/dirent.h> entries.
+ * the problem is that the conversion will blow up some entries by four bytes,
+ * so it can't be done in place. This is too bad. Right now the conversion is
+ * done entry by entry, the converted entry is sent via uiomove.
+ *
+ * XXX allocate a buffer, convert as many entries as possible, then send
+ * the whole buffer to uiomove
+ */
+int
+ext2fs_readdir(v)
+ void *v;
+{
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ u_long *a_cookies;
+ int ncookies;
+ } */ *ap = v;
+ register struct uio *uio = ap->a_uio;
+ int error;
+ size_t count, readcnt, lost;
+ struct m_ext2fs *fs = VTOI(ap->a_vp)->i_e2fs;
+
+ struct ext2fs_direct *dp;
+ struct dirent dstd;
+ struct uio auio;
+ struct iovec aiov;
+ caddr_t dirbuf;
+ off_t off = uio->uio_offset;
+ u_long *cookies = ap->a_cookies;
+ int ncookies = ap->a_ncookies;
+
+ count = uio->uio_resid;
+ /* Make sure we don't return partial entries. */
+ count -= (uio->uio_offset + count) & (fs->e2fs_bsize -1);
+ if (count <= 0)
+ return (EINVAL);
+ 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_segflg = UIO_SYSSPACE;
+ aiov.iov_len = count;
+ MALLOC(dirbuf, caddr_t, count, M_TEMP, M_WAITOK);
+ bzero(dirbuf, count);
+ aiov.iov_base = dirbuf;
+
+ error = VOP_READ(ap->a_vp, &auio, 0, ap->a_cred);
+ if (error == 0) {
+ readcnt = count - auio.uio_resid;
+ for (dp = (struct ext2fs_direct *)dirbuf;
+ (char *)dp < (char *)dirbuf + readcnt; ) {
+ if (dp->e2d_reclen <= 0) {
+ error = EIO;
+ break;
+ }
+ ext2fs_dirconv2ffs(dp, &dstd);
+ if(dstd.d_reclen > uio->uio_resid) {
+ break;
+ }
+ if ((error = uiomove((caddr_t)&dstd, dstd.d_reclen, uio)) != 0) {
+ break;
+ }
+ off = off + dp->e2d_reclen;
+ if (cookies != NULL) {
+ *cookies++ = off;
+ if (--ncookies <= 0){
+ break; /* out of cookies */
+ }
+ }
+ /* advance dp */
+ dp = (struct ext2fs_direct *) ((char *)dp + dp->e2d_reclen);
+ }
+ /* we need to correct uio_offset */
+ uio->uio_offset = off;
+ }
+ FREE(dirbuf, M_TEMP);
+ *ap->a_eofflag = VTOI(ap->a_vp)->i_e2fs_size <= uio->uio_offset;
+ uio->uio_resid += lost;
+ return (error);
+}
+
+/*
+ * Convert a component of a pathname into a pointer to a locked inode.
+ * This is a very central and rather complicated routine.
+ * If the file system is not maintained in a strict tree hierarchy,
+ * this can result in a deadlock situation (see comments in code below).
+ *
+ * The cnp->cn_nameiop argument is LOOKUP, CREATE, RENAME, or DELETE depending
+ * on whether the name is to be looked up, created, renamed, or deleted.
+ * When CREATE, RENAME, or DELETE is specified, information usable in
+ * creating, renaming, or deleting a directory entry may be calculated.
+ * If flag has LOCKPARENT or'ed into it and the target of the pathname
+ * exists, lookup returns both the target and its parent directory locked.
+ * When creating or renaming and LOCKPARENT is specified, the target may
+ * not be ".". When deleting and LOCKPARENT is specified, the target may
+ * be "."., but the caller must check to ensure it does an vrele and vput
+ * instead of two vputs.
+ *
+ * Overall outline of ext2fs_lookup:
+ *
+ * check accessibility of directory
+ * look for name in cache, if found, then if at end of path
+ * and deleting or creating, drop it, else return name
+ * search for name in directory, to found or notfound
+ * notfound:
+ * if creating, return locked directory, leaving info on available slots
+ * else return error
+ * found:
+ * if at end of path and deleting, return information to allow delete
+ * if at end of path and rewriting (RENAME and LOCKPARENT), lock target
+ * inode and return info to allow rewrite
+ * if not at end, add name to cache; if at end and neither creating
+ * nor deleting, add name to cache
+ */
+int
+ext2fs_lookup(v)
+ void *v;
+{
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap = v;
+ register struct vnode *vdp; /* vnode for directory being searched */
+ register struct inode *dp; /* inode for directory being searched */
+ struct buf *bp; /* a buffer of directory entries */
+ register struct ext2fs_direct *ep; /* the current directory entry */
+ int entryoffsetinblock; /* offset of ep in bp's buffer */
+ enum {NONE, COMPACT, FOUND} slotstatus;
+ doff_t slotoffset; /* offset of area with free space */
+ int slotsize; /* size of area at slotoffset */
+ int slotfreespace; /* amount of space free in slot */
+ int slotneeded; /* size of the entry we're seeking */
+ int numdirpasses; /* strategy for directory search */
+ doff_t endsearch; /* offset to end directory search */
+ doff_t prevoff; /* prev entry dp->i_offset */
+ struct vnode *pdp; /* saved dp during symlink work */
+ struct vnode *tdp; /* returned by VFS_VGET */
+ doff_t enduseful; /* pointer past last used dir slot */
+ u_long bmask; /* block offset mask */
+ int lockparent; /* 1 => lockparent flag is set */
+ int wantparent; /* 1 => wantparent or lockparent flag */
+ int namlen, error;
+ struct vnode **vpp = ap->a_vpp;
+ struct componentname *cnp = ap->a_cnp;
+ struct ucred *cred = cnp->cn_cred;
+ int flags = cnp->cn_flags;
+ int nameiop = cnp->cn_nameiop;
+
+ int dirblksize = VTOI(ap->a_dvp)->i_e2fs->e2fs_bsize;
+
+ bp = NULL;
+ slotoffset = -1;
+ *vpp = NULL;
+ vdp = ap->a_dvp;
+ dp = VTOI(vdp);
+ lockparent = flags & LOCKPARENT;
+ wantparent = flags & (LOCKPARENT|WANTPARENT);
+ /*
+ * Check accessiblity of directory.
+ */
+ if ((dp->i_e2fs_mode & IFMT) != IFDIR)
+ return (ENOTDIR);
+ if ((error = VOP_ACCESS(vdp, VEXEC, cred, cnp->cn_proc)) != 0)
+ return (error);
+
+ /*
+ * We now have a segment name to search for, and a directory to search.
+ *
+ * Before tediously performing a linear scan of the directory,
+ * check the name cache to see if the directory/name pair
+ * we are looking for is known already.
+ */
+ if ((error = cache_lookup(vdp, vpp, cnp)) != 0) {
+ int vpid; /* capability number of vnode */
+
+ if (error == ENOENT)
+ return (error);
+ /*
+ * Get the next vnode in the path.
+ * See comment below starting `Step through' for
+ * an explaination of the locking protocol.
+ */
+ pdp = vdp;
+ dp = VTOI(*vpp);
+ vdp = *vpp;
+ vpid = vdp->v_id;
+ if (pdp == vdp) { /* lookup on "." */
+ VREF(vdp);
+ error = 0;
+ } else if (flags & ISDOTDOT) {
+ VOP_UNLOCK(pdp);
+ error = vget(vdp, 1);
+ if (!error && lockparent && (flags & ISLASTCN))
+ error = VOP_LOCK(pdp);
+ } else {
+ error = vget(vdp, 1);
+ if (!lockparent || error || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ }
+ /*
+ * Check that the capability number did not change
+ * while we were waiting for the lock.
+ */
+ if (!error) {
+ if (vpid == vdp->v_id)
+ return (0);
+ vput(vdp);
+ if (lockparent && pdp != vdp && (flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ }
+ if ((error = VOP_LOCK(pdp)) != 0)
+ return (error);
+ vdp = pdp;
+ dp = VTOI(pdp);
+ *vpp = NULL;
+ }
+
+ /*
+ * Suppress search for slots unless creating
+ * file and at end of pathname, in which case
+ * we watch for a place to put the new file in
+ * case it doesn't already exist.
+ */
+ slotstatus = FOUND;
+ slotfreespace = slotsize = slotneeded = 0;
+ if ((nameiop == CREATE || nameiop == RENAME) &&
+ (flags & ISLASTCN)) {
+ slotstatus = NONE;
+ slotneeded = EXT2FS_DIRSIZ(cnp->cn_namelen);
+ }
+
+ /*
+ * If there is cached information on a previous search of
+ * this directory, pick up where we last left off.
+ * We cache only lookups as these are the most common
+ * and have the greatest payoff. Caching CREATE has little
+ * benefit as it usually must search the entire directory
+ * to determine that the entry does not exist. Caching the
+ * location of the last DELETE or RENAME has not reduced
+ * profiling time and hence has been removed in the interest
+ * of simplicity.
+ */
+ bmask = VFSTOUFS(vdp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
+ if (nameiop != LOOKUP || dp->i_diroff == 0 ||
+ dp->i_diroff > dp->i_e2fs_size) {
+ entryoffsetinblock = 0;
+ dp->i_offset = 0;
+ numdirpasses = 1;
+ } else {
+ dp->i_offset = dp->i_diroff;
+ if ((entryoffsetinblock = dp->i_offset & bmask) &&
+ (error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp)))
+ return (error);
+ numdirpasses = 2;
+ }
+ prevoff = dp->i_offset;
+ endsearch = roundup(dp->i_e2fs_size, dirblksize);
+ enduseful = 0;
+
+searchloop:
+ while (dp->i_offset < endsearch) {
+ /*
+ * If necessary, get the next directory block.
+ */
+ if ((dp->i_offset & bmask) == 0) {
+ if (bp != NULL)
+ brelse(bp);
+ error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, NULL, &bp);
+ if (error != 0)
+ return (error);
+ entryoffsetinblock = 0;
+ }
+ /*
+ * If still looking for a slot, and at a dirblksize
+ * boundary, have to start looking for free space again.
+ */
+ if (slotstatus == NONE &&
+ (entryoffsetinblock & (dirblksize - 1)) == 0) {
+ slotoffset = -1;
+ slotfreespace = 0;
+ }
+ /*
+ * Get pointer to next entry.
+ * Full validation checks are slow, so we only check
+ * enough to insure forward progress through the
+ * directory. Complete checks can be run by patching
+ * "dirchk" to be true.
+ */
+ ep = (struct ext2fs_direct *)
+ ((char *)bp->b_data + entryoffsetinblock);
+ if (ep->e2d_reclen == 0 ||
+ (dirchk && ext2fs_dirbadentry(vdp, ep, entryoffsetinblock))) {
+ int i;
+ ufs_dirbad(dp, dp->i_offset, "mangled entry");
+ i = dirblksize - (entryoffsetinblock & (dirblksize - 1));
+ dp->i_offset += i;
+ entryoffsetinblock += i;
+ continue;
+ }
+
+ /*
+ * If an appropriate sized slot has not yet been found,
+ * check to see if one is available. Also accumulate space
+ * in the current block so that we can determine if
+ * compaction is viable.
+ */
+ if (slotstatus != FOUND) {
+ int size = ep->e2d_reclen;
+
+ if (ep->e2d_ino != 0)
+ size -= EXT2FS_DIRSIZ(ep->e2d_namlen);
+ if (size > 0) {
+ if (size >= slotneeded) {
+ slotstatus = FOUND;
+ slotoffset = dp->i_offset;
+ slotsize = ep->e2d_reclen;
+ } else if (slotstatus == NONE) {
+ slotfreespace += size;
+ if (slotoffset == -1)
+ slotoffset = dp->i_offset;
+ if (slotfreespace >= slotneeded) {
+ slotstatus = COMPACT;
+ slotsize = dp->i_offset +
+ ep->e2d_reclen - slotoffset;
+ }
+ }
+ }
+ }
+
+ /*
+ * Check for a name match.
+ */
+ if (ep->e2d_ino) {
+ namlen = ep->e2d_namlen;
+ if (namlen == cnp->cn_namelen &&
+ !bcmp(cnp->cn_nameptr, ep->e2d_name,
+ (unsigned)namlen)) {
+ /*
+ * Save directory entry's inode number and
+ * reclen in ndp->ni_ufs area, and release
+ * directory buffer.
+ */
+ dp->i_ino = ep->e2d_ino;
+ dp->i_reclen = ep->e2d_reclen;
+ brelse(bp);
+ goto found;
+ }
+ }
+ prevoff = dp->i_offset;
+ dp->i_offset += ep->e2d_reclen;
+ entryoffsetinblock += ep->e2d_reclen;
+ if (ep->e2d_ino)
+ enduseful = dp->i_offset;
+ }
+/* notfound: */
+ /*
+ * If we started in the middle of the directory and failed
+ * to find our target, we must check the beginning as well.
+ */
+ if (numdirpasses == 2) {
+ numdirpasses--;
+ dp->i_offset = 0;
+ endsearch = dp->i_diroff;
+ goto searchloop;
+ }
+ if (bp != NULL)
+ brelse(bp);
+ /*
+ * If creating, and at end of pathname and current
+ * directory has not been removed, then can consider
+ * allowing file to be created.
+ */
+ if ((nameiop == CREATE || nameiop == RENAME) &&
+ (flags & ISLASTCN) && dp->i_e2fs_nlink != 0) {
+ /*
+ * Access for write is interpreted as allowing
+ * creation of files in the directory.
+ */
+ if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
+ return (error);
+ /*
+ * Return an indication of where the new directory
+ * entry should be put. If we didn't find a slot,
+ * then set dp->i_count to 0 indicating
+ * that the new slot belongs at the end of the
+ * directory. If we found a slot, then the new entry
+ * can be put in the range from dp->i_offset to
+ * dp->i_offset + dp->i_count.
+ */
+ if (slotstatus == NONE) {
+ dp->i_offset = roundup(dp->i_e2fs_size, dirblksize);
+ dp->i_count = 0;
+ enduseful = dp->i_offset;
+ } else {
+ dp->i_offset = slotoffset;
+ dp->i_count = slotsize;
+ if (enduseful < slotoffset + slotsize)
+ enduseful = slotoffset + slotsize;
+ }
+ dp->i_endoff = roundup(enduseful, dirblksize);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ /*
+ * We return with the directory locked, so that
+ * the parameters we set up above will still be
+ * valid if we actually decide to do a direnter().
+ * We return ni_vp == NULL to indicate that the entry
+ * does not currently exist; we leave a pointer to
+ * the (locked) directory inode in ndp->ni_dvp.
+ * The pathname buffer is saved so that the name
+ * can be obtained later.
+ *
+ * NB - if the directory is unlocked, then this
+ * information cannot be used.
+ */
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ return (EJUSTRETURN);
+ }
+ /*
+ * Insert name into cache (as non-existent) if appropriate.
+ */
+ if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)
+ cache_enter(vdp, *vpp, cnp);
+ return (ENOENT);
+
+found:
+ /*
+ * Check that directory length properly reflects presence
+ * of this entry.
+ */
+ if (entryoffsetinblock + EXT2FS_DIRSIZ(ep->e2d_namlen)
+ > dp->i_e2fs_size) {
+ ufs_dirbad(dp, dp->i_offset, "i_size too small");
+ dp->i_e2fs_size = entryoffsetinblock+EXT2FS_DIRSIZ(ep->e2d_namlen);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ }
+
+ /*
+ * Found component in pathname.
+ * If the final component of path name, save information
+ * in the cache as to where the entry was found.
+ */
+ if ((flags & ISLASTCN) && nameiop == LOOKUP)
+ dp->i_diroff = dp->i_offset &~ (dirblksize - 1);
+
+ /*
+ * 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 (in ndp->ni_dvp), otherwise we go
+ * on and lock the inode, being careful with ".".
+ */
+ if (nameiop == DELETE && (flags & ISLASTCN)) {
+ /*
+ * Write access to directory required to delete files.
+ */
+ if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
+ return (error);
+ /*
+ * Return pointer to current entry in dp->i_offset,
+ * and distance past previous entry (if there
+ * is a previous entry in this block) in dp->i_count.
+ * Save directory inode pointer in ndp->ni_dvp for dirremove().
+ */
+ if ((dp->i_offset & (dirblksize - 1)) == 0)
+ dp->i_count = 0;
+ else
+ dp->i_count = dp->i_offset - prevoff;
+ if (dp->i_number == dp->i_ino) {
+ VREF(vdp);
+ *vpp = vdp;
+ return (0);
+ }
+ if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
+ return (error);
+ /*
+ * If directory is "sticky", then user must own
+ * the directory, or the file in it, else she
+ * may not delete it (unless she's root). This
+ * implements append-only directories.
+ */
+ if ((dp->i_e2fs_mode & ISVTX) &&
+ cred->cr_uid != 0 &&
+ cred->cr_uid != dp->i_e2fs_uid &&
+ VTOI(tdp)->i_e2fs_uid != cred->cr_uid) {
+ vput(tdp);
+ return (EPERM);
+ }
+ *vpp = tdp;
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ 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 &&
+ (flags & ISLASTCN)) {
+ if ((error = VOP_ACCESS(vdp, VWRITE, cred, cnp->cn_proc)) != 0)
+ return (error);
+ /*
+ * Careful about locking second inode.
+ * This can only occur if the target is ".".
+ */
+ if (dp->i_number == dp->i_ino)
+ return (EISDIR);
+ if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
+ return (error);
+ *vpp = tdp;
+ cnp->cn_flags |= SAVENAME;
+ if (!lockparent)
+ VOP_UNLOCK(vdp);
+ 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.
+ */
+ pdp = vdp;
+ if (flags & ISDOTDOT) {
+ VOP_UNLOCK(pdp); /* race to get the inode */
+ if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0) {
+ VOP_LOCK(pdp);
+ return (error);
+ }
+ if (lockparent && (flags & ISLASTCN) &&
+ (error = VOP_LOCK(pdp)) != 0) {
+ vput(tdp);
+ return (error);
+ }
+ *vpp = tdp;
+ } else if (dp->i_number == dp->i_ino) {
+ VREF(vdp); /* we want ourself, ie "." */
+ *vpp = vdp;
+ } else {
+ if ((error = VFS_VGET(vdp->v_mount, dp->i_ino, &tdp)) != 0)
+ return (error);
+ if (!lockparent || !(flags & ISLASTCN))
+ VOP_UNLOCK(pdp);
+ *vpp = tdp;
+ }
+
+ /*
+ * Insert name into cache if appropriate.
+ */
+ if (cnp->cn_flags & MAKEENTRY)
+ cache_enter(vdp, *vpp, cnp);
+ return (0);
+}
+
+/*
+ * Do consistency checking on a directory entry:
+ * record length must be multiple of 4
+ * entry must fit in rest of its dirblksize block
+ * record must be large enough to contain entry
+ * name is not longer than MAXNAMLEN
+ * name must be as long as advertised, and null terminated
+ */
+/*
+ * changed so that it confirms to ext2fs_check_dir_entry
+ */
+static int
+ext2fs_dirbadentry(dp, de, entryoffsetinblock)
+ struct vnode *dp;
+ register struct ext2fs_direct *de;
+ int entryoffsetinblock;
+{
+ int dirblksize = VTOI(dp)->i_e2fs->e2fs_bsize;
+
+ char * error_msg = NULL;
+
+ if (de->e2d_reclen < EXT2FS_DIRSIZ(1)) /* e2d_namlen = 1 */
+ error_msg = "rec_len is smaller than minimal";
+ else if (de->e2d_reclen % 4 != 0)
+ error_msg = "rec_len % 4 != 0";
+ else if (de->e2d_reclen < EXT2FS_DIRSIZ(de->e2d_namlen))
+ error_msg = "reclen is too small for name_len";
+ else if (entryoffsetinblock + de->e2d_reclen > dirblksize)
+ error_msg = "directory entry across blocks";
+ else if (de->e2d_ino > VTOI(dp)->i_e2fs->e2fs.e2fs_icount)
+ error_msg = "inode out of bounds";
+
+ if (error_msg != NULL) {
+ printf( "bad directory entry: %s\n"
+ "offset=%d, inode=%lu, rec_len=%d, name_len=%d \n",
+ error_msg, entryoffsetinblock,
+ (unsigned long) de->e2d_ino, de->e2d_reclen, de->e2d_namlen);
+ panic("ext2fs_dirbadentry");
+ }
+ return error_msg == NULL ? 0 : 1;
+}
+
+/*
+ * Write a directory entry after a call to namei, using the parameters
+ * that it left in nameidata. The argument ip is the inode which the new
+ * directory entry will refer to. Dvp is a pointer to the directory to
+ * be written, which was left locked by namei. Remaining parameters
+ * (dp->i_offset, dp->i_count) indicate how the space for the new
+ * entry is to be obtained.
+ */
+int
+ext2fs_direnter(ip, dvp, cnp)
+ struct inode *ip;
+ struct vnode *dvp;
+ register struct componentname *cnp;
+{
+ register struct ext2fs_direct *ep, *nep;
+ register struct inode *dp;
+ struct buf *bp;
+ struct ext2fs_direct newdir;
+ struct iovec aiov;
+ struct uio auio;
+ u_int dsize;
+ int error, loc, newentrysize, spacefree;
+ char *dirbuf;
+ int dirblksize = ip->i_e2fs->e2fs_bsize;
+
+
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & SAVENAME) == 0)
+ panic("direnter: missing name");
+#endif
+ dp = VTOI(dvp);
+ newdir.e2d_ino = ip->i_number;
+ newdir.e2d_namlen = cnp->cn_namelen;
+ bcopy(cnp->cn_nameptr, newdir.e2d_name, (unsigned)cnp->cn_namelen + 1);
+ newentrysize = EXT2FS_DIRSIZ(newdir.e2d_namlen);
+ if (dp->i_count == 0) {
+ /*
+ * If dp->i_count is 0, then namei could find no
+ * space in the directory. Here, dp->i_offset will
+ * be on a directory block boundary and we will write the
+ * new entry into a fresh block.
+ */
+ if (dp->i_offset & (dirblksize - 1))
+ panic("ext2fs_direnter: newblk");
+ auio.uio_offset = dp->i_offset;
+ newdir.e2d_reclen = dirblksize;
+ auio.uio_resid = newentrysize;
+ aiov.iov_len = newentrysize;
+ aiov.iov_base = (caddr_t)&newdir;
+ 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);
+ if (dirblksize >
+ VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
+ /* XXX should grow with balloc() */
+ panic("ext2fs_direnter: frag size");
+ else if (!error) {
+ dp->i_e2fs_size = roundup(dp->i_e2fs_size, dirblksize);
+ dp->i_flag |= IN_CHANGE;
+ }
+ return (error);
+ }
+
+ /*
+ * If dp->i_count is non-zero, then namei found space
+ * for the new entry in the range dp->i_offset to
+ * dp->i_offset + dp->i_count in the directory.
+ * To use this space, we may have to compact the entries located
+ * there, by copying them together towards the beginning of the
+ * block, leaving the free space in one usable chunk at the end.
+ */
+
+ /*
+ * Get the block containing the space for the new directory entry.
+ */
+ if ((error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, &dirbuf, &bp)) != 0)
+ return (error);
+ /*
+ * Find space for the new entry. In the simple case, the entry at
+ * offset base will have the space. If it does not, then namei
+ * arranged that compacting the region dp->i_offset to
+ * dp->i_offset + dp->i_count would yield the
+ * space.
+ */
+ ep = (struct ext2fs_direct *)dirbuf;
+ dsize = EXT2FS_DIRSIZ(ep->e2d_namlen);
+ spacefree = ep->e2d_reclen - dsize;
+ for (loc = ep->e2d_reclen; loc < dp->i_count; ) {
+ nep = (struct ext2fs_direct *)(dirbuf + loc);
+ if (ep->e2d_ino) {
+ /* trim the existing slot */
+ ep->e2d_reclen = dsize;
+ ep = (struct ext2fs_direct *)((char *)ep + dsize);
+ } else {
+ /* overwrite; nothing there; header is ours */
+ spacefree += dsize;
+ }
+ dsize = EXT2FS_DIRSIZ(nep->e2d_namlen);
+ spacefree += nep->e2d_reclen - dsize;
+ loc += nep->e2d_reclen;
+ bcopy((caddr_t)nep, (caddr_t)ep, dsize);
+ }
+ /*
+ * Update the pointer fields in the previous entry (if any),
+ * copy in the new entry, and write out the block.
+ */
+ if (ep->e2d_ino == 0) {
+#ifdef DIAGNOSTIC
+ if (spacefree + dsize < newentrysize)
+ panic("ext2fs_direnter: compact1");
+#endif
+ newdir.e2d_reclen = spacefree + dsize;
+ } else {
+#ifdef DIAGNOSTIC
+ if (spacefree < newentrysize) {
+ printf("ext2fs_direnter: compact2 %u %u",
+ (u_int)spacefree, (u_int)newentrysize);
+ panic("ext2fs_direnter: compact2");
+ }
+#endif
+ newdir.e2d_reclen = spacefree;
+ ep->e2d_reclen = dsize;
+ ep = (struct ext2fs_direct *)((char *)ep + dsize);
+ }
+ bcopy((caddr_t)&newdir, (caddr_t)ep, (u_int)newentrysize);
+ error = VOP_BWRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ if (!error && dp->i_endoff && dp->i_endoff < dp->i_e2fs_size)
+ error = VOP_TRUNCATE(dvp, (off_t)dp->i_endoff, IO_SYNC,
+ cnp->cn_cred, cnp->cn_proc);
+ return (error);
+}
+
+/*
+ * Remove a directory entry after a call to namei, using
+ * the parameters which it left in nameidata. The entry
+ * dp->i_offset contains the offset into the directory of the
+ * entry to be eliminated. The dp->i_count field contains the
+ * size of the previous record in the directory. If this
+ * is 0, the first entry is being deleted, so we need only
+ * zero the inode number to mark the entry as free. If the
+ * entry is not the first in the directory, we must reclaim
+ * the space of the now empty record by adding the record size
+ * to the size of the previous entry.
+ */
+int
+ext2fs_dirremove(dvp, cnp)
+ struct vnode *dvp;
+ struct componentname *cnp;
+{
+ register struct inode *dp;
+ struct ext2fs_direct *ep;
+ struct buf *bp;
+ int error;
+
+ dp = VTOI(dvp);
+ if (dp->i_count == 0) {
+ /*
+ * First entry in block: set d_ino to zero.
+ */
+ error = VOP_BLKATOFF(dvp, (off_t)dp->i_offset, (char **)&ep, &bp);
+ if (error != 0)
+ return (error);
+ ep->e2d_ino = 0;
+ error = VOP_BWRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+ }
+ /*
+ * Collapse new free space into previous entry.
+ */
+ error = VOP_BLKATOFF(dvp, (off_t)(dp->i_offset - dp->i_count),
+ (char **)&ep, &bp);
+ if (error != 0)
+ return (error);
+ ep->e2d_reclen += dp->i_reclen;
+ error = VOP_BWRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+}
+
+/*
+ * Rewrite an existing directory entry to point at the inode
+ * supplied. The parameters describing the directory entry are
+ * set up by a call to namei.
+ */
+int
+ext2fs_dirrewrite(dp, ip, cnp)
+ struct inode *dp, *ip;
+ struct componentname *cnp;
+{
+ struct buf *bp;
+ struct ext2fs_direct *ep;
+ struct vnode *vdp = ITOV(dp);
+ int error;
+
+ error = VOP_BLKATOFF(vdp, (off_t)dp->i_offset, (char **)&ep, &bp);
+ if (error != 0)
+ return (error);
+ ep->e2d_ino = ip->i_number;
+ error = VOP_BWRITE(bp);
+ dp->i_flag |= IN_CHANGE | IN_UPDATE;
+ return (error);
+}
+
+/*
+ * Check if a directory is empty or not.
+ * Inode supplied must be locked.
+ *
+ * Using a struct dirtemplate here is not precisely
+ * what we want, but better than using a struct ext2fs_direct.
+ *
+ * NB: does not handle corrupted directories.
+ */
+int
+ext2fs_dirempty(ip, parentino, cred)
+ register struct inode *ip;
+ ino_t parentino;
+ struct ucred *cred;
+{
+ register off_t off;
+ struct ext2fs_dirtemplate dbuf;
+ register struct ext2fs_direct *dp = (struct ext2fs_direct *)&dbuf;
+ int error, count, namlen;
+
+#define MINDIRSIZ (sizeof (struct ext2fs_dirtemplate) / 2)
+
+ for (off = 0; off < ip->i_e2fs_size; off += dp->e2d_reclen) {
+ error = vn_rdwr(UIO_READ, ITOV(ip), (caddr_t)dp, MINDIRSIZ, off,
+ UIO_SYSSPACE, IO_NODELOCKED, cred, &count, (struct proc *)0);
+ /*
+ * Since we read MINDIRSIZ, residual must
+ * be 0 unless we're at end of file.
+ */
+ if (error || count != 0)
+ return (0);
+ /* avoid infinite loops */
+ if (dp->e2d_reclen == 0)
+ return (0);
+ /* skip empty entries */
+ if (dp->e2d_ino == 0)
+ continue;
+ /* accept only "." and ".." */
+ namlen = dp->e2d_namlen;
+ if (namlen > 2)
+ return (0);
+ if (dp->e2d_name[0] != '.')
+ return (0);
+ /*
+ * At this point namlen must be 1 or 2.
+ * 1 implies ".", 2 implies ".." if second
+ * char is also "."
+ */
+ if (namlen == 1)
+ continue;
+ if (dp->e2d_name[1] == '.' && dp->e2d_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
+ext2fs_checkpath(source, target, cred)
+ struct inode *source, *target;
+ struct ucred *cred;
+{
+ struct vnode *vp;
+ int error, rootino, namlen;
+ struct ext2fs_dirtemplate dirbuf;
+
+ vp = ITOV(target);
+ if (target->i_number == source->i_number) {
+ error = EEXIST;
+ goto out;
+ }
+ rootino = ROOTINO;
+ error = 0;
+ if (target->i_number == rootino)
+ goto out;
+
+ for (;;) {
+ if (vp->v_type != VDIR) {
+ error = ENOTDIR;
+ break;
+ }
+ error = vn_rdwr(UIO_READ, vp, (caddr_t)&dirbuf,
+ sizeof (struct ext2fs_dirtemplate), (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED, cred, (int *)0, (struct proc *)0);
+ if (error != 0)
+ break;
+ namlen = dirbuf.dotdot_namlen;
+ if (namlen != 2 ||
+ 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)
+ break;
+ vput(vp);
+ error = VFS_VGET(vp->v_mount, dirbuf.dotdot_ino, &vp);
+ if (error != 0) {
+ vp = NULL;
+ break;
+ }
+ }
+
+out:
+ if (error == ENOTDIR) {
+ printf("checkpath: .. not a directory\n");
+ panic("checkpath");
+ }
+ if (vp != NULL)
+ vput(vp);
+ return (error);
+}
+
diff --git a/sys/ufs/ext2fs/ext2fs_readwrite.c b/sys/ufs/ext2fs/ext2fs_readwrite.c
new file mode 100644
index 00000000000..a94e18deb80
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_readwrite.c
@@ -0,0 +1,298 @@
+/* $NetBSD: ufs_readwrite.c,v 1.10 1997/01/30 09:52:26 tls Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*-
+ * Copyright (c) 1993
+ * 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.
+ *
+ * @(#)ufs_readwrite.c 8.8 (Berkeley) 8/4/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/signalvar.h>
+
+#include <vm/vm.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+
+
+#define doclusterread 0 /* XXX underway */
+#define doclusterwrite 0
+
+/*
+ * Vnode op for reading.
+ */
+/* ARGSUSED */
+int
+ext2fs_read(v)
+ void *v;
+{
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap = v;
+ register struct vnode *vp;
+ register struct inode *ip;
+ register struct uio *uio;
+ register struct m_ext2fs *fs;
+ struct buf *bp;
+ daddr_t lbn, nextlbn;
+ off_t bytesinfile;
+ long size, xfersize, blkoffset;
+ int error;
+ u_short mode;
+
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+ mode = ip->i_e2fs_mode;
+ uio = ap->a_uio;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("%s: mode", "ext2fs_read");
+
+ if (vp->v_type == VLNK) {
+ if ((int)ip->i_e2fs_size < vp->v_mount->mnt_maxsymlinklen ||
+ (vp->v_mount->mnt_maxsymlinklen == 0 &&
+ ip->i_e2fs_nblock == 0))
+ panic("%s: short symlink", "ext2fs_read");
+ } else if (vp->v_type != VREG && vp->v_type != VDIR)
+ panic("%s: type %d", "ext2fs_read", vp->v_type);
+#endif
+ fs = ip->i_e2fs;
+ if ((u_int64_t)uio->uio_offset >
+ ((u_int64_t)0x80000000 * fs->e2fs_bsize - 1))
+ return (EFBIG);
+
+ for (error = 0, bp = NULL; uio->uio_resid > 0; bp = NULL) {
+ if ((bytesinfile = ip->i_e2fs_size - uio->uio_offset) <= 0)
+ break;
+ lbn = lblkno(fs, uio->uio_offset);
+ nextlbn = lbn + 1;
+ size = fs->e2fs_bsize;
+ blkoffset = blkoff(fs, uio->uio_offset);
+ xfersize = fs->e2fs_bsize - blkoffset;
+ if (uio->uio_resid < xfersize)
+ xfersize = uio->uio_resid;
+ if (bytesinfile < xfersize)
+ xfersize = bytesinfile;
+
+ if (lblktosize(fs, nextlbn) >= ip->i_e2fs_size)
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ else if (doclusterread)
+ error = cluster_read(vp,
+ ip->i_e2fs_size, lbn, size, NOCRED, &bp);
+ else if (lbn - 1 == vp->v_lastr) {
+ int nextsize = fs->e2fs_bsize;
+ error = breadn(vp, lbn,
+ size, &nextlbn, &nextsize, 1, NOCRED, &bp);
+ } else
+ error = bread(vp, lbn, size, NOCRED, &bp);
+ if (error)
+ break;
+ vp->v_lastr = lbn;
+
+ /*
+ * 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;
+ brelse(bp);
+ }
+ if (bp != NULL)
+ brelse(bp);
+ if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
+ ip->i_flag |= IN_ACCESS;
+ return (error);
+}
+
+/*
+ * Vnode op for writing.
+ */
+int
+ext2fs_write(v)
+ void *v;
+{
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap = v;
+ register struct vnode *vp;
+ register struct uio *uio;
+ register struct inode *ip;
+ register struct m_ext2fs *fs;
+ struct buf *bp;
+ struct proc *p;
+ daddr_t lbn;
+ off_t osize;
+ int blkoffset, error, flags, ioflag, resid, size, xfersize;
+ struct timespec ts;
+
+ ioflag = ap->a_ioflag;
+ uio = ap->a_uio;
+ vp = ap->a_vp;
+ ip = VTOI(vp);
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_WRITE)
+ panic("%s: mode", "ext2fs_write");
+#endif
+
+ switch (vp->v_type) {
+ case VREG:
+ if (ioflag & IO_APPEND)
+ uio->uio_offset = ip->i_e2fs_size;
+ if ((ip->i_e2fs_flags & EXT2_APPEND) &&
+ uio->uio_offset != ip->i_e2fs_size)
+ return (EPERM);
+ /* FALLTHROUGH */
+ case VLNK:
+ break;
+ case VDIR:
+ if ((ioflag & IO_SYNC) == 0)
+ panic("%s: nonsync dir write", "ext2fs_write");
+ break;
+ default:
+ panic("%s: type", "ext2fs_write");
+ }
+
+ fs = ip->i_e2fs;
+ if (uio->uio_offset < 0 ||
+ (u_int64_t)uio->uio_offset + uio->uio_resid >
+ ((u_int64_t)0x80000000 * fs->e2fs_bsize - 1))
+ return (EFBIG);
+ /*
+ * Maybe this should be above the vnode op call, but so long as
+ * file servers have no limits, I don't think it matters.
+ */
+ p = uio->uio_procp;
+ if (vp->v_type == VREG && p &&
+ uio->uio_offset + uio->uio_resid >
+ p->p_rlimit[RLIMIT_FSIZE].rlim_cur) {
+ psignal(p, SIGXFSZ);
+ return (EFBIG);
+ }
+
+ resid = uio->uio_resid;
+ osize = ip->i_e2fs_size;
+ flags = ioflag & IO_SYNC ? B_SYNC : 0;
+
+ for (error = 0; uio->uio_resid > 0;) {
+ lbn = lblkno(fs, uio->uio_offset);
+ blkoffset = blkoff(fs, uio->uio_offset);
+ xfersize = fs->e2fs_bsize - blkoffset;
+ if (uio->uio_resid < xfersize)
+ xfersize = uio->uio_resid;
+ if (fs->e2fs_bsize > xfersize)
+ flags |= B_CLRBUF;
+ else
+ flags &= ~B_CLRBUF;
+
+ error = ext2fs_balloc(ip,
+ lbn, blkoffset + xfersize, ap->a_cred, &bp, flags);
+ if (error)
+ break;
+ if (uio->uio_offset + xfersize > ip->i_e2fs_size) {
+ ip->i_e2fs_size = uio->uio_offset + xfersize;
+ vnode_pager_setsize(vp, (u_long)ip->i_e2fs_size);
+ }
+ (void)vnode_pager_uncache(vp);
+
+ size = fs->e2fs_bsize - bp->b_resid;
+ if (size < xfersize)
+ xfersize = size;
+
+ error =
+ uiomove((char *)bp->b_data + blkoffset, (int)xfersize, uio);
+ if (ioflag & IO_SYNC)
+ (void)bwrite(bp);
+ else if (xfersize + blkoffset == fs->e2fs_bsize)
+ if (doclusterwrite)
+ cluster_write(bp, ip->i_e2fs_size);
+ else
+ bawrite(bp);
+ else
+ bdwrite(bp);
+ if (error || xfersize == 0)
+ break;
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ }
+ /*
+ * 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_e2fs_mode &= ~(ISUID | ISGID);
+ if (error) {
+ if (ioflag & IO_UNIT) {
+ (void)VOP_TRUNCATE(vp, osize,
+ ioflag & IO_SYNC, ap->a_cred, uio->uio_procp);
+ uio->uio_offset -= resid - uio->uio_resid;
+ uio->uio_resid = resid;
+ }
+ } else if (resid > uio->uio_resid && (ioflag & IO_SYNC)) {
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ error = VOP_UPDATE(vp, &ts, &ts, 1);
+ }
+ return (error);
+}
diff --git a/sys/ufs/ext2fs/ext2fs_subr.c b/sys/ufs/ext2fs/ext2fs_subr.c
new file mode 100644
index 00000000000..0c6ce13c12f
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_subr.c
@@ -0,0 +1,120 @@
+/* $NetBSD: ffs_subr.c,v 1.9 1996/10/12 21:58:45 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * 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.
+ *
+ * @(#)ffs_subr.c 8.2 (Berkeley) 9/21/93
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+
+#ifdef _KERNEL
+#include <sys/vnode.h>
+#include <sys/buf.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+
+/*
+ * 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
+ext2fs_blkatoff(v)
+ void *v;
+{
+ struct vop_blkatoff_args /* {
+ struct vnode *a_vp;
+ off_t a_offset;
+ char **a_res;
+ struct buf **a_bpp;
+ } */ *ap = v;
+ struct inode *ip;
+ register struct m_ext2fs *fs;
+ struct buf *bp;
+ daddr_t lbn;
+ int error;
+
+ ip = VTOI(ap->a_vp);
+ fs = ip->i_e2fs;
+ lbn = lblkno(fs, ap->a_offset);
+
+ *ap->a_bpp = NULL;
+ if ((error = bread(ap->a_vp, lbn, fs->e2fs_bsize, NOCRED, &bp)) != 0) {
+ brelse(bp);
+ return (error);
+ }
+ if (ap->a_res)
+ *ap->a_res = (char *)bp->b_data + blkoff(fs, ap->a_offset);
+ *ap->a_bpp = bp;
+ return (0);
+}
+#endif
+
+#if defined(_KERNEL) && defined(DIAGNOSTIC)
+void
+ext2fs_checkoverlap(bp, ip)
+ struct buf *bp;
+ struct inode *ip;
+{
+ register struct buf *ebp, *ep;
+ register daddr_t start, last;
+ struct vnode *vp;
+
+ ebp = &buf[nbuf];
+ start = bp->b_blkno;
+ last = start + btodb(bp->b_bcount) - 1;
+ for (ep = buf; ep < ebp; ep++) {
+ if (ep == bp || (ep->b_flags & B_INVAL) ||
+ ep->b_vp == NULLVP)
+ continue;
+ if (VOP_BMAP(ep->b_vp, (daddr_t)0, &vp, (daddr_t)0, NULL))
+ continue;
+ if (vp != ip->i_devvp)
+ continue;
+ /* look for overlap */
+ if (ep->b_bcount == 0 || ep->b_blkno > last ||
+ ep->b_blkno + btodb(ep->b_bcount) <= start)
+ continue;
+ vprint("Disk overlap", vp);
+ printf("\tstart %d, end %d overlap start %d, end %ld\n",
+ start, last, ep->b_blkno,
+ ep->b_blkno + btodb(ep->b_bcount) - 1);
+ panic("Disk buffer overlap");
+ }
+}
+#endif /* DIAGNOSTIC */
diff --git a/sys/ufs/ext2fs/ext2fs_vfsops.c b/sys/ufs/ext2fs/ext2fs_vfsops.c
new file mode 100644
index 00000000000..bbfa736b6bc
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_vfsops.c
@@ -0,0 +1,996 @@
+/* $NetBSD: ffs_vfsops.c,v 1.21 1996/10/12 21:58:47 christos Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1989, 1991, 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.
+ *
+ * @(#)ffs_vfsops.c 8.14 (Berkeley) 11/28/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/namei.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/mbuf.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/ufsmount.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ufs/ufs_extern.h>
+
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+
+int ext2fs_sbupdate __P((struct ufsmount *, int));
+int ext2fs_check_export __P((struct mount *, struct ufid *, struct mbuf *,
+ struct vnode **, int *, struct ucred **));
+
+struct vfsops ext2fs_vfsops = {
+ MOUNT_EXT2FS,
+ ext2fs_mount,
+ ufs_start,
+ ext2fs_unmount,
+ ufs_root,
+ ufs_quotactl,
+ ext2fs_statfs,
+ ext2fs_sync,
+ ext2fs_vget,
+ ext2fs_fhtovp,
+ ext2fs_vptofh,
+ ext2fs_init,
+ ext2fs_mountroot,
+};
+
+extern u_long ext2gennumber;
+
+/*
+ * This is the generic part of fhtovp called after the underlying
+ * filesystem has validated the file handle.
+ *
+ * Verify that a host should have access to a filesystem, and if so
+ * return a vnode for the presented file handle.
+ */
+int
+ext2fs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct ufid *ufhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ register struct inode *ip;
+ register struct netcred *np;
+ register struct ufsmount *ump = VFSTOUFS(mp);
+ struct vnode *nvp;
+ int error;
+
+ /*
+ * Get the export permission structure for this <mp, client> tuple.
+ */
+ np = vfs_export_lookup(mp, &ump->um_export, nam);
+ if (np == NULL)
+ return (EACCES);
+
+ if ((error = VFS_VGET(mp, ufhp->ufid_ino, &nvp)) != 0) {
+ *vpp = NULLVP;
+ return (error);
+ }
+ ip = VTOI(nvp);
+ if (ip->i_e2fs_mode == 0 || ip->i_e2fs_dtime != 0 ||
+ ip->i_e2fs_gen != ufhp->ufid_gen) {
+ vput(nvp);
+ *vpp = NULLVP;
+ return (ESTALE);
+ }
+ *vpp = nvp;
+ *exflagsp = np->netc_exflags;
+ *credanonp = &np->netc_anon;
+ return (0);
+}
+
+
+/*
+ * Called by main() when ext2fs is going to be mounted as root.
+ *
+ * Name is updated by mount(8) after booting.
+ */
+#define ROOTNAME "root_device"
+
+int
+ext2fs_mountroot()
+{
+ extern struct vnode *rootvp;
+ register struct m_ext2fs *fs;
+ register struct mount *mp;
+ struct proc *p = curproc; /* XXX */
+ struct ufsmount *ump;
+ size_t size;
+ int error;
+
+ if (root_device->dv_class != DV_DISK)
+ return (ENODEV);
+
+ /*
+ * Get vnodes for swapdev and rootdev.
+ */
+ if (bdevvp(swapdev, &swapdev_vp) || bdevvp(rootdev, &rootvp))
+ panic("ext2fs_mountroot: can't setup bdevvp's");
+
+ mp = malloc(sizeof(struct mount), M_MOUNT, M_WAITOK);
+ bzero((char *)mp, sizeof(struct mount));
+ mp->mnt_op = &ext2fs_vfsops;
+ mp->mnt_flag = MNT_RDONLY;
+ if ((error = ext2fs_mountfs(rootvp, mp, p)) != 0) {
+ free(mp, M_MOUNT);
+ return (error);
+ }
+ if ((error = vfs_lock(mp)) != 0) {
+ (void)ext2fs_unmount(mp, 0, p);
+ free(mp, M_MOUNT);
+ return (error);
+ }
+ CIRCLEQ_INSERT_TAIL(&mountlist, mp, mnt_list);
+ mp->mnt_vnodecovered = NULLVP;
+ ump = VFSTOUFS(mp);
+ fs = ump->um_e2fs;
+ bzero(fs->e2fs_fsmnt, sizeof(fs->e2fs_fsmnt));
+ fs->e2fs_fsmnt[0] = '/';
+ bcopy(fs->e2fs_fsmnt, mp->mnt_stat.f_mntonname, MNAMELEN);
+ (void) copystr(ROOTNAME, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ (void)ext2fs_statfs(mp, &mp->mnt_stat, p);
+ vfs_unlock(mp);
+ inittodr(fs->e2fs.e2fs_wtime);
+ return (0);
+}
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+int
+ext2fs_mount(mp, path, data, ndp, p)
+ register struct mount *mp;
+ const char *path;
+ void * data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct vnode *devvp;
+ struct ufs_args args;
+ struct ufsmount *ump = NULL;
+ register struct m_ext2fs *fs;
+ size_t size;
+ int error, flags;
+ mode_t accessmode;
+
+ error = copyin(data, (caddr_t)&args, sizeof (struct ufs_args));
+ if (error)
+ return (error);
+ /*
+ * If updating, check whether changing from read-only to
+ * read/write; if there is no device name, that's all we do.
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ ump = VFSTOUFS(mp);
+ fs = ump->um_e2fs;
+ if (fs->e2fs_ronly == 0 && (mp->mnt_flag & MNT_RDONLY)) {
+ flags = WRITECLOSE;
+ if (mp->mnt_flag & MNT_FORCE)
+ flags |= FORCECLOSE;
+ if (vfs_busy(mp))
+ return (EBUSY);
+ error = ext2fs_flushfiles(mp, flags, p);
+ if (error == 0 &&
+ ext2fs_cgupdate(ump, MNT_WAIT) == 0 &&
+ (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) {
+ fs->e2fs.e2fs_state = E2FS_ISCLEAN;
+ (void) ext2fs_sbupdate(ump, MNT_WAIT);
+ }
+ vfs_unbusy(mp);
+ if (error)
+ return (error);
+ fs->e2fs_ronly = 1;
+ }
+ if (mp->mnt_flag & MNT_RELOAD) {
+ error = ext2fs_reload(mp, ndp->ni_cnd.cn_cred, p);
+ if (error)
+ return (error);
+ }
+ if (fs->e2fs_ronly && (mp->mnt_flag & MNT_WANTRDWR)) {
+ /*
+ * If upgrade to read-write by non-root, then verify
+ * that user has necessary permissions on the device.
+ */
+ if (p->p_ucred->cr_uid != 0) {
+ devvp = ump->um_devvp;
+ VOP_LOCK(devvp);
+ error = VOP_ACCESS(devvp, VREAD | VWRITE,
+ p->p_ucred, p);
+ if (error) {
+ VOP_UNLOCK(devvp);
+ return (error);
+ }
+ VOP_UNLOCK(devvp);
+ }
+ fs->e2fs_ronly = 0;
+ if (fs->e2fs.e2fs_state == E2FS_ISCLEAN)
+ fs->e2fs.e2fs_state = 0;
+ else
+ fs->e2fs.e2fs_state = E2FS_ERRORS;
+ fs->e2fs_fmod = 1;
+ }
+ if (args.fspec == 0) {
+ /*
+ * Process export requests.
+ */
+ return (vfs_export(mp, &ump->um_export, &args.export));
+ }
+ }
+ /*
+ * Not an update, or updating the name: look up the name
+ * and verify that it refers to a sensible block device.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, args.fspec, p);
+ if ((error = namei(ndp)) != 0)
+ return (error);
+ devvp = ndp->ni_vp;
+
+ if (devvp->v_type != VBLK) {
+ vrele(devvp);
+ return (ENOTBLK);
+ }
+ if (major(devvp->v_rdev) >= nblkdev) {
+ vrele(devvp);
+ return (ENXIO);
+ }
+ /*
+ * If mount by non-root, then verify that user has necessary
+ * permissions on the device.
+ */
+ if (p->p_ucred->cr_uid != 0) {
+ accessmode = VREAD;
+ if ((mp->mnt_flag & MNT_RDONLY) == 0)
+ accessmode |= VWRITE;
+ VOP_LOCK(devvp);
+ error = VOP_ACCESS(devvp, accessmode, p->p_ucred, p);
+ if (error) {
+ vput(devvp);
+ return (error);
+ }
+ VOP_UNLOCK(devvp);
+ }
+ if ((mp->mnt_flag & MNT_UPDATE) == 0)
+ error = ext2fs_mountfs(devvp, mp, p);
+ else {
+ if (devvp != ump->um_devvp)
+ error = EINVAL; /* needs translation */
+ else
+ vrele(devvp);
+ }
+ if (error) {
+ vrele(devvp);
+ return (error);
+ }
+ ump = VFSTOUFS(mp);
+ fs = ump->um_e2fs;
+ (void) copyinstr(path, fs->e2fs_fsmnt, sizeof(fs->e2fs_fsmnt) - 1, &size);
+ bzero(fs->e2fs_fsmnt + size, sizeof(fs->e2fs_fsmnt) - size);
+ bcopy(fs->e2fs_fsmnt, mp->mnt_stat.f_mntonname, MNAMELEN);
+ (void) copyinstr(args.fspec, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ if (fs->e2fs_fmod != 0) { /* XXX */
+ fs->e2fs_fmod = 0;
+ if (fs->e2fs.e2fs_state == 0)
+ fs->e2fs.e2fs_wtime = time.tv_sec;
+ else
+ printf("%s: file system not clean; please fsck(8)\n",
+ mp->mnt_stat.f_mntfromname);
+ (void) ext2fs_cgupdate(ump, MNT_WAIT);
+ }
+ return (0);
+}
+
+/*
+ * Reload all incore data for a filesystem (used after running fsck on
+ * the root filesystem and finding things to fix). The filesystem must
+ * be mounted read-only.
+ *
+ * Things to do to update the mount:
+ * 1) invalidate all cached meta-data.
+ * 2) re-read superblock from disk.
+ * 3) re-read summary information from disk.
+ * 4) invalidate all inactive vnodes.
+ * 5) invalidate all cached file data.
+ * 6) re-read inode data for all active vnodes.
+ */
+int
+ext2fs_reload(mountp, cred, p)
+ register struct mount *mountp;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register struct vnode *vp, *nvp, *devvp;
+ struct inode *ip;
+ struct buf *bp;
+ struct m_ext2fs *fs;
+ struct ext2fs *newfs;
+ struct partinfo dpart;
+ int i, size, error;
+
+ if ((mountp->mnt_flag & MNT_RDONLY) == 0)
+ return (EINVAL);
+ /*
+ * Step 1: invalidate all cached meta-data.
+ */
+ devvp = VFSTOUFS(mountp)->um_devvp;
+ if (vinvalbuf(devvp, 0, cred, p, 0, 0))
+ panic("ext2fs_reload: dirty1");
+ /*
+ * Step 2: re-read superblock from disk.
+ */
+ if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, NOCRED, p) != 0)
+ size = DEV_BSIZE;
+ else
+ size = dpart.disklab->d_secsize;
+ error = bread(devvp, (daddr_t)(SBOFF / size), SBSIZE, NOCRED, &bp);
+ if (error)
+ return (error);
+ newfs = (struct ext2fs *)bp->b_data;
+ if (newfs->e2fs_magic != E2FS_MAGIC || newfs->e2fs_rev != E2FS_REV) {
+#ifdef DIAGNOSTIC
+ printf("Wrong magic number: %x (expected %x for ext2 fs)",
+ newfs->e2fs_magic, E2FS_MAGIC);
+ printf("or wrong revision number: %x (expected %x for ext2 fs)",
+ newfs->e2fs_rev, E2FS_REV);
+#endif
+ brelse(bp);
+ return (EIO); /* XXX needs translation */
+ }
+ if (newfs->e2fs_log_bsize > 2) { /* block size = 1024|2048|4096 */
+#ifdef DIAGNOSTIC
+ printf("wrong block size: %d (expected <=2 for ext2 fs)\n",
+ newfs->e2fs_log_bsize);
+#endif
+ brelse(bp);
+ return (EIO); /* XXX needs translation */
+ }
+
+
+ fs = VFSTOUFS(mountp)->um_e2fs;
+ /*
+ * copy in new superblock, and compute in-memory values
+ */
+ bcopy(newfs, &fs->e2fs, SBSIZE);
+ fs->e2fs_ncg = howmany(fs->e2fs.e2fs_bcount - fs->e2fs.e2fs_first_dblock,
+ fs->e2fs.e2fs_bpg);
+ /* XXX assume hw bsize = 512 */
+ fs->e2fs_fsbtodb = fs->e2fs.e2fs_log_bsize + 1;
+ fs->e2fs_bsize = 1024 << fs->e2fs.e2fs_log_bsize;
+ fs->e2fs_bshift = LOG_MINBSIZE + fs->e2fs.e2fs_log_bsize;
+ fs->e2fs_qbmask = fs->e2fs_bsize - 1;
+ fs->e2fs_bmask = ~fs->e2fs_qbmask;
+ fs->e2fs_ngdb = howmany(fs->e2fs_ncg,
+ fs->e2fs_bsize / sizeof(struct ext2_gd));
+ fs->e2fs_ipb = fs->e2fs_bsize / sizeof(struct ext2fs_dinode);
+ fs->e2fs_itpg = fs->e2fs.e2fs_ipg/fs->e2fs_ipb;
+
+ /*
+ * Step 3: re-read summary information from disk.
+ */
+
+ for (i=0; i < fs->e2fs_ngdb; i++) {
+ error = bread(devvp , fsbtodb(fs, ((fs->e2fs_bsize>1024)?0:1)+i+1),
+ fs->e2fs_bsize, NOCRED, &bp);
+ if (error)
+ return (error);
+ bcopy(bp->b_data,
+ &fs->e2fs_gd[i* fs->e2fs_bsize / sizeof(struct ext2_gd)],
+ fs->e2fs_bsize);
+ brelse(bp);
+ }
+
+loop:
+ for (vp = mountp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) {
+ nvp = vp->v_mntvnodes.le_next;
+ /*
+ * Step 4: invalidate all inactive vnodes.
+ */
+ if (vp->v_usecount == 0) {
+ vgone(vp);
+ continue;
+ }
+ /*
+ * Step 5: invalidate all cached file data.
+ */
+ if (vget(vp, 1))
+ goto loop;
+ if (vinvalbuf(vp, 0, cred, p, 0, 0))
+ panic("ext2fs_reload: dirty2");
+ /*
+ * Step 6: re-read inode data for all active vnodes.
+ */
+ ip = VTOI(vp);
+ error = bread(devvp, fsbtodb(fs, ino_to_fsba(fs, ip->i_number)),
+ (int)fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+ bcopy((struct ext2fs_dinode *)bp->b_data +
+ ino_to_fsbo(fs, ip->i_number),
+ &ip->i_din.e2fs_din, sizeof(struct ext2fs_dinode));
+ brelse(bp);
+ vput(vp);
+ if (vp->v_mount != mountp)
+ goto loop;
+ }
+ return (0);
+}
+
+/*
+ * Common code for mount and mountroot
+ */
+int
+ext2fs_mountfs(devvp, mp, p)
+ register struct vnode *devvp;
+ struct mount *mp;
+ struct proc *p;
+{
+ register struct ufsmount *ump;
+ struct buf *bp;
+ register struct ext2fs *fs;
+ register struct m_ext2fs *m_fs;
+ dev_t dev;
+ struct partinfo dpart;
+ int error, i, size, ronly;
+ struct ucred *cred;
+ extern struct vnode *rootvp;
+
+ dev = devvp->v_rdev;
+ cred = p ? p->p_ucred : NOCRED;
+ /*
+ * 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)
+ return (error);
+ if (vcount(devvp) > 1 && devvp != rootvp)
+ return (EBUSY);
+ if ((error = vinvalbuf(devvp, V_SAVE, cred, p, 0, 0)) != 0)
+ return (error);
+
+ ronly = (mp->mnt_flag & MNT_RDONLY) != 0;
+ error = VOP_OPEN(devvp, ronly ? FREAD : FREAD|FWRITE, FSCRED, p);
+ if (error)
+ return (error);
+ if (VOP_IOCTL(devvp, DIOCGPART, (caddr_t)&dpart, FREAD, cred, p) != 0)
+ size = DEV_BSIZE;
+ else
+ size = dpart.disklab->d_secsize;
+
+ bp = NULL;
+ ump = NULL;
+
+#ifdef DEBUG_EXT2
+ printf("sb size: %d ino size %d\n", sizeof(struct ext2fs),
+ sizeof(struct ext2fs_dinode));
+#endif
+ error = bread(devvp, (SBOFF / DEV_BSIZE), SBSIZE, cred, &bp);
+ if (error)
+ goto out;
+ fs = (struct ext2fs *)bp->b_data;
+ if (fs->e2fs_magic != E2FS_MAGIC || fs->e2fs_rev != E2FS_REV) {
+#ifdef DIAGNOSTIC
+ printf("Wrong magic number: %x (expected %x for ext2 fs)",
+ fs->e2fs_magic, E2FS_MAGIC);
+ printf(" or wrong revision number: %x (expected %x for ext2 fs)\n",
+ fs->e2fs_rev, E2FS_REV);
+#endif
+ error = EINVAL; /* XXX needs translation */
+ goto out;
+ }
+
+ if (fs->e2fs_log_bsize > 2) { /* block size = 1024|2048|4096 */
+#ifdef DIAGNOSTIC
+ printf("wrong block size: %d (expected <2 for ext2 fs)\n",
+ fs->e2fs_log_bsize);
+#endif
+ error = EINVAL; /* XXX needs translation */
+ goto out;
+ }
+
+ ump = malloc(sizeof *ump, M_UFSMNT, M_WAITOK);
+ bzero((caddr_t)ump, sizeof *ump);
+ ump->um_e2fs = malloc(sizeof(struct m_ext2fs), M_UFSMNT, M_WAITOK);
+ bcopy(bp->b_data, ump->um_e2fs, SBSIZE);
+ brelse(bp);
+ bp = NULL;
+ m_fs = ump->um_e2fs;
+ m_fs->e2fs_ronly = ronly;
+ if (ronly == 0) {
+ if (m_fs->e2fs.e2fs_state == E2FS_ISCLEAN)
+ m_fs->e2fs.e2fs_state = 0;
+ else
+ m_fs->e2fs.e2fs_state = E2FS_ERRORS;
+ m_fs->e2fs_fmod = 1;
+ }
+
+ /* compute dynamic sb infos */
+ m_fs->e2fs_ncg =
+ howmany(m_fs->e2fs.e2fs_bcount - m_fs->e2fs.e2fs_first_dblock,
+ m_fs->e2fs.e2fs_bpg);
+ /* XXX assume hw bsize = 512 */
+ m_fs->e2fs_fsbtodb = m_fs->e2fs.e2fs_log_bsize + 1;
+ m_fs->e2fs_bsize = 1024 << m_fs->e2fs.e2fs_log_bsize;
+ m_fs->e2fs_bshift = LOG_MINBSIZE + m_fs->e2fs.e2fs_log_bsize;
+ m_fs->e2fs_qbmask = m_fs->e2fs_bsize - 1;
+ m_fs->e2fs_bmask = ~m_fs->e2fs_qbmask;
+ m_fs->e2fs_ngdb = howmany(m_fs->e2fs_ncg,
+ m_fs->e2fs_bsize / sizeof(struct ext2_gd));
+ m_fs->e2fs_ipb = m_fs->e2fs_bsize / sizeof(struct ext2fs_dinode);
+ m_fs->e2fs_itpg = m_fs->e2fs.e2fs_ipg/m_fs->e2fs_ipb;
+
+ m_fs->e2fs_gd = malloc(m_fs->e2fs_ngdb * m_fs->e2fs_bsize,
+ M_UFSMNT, M_WAITOK);
+ for (i=0; i < m_fs->e2fs_ngdb; i++) {
+ error = bread(devvp , fsbtodb(m_fs, ((m_fs->e2fs_bsize>1024)?0:1)+i+1),
+ m_fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ free(m_fs->e2fs_gd, M_UFSMNT);
+ goto out;
+ }
+ bcopy(bp->b_data,
+ &m_fs->e2fs_gd[i* m_fs->e2fs_bsize / sizeof(struct ext2_gd)],
+ m_fs->e2fs_bsize);
+ brelse(bp);
+ bp = NULL;
+ }
+
+ mp->mnt_data = (qaddr_t)ump;
+ mp->mnt_stat.f_fsid.val[0] = (long)dev;
+ mp->mnt_stat.f_fsid.val[1] = makefstype(MOUNT_EXT2FS);
+ mp->mnt_maxsymlinklen = EXT2_MAXSYMLINKLEN;
+ mp->mnt_flag |= MNT_LOCAL;
+ ump->um_mountp = mp;
+ ump->um_dev = dev;
+ ump->um_devvp = devvp;
+ ump->um_nindir = NINDIR(m_fs);
+ ump->um_bptrtodb = m_fs->e2fs_fsbtodb;
+ ump->um_seqinc = 1; /* no frags */
+ devvp->v_specflags |= SI_MOUNTEDON;
+ return (0);
+out:
+ if (bp)
+ brelse(bp);
+ (void)VOP_CLOSE(devvp, ronly ? FREAD : FREAD|FWRITE, cred, p);
+ if (ump) {
+ free(ump->um_e2fs, M_UFSMNT);
+ free(ump, M_UFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ }
+ return (error);
+}
+
+/*
+ * unmount system call
+ */
+int
+ext2fs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ register struct ufsmount *ump;
+ register struct m_ext2fs *fs;
+ int error, flags;
+
+ flags = 0;
+ if (mntflags & MNT_FORCE)
+ flags |= FORCECLOSE;
+ if ((error = ext2fs_flushfiles(mp, flags, p)) != 0)
+ return (error);
+ ump = VFSTOUFS(mp);
+ fs = ump->um_e2fs;
+ if (fs->e2fs_ronly == 0 &&
+ ext2fs_cgupdate(ump, MNT_WAIT) == 0 &&
+ (fs->e2fs.e2fs_state & E2FS_ERRORS) == 0) {
+ fs->e2fs.e2fs_state = E2FS_ISCLEAN;
+ (void) ext2fs_sbupdate(ump, MNT_WAIT);
+ }
+ ump->um_devvp->v_specflags &= ~SI_MOUNTEDON;
+ error = VOP_CLOSE(ump->um_devvp, fs->e2fs_ronly ? FREAD : FREAD|FWRITE,
+ NOCRED, p);
+ vrele(ump->um_devvp);
+ free(fs->e2fs_gd, M_UFSMNT);
+ free(fs, M_UFSMNT);
+ free(ump, M_UFSMNT);
+ mp->mnt_data = (qaddr_t)0;
+ mp->mnt_flag &= ~MNT_LOCAL;
+ return (error);
+}
+
+/*
+ * Flush out all the files in a filesystem.
+ */
+int
+ext2fs_flushfiles(mp, flags, p)
+ register struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ extern int doforce;
+ register struct ufsmount *ump;
+ int error;
+
+ if (!doforce)
+ flags &= ~FORCECLOSE;
+ ump = VFSTOUFS(mp);
+ error = vflush(mp, NULLVP, flags);
+ return (error);
+}
+
+/*
+ * Get file system statistics.
+ */
+int
+ext2fs_statfs(mp, sbp, p)
+ struct mount *mp;
+ register struct statfs *sbp;
+ struct proc *p;
+{
+ register struct ufsmount *ump;
+ register struct m_ext2fs *fs;
+ u_int32_t overhead, overhead_per_group;
+
+ ump = VFSTOUFS(mp);
+ fs = ump->um_e2fs;
+ if (fs->e2fs.e2fs_magic != E2FS_MAGIC)
+ panic("ext2fs_statfs");
+#ifdef COMPAT_09
+ sbp->f_type = 1;
+#else
+ sbp->f_type = 0;
+#endif
+
+ /*
+ * Compute the overhead (FS structures)
+ */
+ overhead_per_group = 1 /* super block */ +
+ fs->e2fs_ngdb +
+ 1 /* block bitmap */ +
+ 1 /* inode bitmap */ +
+ fs->e2fs_itpg;
+ overhead = fs->e2fs.e2fs_first_dblock +
+ fs->e2fs_ncg * overhead_per_group;
+
+
+ sbp->f_bsize = fs->e2fs_bsize;
+ sbp->f_iosize = fs->e2fs_bsize;
+ sbp->f_blocks = fs->e2fs.e2fs_bcount - overhead;
+ sbp->f_bfree = fs->e2fs.e2fs_fbcount;
+ sbp->f_bavail = sbp->f_bfree - fs->e2fs.e2fs_rbcount;
+ sbp->f_files = fs->e2fs.e2fs_icount;
+ sbp->f_ffree = fs->e2fs.e2fs_ficount;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(mp->mnt_stat.f_mntonname, sbp->f_mntonname, MNAMELEN);
+ bcopy(mp->mnt_stat.f_mntfromname, sbp->f_mntfromname, MNAMELEN);
+ }
+ strncpy(sbp->f_fstypename, mp->mnt_op->vfs_name, MFSNAMELEN);
+ return (0);
+}
+
+/*
+ * Go through the disk queues to initiate sandbagged IO;
+ * go through the inodes to write those that have been modified;
+ * initiate the writing of the super block if it has been modified.
+ *
+ * Note: we are always called with the filesystem marked `MPBUSY'.
+ */
+int
+ext2fs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register struct vnode *vp;
+ register struct inode *ip;
+ register struct ufsmount *ump = VFSTOUFS(mp);
+ register struct m_ext2fs *fs;
+ int error, allerror = 0;
+
+ fs = ump->um_e2fs;
+ /*
+ * Write back modified superblock.
+ * Consistency check that the superblock
+ * is still in the buffer cache.
+ */
+ if (fs->e2fs_fmod != 0) {
+ if (fs->e2fs_ronly != 0) { /* XXX */
+ printf("fs = %s\n", fs->e2fs_fsmnt);
+ panic("update: rofs mod");
+ }
+ fs->e2fs_fmod = 0;
+ fs->e2fs.e2fs_wtime = time.tv_sec;
+ allerror = ext2fs_cgupdate(ump, waitfor);
+ }
+ /*
+ * Write back each (modified) inode.
+ */
+loop:
+ for (vp = mp->mnt_vnodelist.lh_first;
+ vp != NULL;
+ vp = vp->v_mntvnodes.le_next) {
+ /*
+ * If the vnode that we are about to sync is no longer
+ * associated with this mount point, start over.
+ */
+ if (vp->v_mount != mp)
+ goto loop;
+ if (VOP_ISLOCKED(vp))
+ continue;
+ ip = VTOI(vp);
+ if ((ip->i_flag &
+ (IN_ACCESS | IN_CHANGE | IN_MODIFIED | IN_UPDATE)) == 0 &&
+ vp->v_dirtyblkhd.lh_first == NULL)
+ continue;
+ if (vget(vp, 1))
+ goto loop;
+ if ((error = VOP_FSYNC(vp, cred, waitfor, p)) != 0)
+ allerror = error;
+ vput(vp);
+ }
+ /*
+ * Force stale file system control information to be flushed.
+ */
+ if ((error = VOP_FSYNC(ump->um_devvp, cred, waitfor, p)) != 0)
+ allerror = error;
+ return (allerror);
+}
+
+/*
+ * Look up a EXT2FS dinode number to find its incore vnode, otherwise read it
+ * in from disk. If it is in core, wait for the lock bit to clear, then
+ * return the inode locked. Detection and handling of mount points must be
+ * done by the calling routine.
+ */
+int
+ext2fs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+ register struct m_ext2fs *fs;
+ register struct inode *ip;
+ struct ufsmount *ump;
+ struct buf *bp;
+ struct vnode *vp;
+ dev_t dev;
+ int error;
+
+ ump = VFSTOUFS(mp);
+ dev = ump->um_dev;
+ if ((*vpp = ufs_ihashget(dev, ino)) != NULL)
+ return (0);
+
+ /* Allocate a new vnode/inode. */
+ if ((error = getnewvnode(VT_EXT2FS, mp, ext2fs_vnodeop_p, &vp)) != 0) {
+ *vpp = NULL;
+ return (error);
+ }
+ MALLOC(ip, struct inode *, sizeof(struct inode), M_EXT2FSNODE, M_WAITOK);
+ bzero((caddr_t)ip, sizeof(struct inode));
+ vp->v_data = ip;
+ ip->i_vnode = vp;
+ ip->i_e2fs = fs = ump->um_e2fs;
+ ip->i_dev = dev;
+ ip->i_number = ino;
+ ip->i_e2fs_last_lblk = 0;
+ ip->i_e2fs_last_blk = 0;
+
+ /*
+ * Put it onto its hash chain and lock it so that other requests for
+ * this inode will block if they arrive while we are sleeping waiting
+ * for old data structures to be purged or for the contents of the
+ * disk portion of this inode to be read.
+ */
+ ufs_ihashins(ip);
+
+ /* Read in the disk contents for the inode, copy into the inode. */
+ error = bread(ump->um_devvp, fsbtodb(fs, ino_to_fsba(fs, ino)),
+ (int)fs->e2fs_bsize, NOCRED, &bp);
+ if (error) {
+ /*
+ * The inode does not contain anything useful, so it would
+ * be misleading to leave it on its hash chain. With mode
+ * still zero, it will be unlinked and returned to the free
+ * list by vput().
+ */
+ vput(vp);
+ brelse(bp);
+ *vpp = NULL;
+ return (error);
+ }
+ bcopy(((struct ext2fs_dinode*)bp->b_data + ino_to_fsbo(fs, ino)),
+ &ip->i_din, sizeof(struct ext2fs_dinode));
+
+ brelse(bp);
+
+ /*
+ * Initialize the vnode from the inode, check for aliases.
+ * Note that the underlying vnode may have changed.
+ */
+ error = ufs_vinit(mp, ext2fs_specop_p, EXT2FS_FIFOOPS, &vp);
+ if (error) {
+ vput(vp);
+ *vpp = NULL;
+ return (error);
+ }
+ /*
+ * Finish inode initialization now that aliasing has been resolved.
+ */
+ ip->i_devvp = ump->um_devvp;
+ VREF(ip->i_devvp);
+ /*
+ * Set up a generation number for this inode if it does not
+ * already have one. This should only happen on old filesystems.
+ */
+ if (ip->i_e2fs_gen == 0) {
+ if (++ext2gennumber < (u_long)time.tv_sec)
+ ext2gennumber = time.tv_sec;
+ ip->i_e2fs_gen = ext2gennumber;
+ if ((vp->v_mount->mnt_flag & MNT_RDONLY) == 0)
+ ip->i_flag |= IN_MODIFIED;
+ }
+
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * File handle to vnode
+ *
+ * Have to be really careful about stale file handles:
+ * - check that the inode number is valid
+ * - call ext2fs_vget() to get the locked inode
+ * - check for an unallocated inode (i_mode == 0)
+ * - check that the given client host has export rights and return
+ * those rights via. exflagsp and credanonp
+ */
+int
+ext2fs_fhtovp(mp, fhp, nam, vpp, exflagsp, credanonp)
+ register struct mount *mp;
+ struct fid *fhp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+ register struct ufid *ufhp;
+ struct m_ext2fs *fs;
+
+ ufhp = (struct ufid *)fhp;
+ fs = VFSTOUFS(mp)->um_e2fs;
+ if ((ufhp->ufid_ino < EXT2_FIRSTINO && ufhp->ufid_ino != EXT2_ROOTINO) ||
+ ufhp->ufid_ino >= fs->e2fs_ncg * fs->e2fs.e2fs_ipg)
+ return (ESTALE);
+ return (ext2fs_check_export(mp, ufhp, nam, vpp, exflagsp, credanonp));
+}
+
+/*
+ * Vnode pointer to File handle
+ */
+/* ARGSUSED */
+int
+ext2fs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ register struct inode *ip;
+ register struct ufid *ufhp;
+
+ ip = VTOI(vp);
+ ufhp = (struct ufid *)fhp;
+ ufhp->ufid_len = sizeof(struct ufid);
+ ufhp->ufid_ino = ip->i_number;
+ ufhp->ufid_gen = ip->i_e2fs_gen;
+ return (0);
+}
+
+/*
+ * Write a superblock and associated information back to disk.
+ */
+int
+ext2fs_sbupdate(mp, waitfor)
+ struct ufsmount *mp;
+ int waitfor;
+{
+ register struct m_ext2fs *fs = mp->um_e2fs;
+ register struct buf *bp;
+ int error = 0;
+
+ bp = getblk(mp->um_devvp, SBLOCK, SBSIZE, 0, 0);
+ bcopy((caddr_t)(&fs->e2fs), bp->b_data, SBSIZE);
+ if (waitfor == MNT_WAIT)
+ error = bwrite(bp);
+ else
+ bawrite(bp);
+ return (error);
+}
+
+int
+ext2fs_cgupdate(mp, waitfor)
+ struct ufsmount *mp;
+ int waitfor;
+{
+ register struct m_ext2fs *fs = mp->um_e2fs;
+ register struct buf *bp;
+ int i, error = 0, allerror = 0;
+
+ allerror = ext2fs_sbupdate(mp, waitfor);
+ for (i = 0; i < fs->e2fs_ngdb; i++) {
+ bp = getblk(mp->um_devvp, fsbtodb(fs, ((fs->e2fs_bsize>1024)?0:1)+i+1),
+ fs->e2fs_bsize, 0, 0);
+ bcopy(&fs->e2fs_gd[i* fs->e2fs_bsize / sizeof(struct ext2_gd)],
+ bp->b_data, fs->e2fs_bsize);
+ if (waitfor == MNT_WAIT)
+ error = bwrite(bp);
+ else
+ bawrite(bp);
+ }
+
+ if (!allerror && error)
+ allerror = error;
+ return (allerror);
+}
diff --git a/sys/ufs/ext2fs/ext2fs_vnops.c b/sys/ufs/ext2fs/ext2fs_vnops.c
new file mode 100644
index 00000000000..1ce4efaf3bc
--- /dev/null
+++ b/sys/ufs/ext2fs/ext2fs_vnops.c
@@ -0,0 +1,1541 @@
+/* $NetBSD: ufs_vnops.c,v 1.22 1997/01/30 09:52:27 tls Exp $ */
+
+/* Modified for EXT2FS on NetBSD by Manuel Bouyer, April 1997 */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * 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.
+ *
+ * @(#)ufs_vnops.c 8.14 (Berkeley) 10/26/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/resourcevar.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/buf.h>
+#include <sys/proc.h>
+#include <sys/conf.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/lockf.h>
+#include <sys/malloc.h>
+#include <sys/signalvar.h>
+
+#include <vm/vm.h>
+
+#include <miscfs/fifofs/fifo.h>
+#include <miscfs/genfs/genfs.h>
+#include <miscfs/specfs/specdev.h>
+
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <ufs/ufs/ufs_extern.h>
+#include <ufs/ufs/ufsmount.h>
+
+#include <ufs/ext2fs/ext2fs.h>
+#include <ufs/ext2fs/ext2fs_extern.h>
+#include <ufs/ext2fs/ext2fs_dir.h>
+
+
+static int ext2fs_chmod
+ __P((struct vnode *, int, struct ucred *, struct proc *));
+static int ext2fs_chown
+ __P((struct vnode *, uid_t, gid_t, struct ucred *, struct proc *));
+
+union _qcvt {
+ int64_t qcvt;
+ int32_t val[2];
+};
+#define SETHIGH(q, h) { \
+ union _qcvt tmp; \
+ tmp.qcvt = (q); \
+ tmp.val[_QUAD_HIGHWORD] = (h); \
+ (q) = tmp.qcvt; \
+}
+#define SETLOW(q, l) { \
+ union _qcvt tmp; \
+ tmp.qcvt = (q); \
+ tmp.val[_QUAD_LOWWORD] = (l); \
+ (q) = tmp.qcvt; \
+}
+
+/*
+ * Create a regular file
+ */
+int
+ext2fs_create(v)
+ void *v;
+{
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap = v;
+ return
+ ext2fs_makeinode(MAKEIMODE(ap->a_vap->va_type, ap->a_vap->va_mode),
+ ap->a_dvp, ap->a_vpp, ap->a_cnp);
+}
+
+/*
+ * Mknod vnode call
+ */
+/* ARGSUSED */
+int
+ext2fs_mknod(v)
+ void *v;
+{
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap = v;
+ register struct vattr *vap = ap->a_vap;
+ register struct vnode **vpp = ap->a_vpp;
+ register struct inode *ip;
+ int error;
+
+ if ((error =
+ ext2fs_makeinode(MAKEIMODE(vap->va_type, vap->va_mode),
+ ap->a_dvp, vpp, ap->a_cnp)) != 0)
+ return (error);
+ ip = VTOI(*vpp);
+ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+ if (vap->va_rdev != VNOVAL) {
+ /*
+ * Want to be able to use this to make badblock
+ * inodes, so don't truncate the dev number.
+ */
+ ip->i_din.e2fs_din.e2di_rdev = vap->va_rdev;
+ }
+ /*
+ * Remove inode so that it will be reloaded by VFS_VGET and
+ * checked to see if it is an alias of an existing entry in
+ * the inode cache.
+ */
+ vput(*vpp);
+ (*vpp)->v_type = VNON;
+ vgone(*vpp);
+ *vpp = 0;
+ return (0);
+}
+
+/*
+ * Open called.
+ *
+ * Just check the APPEND flag.
+ */
+/* ARGSUSED */
+int
+ext2fs_open(v)
+ void *v;
+{
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap = v;
+
+ /*
+ * Files marked append-only must be opened for appending.
+ */
+ if ((VTOI(ap->a_vp)->i_e2fs_flags & EXT2_APPEND) &&
+ (ap->a_mode & (FWRITE | O_APPEND)) == FWRITE)
+ return (EPERM);
+ return (0);
+}
+
+int
+ext2fs_access(v)
+ void *v;
+{
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap = v;
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ mode_t mode = ap->a_mode;
+
+#ifdef DIAGNOSTIC
+ if (!VOP_ISLOCKED(vp)) {
+ vprint("ext2fs_access: not locked", vp);
+ panic("ext2fs_access: not locked");
+ }
+#endif
+
+ /* If immutable bit set, nobody gets to write it. */
+ if ((mode & VWRITE) && (ip->i_e2fs_flags & EXT2_IMMUTABLE))
+ return (EPERM);
+
+ return (vaccess(ip->i_e2fs_mode, ip->i_e2fs_uid, ip->i_e2fs_gid, mode,
+ ap->a_cred));
+}
+
+/* ARGSUSED */
+int
+ext2fs_getattr(v)
+ void *v;
+{
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap = v;
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ register struct vattr *vap = ap->a_vap;
+ struct timespec ts;
+
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ EXT2FS_ITIMES(ip, &ts, &ts, &ts);
+ /*
+ * Copy from inode table
+ */
+ vap->va_fsid = ip->i_dev;
+ vap->va_fileid = ip->i_number;
+ vap->va_mode = ip->i_e2fs_mode & ~IFMT;
+ vap->va_nlink = ip->i_e2fs_nlink;
+ vap->va_uid = ip->i_e2fs_uid;
+ vap->va_gid = ip->i_e2fs_gid;
+ vap->va_rdev = (dev_t)ip->i_din.e2fs_din.e2di_rdev;
+ vap->va_size = ip->i_e2fs_size;
+ vap->va_atime.tv_sec = ip->i_e2fs_atime;
+ vap->va_atime.tv_nsec = 0;
+ vap->va_mtime.tv_sec = ip->i_e2fs_mtime;
+ vap->va_mtime.tv_nsec = 0;
+ vap->va_ctime.tv_sec = ip->i_e2fs_ctime;
+ vap->va_ctime.tv_nsec = 0;
+#ifdef EXT2FS_SYSTEM_FLAGS
+ vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? SF_APPEND : 0;
+ vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? SF_IMMUTABLE : 0;
+#else
+ vap->va_flags = (ip->i_e2fs_flags & EXT2_APPEND) ? UF_APPEND : 0;
+ vap->va_flags |= (ip->i_e2fs_flags & EXT2_IMMUTABLE) ? UF_IMMUTABLE : 0;
+#endif
+ vap->va_gen = ip->i_e2fs_gen;
+ /* this doesn't belong here */
+ if (vp->v_type == VBLK)
+ vap->va_blocksize = BLKDEV_IOSIZE;
+ else if (vp->v_type == VCHR)
+ vap->va_blocksize = MAXBSIZE;
+ else
+ vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
+ vap->va_bytes = dbtob(ip->i_e2fs_nblock);
+ vap->va_type = vp->v_type;
+ vap->va_filerev = ip->i_modrev;
+ return (0);
+}
+
+/*
+ * Set attribute vnode op. called from several syscalls
+ */
+int
+ext2fs_setattr(v)
+ void *v;
+{
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap = v;
+ register struct vattr *vap = ap->a_vap;
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ register struct ucred *cred = ap->a_cred;
+ register 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);
+ }
+ if (vap->va_flags != VNOVAL) {
+ if (cred->cr_uid != ip->i_e2fs_uid &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+#ifdef EXT2FS_SYSTEM_FLAGS
+ if (cred->cr_uid == 0) {
+ if ((ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE)) &&
+ securelevel > 0)
+ return (EPERM);
+ ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
+ ip->i_e2fs_flags |= (vap->va_flags & SF_APPEND) ? EXT2_APPEND : 0 |
+ (vap->va_flags & SF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
+ } else {
+ return (EPERM);
+ }
+#else
+ ip->i_e2fs_flags &= ~(EXT2_APPEND | EXT2_IMMUTABLE);
+ ip->i_e2fs_flags |= (vap->va_flags & UF_APPEND) ? EXT2_APPEND : 0 |
+ (vap->va_flags & UF_IMMUTABLE) ? EXT2_IMMUTABLE: 0;
+#endif
+ ip->i_flag |= IN_CHANGE;
+ if (vap->va_flags & (IMMUTABLE | APPEND))
+ return (0);
+ }
+ if (ip->i_e2fs_flags & (EXT2_APPEND | EXT2_IMMUTABLE))
+ return (EPERM);
+ /*
+ * Go through the fields and update iff not VNOVAL.
+ */
+ if (vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL) {
+ error = ext2fs_chown(vp, vap->va_uid, vap->va_gid, cred, p);
+ if (error)
+ return (error);
+ }
+ if (vap->va_size != VNOVAL) {
+ if (vp->v_type == VDIR)
+ return (EISDIR);
+ error = VOP_TRUNCATE(vp, vap->va_size, 0, cred, p);
+ if (error)
+ return (error);
+ }
+ ip = VTOI(vp);
+ if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
+ if (cred->cr_uid != ip->i_e2fs_uid &&
+ (error = suser(cred, &p->p_acflag)) &&
+ ((vap->va_vaflags & VA_UTIMES_NULL) == 0 ||
+ (error = VOP_ACCESS(vp, VWRITE, cred, p))))
+ return (error);
+ if (vap->va_atime.tv_sec != VNOVAL)
+ if (!(vp->v_mount->mnt_flag & MNT_NOATIME))
+ ip->i_flag |= IN_ACCESS;
+ if (vap->va_mtime.tv_sec != VNOVAL)
+ ip->i_flag |= IN_CHANGE | IN_UPDATE;
+ error = VOP_UPDATE(vp, &vap->va_atime, &vap->va_mtime, 1);
+ if (error)
+ return (error);
+ }
+ error = 0;
+ if (vap->va_mode != (mode_t)VNOVAL)
+ error = ext2fs_chmod(vp, (int)vap->va_mode, cred, p);
+ return (error);
+}
+
+/*
+ * Change the mode on a file.
+ * Inode must be locked before calling.
+ */
+static int
+ext2fs_chmod(vp, mode, cred, p)
+ register struct vnode *vp;
+ register int mode;
+ register struct ucred *cred;
+ struct proc *p;
+{
+ register struct inode *ip = VTOI(vp);
+ int error;
+
+ if (cred->cr_uid != ip->i_e2fs_uid &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ if (cred->cr_uid) {
+ if (vp->v_type != VDIR && (mode & S_ISTXT))
+ return (EFTYPE);
+ if (!groupmember(ip->i_e2fs_gid, cred) && (mode & ISGID))
+ return (EPERM);
+ }
+ ip->i_e2fs_mode &= ~ALLPERMS;
+ ip->i_e2fs_mode |= (mode & ALLPERMS);
+ ip->i_flag |= IN_CHANGE;
+ if ((vp->v_flag & VTEXT) && (ip->i_e2fs_mode & S_ISTXT) == 0)
+ (void) vnode_pager_uncache(vp);
+ return (0);
+}
+
+/*
+ * Perform chown operation on inode ip;
+ * inode must be locked prior to call.
+ */
+static int
+ext2fs_chown(vp, uid, gid, cred, p)
+ register struct vnode *vp;
+ uid_t uid;
+ gid_t gid;
+ struct ucred *cred;
+ struct proc *p;
+{
+ register struct inode *ip = VTOI(vp);
+ uid_t ouid;
+ gid_t ogid;
+ int error = 0;
+
+ if (uid == (uid_t)VNOVAL)
+ uid = ip->i_e2fs_uid;
+ if (gid == (gid_t)VNOVAL)
+ gid = ip->i_e2fs_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 != ip->i_e2fs_uid || uid != ip->i_e2fs_uid ||
+ (gid != ip->i_e2fs_gid && !groupmember((gid_t)gid, cred))) &&
+ (error = suser(cred, &p->p_acflag)))
+ return (error);
+ ogid = ip->i_e2fs_gid;
+ ouid = ip->i_e2fs_uid;
+
+ ip->i_e2fs_gid = gid;
+ ip->i_e2fs_uid = uid;
+ if (ouid != uid || ogid != gid)
+ ip->i_flag |= IN_CHANGE;
+ if (ouid != uid && cred->cr_uid != 0)
+ ip->i_e2fs_mode &= ~ISUID;
+ if (ogid != gid && cred->cr_uid != 0)
+ ip->i_e2fs_mode &= ~ISGID;
+ return (0);
+}
+
+int
+ext2fs_remove(v)
+ void *v;
+{
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap = v;
+ register struct inode *ip;
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *dvp = ap->a_dvp;
+ int error;
+
+ if (vp->v_type == VDIR) {
+ error = EISDIR;
+ goto out;
+ }
+ ip = VTOI(vp);
+ if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
+ (VTOI(dvp)->i_e2fs_flags & EXT2_APPEND)) {
+ error = EPERM;
+ goto out;
+ }
+ error = ext2fs_dirremove(dvp, ap->a_cnp);
+ if (error == 0) {
+ ip->i_e2fs_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ }
+out:
+ if (dvp == vp)
+ vrele(vp);
+ else
+ vput(vp);
+ vput(dvp);
+ return (error);
+}
+
+/*
+ * link vnode call
+ */
+int
+ext2fs_link(v)
+ void *v;
+{
+ struct vop_link_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap = v;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vnode *vp = ap->a_vp;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct inode *ip;
+ struct timespec ts;
+ int error;
+
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ext2fs_link: no name");
+#endif
+ if (vp->v_type == VDIR) {
+ VOP_ABORTOP(dvp, cnp);
+ error = EISDIR;
+ goto out2;
+ }
+ if (dvp->v_mount != vp->v_mount) {
+ VOP_ABORTOP(dvp, cnp);
+ error = EXDEV;
+ goto out2;
+ }
+ if (dvp != vp && (error = VOP_LOCK(vp))) {
+ VOP_ABORTOP(dvp, cnp);
+ goto out2;
+ }
+ ip = VTOI(vp);
+ if ((nlink_t)ip->i_e2fs_nlink >= LINK_MAX) {
+ VOP_ABORTOP(dvp, cnp);
+ error = EMLINK;
+ goto out1;
+ }
+ if (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) {
+ VOP_ABORTOP(dvp, cnp);
+ error = EPERM;
+ goto out1;
+ }
+ ip->i_e2fs_nlink++;
+ ip->i_flag |= IN_CHANGE;
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ error = VOP_UPDATE(vp, &ts, &ts, 1);
+ if (!error)
+ error = ext2fs_direnter(ip, dvp, cnp);
+ if (error) {
+ ip->i_e2fs_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ }
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+out1:
+ if (dvp != vp)
+ VOP_UNLOCK(vp);
+out2:
+ vput(dvp);
+ return (error);
+}
+
+/*
+ * Rename system call.
+ * 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 ensure 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.
+ */
+int
+ext2fs_rename(v)
+ void *v;
+{
+ struct vop_rename_args /* {
+ struct vnode *a_fdvp;
+ struct vnode *a_fvp;
+ struct componentname *a_fcnp;
+ struct vnode *a_tdvp;
+ struct vnode *a_tvp;
+ struct componentname *a_tcnp;
+ } */ *ap = v;
+ struct vnode *tvp = ap->a_tvp;
+ register struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *fvp = ap->a_fvp;
+ register struct vnode *fdvp = ap->a_fdvp;
+ register struct componentname *tcnp = ap->a_tcnp;
+ register struct componentname *fcnp = ap->a_fcnp;
+ register struct inode *ip, *xp, *dp;
+ struct ext2fs_dirtemplate dirbuf;
+ struct timespec ts;
+ int doingdirectory = 0, oldparent = 0, newparent = 0;
+ int error = 0;
+ u_char namlen;
+
+#ifdef DIAGNOSTIC
+ if ((tcnp->cn_flags & HASBUF) == 0 ||
+ (fcnp->cn_flags & HASBUF) == 0)
+ panic("ext2fs_rename: no name");
+#endif
+ /*
+ * Check for cross-device rename.
+ */
+ if ((fvp->v_mount != tdvp->v_mount) ||
+ (tvp && (fvp->v_mount != tvp->v_mount))) {
+ error = EXDEV;
+abortit:
+ VOP_ABORTOP(tdvp, tcnp); /* XXX, why not in NFS? */
+ if (tdvp == tvp)
+ vrele(tdvp);
+ else
+ vput(tdvp);
+ if (tvp)
+ vput(tvp);
+ VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
+ vrele(fdvp);
+ vrele(fvp);
+ return (error);
+ }
+
+ /*
+ * Check if just deleting a link name.
+ */
+ if (tvp && ((VTOI(tvp)->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
+ (VTOI(tdvp)->i_e2fs_flags & EXT2_APPEND))) {
+ error = EPERM;
+ goto abortit;
+ }
+ if (fvp == tvp) {
+ if (fvp->v_type == VDIR) {
+ error = EINVAL;
+ goto abortit;
+ }
+
+ /* Release destination completely. */
+ VOP_ABORTOP(tdvp, tcnp);
+ vput(tdvp);
+ vput(tvp);
+
+ /* Delete source. */
+ vrele(fdvp);
+ vrele(fvp);
+ fcnp->cn_flags &= ~MODMASK;
+ fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+ if ((fcnp->cn_flags & SAVESTART) == 0)
+ panic("ext2fs_rename: lost from startdir");
+ fcnp->cn_nameiop = DELETE;
+ (void) relookup(fdvp, &fvp, fcnp);
+ return (VOP_REMOVE(fdvp, fvp, fcnp));
+ }
+ if ((error = VOP_LOCK(fvp)) != 0)
+ goto abortit;
+ dp = VTOI(fdvp);
+ ip = VTOI(fvp);
+ if ((ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND)) ||
+ (dp->i_e2fs_flags & EXT2_APPEND)) {
+ VOP_UNLOCK(fvp);
+ error = EPERM;
+ goto abortit;
+ }
+ if ((ip->i_e2fs_mode & IFMT) == IFDIR) {
+ error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
+ if (!error && tvp)
+ error = VOP_ACCESS(tvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
+ if (error) {
+ VOP_UNLOCK(fvp);
+ error = EACCES;
+ goto abortit;
+ }
+ /*
+ * Avoid ".", "..", and aliases of "." for obvious reasons.
+ */
+ if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') ||
+ dp == ip || (fcnp->cn_flags&ISDOTDOT) ||
+ (ip->i_flag & IN_RENAME)) {
+ VOP_UNLOCK(fvp);
+ error = EINVAL;
+ goto abortit;
+ }
+ ip->i_flag |= IN_RENAME;
+ oldparent = dp->i_number;
+ doingdirectory++;
+ }
+ vrele(fdvp);
+
+ /*
+ * When the target exists, both the directory
+ * and target vnodes are returned locked.
+ */
+ dp = VTOI(tdvp);
+ xp = NULL;
+ if (tvp)
+ xp = VTOI(tvp);
+
+ /*
+ * 1) Bump link count while we're moving stuff
+ * around. If we crash somewhere before
+ * completing our work, the link count
+ * may be wrong, but correctable.
+ */
+ ip->i_e2fs_nlink++;
+ ip->i_flag |= IN_CHANGE;
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ if ((error = VOP_UPDATE(fvp, &ts, &ts, 1)) != 0) {
+ VOP_UNLOCK(fvp);
+ goto bad;
+ }
+
+ /*
+ * If ".." must be changed (ie the directory gets a new
+ * parent) then the source directory must not be in the
+ * directory heirarchy 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 "..". We must repeat the call
+ * to namei, as the parent directory is unlocked by the
+ * call to checkpath().
+ */
+ error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
+ VOP_UNLOCK(fvp);
+ 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);
+ error = ext2fs_checkpath(ip, dp, tcnp->cn_cred);
+ if (error != 0)
+ goto out;
+ if ((tcnp->cn_flags & SAVESTART) == 0)
+ panic("ext2fs_rename: lost to startdir");
+ if ((error = relookup(tdvp, &tvp, tcnp)) != 0)
+ goto out;
+ dp = VTOI(tdvp);
+ xp = NULL;
+ if (tvp)
+ xp = VTOI(tvp);
+ }
+ /*
+ * 2) If target doesn't exist, link the target
+ * to the source and unlink the source.
+ * Otherwise, rewrite the target directory
+ * entry to reference the source inode and
+ * expunge the original entry's existence.
+ */
+ if (xp == NULL) {
+ if (dp->i_dev != ip->i_dev)
+ panic("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_e2fs_nlink >= LINK_MAX) {
+ error = EMLINK;
+ goto bad;
+ }
+ dp->i_e2fs_nlink++;
+ dp->i_flag |= IN_CHANGE;
+ if ((error = VOP_UPDATE(tdvp, &ts, &ts, 1)) != 0)
+ goto bad;
+ }
+ error = ext2fs_direnter(ip, tdvp, tcnp);
+ if (error != 0) {
+ if (doingdirectory && newparent) {
+ dp->i_e2fs_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ (void)VOP_UPDATE(tdvp, &ts, &ts, 1);
+ }
+ goto bad;
+ }
+ vput(tdvp);
+ } else {
+ if (xp->i_dev != dp->i_dev || xp->i_dev != ip->i_dev)
+ panic("rename: EXDEV");
+ /*
+ * Short circuit rename(foo, foo).
+ */
+ if (xp->i_number == ip->i_number)
+ panic("rename: same file");
+ /*
+ * If the parent directory is "sticky", then the user must
+ * own the parent directory, or the destination of the rename,
+ * otherwise the destination may not be changed (except by
+ * root). This implements append-only directories.
+ */
+ if ((dp->i_e2fs_mode & S_ISTXT) && tcnp->cn_cred->cr_uid != 0 &&
+ tcnp->cn_cred->cr_uid != dp->i_e2fs_uid &&
+ xp->i_e2fs_uid != tcnp->cn_cred->cr_uid) {
+ error = EPERM;
+ goto bad;
+ }
+ /*
+ * 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_e2fs_mode & IFMT) == IFDIR) {
+ if (!ext2fs_dirempty(xp, dp->i_number, tcnp->cn_cred) ||
+ xp->i_e2fs_nlink > 2) {
+ error = ENOTEMPTY;
+ goto bad;
+ }
+ if (!doingdirectory) {
+ error = ENOTDIR;
+ goto bad;
+ }
+ cache_purge(tdvp);
+ } else if (doingdirectory) {
+ error = EISDIR;
+ goto bad;
+ }
+ error = ext2fs_dirrewrite(dp, ip, tcnp);
+ if (error != 0)
+ goto bad;
+ /*
+ * If the target directory is in the same
+ * directory as the source directory,
+ * decrement the link count on the parent
+ * of the target directory.
+ */
+ if (doingdirectory && !newparent) {
+ dp->i_e2fs_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ }
+ vput(tdvp);
+ /*
+ * Adjust the link count of the target to
+ * reflect the dirrewrite above. If this is
+ * a directory it is empty and there are
+ * no links to it, so we can squash the inode and
+ * any space associated with it. We disallowed
+ * renaming over top of a directory with links to
+ * it above, as the remaining link would point to
+ * a directory without "." or ".." entries.
+ */
+ xp->i_e2fs_nlink--;
+ if (doingdirectory) {
+ if (--xp->i_e2fs_nlink != 0)
+ panic("rename: linked directory");
+ error = VOP_TRUNCATE(tvp, (off_t)0, IO_SYNC,
+ tcnp->cn_cred, tcnp->cn_proc);
+ }
+ xp->i_flag |= IN_CHANGE;
+ vput(tvp);
+ xp = NULL;
+ }
+
+ /*
+ * 3) Unlink the source.
+ */
+ fcnp->cn_flags &= ~MODMASK;
+ fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
+ if ((fcnp->cn_flags & SAVESTART) == 0)
+ panic("ext2fs_rename: lost from startdir");
+ (void) relookup(fdvp, &fvp, fcnp);
+ if (fvp != NULL) {
+ xp = VTOI(fvp);
+ dp = VTOI(fdvp);
+ } else {
+ /*
+ * From name has disappeared.
+ */
+ if (doingdirectory)
+ panic("ext2fs_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; its link
+ * count of three would cause a rmdir to fail with ENOTEMPTY.
+ * The IRENAME flag ensures that it cannot be moved by another
+ * rename.
+ */
+ if (xp != ip) {
+ if (doingdirectory)
+ panic("ext2fs_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) {
+ dp->i_e2fs_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ error = vn_rdwr(UIO_READ, fvp, (caddr_t)&dirbuf,
+ sizeof (struct ext2fs_dirtemplate), (off_t)0,
+ UIO_SYSSPACE, IO_NODELOCKED,
+ tcnp->cn_cred, (int *)0, (struct proc *)0);
+ if (error == 0) {
+ namlen = dirbuf.dotdot_namlen;
+ if (namlen != 2 ||
+ dirbuf.dotdot_name[0] != '.' ||
+ dirbuf.dotdot_name[1] != '.') {
+ ufs_dirbad(xp, (doff_t)12,
+ "ext2fs_rename: mangled dir");
+ } else {
+ dirbuf.dotdot_ino = newparent;
+ (void) vn_rdwr(UIO_WRITE, fvp,
+ (caddr_t)&dirbuf,
+ sizeof (struct dirtemplate),
+ (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED|IO_SYNC,
+ tcnp->cn_cred, (int *)0,
+ (struct proc *)0);
+ cache_purge(fdvp);
+ }
+ }
+ }
+ error = ext2fs_dirremove(fdvp, fcnp);
+ if (!error) {
+ xp->i_e2fs_nlink--;
+ xp->i_flag |= IN_CHANGE;
+ }
+ xp->i_flag &= ~IN_RENAME;
+ }
+ if (dp)
+ vput(fdvp);
+ if (xp)
+ vput(fvp);
+ vrele(ap->a_fvp);
+ return (error);
+
+bad:
+ if (xp)
+ vput(ITOV(xp));
+ vput(ITOV(dp));
+out:
+ if (doingdirectory)
+ ip->i_flag &= ~IN_RENAME;
+ if (VOP_LOCK(fvp) == 0) {
+ ip->i_e2fs_nlink--;
+ ip->i_flag |= IN_CHANGE;
+ vput(fvp);
+ } else
+ vrele(fvp);
+ return (error);
+}
+
+/*
+ * A virgin directory (no blushing please).
+ */
+static struct ext2fs_dirtemplate mastertemplate = {
+ 0, 12, 1, ".",
+ 0, - 12, 2, ".." /* XXX -12 should be e2fs_bsize-12 */
+};
+
+/*
+ * Mkdir system call
+ */
+int
+ext2fs_mkdir(v)
+ void *v;
+{
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap = v;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct vattr *vap = ap->a_vap;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct inode *ip, *dp;
+ struct vnode *tvp;
+ struct ext2fs_dirtemplate dirtemplate, *dtp;
+ struct timespec ts;
+ int error, dmode;
+
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ext2fs_mkdir: no name");
+#endif
+ dp = VTOI(dvp);
+ if ((nlink_t)dp->i_e2fs_nlink >= LINK_MAX) {
+ error = EMLINK;
+ goto out;
+ }
+ dmode = vap->va_mode & 0777;
+ dmode |= IFDIR;
+ /*
+ * Must simulate part of ext2fs_makeinode here to acquire the inode,
+ * but not have it entered in the parent directory. The entry is
+ * made later after writing "." and ".." entries.
+ */
+ if ((error = VOP_VALLOC(dvp, dmode, cnp->cn_cred, &tvp)) != 0)
+ goto out;
+ ip = VTOI(tvp);
+ ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
+ ip->i_e2fs_gid = dp->i_e2fs_gid;
+ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+ ip->i_e2fs_mode = dmode;
+ tvp->v_type = VDIR; /* Rest init'd in getnewvnode(). */
+ ip->i_e2fs_nlink = 2;
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ error = VOP_UPDATE(tvp, &ts, &ts, 1);
+
+ /*
+ * Bump link count in parent directory
+ * to reflect work done below. Should
+ * be done before reference is created
+ * so reparation is possible if we crash.
+ */
+ dp->i_e2fs_nlink++;
+ dp->i_flag |= IN_CHANGE;
+ if ((error = VOP_UPDATE(dvp, &ts, &ts, 1)) != 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;
+ /* Correct reclen of second entry */
+ dirtemplate.dotdot_reclen = VTOI(dvp)->i_e2fs->e2fs_bsize - 12;
+ error = vn_rdwr(UIO_WRITE, tvp, (caddr_t)&dirtemplate,
+ sizeof (dirtemplate), (off_t)0, UIO_SYSSPACE,
+ IO_NODELOCKED|IO_SYNC, cnp->cn_cred, (int *)0, (struct proc *)0);
+ if (error) {
+ dp->i_e2fs_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ goto bad;
+ }
+ if (VTOI(dvp)->i_e2fs->e2fs_bsize >
+ VFSTOUFS(dvp->v_mount)->um_mountp->mnt_stat.f_bsize)
+ panic("ext2fs_mkdir: blksize"); /* XXX should grow with balloc() */
+ else {
+ ip->i_e2fs_size = VTOI(dvp)->i_e2fs->e2fs_bsize;
+ ip->i_flag |= IN_CHANGE;
+ }
+
+ /* Directory set up, now install it's entry in the parent directory. */
+ error = ext2fs_direnter(ip, dvp, cnp);
+ if (error != 0) {
+ dp->i_e2fs_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ }
+bad:
+ /*
+ * No need to do an explicit VOP_TRUNCATE here, vrele will do this
+ * for us because we set the link count to 0.
+ */
+ if (error) {
+ ip->i_e2fs_nlink = 0;
+ ip->i_flag |= IN_CHANGE;
+ vput(tvp);
+ } else
+ *ap->a_vpp = tvp;
+out:
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ vput(dvp);
+ return (error);
+}
+
+/*
+ * Rmdir system call.
+ */
+int
+ext2fs_rmdir(v)
+ void *v;
+{
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap = v;
+ register struct vnode *vp = ap->a_vp;
+ register struct vnode *dvp = ap->a_dvp;
+ register struct componentname *cnp = ap->a_cnp;
+ register struct inode *ip, *dp;
+ int error;
+
+ ip = VTOI(vp);
+ dp = VTOI(dvp);
+ /*
+ * No rmdir "." please.
+ */
+ if (dp == ip) {
+ vrele(dvp);
+ vput(vp);
+ return (EINVAL);
+ }
+ /*
+ * Verify the directory is empty (and valid).
+ * (Rmdir ".." won't be valid since
+ * ".." will contain a reference to
+ * the current directory and thus be
+ * non-empty.)
+ */
+ error = 0;
+ if (ip->i_e2fs_nlink != 2 ||
+ !ext2fs_dirempty(ip, dp->i_number, cnp->cn_cred)) {
+ error = ENOTEMPTY;
+ goto out;
+ }
+ if ((dp->i_e2fs_flags & EXT2_APPEND) ||
+ (ip->i_e2fs_flags & (EXT2_IMMUTABLE | EXT2_APPEND))) {
+ error = EPERM;
+ goto out;
+ }
+ /*
+ * Delete reference to directory before purging
+ * inode. If we crash in between, the directory
+ * will be reattached to lost+found,
+ */
+ error = ext2fs_dirremove(dvp, cnp);
+ if (error != 0)
+ goto out;
+ dp->i_e2fs_nlink--;
+ dp->i_flag |= IN_CHANGE;
+ cache_purge(dvp);
+ vput(dvp);
+ dvp = NULL;
+ /*
+ * Truncate inode. The only stuff left
+ * in the directory is "." and "..". The
+ * "." reference is inconsequential since
+ * we're quashing it. The ".." reference
+ * has already been adjusted above. We've
+ * removed the "." reference and the reference
+ * in the parent directory, but there may be
+ * other hard links so decrement by 2 and
+ * worry about them later.
+ */
+ ip->i_e2fs_nlink -= 2;
+ error = VOP_TRUNCATE(vp, (off_t)0, IO_SYNC, cnp->cn_cred,
+ cnp->cn_proc);
+ cache_purge(ITOV(ip));
+out:
+ if (dvp)
+ vput(dvp);
+ vput(vp);
+ return (error);
+}
+
+/*
+ * symlink -- make a symbolic link
+ */
+int
+ext2fs_symlink(v)
+ void *v;
+{
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap = v;
+ register struct vnode *vp, **vpp = ap->a_vpp;
+ register struct inode *ip;
+ int len, error;
+
+ error = ext2fs_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 < vp->v_mount->mnt_maxsymlinklen) {
+ ip = VTOI(vp);
+ bcopy(ap->a_target, (char *)ip->i_din.e2fs_din.e2di_shortlink, len);
+ ip->i_e2fs_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);
+ vput(vp);
+ return (error);
+}
+
+/*
+ * Return target name of a symbolic link
+ */
+int
+ext2fs_readlink(v)
+ void *v;
+{
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap = v;
+ register struct vnode *vp = ap->a_vp;
+ register struct inode *ip = VTOI(vp);
+ int isize;
+
+ isize = ip->i_e2fs_size;
+ if (isize < vp->v_mount->mnt_maxsymlinklen ||
+ (vp->v_mount->mnt_maxsymlinklen == 0 && ip->i_e2fs_nblock == 0)) {
+ uiomove((char *)ip->i_din.e2fs_din.e2di_shortlink, isize, ap->a_uio);
+ return (0);
+ }
+ return (VOP_READ(vp, ap->a_uio, 0, ap->a_cred));
+}
+
+/*
+ * Advisory record locking support
+ */
+int
+ext2fs_advlock(v)
+ void *v;
+{
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap = v;
+ register struct inode *ip = VTOI(ap->a_vp);
+
+ return (lf_advlock(&ip->i_lockf, ip->i_e2fs_size, ap->a_id, ap->a_op,
+ ap->a_fl, ap->a_flags));
+}
+
+/*
+ * Initialize the vnode associated with a new inode, handle aliased
+ * vnodes.
+ */
+int
+ext2fs_vinit(mntp, specops, fifoops, vpp)
+ struct mount *mntp;
+ int (**specops) __P((void *));
+ int (**fifoops) __P((void *));
+ struct vnode **vpp;
+{
+ struct inode *ip;
+ struct vnode *vp, *nvp;
+
+ vp = *vpp;
+ ip = VTOI(vp);
+ switch(vp->v_type = IFTOVT(ip->i_e2fs_mode)) {
+ case VCHR:
+ case VBLK:
+ vp->v_op = specops;
+ if ((nvp = checkalias(vp, ip->i_din.e2fs_din.e2di_rdev, mntp))
+ != NULL) {
+ /*
+ * Discard unneeded vnode, but save its inode.
+ */
+ ufs_ihashrem(ip);
+ VOP_UNLOCK(vp);
+ nvp->v_data = vp->v_data;
+ vp->v_data = NULL;
+ vp->v_op = spec_vnodeop_p;
+ vrele(vp);
+ vgone(vp);
+ /*
+ * Reinitialize aliased inode.
+ */
+ vp = nvp;
+ ip->i_vnode = vp;
+ ufs_ihashins(ip);
+ }
+ break;
+ case VFIFO:
+#ifdef FIFO
+ vp->v_op = fifoops;
+ break;
+#else
+ return (EOPNOTSUPP);
+#endif
+ case VNON:
+ case VBAD:
+ case VSOCK:
+ case VLNK:
+ case VDIR:
+ case VREG:
+ break;
+ }
+ if (ip->i_number == ROOTINO)
+ vp->v_flag |= VROOT;
+ /*
+ * Initialize modrev times
+ */
+ SETHIGH(ip->i_modrev, mono_time.tv_sec);
+ SETLOW(ip->i_modrev, mono_time.tv_usec * 4294);
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ * Allocate a new inode.
+ */
+int
+ext2fs_makeinode(mode, dvp, vpp, cnp)
+ int mode;
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+{
+ register struct inode *ip, *pdir;
+ struct timespec ts;
+ struct vnode *tvp;
+ int error;
+
+ pdir = VTOI(dvp);
+#ifdef DIAGNOSTIC
+ if ((cnp->cn_flags & HASBUF) == 0)
+ panic("ext2fs_makeinode: no name");
+#endif
+ *vpp = NULL;
+ if ((mode & IFMT) == 0)
+ mode |= IFREG;
+
+ if ((error = VOP_VALLOC(dvp, mode, cnp->cn_cred, &tvp)) != 0) {
+ free(cnp->cn_pnbuf, M_NAMEI);
+ vput(dvp);
+ return (error);
+ }
+ ip = VTOI(tvp);
+ ip->i_e2fs_gid = pdir->i_e2fs_gid;
+ ip->i_e2fs_uid = cnp->cn_cred->cr_uid;
+ ip->i_flag |= IN_ACCESS | IN_CHANGE | IN_UPDATE;
+ ip->i_e2fs_mode = mode;
+ tvp->v_type = IFTOVT(mode); /* Rest init'd in getnewvnode(). */
+ ip->i_e2fs_nlink = 1;
+ if ((ip->i_e2fs_mode & ISGID) &&
+ !groupmember(ip->i_e2fs_gid, cnp->cn_cred) &&
+ suser(cnp->cn_cred, NULL))
+ ip->i_e2fs_mode &= ~ISGID;
+
+ /*
+ * Make sure inode goes to disk before directory entry.
+ */
+ TIMEVAL_TO_TIMESPEC(&time, &ts);
+ if ((error = VOP_UPDATE(tvp, &ts, &ts, 1)) != 0)
+ goto bad;
+ error = ext2fs_direnter(ip, dvp, cnp);
+ if (error != 0)
+ goto bad;
+ if ((cnp->cn_flags & SAVESTART) == 0)
+ FREE(cnp->cn_pnbuf, M_NAMEI);
+ vput(dvp);
+ *vpp = tvp;
+ return (0);
+
+bad:
+ /*
+ * Write error occurred trying to update the inode
+ * or the directory so must deallocate the inode.
+ */
+ free(cnp->cn_pnbuf, M_NAMEI);
+ vput(dvp);
+ ip->i_e2fs_nlink = 0;
+ ip->i_flag |= IN_CHANGE;
+ vput(tvp);
+ return (error);
+}
+
+/*
+ * Reclaim an inode so that it can be used for other purposes.
+ */
+int
+ext2fs_reclaim(v)
+ void *v;
+{
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap = v;
+ register struct vnode *vp = ap->a_vp;
+ struct inode *ip;
+ extern int prtactive;
+
+ if (prtactive && vp->v_usecount != 0)
+ vprint("ext2fs_reclaim: pushing active", vp);
+ /*
+ * Remove the inode from its hash chain.
+ */
+ ip = VTOI(vp);
+ ufs_ihashrem(ip);
+ /*
+ * Purge old data structures associated with the inode.
+ */
+ cache_purge(vp);
+ if (ip->i_devvp) {
+ vrele(ip->i_devvp);
+ ip->i_devvp = 0;
+ }
+
+ FREE(vp->v_data, M_EXT2FSNODE);
+ vp->v_data = NULL;
+ return (0);
+}
+
+/* Global vfs data structures for ext2fs. */
+int (**ext2fs_vnodeop_p) __P((void *));
+struct vnodeopv_entry_desc ext2fs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, ext2fs_lookup }, /* lookup */
+ { &vop_create_desc, ext2fs_create }, /* create */
+ { &vop_mknod_desc, ext2fs_mknod }, /* mknod */
+ { &vop_open_desc, ext2fs_open }, /* open */
+ { &vop_close_desc, ufs_close }, /* close */
+ { &vop_access_desc, ext2fs_access }, /* access */
+ { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
+ { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
+ { &vop_read_desc, ext2fs_read }, /* read */
+ { &vop_write_desc, ext2fs_write }, /* write */
+ { &vop_lease_desc, ufs_lease_check }, /* lease */
+ { &vop_ioctl_desc, ufs_ioctl }, /* ioctl */
+ { &vop_poll_desc, ufs_poll }, /* poll */
+ { &vop_mmap_desc, ufs_mmap }, /* mmap */
+ { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
+ { &vop_seek_desc, ufs_seek }, /* seek */
+ { &vop_remove_desc, ext2fs_remove }, /* remove */
+ { &vop_link_desc, ext2fs_link }, /* link */
+ { &vop_rename_desc, ext2fs_rename }, /* rename */
+ { &vop_mkdir_desc, ext2fs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, ext2fs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, ext2fs_symlink }, /* symlink */
+ { &vop_readdir_desc, ext2fs_readdir }, /* readdir */
+ { &vop_readlink_desc, ext2fs_readlink },/* readlink */
+ { &vop_abortop_desc, ufs_abortop }, /* abortop */
+ { &vop_inactive_desc, ext2fs_inactive },/* inactive */
+ { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, ext2fs_bmap }, /* bmap */
+ { &vop_strategy_desc, ufs_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, ufs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, ext2fs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, ext2fs_blkatoff },/* blkatoff */
+ { &vop_valloc_desc, ext2fs_valloc }, /* valloc */
+ { &vop_vfree_desc, ext2fs_vfree }, /* vfree */
+ { &vop_truncate_desc, ext2fs_truncate },/* truncate */
+ { &vop_update_desc, ext2fs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*) __P((void*)))NULL }
+};
+struct vnodeopv_desc ext2fs_vnodeop_opv_desc =
+ { &ext2fs_vnodeop_p, ext2fs_vnodeop_entries };
+
+int (**ext2fs_specop_p) __P((void *));
+struct vnodeopv_entry_desc ext2fs_specop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, spec_lookup }, /* lookup */
+ { &vop_create_desc, spec_create }, /* create */
+ { &vop_mknod_desc, spec_mknod }, /* mknod */
+ { &vop_open_desc, spec_open }, /* open */
+ { &vop_close_desc, ufsspec_close }, /* close */
+ { &vop_access_desc, ext2fs_access }, /* access */
+ { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
+ { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
+ { &vop_read_desc, ufsspec_read }, /* read */
+ { &vop_write_desc, ufsspec_write }, /* write */
+ { &vop_lease_desc, spec_lease_check }, /* lease */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_poll_desc, spec_poll }, /* poll */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
+ { &vop_seek_desc, spec_seek }, /* seek */
+ { &vop_remove_desc, spec_remove }, /* remove */
+ { &vop_link_desc, spec_link }, /* link */
+ { &vop_rename_desc, spec_rename }, /* rename */
+ { &vop_mkdir_desc, spec_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, spec_rmdir }, /* rmdir */
+ { &vop_symlink_desc, spec_symlink }, /* symlink */
+ { &vop_readdir_desc, spec_readdir }, /* readdir */
+ { &vop_readlink_desc, spec_readlink }, /* readlink */
+ { &vop_abortop_desc, spec_abortop }, /* abortop */
+ { &vop_inactive_desc, ext2fs_inactive },/* inactive */
+ { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, spec_pathconf }, /* pathconf */
+ { &vop_advlock_desc, spec_advlock }, /* advlock */
+ { &vop_blkatoff_desc, spec_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, spec_valloc }, /* valloc */
+ { &vop_vfree_desc, ext2fs_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, ext2fs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*) __P((void *)))NULL }
+};
+struct vnodeopv_desc ext2fs_specop_opv_desc =
+ { &ext2fs_specop_p, ext2fs_specop_entries };
+
+#ifdef FIFO
+int (**ext2fs_fifoop_p) __P((void *));
+struct vnodeopv_entry_desc ext2fs_fifoop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fifo_lookup }, /* lookup */
+ { &vop_create_desc, fifo_create }, /* create */
+ { &vop_mknod_desc, fifo_mknod }, /* mknod */
+ { &vop_open_desc, fifo_open }, /* open */
+ { &vop_close_desc, ufsfifo_close }, /* close */
+ { &vop_access_desc, ext2fs_access }, /* access */
+ { &vop_getattr_desc, ext2fs_getattr }, /* getattr */
+ { &vop_setattr_desc, ext2fs_setattr }, /* setattr */
+ { &vop_read_desc, ufsfifo_read }, /* read */
+ { &vop_write_desc, ufsfifo_write }, /* write */
+ { &vop_lease_desc, fifo_lease_check }, /* lease */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_poll_desc, fifo_poll }, /* poll */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, ext2fs_fsync }, /* fsync */
+ { &vop_seek_desc, fifo_seek }, /* seek */
+ { &vop_remove_desc, fifo_remove }, /* remove */
+ { &vop_link_desc, fifo_link }, /* link */
+ { &vop_rename_desc, fifo_rename }, /* rename */
+ { &vop_mkdir_desc, fifo_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fifo_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fifo_symlink }, /* symlink */
+ { &vop_readdir_desc, fifo_readdir }, /* readdir */
+ { &vop_readlink_desc, fifo_readlink }, /* readlink */
+ { &vop_abortop_desc, fifo_abortop }, /* abortop */
+ { &vop_inactive_desc, ext2fs_inactive },/* inactive */
+ { &vop_reclaim_desc, ext2fs_reclaim }, /* reclaim */
+ { &vop_lock_desc, ufs_lock }, /* lock */
+ { &vop_unlock_desc, ufs_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_strategy }, /* strategy */
+ { &vop_print_desc, ufs_print }, /* print */
+ { &vop_islocked_desc, ufs_islocked }, /* islocked */
+ { &vop_pathconf_desc, fifo_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fifo_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fifo_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fifo_valloc }, /* valloc */
+ { &vop_vfree_desc, ext2fs_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, ext2fs_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*) __P((void *)))NULL }
+};
+struct vnodeopv_desc ext2fs_fifoop_opv_desc =
+ { &ext2fs_fifoop_p, ext2fs_fifoop_entries };
+#endif /* FIFO */