summaryrefslogtreecommitdiffstats
path: root/sys/miscfs
diff options
context:
space:
mode:
authorderaadt <deraadt@openbsd.org>1995-10-18 08:37:01 +0000
committerderaadt <deraadt@openbsd.org>1995-10-18 08:37:01 +0000
commitdf930be708d50e9715f173caa26ffe1b7599b157 (patch)
treeaa317e49e28cb999c9cf3db7f00c20903fe6010a /sys/miscfs
downloadwireguard-openbsd-df930be708d50e9715f173caa26ffe1b7599b157.tar.xz
wireguard-openbsd-df930be708d50e9715f173caa26ffe1b7599b157.zip
initial import of NetBSD tree
Diffstat (limited to 'sys/miscfs')
-rw-r--r--sys/miscfs/deadfs/dead_vnops.c356
-rw-r--r--sys/miscfs/fdesc/fdesc.h83
-rw-r--r--sys/miscfs/fdesc/fdesc_vfsops.c294
-rw-r--r--sys/miscfs/fdesc/fdesc_vnops.c958
-rw-r--r--sys/miscfs/fifofs/fifo.h88
-rw-r--r--sys/miscfs/fifofs/fifo_vnops.c519
-rw-r--r--sys/miscfs/kernfs/kernfs.h58
-rw-r--r--sys/miscfs/kernfs/kernfs_vfsops.c314
-rw-r--r--sys/miscfs/kernfs/kernfs_vnops.c747
-rw-r--r--sys/miscfs/nullfs/null.h75
-rw-r--r--sys/miscfs/nullfs/null_subr.c334
-rw-r--r--sys/miscfs/nullfs/null_vfsops.c373
-rw-r--r--sys/miscfs/nullfs/null_vnops.c465
-rw-r--r--sys/miscfs/portal/portal.h74
-rw-r--r--sys/miscfs/portal/portal_vfsops.c311
-rw-r--r--sys/miscfs/portal/portal_vnops.c718
-rw-r--r--sys/miscfs/procfs/README113
-rw-r--r--sys/miscfs/procfs/procfs.h170
-rw-r--r--sys/miscfs/procfs/procfs_ctl.c297
-rw-r--r--sys/miscfs/procfs/procfs_fpregs.c107
-rw-r--r--sys/miscfs/procfs/procfs_mem.c301
-rw-r--r--sys/miscfs/procfs/procfs_note.c72
-rw-r--r--sys/miscfs/procfs/procfs_regs.c107
-rw-r--r--sys/miscfs/procfs/procfs_status.c145
-rw-r--r--sys/miscfs/procfs/procfs_subr.c312
-rw-r--r--sys/miscfs/procfs/procfs_vfsops.c229
-rw-r--r--sys/miscfs/procfs/procfs_vnops.c884
-rw-r--r--sys/miscfs/specfs/spec_vnops.c702
-rw-r--r--sys/miscfs/specfs/specdev.h129
-rw-r--r--sys/miscfs/umapfs/umap.h92
-rw-r--r--sys/miscfs/umapfs/umap_subr.c437
-rw-r--r--sys/miscfs/umapfs/umap_vfsops.c414
-rw-r--r--sys/miscfs/umapfs/umap_vnops.c496
-rw-r--r--sys/miscfs/union/union.h132
-rw-r--r--sys/miscfs/union/union_subr.c1099
-rw-r--r--sys/miscfs/union/union_vfsops.c540
-rw-r--r--sys/miscfs/union/union_vnops.c1606
37 files changed, 14151 insertions, 0 deletions
diff --git a/sys/miscfs/deadfs/dead_vnops.c b/sys/miscfs/deadfs/dead_vnops.c
new file mode 100644
index 00000000000..374bd4e3586
--- /dev/null
+++ b/sys/miscfs/deadfs/dead_vnops.c
@@ -0,0 +1,356 @@
+/* $NetBSD: dead_vnops.c,v 1.13 1995/04/10 00:48:46 mycroft Exp $ */
+
+/*
+ * Copyright (c) 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.
+ *
+ * @(#)dead_vnops.c 8.2 (Berkeley) 11/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/errno.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+
+/*
+ * Prototypes for dead operations on vnodes.
+ */
+int dead_badop(),
+ dead_ebadf();
+int dead_lookup __P((struct vop_lookup_args *));
+#define dead_create ((int (*) __P((struct vop_create_args *)))dead_badop)
+#define dead_mknod ((int (*) __P((struct vop_mknod_args *)))dead_badop)
+int dead_open __P((struct vop_open_args *));
+#define dead_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define dead_access ((int (*) __P((struct vop_access_args *)))dead_ebadf)
+#define dead_getattr ((int (*) __P((struct vop_getattr_args *)))dead_ebadf)
+#define dead_setattr ((int (*) __P((struct vop_setattr_args *)))dead_ebadf)
+int dead_read __P((struct vop_read_args *));
+int dead_write __P((struct vop_write_args *));
+int dead_ioctl __P((struct vop_ioctl_args *));
+int dead_select __P((struct vop_select_args *));
+#define dead_mmap ((int (*) __P((struct vop_mmap_args *)))dead_badop)
+#define dead_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define dead_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define dead_remove ((int (*) __P((struct vop_remove_args *)))dead_badop)
+#define dead_link ((int (*) __P((struct vop_link_args *)))dead_badop)
+#define dead_rename ((int (*) __P((struct vop_rename_args *)))dead_badop)
+#define dead_mkdir ((int (*) __P((struct vop_mkdir_args *)))dead_badop)
+#define dead_rmdir ((int (*) __P((struct vop_rmdir_args *)))dead_badop)
+#define dead_symlink ((int (*) __P((struct vop_symlink_args *)))dead_badop)
+#define dead_readdir ((int (*) __P((struct vop_readdir_args *)))dead_ebadf)
+#define dead_readlink ((int (*) __P((struct vop_readlink_args *)))dead_ebadf)
+#define dead_abortop ((int (*) __P((struct vop_abortop_args *)))dead_badop)
+#define dead_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
+#define dead_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
+int dead_lock __P((struct vop_lock_args *));
+#define dead_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+int dead_bmap __P((struct vop_bmap_args *));
+int dead_strategy __P((struct vop_strategy_args *));
+int dead_print __P((struct vop_print_args *));
+#define dead_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define dead_pathconf ((int (*) __P((struct vop_pathconf_args *)))dead_ebadf)
+#define dead_advlock ((int (*) __P((struct vop_advlock_args *)))dead_ebadf)
+#define dead_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))dead_badop)
+#define dead_valloc ((int (*) __P((struct vop_valloc_args *)))dead_badop)
+#define dead_vfree ((int (*) __P((struct vop_vfree_args *)))dead_badop)
+#define dead_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
+#define dead_update ((int (*) __P((struct vop_update_args *)))nullop)
+#define dead_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
+
+int (**dead_vnodeop_p)();
+struct vnodeopv_entry_desc dead_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, dead_lookup }, /* lookup */
+ { &vop_create_desc, dead_create }, /* create */
+ { &vop_mknod_desc, dead_mknod }, /* mknod */
+ { &vop_open_desc, dead_open }, /* open */
+ { &vop_close_desc, dead_close }, /* close */
+ { &vop_access_desc, dead_access }, /* access */
+ { &vop_getattr_desc, dead_getattr }, /* getattr */
+ { &vop_setattr_desc, dead_setattr }, /* setattr */
+ { &vop_read_desc, dead_read }, /* read */
+ { &vop_write_desc, dead_write }, /* write */
+ { &vop_ioctl_desc, dead_ioctl }, /* ioctl */
+ { &vop_select_desc, dead_select }, /* select */
+ { &vop_mmap_desc, dead_mmap }, /* mmap */
+ { &vop_fsync_desc, dead_fsync }, /* fsync */
+ { &vop_seek_desc, dead_seek }, /* seek */
+ { &vop_remove_desc, dead_remove }, /* remove */
+ { &vop_link_desc, dead_link }, /* link */
+ { &vop_rename_desc, dead_rename }, /* rename */
+ { &vop_mkdir_desc, dead_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, dead_rmdir }, /* rmdir */
+ { &vop_symlink_desc, dead_symlink }, /* symlink */
+ { &vop_readdir_desc, dead_readdir }, /* readdir */
+ { &vop_readlink_desc, dead_readlink }, /* readlink */
+ { &vop_abortop_desc, dead_abortop }, /* abortop */
+ { &vop_inactive_desc, dead_inactive }, /* inactive */
+ { &vop_reclaim_desc, dead_reclaim }, /* reclaim */
+ { &vop_lock_desc, dead_lock }, /* lock */
+ { &vop_unlock_desc, dead_unlock }, /* unlock */
+ { &vop_bmap_desc, dead_bmap }, /* bmap */
+ { &vop_strategy_desc, dead_strategy }, /* strategy */
+ { &vop_print_desc, dead_print }, /* print */
+ { &vop_islocked_desc, dead_islocked }, /* islocked */
+ { &vop_pathconf_desc, dead_pathconf }, /* pathconf */
+ { &vop_advlock_desc, dead_advlock }, /* advlock */
+ { &vop_blkatoff_desc, dead_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, dead_valloc }, /* valloc */
+ { &vop_vfree_desc, dead_vfree }, /* vfree */
+ { &vop_truncate_desc, dead_truncate }, /* truncate */
+ { &vop_update_desc, dead_update }, /* update */
+ { &vop_bwrite_desc, dead_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc dead_vnodeop_opv_desc =
+ { &dead_vnodeop_p, dead_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+/* ARGSUSED */
+int
+dead_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open always fails as if device did not exist.
+ */
+/* ARGSUSED */
+dead_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (ENXIO);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+dead_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ if (chkvnlock(ap->a_vp))
+ panic("dead_read: lock");
+ /*
+ * Return EOF for tty devices, EIO for others
+ */
+ if ((ap->a_vp->v_flag & VISTTY) == 0)
+ return (EIO);
+ return (0);
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+dead_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ if (chkvnlock(ap->a_vp))
+ panic("dead_write: lock");
+ return (EIO);
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+dead_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ u_long a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (EBADF);
+ return (VCALL(ap->a_vp, VOFFSET(vop_ioctl), ap));
+}
+
+/* ARGSUSED */
+dead_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Let the user find out that the descriptor is gone.
+ */
+ return (1);
+}
+
+/*
+ * Just call the device strategy routine
+ */
+dead_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+
+ if (ap->a_bp->b_vp == NULL || !chkvnlock(ap->a_bp->b_vp)) {
+ ap->a_bp->b_flags |= B_ERROR;
+ biodone(ap->a_bp);
+ return (EIO);
+ }
+ return (VOP_STRATEGY(ap->a_bp));
+}
+
+/*
+ * Wait until the vnode has finished changing state.
+ */
+dead_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (0);
+ return (VCALL(ap->a_vp, VOFFSET(vop_lock), ap));
+}
+
+/*
+ * Wait until the vnode has finished changing state.
+ */
+dead_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+
+ if (!chkvnlock(ap->a_vp))
+ return (EIO);
+ return (VOP_BMAP(ap->a_vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp));
+}
+
+/*
+ * Print out the contents of a dead vnode.
+ */
+/* ARGSUSED */
+dead_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, dead vnode\n");
+}
+
+/*
+ * Empty vnode failed operation
+ */
+dead_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Empty vnode bad operation
+ */
+dead_badop()
+{
+
+ panic("dead_badop called");
+ /* NOTREACHED */
+}
+
+/*
+ * Empty vnode null operation
+ */
+dead_nullop()
+{
+
+ return (0);
+}
+
+/*
+ * We have to wait during times when the vnode is
+ * in a state of change.
+ */
+chkvnlock(vp)
+ register struct vnode *vp;
+{
+ int locked = 0;
+
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ locked = 1;
+ }
+ return (locked);
+}
diff --git a/sys/miscfs/fdesc/fdesc.h b/sys/miscfs/fdesc/fdesc.h
new file mode 100644
index 00000000000..941082d40b6
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc.h
@@ -0,0 +1,83 @@
+/* $NetBSD: fdesc.h,v 1.8 1995/03/29 22:08:11 briggs Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)fdesc.h 8.6 (Berkeley) 8/20/94
+ *
+ * #Id: fdesc.h,v 1.8 1993/04/06 15:28:33 jsp Exp #
+ */
+
+#ifdef _KERNEL
+struct fdescmount {
+ struct vnode *f_root; /* Root node */
+};
+
+#define FD_ROOT 2
+#define FD_DEVFD 3
+#define FD_STDIN 4
+#define FD_STDOUT 5
+#define FD_STDERR 6
+#define FD_CTTY 7
+#define FD_DESC 8
+#define FD_MAX 12
+
+typedef enum {
+ Froot,
+ Fdevfd,
+ Fdesc,
+ Flink,
+ Fctty
+} fdntype;
+
+struct fdescnode {
+ LIST_ENTRY(fdescnode) fd_hash; /* Hash list */
+ struct vnode *fd_vnode; /* Back ptr to vnode */
+ fdntype fd_type; /* Type of this node */
+ unsigned fd_fd; /* Fd to be dup'ed */
+ char *fd_link; /* Link to fd/n */
+ int fd_ix; /* filesystem index */
+};
+
+#define VFSTOFDESC(mp) ((struct fdescmount *)((mp)->mnt_data))
+#define VTOFDESC(vp) ((struct fdescnode *)(vp)->v_data)
+
+extern dev_t devctty;
+extern int fdesc_init __P((void));
+extern int fdesc_root __P((struct mount *, struct vnode **));
+extern int fdesc_allocvp __P((fdntype, int, struct mount *, struct vnode **));
+extern int (**fdesc_vnodeop_p)();
+extern struct vfsops fdesc_vfsops;
+#endif /* _KERNEL */
diff --git a/sys/miscfs/fdesc/fdesc_vfsops.c b/sys/miscfs/fdesc/fdesc_vfsops.c
new file mode 100644
index 00000000000..3d3ee6b6869
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc_vfsops.c
@@ -0,0 +1,294 @@
+/* $NetBSD: fdesc_vfsops.c,v 1.20 1995/06/18 14:47:22 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)fdesc_vfsops.c 8.4 (Berkeley) 1/21/94
+ *
+ * #Id: fdesc_vfsops.c,v 1.9 1993/04/06 15:28:33 jsp Exp #
+ */
+
+/*
+ * /dev/fd Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/resourcevar.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/fdesc/fdesc.h>
+
+/*
+ * Mount the per-process file descriptors (/dev/fd)
+ */
+int
+fdesc_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ size_t size;
+ struct fdescmount *fmp;
+ struct vnode *rvp;
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ error = fdesc_allocvp(Froot, FD_ROOT, mp, &rvp);
+ if (error)
+ return (error);
+
+ MALLOC(fmp, struct fdescmount *, sizeof(struct fdescmount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+ rvp->v_type = VDIR;
+ rvp->v_flag |= VROOT;
+ fmp->f_root = rvp;
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t)fmp;
+ getnewfsid(mp, makefstype(MOUNT_FDESC));
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("fdesc", mp->mnt_stat.f_mntfromname, sizeof("fdesc"));
+ return (0);
+}
+
+int
+fdesc_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+ return (0);
+}
+
+int
+fdesc_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int error;
+ int flags = 0;
+ extern int doforce;
+ struct vnode *rootvp = VFSTOFDESC(mp)->f_root;
+
+ if (mntflags & MNT_FORCE) {
+ /* fdesc can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+ if (rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, rootvp, flags))
+ return (error);
+
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(rootvp);
+ /*
+ * Finally, throw away the fdescmount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+
+ return (0);
+}
+
+int
+fdesc_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = VFSTOFDESC(mp)->f_root;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+fdesc_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ struct filedesc *fdp;
+ int lim;
+ int i;
+ int last;
+ int freefd;
+
+ /*
+ * Compute number of free file descriptors.
+ * [ Strange results will ensue if the open file
+ * limit is ever reduced below the current number
+ * of open files... ]
+ */
+ lim = p->p_rlimit[RLIMIT_NOFILE].rlim_cur;
+ fdp = p->p_fd;
+ last = min(fdp->fd_nfiles, lim);
+ freefd = 0;
+ for (i = fdp->fd_freefile; i < last; i++)
+ if (fdp->fd_ofiles[i] == NULL)
+ freefd++;
+
+ /*
+ * Adjust for the fact that the fdesc array may not
+ * have been fully allocated yet.
+ */
+ if (fdp->fd_nfiles < lim)
+ freefd += (lim - fdp->fd_nfiles);
+
+#ifdef COMPAT_09
+ sbp->f_type = 6;
+#else
+ sbp->f_type = 0;
+#endif
+ sbp->f_bsize = DEV_BSIZE;
+ sbp->f_iosize = DEV_BSIZE;
+ sbp->f_blocks = 2; /* 1K to keep df happy */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = lim + 1; /* Allow for "." */
+ sbp->f_ffree = freefd; /* See comments above */
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ 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);
+}
+
+int
+fdesc_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+/*
+ * Fdesc flat namespace lookup.
+ * Currently unsupported.
+ */
+int
+fdesc_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_fhtovp(mp, fhp, setgen, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ int setgen;
+ struct vnode **vpp;
+{
+ return (EOPNOTSUPP);
+}
+
+int
+fdesc_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+struct vfsops fdesc_vfsops = {
+ MOUNT_FDESC,
+ fdesc_mount,
+ fdesc_start,
+ fdesc_unmount,
+ fdesc_root,
+ fdesc_quotactl,
+ fdesc_statfs,
+ fdesc_sync,
+ fdesc_vget,
+ fdesc_fhtovp,
+ fdesc_vptofh,
+ fdesc_init,
+};
diff --git a/sys/miscfs/fdesc/fdesc_vnops.c b/sys/miscfs/fdesc/fdesc_vnops.c
new file mode 100644
index 00000000000..b4418b72872
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc_vnops.c
@@ -0,0 +1,958 @@
+/* $NetBSD: fdesc_vnops.c,v 1.27 1995/10/09 14:20:04 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)fdesc_vnops.c 8.12 (Berkeley) 8/20/94
+ *
+ * #Id: fdesc_vnops.c,v 1.12 1993/04/06 16:17:17 jsp Exp #
+ */
+
+/*
+ * /dev/fd Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/kernel.h> /* boottime */
+#include <sys/resourcevar.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/dirent.h>
+#include <miscfs/fdesc/fdesc.h>
+
+#define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL)
+
+#define FDL_WANT 0x01
+#define FDL_LOCKED 0x02
+static int fdcache_lock;
+
+dev_t devctty;
+
+#if (FD_STDIN != FD_STDOUT-1) || (FD_STDOUT != FD_STDERR-1)
+FD_STDIN, FD_STDOUT, FD_STDERR must be a sequence n, n+1, n+2
+#endif
+
+#define NFDCACHE 4
+
+#define FD_NHASH(ix) \
+ (&fdhashtbl[(ix) & fdhash])
+LIST_HEAD(fdhashhead, fdescnode) *fdhashtbl;
+u_long fdhash;
+
+/*
+ * Initialise cache headers
+ */
+fdesc_init()
+{
+
+ devctty = makedev(nchrdev, 0);
+ fdhashtbl = hashinit(NFDCACHE, M_CACHE, &fdhash);
+}
+
+int
+fdesc_allocvp(ftype, ix, mp, vpp)
+ fdntype ftype;
+ int ix;
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct fdhashhead *fc;
+ struct fdescnode *fd;
+ int error = 0;
+
+ fc = FD_NHASH(ix);
+loop:
+ for (fd = fc->lh_first; fd != 0; fd = fd->fd_hash.le_next) {
+ if (fd->fd_ix == ix && fd->fd_vnode->v_mount == mp) {
+ if (vget(fd->fd_vnode, 0))
+ goto loop;
+ *vpp = fd->fd_vnode;
+ return (error);
+ }
+ }
+
+ /*
+ * otherwise lock the array while we call getnewvnode
+ * since that can block.
+ */
+ if (fdcache_lock & FDL_LOCKED) {
+ fdcache_lock |= FDL_WANT;
+ sleep((caddr_t) &fdcache_lock, PINOD);
+ goto loop;
+ }
+ fdcache_lock |= FDL_LOCKED;
+
+ error = getnewvnode(VT_FDESC, mp, fdesc_vnodeop_p, vpp);
+ if (error)
+ goto out;
+ MALLOC(fd, void *, sizeof(struct fdescnode), M_TEMP, M_WAITOK);
+ (*vpp)->v_data = fd;
+ fd->fd_vnode = *vpp;
+ fd->fd_type = ftype;
+ fd->fd_fd = -1;
+ fd->fd_link = 0;
+ fd->fd_ix = ix;
+ LIST_INSERT_HEAD(fc, fd, fd_hash);
+
+out:;
+ fdcache_lock &= ~FDL_LOCKED;
+
+ if (fdcache_lock & FDL_WANT) {
+ fdcache_lock &= ~FDL_WANT;
+ wakeup((caddr_t) &fdcache_lock);
+ }
+
+ return (error);
+}
+
+/*
+ * vp is the current namei directory
+ * ndp is the name to locate in that directory...
+ */
+int
+fdesc_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname;
+ struct proc *p;
+ int nfiles;
+ unsigned fd;
+ int error;
+ struct vnode *fvp;
+ char *ln;
+
+ pname = ap->a_cnp->cn_nameptr;
+ if (ap->a_cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ VOP_LOCK(dvp);
+ return (0);
+ }
+
+ p = ap->a_cnp->cn_proc;
+ nfiles = p->p_fd->fd_nfiles;
+
+ switch (VTOFDESC(dvp)->fd_type) {
+ default:
+ case Flink:
+ case Fdesc:
+ case Fctty:
+ error = ENOTDIR;
+ goto bad;
+
+ case Froot:
+ if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "fd", 2) == 0) {
+ error = fdesc_allocvp(Fdevfd, FD_DEVFD, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ *vpp = fvp;
+ fvp->v_type = VDIR;
+ VOP_LOCK(fvp);
+ return (0);
+ }
+
+ if (ap->a_cnp->cn_namelen == 3 && bcmp(pname, "tty", 3) == 0) {
+ struct vnode *ttyvp = cttyvp(p);
+ if (ttyvp == NULL) {
+ error = ENXIO;
+ goto bad;
+ }
+ error = fdesc_allocvp(Fctty, FD_CTTY, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ *vpp = fvp;
+ fvp->v_type = VFIFO;
+ VOP_LOCK(fvp);
+ return (0);
+ }
+
+ ln = 0;
+ switch (ap->a_cnp->cn_namelen) {
+ case 5:
+ if (bcmp(pname, "stdin", 5) == 0) {
+ ln = "fd/0";
+ fd = FD_STDIN;
+ }
+ break;
+ case 6:
+ if (bcmp(pname, "stdout", 6) == 0) {
+ ln = "fd/1";
+ fd = FD_STDOUT;
+ } else
+ if (bcmp(pname, "stderr", 6) == 0) {
+ ln = "fd/2";
+ fd = FD_STDERR;
+ }
+ break;
+ }
+
+ if (ln) {
+ error = fdesc_allocvp(Flink, fd, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ VTOFDESC(fvp)->fd_link = ln;
+ *vpp = fvp;
+ fvp->v_type = VLNK;
+ VOP_LOCK(fvp);
+ return (0);
+ } else {
+ error = ENOENT;
+ goto bad;
+ }
+
+ /* FALL THROUGH */
+
+ case Fdevfd:
+ if (ap->a_cnp->cn_namelen == 2 && bcmp(pname, "..", 2) == 0) {
+ error = fdesc_root(dvp->v_mount, vpp);
+ return (error);
+ }
+
+ fd = 0;
+ while (*pname >= '0' && *pname <= '9') {
+ fd = 10 * fd + *pname++ - '0';
+ if (fd >= nfiles)
+ break;
+ }
+
+ if (*pname != '\0') {
+ error = ENOENT;
+ goto bad;
+ }
+
+ if (fd >= nfiles || p->p_fd->fd_ofiles[fd] == NULL) {
+ error = EBADF;
+ goto bad;
+ }
+
+ error = fdesc_allocvp(Fdesc, FD_DESC+fd, dvp->v_mount, &fvp);
+ if (error)
+ goto bad;
+ VTOFDESC(fvp)->fd_fd = fd;
+ *vpp = fvp;
+ return (0);
+ }
+
+bad:;
+ *vpp = NULL;
+ return (error);
+}
+
+int
+fdesc_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Fdesc:
+ /*
+ * XXX Kludge: set p->p_dupfd to contain the value of the
+ * the file descriptor being sought for duplication. The error
+ * return ensures that the vnode for this device will be
+ * released by vn_open. Open will detect this special error and
+ * take the actions in dupfdopen. Other callers of vn_open or
+ * VOP_OPEN will simply report the error.
+ */
+ ap->a_p->p_dupfd = VTOFDESC(vp)->fd_fd; /* XXX */
+ return (ENODEV);
+
+ case Fctty:
+ return (cttyopen(devctty, ap->a_mode, 0, ap->a_p));
+ }
+
+ return (0);
+}
+
+static int
+fdesc_attr(fd, vap, cred, p)
+ int fd;
+ struct vattr *vap;
+ struct ucred *cred;
+ struct proc *p;
+{
+ struct filedesc *fdp = p->p_fd;
+ struct file *fp;
+ struct stat stb;
+ int error;
+
+ if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL)
+ return (EBADF);
+
+ switch (fp->f_type) {
+ case DTYPE_VNODE:
+ error = VOP_GETATTR((struct vnode *) fp->f_data, vap, cred, p);
+ if (error == 0 && vap->va_type == VDIR) {
+ /*
+ * directories can cause loops in the namespace,
+ * so turn off the 'x' bits to avoid trouble.
+ */
+ vap->va_mode &= ~((VEXEC)|(VEXEC>>3)|(VEXEC>>6));
+ }
+ break;
+
+ case DTYPE_SOCKET:
+ error = soo_stat((struct socket *)fp->f_data, &stb);
+ if (error == 0) {
+ vattr_null(vap);
+ vap->va_type = VSOCK;
+ vap->va_mode = stb.st_mode;
+ vap->va_nlink = stb.st_nlink;
+ vap->va_uid = stb.st_uid;
+ vap->va_gid = stb.st_gid;
+ vap->va_fsid = stb.st_dev;
+ vap->va_fileid = stb.st_ino;
+ vap->va_size = stb.st_size;
+ vap->va_blocksize = stb.st_blksize;
+ vap->va_atime = stb.st_atimespec;
+ vap->va_mtime = stb.st_mtimespec;
+ vap->va_ctime = stb.st_ctimespec;
+ vap->va_gen = stb.st_gen;
+ vap->va_flags = stb.st_flags;
+ vap->va_rdev = stb.st_rdev;
+ vap->va_bytes = stb.st_blocks * stb.st_blksize;
+ }
+ break;
+
+ default:
+ panic("fdesc attr");
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ unsigned fd;
+ int error = 0;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Froot:
+ case Fdevfd:
+ case Flink:
+ case Fctty:
+ bzero((caddr_t) vap, sizeof(*vap));
+ vattr_null(vap);
+ vap->va_fileid = VTOFDESC(vp)->fd_ix;
+
+ switch (VTOFDESC(vp)->fd_type) {
+ case Flink:
+ vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+ vap->va_type = VLNK;
+ vap->va_nlink = 1;
+ vap->va_size = strlen(VTOFDESC(vp)->fd_link);
+ break;
+
+ case Fctty:
+ vap->va_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ vap->va_type = VFIFO;
+ vap->va_nlink = 1;
+ vap->va_size = 0;
+ break;
+
+ default:
+ vap->va_mode = S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
+ vap->va_type = VDIR;
+ vap->va_nlink = 2;
+ vap->va_size = DEV_BSIZE;
+ break;
+ }
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_blocksize = DEV_BSIZE;
+ vap->va_atime.ts_sec = boottime.tv_sec;
+ vap->va_atime.ts_nsec = 0;
+ vap->va_mtime = vap->va_atime;
+ vap->va_ctime = vap->va_mtime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ vap->va_bytes = 0;
+ break;
+
+ case Fdesc:
+ fd = VTOFDESC(vp)->fd_fd;
+ error = fdesc_attr(fd, vap, ap->a_cred, ap->a_p);
+ break;
+
+ default:
+ panic("fdesc_getattr");
+ break;
+ }
+
+ if (error == 0)
+ vp->v_type = vap->va_type;
+
+ return (error);
+}
+
+int
+fdesc_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct filedesc *fdp = ap->a_p->p_fd;
+ struct file *fp;
+ unsigned fd;
+ int error;
+
+ /*
+ * Can't mess with the root vnode
+ */
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fdesc:
+ break;
+
+ case Fctty:
+ return (0);
+
+ default:
+ return (EACCES);
+ }
+
+ fd = VTOFDESC(ap->a_vp)->fd_fd;
+ if (fd >= fdp->fd_nfiles || (fp = fdp->fd_ofiles[fd]) == NULL) {
+ return (EBADF);
+ }
+
+ /*
+ * Can setattr the underlying vnode, but not sockets!
+ */
+ switch (fp->f_type) {
+ case DTYPE_VNODE:
+ error = VOP_SETATTR((struct vnode *) fp->f_data, ap->a_vap, ap->a_cred, ap->a_p);
+ break;
+
+ case DTYPE_SOCKET:
+ error = 0;
+ break;
+
+ default:
+ panic("fdesc setattr");
+ break;
+ }
+
+ return (error);
+}
+
+#define UIO_MX 32
+
+struct fdesc_target {
+ ino_t ft_fileno;
+ u_char ft_type;
+ u_char ft_namlen;
+ char *ft_name;
+} fdesc_targets[] = {
+/* NOTE: The name must be less than UIO_MX-16 chars in length */
+#define N(s) sizeof(s)-1, s
+ { FD_DEVFD, DT_DIR, N("fd") },
+ { FD_STDIN, DT_LNK, N("stdin") },
+ { FD_STDOUT, DT_LNK, N("stdout") },
+ { FD_STDERR, DT_LNK, N("stderr") },
+ { FD_CTTY, DT_UNKNOWN, N("tty") },
+#undef N
+};
+static int nfdesc_targets = sizeof(fdesc_targets) / sizeof(fdesc_targets[0]);
+
+int
+fdesc_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ u_long *a_cookies;
+ int a_ncookies;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct dirent d;
+ struct filedesc *fdp;
+ int i;
+ int error;
+ u_long *cookies = ap->a_cookies;
+ int ncookies = ap->a_ncookies;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ return (0);
+
+ case Fdesc:
+ return (ENOTDIR);
+ }
+
+ fdp = uio->uio_procp->p_fd;
+
+ if (uio->uio_resid < UIO_MX)
+ return (EINVAL);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ error = 0;
+ i = uio->uio_offset;
+ bzero((caddr_t)&d, UIO_MX);
+ d.d_reclen = UIO_MX;
+
+ if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
+ struct fdesc_target *ft;
+
+ for (ft = &fdesc_targets[i];
+ uio->uio_resid >= UIO_MX && i < nfdesc_targets; ft++, i++) {
+ switch (ft->ft_fileno) {
+ case FD_CTTY:
+ if (cttyvp(uio->uio_procp) == NULL)
+ continue;
+ break;
+
+ case FD_STDIN:
+ case FD_STDOUT:
+ case FD_STDERR:
+ if ((ft->ft_fileno - FD_STDIN) >= fdp->fd_nfiles)
+ continue;
+ if (fdp->fd_ofiles[ft->ft_fileno - FD_STDIN] == NULL)
+ continue;
+ break;
+ }
+
+ d.d_fileno = ft->ft_fileno;
+ d.d_namlen = ft->ft_namlen;
+ bcopy(ft->ft_name, d.d_name, ft->ft_namlen + 1);
+ d.d_type = ft->ft_type;
+
+ if (error = uiomove((caddr_t)&d, UIO_MX, uio))
+ break;
+ if (ncookies-- > 0)
+ *cookies++ = i + 1;
+ }
+ } else {
+ for (; i - 2 < fdp->fd_nfiles && uio->uio_resid >= UIO_MX;
+ i++) {
+ switch (i) {
+ case 0:
+ case 1:
+ d.d_fileno = FD_ROOT; /* XXX */
+ d.d_namlen = i + 1;
+ bcopy("..", d.d_name, d.d_namlen);
+ d.d_name[i + 1] = '\0';
+ d.d_type = DT_DIR;
+ break;
+
+ default:
+ if (fdp->fd_ofiles[i - 2] == NULL)
+ continue;
+ d.d_fileno = i - 2 + FD_STDIN;
+ d.d_namlen = sprintf(d.d_name, "%d", i - 2);
+ d.d_type = DT_UNKNOWN;
+ break;
+ }
+
+ if (error = uiomove((caddr_t)&d, UIO_MX, uio))
+ break;
+ if (ncookies-- > 0)
+ *cookies++ = i + 1;
+ }
+ }
+
+ uio->uio_offset = i;
+ return (error);
+}
+
+int
+fdesc_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ int error;
+
+ if (vp->v_type != VLNK)
+ return (EPERM);
+
+ if (VTOFDESC(vp)->fd_type == Flink) {
+ char *ln = VTOFDESC(vp)->fd_link;
+ error = uiomove(ln, strlen(ln), ap->a_uio);
+ } else {
+ error = EOPNOTSUPP;
+ }
+
+ return (error);
+}
+
+int
+fdesc_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyread(devctty, ap->a_uio, ap->a_ioflag);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttywrite(devctty, ap->a_uio, ap->a_ioflag);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ u_long a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyioctl(devctty, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = EOPNOTSUPP;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ error = cttyselect(devctty, ap->a_fflags, ap->a_p);
+ break;
+
+ default:
+ error = EOPNOTSUPP;
+ break;
+ }
+
+ return (error);
+}
+
+int
+fdesc_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ /*
+ * Clear out the v_type field to avoid
+ * nasty things happening in vgone().
+ */
+ vp->v_type = VNON;
+ return (0);
+}
+
+int
+fdesc_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct fdescnode *fd = VTOFDESC(vp);
+
+ LIST_REMOVE(fd, fd_hash);
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+fdesc_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ register_t *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Print out the contents of a /dev/fd vnode.
+ */
+/* ARGSUSED */
+int
+fdesc_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, fdesc vnode\n");
+ return (0);
+}
+
+/*void*/
+int
+fdesc_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * /dev/fd vnode unsupported operation
+ */
+int
+fdesc_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * /dev/fd "should never get here" operation
+ */
+int
+fdesc_badop()
+{
+
+ panic("fdesc: bad op");
+ /* NOTREACHED */
+}
+
+/*
+ * /dev/fd vnode null operation
+ */
+int
+fdesc_nullop()
+{
+
+ return (0);
+}
+
+#define fdesc_create ((int (*) __P((struct vop_create_args *)))fdesc_enotsupp)
+#define fdesc_mknod ((int (*) __P((struct vop_mknod_args *)))fdesc_enotsupp)
+#define fdesc_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define fdesc_access ((int (*) __P((struct vop_access_args *)))nullop)
+#define fdesc_mmap ((int (*) __P((struct vop_mmap_args *)))fdesc_enotsupp)
+#define fdesc_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define fdesc_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define fdesc_remove ((int (*) __P((struct vop_remove_args *)))fdesc_enotsupp)
+#define fdesc_link ((int (*) __P((struct vop_link_args *)))fdesc_enotsupp)
+#define fdesc_rename ((int (*) __P((struct vop_rename_args *)))fdesc_enotsupp)
+#define fdesc_mkdir ((int (*) __P((struct vop_mkdir_args *)))fdesc_enotsupp)
+#define fdesc_rmdir ((int (*) __P((struct vop_rmdir_args *)))fdesc_enotsupp)
+#define fdesc_symlink ((int (*) __P((struct vop_symlink_args *)))fdesc_enotsupp)
+#define fdesc_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
+#define fdesc_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define fdesc_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define fdesc_bmap ((int (*) __P((struct vop_bmap_args *)))fdesc_badop)
+#define fdesc_strategy ((int (*) __P((struct vop_strategy_args *)))fdesc_badop)
+#define fdesc_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define fdesc_advlock ((int (*) __P((struct vop_advlock_args *)))fdesc_enotsupp)
+#define fdesc_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))fdesc_enotsupp)
+#define fdesc_vget ((int (*) __P((struct vop_vget_args *)))fdesc_enotsupp)
+#define fdesc_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) fdesc_enotsupp)
+#define fdesc_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))fdesc_enotsupp)
+#define fdesc_update ((int (*) __P((struct vop_update_args *)))fdesc_enotsupp)
+#define fdesc_bwrite ((int (*) __P((struct vop_bwrite_args *)))fdesc_enotsupp)
+
+int (**fdesc_vnodeop_p)();
+struct vnodeopv_entry_desc fdesc_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, fdesc_lookup }, /* lookup */
+ { &vop_create_desc, fdesc_create }, /* create */
+ { &vop_mknod_desc, fdesc_mknod }, /* mknod */
+ { &vop_open_desc, fdesc_open }, /* open */
+ { &vop_close_desc, fdesc_close }, /* close */
+ { &vop_access_desc, fdesc_access }, /* access */
+ { &vop_getattr_desc, fdesc_getattr }, /* getattr */
+ { &vop_setattr_desc, fdesc_setattr }, /* setattr */
+ { &vop_read_desc, fdesc_read }, /* read */
+ { &vop_write_desc, fdesc_write }, /* write */
+ { &vop_ioctl_desc, fdesc_ioctl }, /* ioctl */
+ { &vop_select_desc, fdesc_select }, /* select */
+ { &vop_mmap_desc, fdesc_mmap }, /* mmap */
+ { &vop_fsync_desc, fdesc_fsync }, /* fsync */
+ { &vop_seek_desc, fdesc_seek }, /* seek */
+ { &vop_remove_desc, fdesc_remove }, /* remove */
+ { &vop_link_desc, fdesc_link }, /* link */
+ { &vop_rename_desc, fdesc_rename }, /* rename */
+ { &vop_mkdir_desc, fdesc_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, fdesc_rmdir }, /* rmdir */
+ { &vop_symlink_desc, fdesc_symlink }, /* symlink */
+ { &vop_readdir_desc, fdesc_readdir }, /* readdir */
+ { &vop_readlink_desc, fdesc_readlink }, /* readlink */
+ { &vop_abortop_desc, fdesc_abortop }, /* abortop */
+ { &vop_inactive_desc, fdesc_inactive }, /* inactive */
+ { &vop_reclaim_desc, fdesc_reclaim }, /* reclaim */
+ { &vop_lock_desc, fdesc_lock }, /* lock */
+ { &vop_unlock_desc, fdesc_unlock }, /* unlock */
+ { &vop_bmap_desc, fdesc_bmap }, /* bmap */
+ { &vop_strategy_desc, fdesc_strategy }, /* strategy */
+ { &vop_print_desc, fdesc_print }, /* print */
+ { &vop_islocked_desc, fdesc_islocked }, /* islocked */
+ { &vop_pathconf_desc, fdesc_pathconf }, /* pathconf */
+ { &vop_advlock_desc, fdesc_advlock }, /* advlock */
+ { &vop_blkatoff_desc, fdesc_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, fdesc_valloc }, /* valloc */
+ { &vop_vfree_desc, fdesc_vfree }, /* vfree */
+ { &vop_truncate_desc, fdesc_truncate }, /* truncate */
+ { &vop_update_desc, fdesc_update }, /* update */
+ { &vop_bwrite_desc, fdesc_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc fdesc_vnodeop_opv_desc =
+ { &fdesc_vnodeop_p, fdesc_vnodeop_entries };
diff --git a/sys/miscfs/fifofs/fifo.h b/sys/miscfs/fifofs/fifo.h
new file mode 100644
index 00000000000..9b7ca197b39
--- /dev/null
+++ b/sys/miscfs/fifofs/fifo.h
@@ -0,0 +1,88 @@
+/* $NetBSD: fifo.h,v 1.9 1994/12/13 20:14:38 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1991, 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.
+ *
+ * @(#)fifo.h 8.3 (Berkeley) 8/10/94
+ */
+
+#ifdef FIFO
+/*
+ * Prototypes for fifo operations on vnodes.
+ */
+int fifo_badop(),
+ fifo_ebadf();
+
+int fifo_lookup __P((struct vop_lookup_args *));
+#define fifo_create ((int (*) __P((struct vop_create_args *)))fifo_badop)
+#define fifo_mknod ((int (*) __P((struct vop_mknod_args *)))fifo_badop)
+int fifo_open __P((struct vop_open_args *));
+int fifo_close __P((struct vop_close_args *));
+#define fifo_access ((int (*) __P((struct vop_access_args *)))fifo_ebadf)
+#define fifo_getattr ((int (*) __P((struct vop_getattr_args *)))fifo_ebadf)
+#define fifo_setattr ((int (*) __P((struct vop_setattr_args *)))fifo_ebadf)
+int fifo_read __P((struct vop_read_args *));
+int fifo_write __P((struct vop_write_args *));
+#define fifo_lease_check ((int (*) __P((struct vop_lease_args *)))nullop)
+int fifo_ioctl __P((struct vop_ioctl_args *));
+int fifo_select __P((struct vop_select_args *));
+#define fifo_mmap ((int (*) __P((struct vop_mmap_args *)))fifo_badop)
+#define fifo_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define fifo_seek ((int (*) __P((struct vop_seek_args *)))fifo_badop)
+#define fifo_remove ((int (*) __P((struct vop_remove_args *)))fifo_badop)
+#define fifo_link ((int (*) __P((struct vop_link_args *)))fifo_badop)
+#define fifo_rename ((int (*) __P((struct vop_rename_args *)))fifo_badop)
+#define fifo_mkdir ((int (*) __P((struct vop_mkdir_args *)))fifo_badop)
+#define fifo_rmdir ((int (*) __P((struct vop_rmdir_args *)))fifo_badop)
+#define fifo_symlink ((int (*) __P((struct vop_symlink_args *)))fifo_badop)
+#define fifo_readdir ((int (*) __P((struct vop_readdir_args *)))fifo_badop)
+#define fifo_readlink ((int (*) __P((struct vop_readlink_args *)))fifo_badop)
+#define fifo_abortop ((int (*) __P((struct vop_abortop_args *)))fifo_badop)
+#define fifo_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
+#define fifo_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
+int fifo_lock __P((struct vop_lock_args *));
+int fifo_unlock __P((struct vop_unlock_args *));
+int fifo_bmap __P((struct vop_bmap_args *));
+#define fifo_strategy ((int (*) __P((struct vop_strategy_args *)))fifo_badop)
+int fifo_print __P((struct vop_print_args *));
+#define fifo_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+int fifo_pathconf __P((struct vop_pathconf_args *));
+int fifo_advlock __P((struct vop_advlock_args *));
+#define fifo_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))fifo_badop)
+#define fifo_valloc ((int (*) __P((struct vop_valloc_args *)))fifo_badop)
+#define fifo_reallocblks \
+ ((int (*) __P((struct vop_reallocblks_args *)))fifo_badop)
+#define fifo_vfree ((int (*) __P((struct vop_vfree_args *)))fifo_badop)
+#define fifo_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
+#define fifo_update ((int (*) __P((struct vop_update_args *)))nullop)
+#define fifo_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
+#endif /* FIFO */
diff --git a/sys/miscfs/fifofs/fifo_vnops.c b/sys/miscfs/fifofs/fifo_vnops.c
new file mode 100644
index 00000000000..9d7193d92a9
--- /dev/null
+++ b/sys/miscfs/fifofs/fifo_vnops.c
@@ -0,0 +1,519 @@
+/* $NetBSD: fifo_vnops.c,v 1.16 1995/04/14 23:30:14 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1990, 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.
+ *
+ * @(#)fifo_vnops.c 8.4 (Berkeley) 8/10/94
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/stat.h>
+#include <sys/systm.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/malloc.h>
+#include <miscfs/fifofs/fifo.h>
+
+/*
+ * This structure is associated with the FIFO vnode and stores
+ * the state associated with the FIFO.
+ */
+struct fifoinfo {
+ struct socket *fi_readsock;
+ struct socket *fi_writesock;
+ long fi_readers;
+ long fi_writers;
+};
+
+int (**fifo_vnodeop_p)();
+struct vnodeopv_entry_desc fifo_vnodeop_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, fifo_close }, /* close */
+ { &vop_access_desc, fifo_access }, /* access */
+ { &vop_getattr_desc, fifo_getattr }, /* getattr */
+ { &vop_setattr_desc, fifo_setattr }, /* setattr */
+ { &vop_read_desc, fifo_read }, /* read */
+ { &vop_write_desc, fifo_write }, /* write */
+ { &vop_lease_desc, fifo_lease_check }, /* lease */
+ { &vop_ioctl_desc, fifo_ioctl }, /* ioctl */
+ { &vop_select_desc, fifo_select }, /* select */
+ { &vop_mmap_desc, fifo_mmap }, /* mmap */
+ { &vop_fsync_desc, fifo_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, fifo_inactive }, /* inactive */
+ { &vop_reclaim_desc, fifo_reclaim }, /* reclaim */
+ { &vop_lock_desc, fifo_lock }, /* lock */
+ { &vop_unlock_desc, fifo_unlock }, /* unlock */
+ { &vop_bmap_desc, fifo_bmap }, /* bmap */
+ { &vop_strategy_desc, fifo_strategy }, /* strategy */
+ { &vop_print_desc, fifo_print }, /* print */
+ { &vop_islocked_desc, fifo_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, fifo_vfree }, /* vfree */
+ { &vop_truncate_desc, fifo_truncate }, /* truncate */
+ { &vop_update_desc, fifo_update }, /* update */
+ { &vop_bwrite_desc, fifo_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc fifo_vnodeop_opv_desc =
+ { &fifo_vnodeop_p, fifo_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+/* ARGSUSED */
+fifo_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open called to set up a new instance of a fifo or
+ * to find an active instance of a fifo.
+ */
+/* ARGSUSED */
+fifo_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct fifoinfo *fip;
+ struct socket *rso, *wso;
+ int error;
+ static char openstr[] = "fifo";
+
+ if ((fip = vp->v_fifoinfo) == NULL) {
+ MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK);
+ vp->v_fifoinfo = fip;
+ if (error = socreate(AF_LOCAL, &rso, SOCK_STREAM, 0)) {
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_readsock = rso;
+ if (error = socreate(AF_LOCAL, &wso, SOCK_STREAM, 0)) {
+ (void)soclose(rso);
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_writesock = wso;
+ if (error = unp_connect2(wso, rso)) {
+ (void)soclose(wso);
+ (void)soclose(rso);
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_readers = fip->fi_writers = 0;
+ wso->so_state |= SS_CANTRCVMORE;
+ rso->so_state |= SS_CANTSENDMORE;
+ }
+ if (ap->a_mode & FREAD) {
+ if (fip->fi_readers++ == 0) {
+ fip->fi_writesock->so_state &= ~SS_CANTSENDMORE;
+ if (fip->fi_writers > 0)
+ wakeup((caddr_t)&fip->fi_writers);
+ }
+ }
+ if (ap->a_mode & FWRITE) {
+ if (fip->fi_writers++ == 0) {
+ fip->fi_readsock->so_state &= ~SS_CANTRCVMORE;
+ if (fip->fi_readers > 0)
+ wakeup((caddr_t)&fip->fi_readers);
+ }
+ }
+ if (ap->a_mode & FREAD) {
+ if (ap->a_mode & O_NONBLOCK) {
+ } else {
+ while (fip->fi_writers == 0) {
+ VOP_UNLOCK(vp);
+ error = tsleep((caddr_t)&fip->fi_readers,
+ PCATCH | PSOCK, openstr, 0);
+ VOP_LOCK(vp);
+ if (error)
+ goto bad;
+ }
+ }
+ }
+ if (ap->a_mode & FWRITE) {
+ if (ap->a_mode & O_NONBLOCK) {
+ if (fip->fi_readers == 0) {
+ error = ENXIO;
+ goto bad;
+ }
+ } else {
+ while (fip->fi_readers == 0) {
+ VOP_UNLOCK(vp);
+ error = tsleep((caddr_t)&fip->fi_writers,
+ PCATCH | PSOCK, openstr, 0);
+ VOP_LOCK(vp);
+ if (error)
+ goto bad;
+ }
+ }
+ }
+ return (0);
+bad:
+ VOP_CLOSE(vp, ap->a_mode, ap->a_cred, ap->a_p);
+ return (error);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+fifo_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct uio *uio = ap->a_uio;
+ register struct socket *rso = ap->a_vp->v_fifoinfo->fi_readsock;
+ int error, startresid;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("fifo_read mode");
+#endif
+ if (uio->uio_resid == 0)
+ return (0);
+ if (ap->a_ioflag & IO_NDELAY)
+ rso->so_state |= SS_NBIO;
+ startresid = uio->uio_resid;
+ VOP_UNLOCK(ap->a_vp);
+ error = soreceive(rso, (struct mbuf **)0, uio, (struct mbuf **)0,
+ (struct mbuf **)0, (int *)0);
+ VOP_LOCK(ap->a_vp);
+ /*
+ * Clear EOF indication after first such return.
+ */
+ if (uio->uio_resid == startresid)
+ rso->so_state &= ~SS_CANTRCVMORE;
+ if (ap->a_ioflag & IO_NDELAY)
+ rso->so_state &= ~SS_NBIO;
+ return (error);
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+fifo_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct socket *wso = ap->a_vp->v_fifoinfo->fi_writesock;
+ int error;
+
+#ifdef DIAGNOSTIC
+ if (ap->a_uio->uio_rw != UIO_WRITE)
+ panic("fifo_write mode");
+#endif
+ if (ap->a_ioflag & IO_NDELAY)
+ wso->so_state |= SS_NBIO;
+ VOP_UNLOCK(ap->a_vp);
+ error = sosend(wso, (struct mbuf *)0, ap->a_uio, 0, (struct mbuf *)0, 0);
+ VOP_LOCK(ap->a_vp);
+ if (ap->a_ioflag & IO_NDELAY)
+ wso->so_state &= ~SS_NBIO;
+ return (error);
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+fifo_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ u_long a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct file filetmp;
+ int error;
+
+ if (ap->a_command == FIONBIO)
+ return (0);
+ if (ap->a_fflag & FREAD) {
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
+ error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p);
+ if (error)
+ return (error);
+ }
+ if (ap->a_fflag & FWRITE) {
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
+ error = soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p);
+ if (error)
+ return (error);
+ }
+ return (0);
+}
+
+/* ARGSUSED */
+fifo_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct file filetmp;
+ int ready;
+
+ if (ap->a_fflags & FREAD) {
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
+ ready = soo_select(&filetmp, ap->a_which, ap->a_p);
+ if (ready)
+ return (ready);
+ }
+ if (ap->a_fflags & FWRITE) {
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
+ ready = soo_select(&filetmp, ap->a_which, ap->a_p);
+ if (ready)
+ return (ready);
+ }
+ return (0);
+}
+
+/*
+ * This is a noop, simply returning what one has been given.
+ */
+fifo_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ } */ *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * At the moment we do not do any locking.
+ */
+/* ARGSUSED */
+fifo_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/* ARGSUSED */
+fifo_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Device close routine
+ */
+/* ARGSUSED */
+fifo_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct fifoinfo *fip = vp->v_fifoinfo;
+ int error1, error2;
+
+ if (ap->a_fflag & FREAD) {
+ if (--fip->fi_readers == 0)
+ socantsendmore(fip->fi_writesock);
+ }
+ if (ap->a_fflag & FWRITE) {
+ if (--fip->fi_writers == 0)
+ socantrcvmore(fip->fi_readsock);
+ }
+ if (vp->v_usecount > 1)
+ return (0);
+ error1 = soclose(fip->fi_readsock);
+ error2 = soclose(fip->fi_writesock);
+ FREE(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ if (error1)
+ return (error1);
+ return (error2);
+}
+
+/*
+ * Print out the contents of a fifo vnode.
+ */
+fifo_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON");
+ fifo_printinfo(ap->a_vp);
+ printf("\n");
+}
+
+/*
+ * Print out internal contents of a fifo vnode.
+ */
+fifo_printinfo(vp)
+ struct vnode *vp;
+{
+ register struct fifoinfo *fip = vp->v_fifoinfo;
+
+ printf(", fifo with %d readers and %d writers",
+ fip->fi_readers, fip->fi_writers);
+}
+
+/*
+ * Return POSIX pathconf information applicable to fifo's.
+ */
+fifo_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ register_t *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Fifo failed operation
+ */
+fifo_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Fifo advisory byte-level locks.
+ */
+/* ARGSUSED */
+fifo_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Fifo bad operation
+ */
+fifo_badop()
+{
+
+ panic("fifo_badop called");
+ /* NOTREACHED */
+}
diff --git a/sys/miscfs/kernfs/kernfs.h b/sys/miscfs/kernfs/kernfs.h
new file mode 100644
index 00000000000..88434ff093f
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs.h
@@ -0,0 +1,58 @@
+/* $NetBSD: kernfs.h,v 1.9 1995/03/29 22:08:22 briggs Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)kernfs.h 8.5 (Berkeley) 6/15/94
+ */
+
+#define _PATH_KERNFS "/kern" /* Default mountpoint */
+
+#ifdef _KERNEL
+struct kernfs_mount {
+ struct vnode *kf_root; /* Root node */
+};
+
+struct kernfs_node {
+ struct kern_target *kf_kt;
+};
+
+#define VFSTOKERNFS(mp) ((struct kernfs_mount *)((mp)->mnt_data))
+#define VTOKERN(vp) ((struct kernfs_node *)(vp)->v_data)
+
+extern int (**kernfs_vnodeop_p)();
+extern struct vfsops kernfs_vfsops;
+extern dev_t rrootdev;
+#endif /* _KERNEL */
diff --git a/sys/miscfs/kernfs/kernfs_vfsops.c b/sys/miscfs/kernfs/kernfs_vfsops.c
new file mode 100644
index 00000000000..e224cabed8c
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs_vfsops.c
@@ -0,0 +1,314 @@
+/* $NetBSD: kernfs_vfsops.c,v 1.24 1995/06/18 14:47:27 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)kernfs_vfsops.c 8.5 (Berkeley) 6/15/94
+ */
+
+/*
+ * Kernel params Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/kernfs/kernfs.h>
+
+dev_t rrootdev = NODEV;
+
+kernfs_init()
+{
+
+}
+
+void
+kernfs_get_rrootdev()
+{
+ static int tried = 0;
+ int cmaj;
+
+ if (tried) {
+ /* Already did it once. */
+ return;
+ }
+ tried = 1;
+
+ if (rootdev == NODEV)
+ return;
+ for (cmaj = 0; cmaj < nchrdev; cmaj++) {
+ rrootdev = makedev(cmaj, minor(rootdev));
+ if (chrtoblk(rrootdev) == rootdev)
+ return;
+ }
+ rrootdev = NODEV;
+ printf("kernfs_get_rrootdev: no raw root device\n");
+}
+
+/*
+ * Mount the Kernel params filesystem
+ */
+kernfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ size_t size;
+ struct kernfs_mount *fmp;
+ struct vnode *rvp;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ if (error = getnewvnode(VT_KERNFS, mp, kernfs_vnodeop_p, &rvp))
+ return (error);
+
+ MALLOC(fmp, struct kernfs_mount *, sizeof(struct kernfs_mount),
+ M_MISCFSMNT, M_WAITOK);
+ rvp->v_type = VDIR;
+ rvp->v_flag |= VROOT;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_mount: root vp = %x\n", rvp);
+#endif
+ fmp->kf_root = rvp;
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t)fmp;
+ getnewfsid(mp, makefstype(MOUNT_KERNFS));
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("kernfs", mp->mnt_stat.f_mntfromname, sizeof("kernfs"));
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_mount: at %s\n", mp->mnt_stat.f_mntonname);
+#endif
+
+ kernfs_get_rrootdev();
+ return (0);
+}
+
+kernfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+kernfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int error;
+ int flags = 0;
+ extern int doforce;
+ struct vnode *rootvp = VFSTOKERNFS(mp)->kf_root;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* kernfs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+ if (rootvp->v_usecount > 1)
+ return (EBUSY);
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_unmount: calling vflush\n");
+#endif
+ if (error = vflush(mp, rootvp, flags))
+ return (error);
+
+#ifdef KERNFS_DIAGNOSTIC
+ vprint("kernfs root", rootvp);
+#endif
+ /*
+ * Clean out the old root vnode for reuse.
+ */
+ vrele(rootvp);
+ vgone(rootvp);
+ /*
+ * Finally, throw away the kernfs_mount structure
+ */
+ free(mp->mnt_data, M_MISCFSMNT);
+ mp->mnt_data = 0;
+ return (0);
+}
+
+kernfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_root(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = VFSTOKERNFS(mp)->kf_root;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+kernfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+kernfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_statfs(mp = %x)\n", mp);
+#endif
+
+#ifdef COMPAT_09
+ sbp->f_type = 7;
+#else
+ sbp->f_type = 0;
+#endif
+ sbp->f_bsize = DEV_BSIZE;
+ sbp->f_iosize = DEV_BSIZE;
+ sbp->f_blocks = 2; /* 1K to keep df happy */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = 0;
+ sbp->f_ffree = 0;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ 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);
+}
+
+kernfs_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+/*
+ * Kernfs flat namespace lookup.
+ * Currently unsupported.
+ */
+kernfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+
+kernfs_fhtovp(mp, fhp, setgen, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ int setgen;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+kernfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+struct vfsops kernfs_vfsops = {
+ MOUNT_KERNFS,
+ kernfs_mount,
+ kernfs_start,
+ kernfs_unmount,
+ kernfs_root,
+ kernfs_quotactl,
+ kernfs_statfs,
+ kernfs_sync,
+ kernfs_vget,
+ kernfs_fhtovp,
+ kernfs_vptofh,
+ kernfs_init,
+};
diff --git a/sys/miscfs/kernfs/kernfs_vnops.c b/sys/miscfs/kernfs/kernfs_vnops.c
new file mode 100644
index 00000000000..e7e565bcd55
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs_vnops.c
@@ -0,0 +1,747 @@
+/* $NetBSD: kernfs_vnops.c,v 1.39 1995/10/09 14:25:02 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)kernfs_vnops.c 8.9 (Berkeley) 6/15/94
+ */
+
+/*
+ * Kernel parameter filesystem (/kern)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/vmmeter.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/buf.h>
+#include <sys/dirent.h>
+#include <sys/msgbuf.h>
+#include <miscfs/kernfs/kernfs.h>
+
+#define KSTRING 256 /* Largest I/O available via this filesystem */
+#define UIO_MX 32
+
+#define READ_MODE (S_IRUSR|S_IRGRP|S_IROTH)
+#define WRITE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
+#define DIR_MODE (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+
+struct kern_target {
+ u_char kt_type;
+ u_char kt_namlen;
+ char *kt_name;
+ void *kt_data;
+#define KTT_NULL 1
+#define KTT_TIME 5
+#define KTT_INT 17
+#define KTT_STRING 31
+#define KTT_HOSTNAME 47
+#define KTT_AVENRUN 53
+#define KTT_DEVICE 71
+#define KTT_MSGBUF 89
+ u_char kt_tag;
+ u_char kt_vtype;
+ mode_t kt_mode;
+} kern_targets[] = {
+/* NOTE: The name must be less than UIO_MX-16 chars in length */
+#define N(s) sizeof(s)-1, s
+ /* name data tag type ro/rw */
+ { DT_DIR, N("."), 0, KTT_NULL, VDIR, DIR_MODE },
+ { DT_DIR, N(".."), 0, KTT_NULL, VDIR, DIR_MODE },
+ { DT_REG, N("boottime"), &boottime.tv_sec, KTT_INT, VREG, READ_MODE },
+ { DT_REG, N("copyright"), copyright, KTT_STRING, VREG, READ_MODE },
+ { DT_REG, N("hostname"), 0, KTT_HOSTNAME, VREG, WRITE_MODE },
+ { DT_REG, N("hz"), &hz, KTT_INT, VREG, READ_MODE },
+ { DT_REG, N("loadavg"), 0, KTT_AVENRUN, VREG, READ_MODE },
+ { DT_REG, N("msgbuf"), 0, KTT_MSGBUF, VREG, READ_MODE },
+ { DT_REG, N("pagesize"), &cnt.v_page_size, KTT_INT, VREG, READ_MODE },
+ { DT_REG, N("physmem"), &physmem, KTT_INT, VREG, READ_MODE },
+#if 0
+ { DT_DIR, N("root"), 0, KTT_NULL, VDIR, DIR_MODE },
+#endif
+ { DT_BLK, N("rootdev"), &rootdev, KTT_DEVICE, VBLK, READ_MODE },
+ { DT_CHR, N("rrootdev"), &rrootdev, KTT_DEVICE, VCHR, READ_MODE },
+ { DT_REG, N("time"), 0, KTT_TIME, VREG, READ_MODE },
+ { DT_REG, N("version"), version, KTT_STRING, VREG, READ_MODE },
+#undef N
+};
+static int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]);
+
+int
+kernfs_xread(kt, off, bufp, len)
+ struct kern_target *kt;
+ int off;
+ char **bufp;
+ int len;
+{
+
+ switch (kt->kt_tag) {
+ case KTT_TIME: {
+ struct timeval tv;
+
+ microtime(&tv);
+ sprintf(*bufp, "%d %d\n", tv.tv_sec, tv.tv_usec);
+ break;
+ }
+
+ case KTT_INT: {
+ int *ip = kt->kt_data;
+
+ sprintf(*bufp, "%d\n", *ip);
+ break;
+ }
+
+ case KTT_STRING: {
+ char *cp = kt->kt_data;
+
+ *bufp = cp;
+ break;
+ }
+
+ case KTT_MSGBUF: {
+ extern struct msgbuf *msgbufp;
+ long n;
+
+ if (off >= MSG_BSIZE)
+ return (0);
+ n = msgbufp->msg_bufx + off;
+ if (n >= MSG_BSIZE)
+ n -= MSG_BSIZE;
+ len = min(MSG_BSIZE - n, MSG_BSIZE - off);
+ *bufp = msgbufp->msg_bufc + n;
+ return (len);
+ }
+
+ case KTT_HOSTNAME: {
+ char *cp = hostname;
+ int xlen = hostnamelen;
+
+ if (xlen >= (len-2))
+ return (EINVAL);
+
+ bcopy(cp, *bufp, xlen);
+ (*bufp)[xlen] = '\n';
+ (*bufp)[xlen+1] = '\0';
+ break;
+ }
+
+ case KTT_AVENRUN:
+ averunnable.fscale = FSCALE;
+ sprintf(*bufp, "%ld %ld %ld %ld\n",
+ averunnable.ldavg[0], averunnable.ldavg[1],
+ averunnable.ldavg[2], averunnable.fscale);
+ break;
+
+ default:
+ return (0);
+ }
+
+ len = strlen(*bufp);
+ if (len <= off)
+ return (0);
+ *bufp += off;
+ return (len - off);
+}
+
+int
+kernfs_xwrite(kt, buf, len)
+ struct kern_target *kt;
+ char *buf;
+ int len;
+{
+
+ switch (kt->kt_tag) {
+ case KTT_HOSTNAME:
+ if (buf[len-1] == '\n')
+ --len;
+ bcopy(buf, hostname, len);
+ hostname[len] = '\0';
+ hostnamelen = len;
+ return (0);
+
+ default:
+ return (EIO);
+ }
+}
+
+
+/*
+ * vp is the current namei directory
+ * ndp is the name to locate in that directory...
+ */
+kernfs_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname = cnp->cn_nameptr;
+ struct kern_target *kt;
+ struct vnode *fvp;
+ int error, i;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup(%x)\n", ap);
+ printf("kernfs_lookup(dp = %x, vpp = %x, cnp = %x)\n", dvp, vpp, ap->a_cnp);
+ printf("kernfs_lookup(%s)\n", pname);
+#endif
+
+ *vpp = NULLVP;
+
+ if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
+ return (EROFS);
+
+ if (cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ /*VOP_LOCK(dvp);*/
+ return (0);
+ }
+
+#if 0
+ if (cnp->cn_namelen == 4 && bcmp(pname, "root", 4) == 0) {
+ *vpp = rootdir;
+ VREF(rootdir);
+ VOP_LOCK(rootdir);
+ return (0);
+ }
+#endif
+
+ for (kt = kern_targets, i = 0; i < nkern_targets; kt++, i++) {
+ if (cnp->cn_namelen == kt->kt_namlen &&
+ bcmp(kt->kt_name, pname, cnp->cn_namelen) == 0)
+ goto found;
+ }
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: i = %d, failed", i);
+#endif
+
+ return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS);
+
+found:
+ if (kt->kt_tag == KTT_DEVICE) {
+ dev_t *dp = kt->kt_data;
+ loop:
+ if (*dp == NODEV || !vfinddev(*dp, kt->kt_vtype, &fvp))
+ return (ENOENT);
+ *vpp = fvp;
+ if (vget(fvp, 1))
+ goto loop;
+ return (0);
+ }
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: allocate new vnode\n");
+#endif
+ if (error = getnewvnode(VT_KERNFS, dvp->v_mount, kernfs_vnodeop_p,
+ &fvp))
+ return (error);
+
+ MALLOC(fvp->v_data, void *, sizeof(struct kernfs_node), M_TEMP,
+ M_WAITOK);
+ VTOKERN(fvp)->kf_kt = kt;
+ fvp->v_type = kt->kt_vtype;
+ *vpp = fvp;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: newvp = %x\n", fvp);
+#endif
+ return (0);
+}
+
+kernfs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /* Only need to check access permissions. */
+ return (0);
+}
+
+int
+kernfs_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ mode_t fmode =
+ (vp->v_flag & VROOT) ? DIR_MODE : VTOKERN(vp)->kf_kt->kt_mode;
+
+ return (vaccess(fmode, (uid_t)0, (gid_t)0, ap->a_mode, ap->a_cred));
+}
+
+kernfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ struct timeval tv;
+ int error = 0;
+ char strbuf[KSTRING], *buf;
+
+ bzero((caddr_t) vap, sizeof(*vap));
+ vattr_null(vap);
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_size = 0;
+ vap->va_blocksize = DEV_BSIZE;
+ microtime(&tv);
+ TIMEVAL_TO_TIMESPEC(&tv, &vap->va_atime);
+ vap->va_mtime = vap->va_atime;
+ vap->va_ctime = vap->va_ctime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ vap->va_bytes = 0;
+
+ if (vp->v_flag & VROOT) {
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_getattr: stat rootdir\n");
+#endif
+ vap->va_type = VDIR;
+ vap->va_mode = DIR_MODE;
+ vap->va_nlink = 2;
+ vap->va_fileid = 2;
+ vap->va_size = DEV_BSIZE;
+ } else {
+ struct kern_target *kt = VTOKERN(vp)->kf_kt;
+ int nbytes, total;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_getattr: stat target %s\n", kt->kt_name);
+#endif
+ vap->va_type = kt->kt_vtype;
+ vap->va_mode = kt->kt_mode;
+ vap->va_nlink = 1;
+ vap->va_fileid = 3 + (kt - kern_targets);
+ total = 0;
+ while (buf = strbuf,
+ nbytes = kernfs_xread(kt, total, &buf, sizeof(strbuf)))
+ total += nbytes;
+ vap->va_size = total;
+ }
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_getattr: return error %d\n", error);
+#endif
+ return (error);
+}
+
+kernfs_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Silently ignore attribute changes.
+ * This allows for open with truncate to have no
+ * effect until some data is written. I want to
+ * do it this way because all writes are atomic.
+ */
+ return (0);
+}
+
+int
+kernfs_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct kern_target *kt;
+ char strbuf[KSTRING], *buf;
+ int off, len;
+ int error;
+
+ if (vp->v_type == VDIR)
+ return (EOPNOTSUPP);
+
+ kt = VTOKERN(vp)->kf_kt;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kern_read %s\n", kt->kt_name);
+#endif
+
+ off = uio->uio_offset;
+#if 0
+ while (buf = strbuf,
+#else
+ if (buf = strbuf,
+#endif
+ len = kernfs_xread(kt, off, &buf, sizeof(strbuf))) {
+ if (error = uiomove(buf, len, uio))
+ return (error);
+ off += len;
+ }
+ return (0);
+}
+
+int
+kernfs_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct kern_target *kt;
+ int error, xlen;
+ char strbuf[KSTRING];
+
+ if (vp->v_type == VDIR)
+ return (EOPNOTSUPP);
+
+ kt = VTOKERN(vp)->kf_kt;
+
+ if (uio->uio_offset != 0)
+ return (EINVAL);
+
+ xlen = min(uio->uio_resid, KSTRING-1);
+ if (error = uiomove(strbuf, xlen, uio))
+ return (error);
+
+ if (uio->uio_resid != 0)
+ return (EIO);
+
+ strbuf[xlen] = '\0';
+ xlen = strlen(strbuf);
+ return (kernfs_xwrite(kt, strbuf, xlen));
+}
+
+kernfs_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ u_long *a_cookies;
+ int a_ncookies;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct dirent d;
+ struct kern_target *kt;
+ int i;
+ int error;
+ u_long *cookies = ap->a_cookies;
+ int ncookies = ap->a_ncookies;
+
+ if (ap->a_vp->v_type != VDIR)
+ return (ENOTDIR);
+
+ if (uio->uio_resid < UIO_MX)
+ return (EINVAL);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ error = 0;
+ i = uio->uio_offset;
+ bzero((caddr_t)&d, UIO_MX);
+ d.d_reclen = UIO_MX;
+
+ for (kt = &kern_targets[i];
+ uio->uio_resid >= UIO_MX && i < nkern_targets; kt++, i++) {
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_readdir: i = %d\n", i);
+#endif
+
+ if (kt->kt_tag == KTT_DEVICE) {
+ dev_t *dp = kt->kt_data;
+ struct vnode *fvp;
+
+ if (*dp == NODEV || !vfinddev(*dp, kt->kt_vtype, &fvp))
+ continue;
+ }
+
+ d.d_fileno = i + 3;
+ d.d_namlen = kt->kt_namlen;
+ bcopy(kt->kt_name, d.d_name, kt->kt_namlen + 1);
+ d.d_type = kt->kt_type;
+
+ if (error = uiomove((caddr_t)&d, UIO_MX, uio))
+ break;
+ if (ncookies-- > 0)
+ *cookies++ = i + 1;
+ }
+
+ uio->uio_offset = i;
+ return (error);
+}
+
+kernfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_inactive(%x)\n", vp);
+#endif
+ /*
+ * Clear out the v_type field to avoid
+ * nasty things happening in vgone().
+ */
+ vp->v_type = VNON;
+ return (0);
+}
+
+kernfs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_reclaim(%x)\n", vp);
+#endif
+ if (vp->v_data) {
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+ }
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+kernfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ register_t *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Print out the contents of a /dev/fd vnode.
+ */
+/* ARGSUSED */
+kernfs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_KERNFS, kernfs vnode\n");
+ return (0);
+}
+
+/*void*/
+kernfs_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * /dev/fd vnode unsupported operation
+ */
+kernfs_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * /dev/fd "should never get here" operation
+ */
+kernfs_badop()
+{
+
+ panic("kernfs: bad op");
+ /* NOTREACHED */
+}
+
+/*
+ * kernfs vnode null operation
+ */
+kernfs_nullop()
+{
+
+ return (0);
+}
+
+#define kernfs_create ((int (*) __P((struct vop_create_args *)))kernfs_enotsupp)
+#define kernfs_mknod ((int (*) __P((struct vop_mknod_args *)))kernfs_enotsupp)
+#define kernfs_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define kernfs_ioctl ((int (*) __P((struct vop_ioctl_args *)))kernfs_enotsupp)
+#define kernfs_select ((int (*) __P((struct vop_select_args *)))kernfs_enotsupp)
+#define kernfs_mmap ((int (*) __P((struct vop_mmap_args *)))kernfs_enotsupp)
+#define kernfs_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define kernfs_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define kernfs_remove ((int (*) __P((struct vop_remove_args *)))kernfs_enotsupp)
+#define kernfs_link ((int (*) __P((struct vop_link_args *)))kernfs_enotsupp)
+#define kernfs_rename ((int (*) __P((struct vop_rename_args *)))kernfs_enotsupp)
+#define kernfs_mkdir ((int (*) __P((struct vop_mkdir_args *)))kernfs_enotsupp)
+#define kernfs_rmdir ((int (*) __P((struct vop_rmdir_args *)))kernfs_enotsupp)
+#define kernfs_symlink ((int (*) __P((struct vop_symlink_args *)))kernfs_enotsupp)
+#define kernfs_readlink \
+ ((int (*) __P((struct vop_readlink_args *)))kernfs_enotsupp)
+#define kernfs_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
+#define kernfs_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define kernfs_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define kernfs_bmap ((int (*) __P((struct vop_bmap_args *)))kernfs_badop)
+#define kernfs_strategy ((int (*) __P((struct vop_strategy_args *)))kernfs_badop)
+#define kernfs_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define kernfs_advlock ((int (*) __P((struct vop_advlock_args *)))kernfs_enotsupp)
+#define kernfs_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))kernfs_enotsupp)
+#define kernfs_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) kernfs_enotsupp)
+#define kernfs_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))kernfs_enotsupp)
+#define kernfs_update ((int (*) __P((struct vop_update_args *)))kernfs_enotsupp)
+#define kernfs_bwrite ((int (*) __P((struct vop_bwrite_args *)))kernfs_enotsupp)
+
+int (**kernfs_vnodeop_p)();
+struct vnodeopv_entry_desc kernfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, kernfs_lookup }, /* lookup */
+ { &vop_create_desc, kernfs_create }, /* create */
+ { &vop_mknod_desc, kernfs_mknod }, /* mknod */
+ { &vop_open_desc, kernfs_open }, /* open */
+ { &vop_close_desc, kernfs_close }, /* close */
+ { &vop_access_desc, kernfs_access }, /* access */
+ { &vop_getattr_desc, kernfs_getattr }, /* getattr */
+ { &vop_setattr_desc, kernfs_setattr }, /* setattr */
+ { &vop_read_desc, kernfs_read }, /* read */
+ { &vop_write_desc, kernfs_write }, /* write */
+ { &vop_ioctl_desc, kernfs_ioctl }, /* ioctl */
+ { &vop_select_desc, kernfs_select }, /* select */
+ { &vop_mmap_desc, kernfs_mmap }, /* mmap */
+ { &vop_fsync_desc, kernfs_fsync }, /* fsync */
+ { &vop_seek_desc, kernfs_seek }, /* seek */
+ { &vop_remove_desc, kernfs_remove }, /* remove */
+ { &vop_link_desc, kernfs_link }, /* link */
+ { &vop_rename_desc, kernfs_rename }, /* rename */
+ { &vop_mkdir_desc, kernfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, kernfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, kernfs_symlink }, /* symlink */
+ { &vop_readdir_desc, kernfs_readdir }, /* readdir */
+ { &vop_readlink_desc, kernfs_readlink },/* readlink */
+ { &vop_abortop_desc, kernfs_abortop }, /* abortop */
+ { &vop_inactive_desc, kernfs_inactive },/* inactive */
+ { &vop_reclaim_desc, kernfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, kernfs_lock }, /* lock */
+ { &vop_unlock_desc, kernfs_unlock }, /* unlock */
+ { &vop_bmap_desc, kernfs_bmap }, /* bmap */
+ { &vop_strategy_desc, kernfs_strategy },/* strategy */
+ { &vop_print_desc, kernfs_print }, /* print */
+ { &vop_islocked_desc, kernfs_islocked },/* islocked */
+ { &vop_pathconf_desc, kernfs_pathconf },/* pathconf */
+ { &vop_advlock_desc, kernfs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, kernfs_blkatoff },/* blkatoff */
+ { &vop_valloc_desc, kernfs_valloc }, /* valloc */
+ { &vop_vfree_desc, kernfs_vfree }, /* vfree */
+ { &vop_truncate_desc, kernfs_truncate },/* truncate */
+ { &vop_update_desc, kernfs_update }, /* update */
+ { &vop_bwrite_desc, kernfs_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc kernfs_vnodeop_opv_desc =
+ { &kernfs_vnodeop_p, kernfs_vnodeop_entries };
diff --git a/sys/miscfs/nullfs/null.h b/sys/miscfs/nullfs/null.h
new file mode 100644
index 00000000000..27ed4972325
--- /dev/null
+++ b/sys/miscfs/nullfs/null.h
@@ -0,0 +1,75 @@
+/* $NetBSD: null.h,v 1.4 1995/03/29 22:08:24 briggs Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * from: Id: lofs.h,v 1.8 1992/05/30 10:05:43 jsp Exp
+ * @(#)null.h 8.2 (Berkeley) 1/21/94
+ */
+
+struct null_args {
+ char *target; /* Target of loopback */
+};
+
+struct null_mount {
+ struct mount *nullm_vfs;
+ struct vnode *nullm_rootvp; /* Reference to root null_node */
+};
+
+#ifdef _KERNEL
+/*
+ * A cache of vnode references
+ */
+struct null_node {
+ LIST_ENTRY(null_node) null_hash; /* Hash list */
+ struct vnode *null_lowervp; /* VREFed once */
+ struct vnode *null_vnode; /* Back pointer */
+};
+
+extern int null_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp));
+
+#define MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data))
+#define VTONULL(vp) ((struct null_node *)(vp)->v_data)
+#define NULLTOV(xp) ((xp)->null_vnode)
+#ifdef NULLFS_DIAGNOSTIC
+extern struct vnode *null_checkvp __P((struct vnode *vp, char *fil, int lno));
+#define NULLVPTOLOWERVP(vp) null_checkvp((vp), __FILE__, __LINE__)
+#else
+#define NULLVPTOLOWERVP(vp) (VTONULL(vp)->null_lowervp)
+#endif
+
+extern int (**null_vnodeop_p)();
+extern struct vfsops null_vfsops;
+#endif /* _KERNEL */
diff --git a/sys/miscfs/nullfs/null_subr.c b/sys/miscfs/nullfs/null_subr.c
new file mode 100644
index 00000000000..05c1987ae72
--- /dev/null
+++ b/sys/miscfs/nullfs/null_subr.c
@@ -0,0 +1,334 @@
+/* $NetBSD: null_subr.c,v 1.4 1994/09/20 06:43:00 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * from: Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp
+ * @(#)null_subr.c 8.4 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/nullfs/null.h>
+
+#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
+#define NNULLNODECACHE 16
+
+/*
+ * Null layer cache:
+ * Each cache entry holds a reference to the lower vnode
+ * along with a pointer to the alias vnode. When an
+ * entry is added the lower vnode is VREF'd. When the
+ * alias is removed the lower vnode is vrele'd.
+ */
+
+#define NULL_NHASH(vp) \
+ (&null_node_hashtbl[(((u_long)vp)>>LOG2_SIZEVNODE) & null_node_hash])
+LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
+u_long null_node_hash;
+
+/*
+ * Initialise cache headers
+ */
+nullfs_init()
+{
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_init\n"); /* printed during system boot */
+#endif
+ null_node_hashtbl = hashinit(NNULLNODECACHE, M_CACHE, &null_node_hash);
+}
+
+/*
+ * Return a VREF'ed alias for lower vnode if already exists, else 0.
+ */
+static struct vnode *
+null_node_find(mp, lowervp)
+ struct mount *mp;
+ struct vnode *lowervp;
+{
+ struct null_node_hashhead *hd;
+ struct null_node *a;
+ struct vnode *vp;
+
+ /*
+ * Find hash base, and then search the (two-way) linked
+ * list looking for a null_node structure which is referencing
+ * the lower vnode. If found, the increment the null_node
+ * reference count (but NOT the lower vnode's VREF counter).
+ */
+ hd = NULL_NHASH(lowervp);
+loop:
+ for (a = hd->lh_first; a != 0; a = a->null_hash.le_next) {
+ if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
+ vp = NULLTOV(a);
+ /*
+ * We need vget for the VXLOCK
+ * stuff, but we don't want to lock
+ * the lower node.
+ */
+ if (vget(vp, 0)) {
+ printf ("null_node_find: vget failed.\n");
+ goto loop;
+ };
+ return (vp);
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ * Make a new null_node node.
+ * Vp is the alias vnode, lowervp is the lower vnode.
+ * Maintain a reference to lowervp.
+ */
+static int
+null_node_alloc(mp, lowervp, vpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **vpp;
+{
+ struct null_node_hashhead *hd;
+ struct null_node *xp;
+ struct vnode *vp, *nvp;
+ int error;
+ extern int (**dead_vnodeop_p)();
+
+ if (error = getnewvnode(VT_NULL, mp, null_vnodeop_p, &vp))
+ return (error);
+ vp->v_type = lowervp->v_type;
+
+ MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP,
+ M_WAITOK);
+ if (vp->v_type == VBLK || vp->v_type == VCHR) {
+ MALLOC(vp->v_specinfo, struct specinfo *,
+ sizeof(struct specinfo), M_VNODE, M_WAITOK);
+ vp->v_rdev = lowervp->v_rdev;
+ }
+
+ vp->v_data = xp;
+ xp->null_vnode = vp;
+ xp->null_lowervp = lowervp;
+ /*
+ * Before we insert our new node onto the hash chains,
+ * check to see if someone else has beaten us to it.
+ * (We could have slept in MALLOC.)
+ */
+ if (nvp = null_node_find(lowervp)) {
+ *vpp = nvp;
+
+ /* free the substructures we've allocated. */
+ FREE(xp, M_TEMP);
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ FREE(vp->v_specinfo, M_VNODE);
+
+ vp->v_type = VBAD; /* node is discarded */
+ vp->v_op = dead_vnodeop_p; /* so ops will still work */
+ vrele(vp); /* get rid of it. */
+ return (0);
+ }
+
+ /*
+ * XXX if it's a device node, it needs to be checkalias()ed.
+ * however, for locking reasons, that's just not possible.
+ * so we have to do most of the dirty work inline. Note that
+ * this is a limited case; we know that there's going to be
+ * an alias, and we know that that alias will be a "real"
+ * device node, i.e. not tagged VT_NON.
+ */
+ if (vp->v_type == VBLK || vp->v_type == VCHR) {
+ struct vnode *cvp, **cvpp;
+
+ cvpp = &speclisth[SPECHASH(vp->v_rdev)];
+loop:
+ for (cvp = *cvpp; cvp; cvp = cvp->v_specnext) {
+ if (vp->v_rdev != cvp->v_rdev ||
+ vp->v_type != cvp->v_type)
+ continue;
+
+ /*
+ * Alias, but not in use, so flush it out.
+ */
+ if (cvp->v_usecount == 0) {
+ vgone(cvp);
+ goto loop;
+ }
+ if (vget(cvp, 0)) /* can't lock; will die! */
+ goto loop;
+ break;
+ }
+
+ vp->v_hashchain = cvpp;
+ vp->v_specnext = *cvpp;
+ vp->v_specflags = 0;
+ *cvpp = vp;
+#ifdef DIAGNOSTIC
+ if (cvp == NULLVP)
+ panic("null_node_alloc: no alias for device");
+#endif
+ vp->v_flag |= VALIASED;
+ cvp->v_flag |= VALIASED;
+ vrele(cvp);
+ }
+ /* XXX end of transmogrified checkalias() */
+
+ *vpp = vp;
+ VREF(lowervp); /* Extra VREF will be vrele'd in null_node_create */
+ hd = NULL_NHASH(lowervp);
+ LIST_INSERT_HEAD(hd, xp, null_hash);
+ return (0);
+}
+
+
+/*
+ * Try to find an existing null_node vnode refering
+ * to it, otherwise make a new null_node vnode which
+ * contains a reference to the lower vnode.
+ */
+int
+null_node_create(mp, lowervp, newvpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **newvpp;
+{
+ struct vnode *aliasvp;
+
+ if (aliasvp = null_node_find(mp, lowervp)) {
+ /*
+ * null_node_find has taken another reference
+ * to the alias vnode.
+ */
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("null_node_create: exists", NULLTOV(ap));
+#endif
+ /* VREF(aliasvp); --- done in null_node_find */
+ } else {
+ int error;
+
+ /*
+ * Get new vnode.
+ */
+#ifdef NULLFS_DIAGNOSTIC
+ printf("null_node_create: create new alias vnode\n");
+#endif
+
+ /*
+ * Make new vnode reference the null_node.
+ */
+ if (error = null_node_alloc(mp, lowervp, &aliasvp))
+ return error;
+
+ /*
+ * aliasvp is already VREF'd by getnewvnode()
+ */
+ }
+
+ vrele(lowervp);
+
+#ifdef DIAGNOSTIC
+ if (lowervp->v_usecount < 1) {
+ /* Should never happen... */
+ vprint("null_node_create: alias", aliasvp);
+ vprint("null_node_create: lower", lowervp);
+ panic("null_node_create: lower has 0 usecount.");
+ };
+#endif
+
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("null_node_create: alias", aliasvp);
+ vprint("null_node_create: lower", lowervp);
+#endif
+
+ *newvpp = aliasvp;
+ return (0);
+}
+#ifdef NULLFS_DIAGNOSTIC
+struct vnode *
+null_checkvp(vp, fil, lno)
+ struct vnode *vp;
+ char *fil;
+ int lno;
+{
+ struct null_node *a = VTONULL(vp);
+#ifdef notyet
+ /*
+ * Can't do this check because vop_reclaim runs
+ * with a funny vop vector.
+ */
+ if (vp->v_op != null_vnodeop_p) {
+ printf ("null_checkvp: on non-null-node\n");
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic("null_checkvp");
+ };
+#endif
+ if (a->null_lowervp == NULL) {
+ /* Should never happen */
+ int i; u_long *p;
+ printf("vp = %x, ZERO ptr\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic("null_checkvp");
+ }
+ if (a->null_lowervp->v_usecount < 1) {
+ int i; u_long *p;
+ printf("vp = %x, unref'ed lowervp\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (null_checkvp_barrier) /*WAIT*/ ;
+ panic ("null with unref'ed lowervp");
+ };
+#ifdef notyet
+ printf("null %x/%d -> %x/%d [%s, %d]\n",
+ NULLTOV(a), NULLTOV(a)->v_usecount,
+ a->null_lowervp, a->null_lowervp->v_usecount,
+ fil, lno);
+#endif
+ return a->null_lowervp;
+}
+#endif
diff --git a/sys/miscfs/nullfs/null_vfsops.c b/sys/miscfs/nullfs/null_vfsops.c
new file mode 100644
index 00000000000..ac9f63896a1
--- /dev/null
+++ b/sys/miscfs/nullfs/null_vfsops.c
@@ -0,0 +1,373 @@
+/* $NetBSD: null_vfsops.c,v 1.9 1995/06/18 14:47:32 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * from: Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp
+ * from: @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92
+ * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94
+ */
+
+/*
+ * Null Layer
+ * (See null_vnops.c for a description of what this does.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/nullfs/null.h>
+
+/*
+ * Mount null layer
+ */
+int
+nullfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ struct null_args args;
+ struct vnode *lowerrootvp, *vp;
+ struct vnode *nullm_rootvp;
+ struct null_mount *xmp;
+ size_t size;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ return (EOPNOTSUPP);
+ /* return VFS_MOUNT(MOUNTTONULLMOUNT(mp)->nullm_vfs, path, data, ndp, p);*/
+ }
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct null_args)))
+ return (error);
+
+ /*
+ * Find lower node
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
+ UIO_USERSPACE, args.target, p);
+ if (error = namei(ndp))
+ return (error);
+
+ /*
+ * Sanity check on lower vnode
+ */
+ lowerrootvp = ndp->ni_vp;
+
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = NULL;
+
+ xmp = (struct null_mount *) malloc(sizeof(struct null_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Save reference to underlying FS
+ */
+ xmp->nullm_vfs = lowerrootvp->v_mount;
+
+ /*
+ * Save reference. Each mount also holds
+ * a reference on the root vnode.
+ */
+ error = null_node_create(mp, lowerrootvp, &vp);
+ /*
+ * Unlock the node (either the lower or the alias)
+ */
+ VOP_UNLOCK(vp);
+ /*
+ * Make sure the node alias worked
+ */
+ if (error) {
+ vrele(lowerrootvp);
+ free(xmp, M_UFSMNT); /* XXX */
+ return (error);
+ }
+
+ /*
+ * Keep a held reference to the root vnode.
+ * It is vrele'd in nullfs_unmount.
+ */
+ nullm_rootvp = vp;
+ nullm_rootvp->v_flag |= VROOT;
+ xmp->nullm_rootvp = nullm_rootvp;
+ if (NULLVPTOLOWERVP(nullm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) xmp;
+ getnewfsid(mp, makefstype(MOUNT_LOFS));
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_mount: lower %s, alias at %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem will have been called
+ * when that filesystem was mounted.
+ */
+int
+nullfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+ /* return VFS_START(MOUNTTONULLMOUNT(mp)->nullm_vfs, flags, p); */
+}
+
+/*
+ * Free reference to null layer
+ */
+int
+nullfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct vnode *nullm_rootvp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
+ int error;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* lofs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#if 0
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (nullm_rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, nullm_rootvp, flags))
+ return (error);
+
+#ifdef NULLFS_DIAGNOSTIC
+ vprint("alias root of lower", nullm_rootvp);
+#endif
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(nullm_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(nullm_rootvp);
+ /*
+ * Finally, throw away the null_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return 0;
+}
+
+int
+nullfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_root(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTONULLMOUNT(mp)->nullm_rootvp,
+ NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)
+ );
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = MOUNTTONULLMOUNT(mp)->nullm_rootvp;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return 0;
+}
+
+int
+nullfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return VFS_QUOTACTL(MOUNTTONULLMOUNT(mp)->nullm_vfs, cmd, uid, arg, p);
+}
+
+int
+nullfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct statfs mstat;
+
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_statfs(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTONULLMOUNT(mp)->nullm_rootvp,
+ NULLVPTOLOWERVP(MOUNTTONULLMOUNT(mp)->nullm_rootvp)
+ );
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ error = VFS_STATFS(MOUNTTONULLMOUNT(mp)->nullm_vfs, &mstat, p);
+ if (error)
+ return (error);
+
+ /* now copy across the "interesting" information and fake the rest */
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ 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);
+}
+
+int
+nullfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ /*
+ * XXX - Assumes no data cached at null layer.
+ */
+ return (0);
+}
+
+int
+nullfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return VFS_VGET(MOUNTTONULLMOUNT(mp)->nullm_vfs, ino, vpp);
+}
+
+int
+nullfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred**credanonp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+nullfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int nullfs_init __P((void));
+
+struct vfsops null_vfsops = {
+ MOUNT_NULL,
+ nullfs_mount,
+ nullfs_start,
+ nullfs_unmount,
+ nullfs_root,
+ nullfs_quotactl,
+ nullfs_statfs,
+ nullfs_sync,
+ nullfs_vget,
+ nullfs_fhtovp,
+ nullfs_vptofh,
+ nullfs_init,
+};
diff --git a/sys/miscfs/nullfs/null_vnops.c b/sys/miscfs/nullfs/null_vnops.c
new file mode 100644
index 00000000000..937ac5a4ae2
--- /dev/null
+++ b/sys/miscfs/nullfs/null_vnops.c
@@ -0,0 +1,465 @@
+/* $NetBSD: null_vnops.c,v 1.4 1994/08/19 11:25:37 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * John Heidemann of the UCLA Ficus project.
+ *
+ * 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.
+ *
+ * @(#)null_vnops.c 8.1 (Berkeley) 6/10/93
+ *
+ * Ancestors:
+ * @(#)lofs_vnops.c 1.2 (Berkeley) 6/18/92
+ * Id: lofs_vnops.c,v 1.11 1992/05/30 10:05:43 jsp Exp
+ * ...and...
+ * @(#)null_vnodeops.c 1.20 92/07/07 UCLA Ficus project
+ */
+
+/*
+ * Null Layer
+ *
+ * (See mount_null(8) for more information.)
+ *
+ * The null layer duplicates a portion of the file system
+ * name space under a new name. In this respect, it is
+ * similar to the loopback file system. It differs from
+ * the loopback fs in two respects: it is implemented using
+ * a stackable layers techniques, and it's "null-node"s stack above
+ * all lower-layer vnodes, not just over directory vnodes.
+ *
+ * The null layer has two purposes. First, it serves as a demonstration
+ * of layering by proving a layer which does nothing. (It actually
+ * does everything the loopback file system does, which is slightly
+ * more than nothing.) Second, the null layer can serve as a prototype
+ * layer. Since it provides all necessary layer framework,
+ * new file system layers can be created very easily be starting
+ * with a null layer.
+ *
+ * The remainder of this man page examines the null layer as a basis
+ * for constructing new layers.
+ *
+ *
+ * INSTANTIATING NEW NULL LAYERS
+ *
+ * New null layers are created with mount_null(8).
+ * Mount_null(8) takes two arguments, the pathname
+ * of the lower vfs (target-pn) and the pathname where the null
+ * layer will appear in the namespace (alias-pn). After
+ * the null layer is put into place, the contents
+ * of target-pn subtree will be aliased under alias-pn.
+ *
+ *
+ * OPERATION OF A NULL LAYER
+ *
+ * The null layer is the minimum file system layer,
+ * simply bypassing all possible operations to the lower layer
+ * for processing there. The majority of its activity centers
+ * on the bypass routine, though which nearly all vnode operations
+ * pass.
+ *
+ * The bypass routine accepts arbitrary vnode operations for
+ * handling by the lower layer. It begins by examing vnode
+ * operation arguments and replacing any null-nodes by their
+ * lower-layer equivlants. It then invokes the operation
+ * on the lower layer. Finally, it replaces the null-nodes
+ * in the arguments and, if a vnode is return by the operation,
+ * stacks a null-node on top of the returned vnode.
+ *
+ * Although bypass handles most operations,
+ * vop_getattr, _inactive, _reclaim, and _print are not bypassed.
+ * Vop_getattr must change the fsid being returned.
+ * Vop_inactive and vop_reclaim are not bypassed so that
+ * they can handle freeing null-layer specific data.
+ * Vop_print is not bypassed to avoid excessive debugging
+ * information.
+ *
+ *
+ * INSTANTIATING VNODE STACKS
+ *
+ * Mounting associates the null layer with a lower layer,
+ * effect stacking two VFSes. Vnode stacks are instead
+ * created on demand as files are accessed.
+ *
+ * The initial mount creates a single vnode stack for the
+ * root of the new null layer. All other vnode stacks
+ * are created as a result of vnode operations on
+ * this or other null vnode stacks.
+ *
+ * New vnode stacks come into existance as a result of
+ * an operation which returns a vnode.
+ * The bypass routine stacks a null-node above the new
+ * vnode before returning it to the caller.
+ *
+ * For example, imagine mounting a null layer with
+ * "mount_null /usr/include /dev/layer/null".
+ * Changing directory to /dev/layer/null will assign
+ * the root null-node (which was created when the null layer was mounted).
+ * Now consider opening "sys". A vop_lookup would be
+ * done on the root null-node. This operation would bypass through
+ * to the lower layer which would return a vnode representing
+ * the UFS "sys". Null_bypass then builds a null-node
+ * aliasing the UFS "sys" and returns this to the caller.
+ * Later operations on the null-node "sys" will repeat this
+ * process when constructing other vnode stacks.
+ *
+ *
+ * CREATING OTHER FILE SYSTEM LAYERS
+ *
+ * One of the easiest ways to construct new file system layers is to make
+ * a copy of the null layer, rename all files and variables, and
+ * then begin modifing the copy. Sed can be used to easily rename
+ * all variables.
+ *
+ * The umap layer is an example of a layer descended from the
+ * null layer.
+ *
+ *
+ * INVOKING OPERATIONS ON LOWER LAYERS
+ *
+ * There are two techniques to invoke operations on a lower layer
+ * when the operation cannot be completely bypassed. Each method
+ * is appropriate in different situations. In both cases,
+ * it is the responsibility of the aliasing layer to make
+ * the operation arguments "correct" for the lower layer
+ * by mapping an vnode arguments to the lower layer.
+ *
+ * The first approach is to call the aliasing layer's bypass routine.
+ * This method is most suitable when you wish to invoke the operation
+ * currently being hanldled on the lower layer. It has the advantage
+ * that the bypass routine already must do argument mapping.
+ * An example of this is null_getattrs in the null layer.
+ *
+ * A second approach is to directly invoked vnode operations on
+ * the lower layer with the VOP_OPERATIONNAME interface.
+ * The advantage of this method is that it is easy to invoke
+ * arbitrary operations on the lower layer. The disadvantage
+ * is that vnodes arguments must be manualy mapped.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <miscfs/nullfs/null.h>
+
+
+int null_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
+
+/*
+ * This is the 10-Apr-92 bypass routine.
+ * This version has been optimized for speed, throwing away some
+ * safety checks. It should still always work, but it's not as
+ * robust to programmer errors.
+ * Define SAFETY to include some error checking code.
+ *
+ * In general, we map all vnodes going down and unmap them on the way back.
+ * As an exception to this, vnodes can be marked "unmapped" by setting
+ * the Nth bit in operation's vdesc_flags.
+ *
+ * Also, some BSD vnode operations have the side effect of vrele'ing
+ * their arguments. With stacking, the reference counts are held
+ * by the upper node, not the lower one, so we must handle these
+ * side-effects here. This is not of concern in Sun-derived systems
+ * since there are no such side-effects.
+ *
+ * This makes the following assumptions:
+ * - only one returned vpp
+ * - no INOUT vpp's (Sun's vop_open has one of these)
+ * - the vnode operation vector of the first vnode should be used
+ * to determine what implementation of the op should be invoked
+ * - all mapped vnodes are of our vnode-type (NEEDSWORK:
+ * problems on rmdir'ing mount points and renaming?)
+ */
+int
+null_bypass(ap)
+ struct vop_generic_args /* {
+ struct vnodeop_desc *a_desc;
+ <other random data follows, presumably>
+ } */ *ap;
+{
+ extern int (**null_vnodeop_p)(); /* not extern, really "forward" */
+ register struct vnode **this_vp_p;
+ int error;
+ struct vnode *old_vps[VDESC_MAX_VPS];
+ struct vnode **vps_p[VDESC_MAX_VPS];
+ struct vnode ***vppp;
+ struct vnodeop_desc *descp = ap->a_desc;
+ int reles, i;
+
+ if (null_bug_bypass)
+ printf ("null_bypass: %s\n", descp->vdesc_name);
+
+#ifdef SAFETY
+ /*
+ * We require at least one vp.
+ */
+ if (descp->vdesc_vp_offsets == NULL ||
+ descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
+ panic ("null_bypass: no vp's in map.\n");
+#endif
+
+ /*
+ * Map the vnodes going in.
+ * Later, we'll invoke the operation based on
+ * the first mapped vnode's operation vector.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ vps_p[i] = this_vp_p =
+ VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap);
+ /*
+ * We're not guaranteed that any but the first vnode
+ * are of our type. Check for and don't map any
+ * that aren't. (We must always map first vp or vclean fails.)
+ */
+ if (i && (*this_vp_p == NULLVP ||
+ (*this_vp_p)->v_op != null_vnodeop_p)) {
+ old_vps[i] = NULLVP;
+ } else {
+ old_vps[i] = *this_vp_p;
+ *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p);
+ /*
+ * XXX - Several operations have the side effect
+ * of vrele'ing their vp's. We must account for
+ * that. (This should go away in the future.)
+ */
+ if (reles & 1)
+ VREF(*this_vp_p);
+ }
+
+ }
+
+ /*
+ * Call the operation on the lower layer
+ * with the modified argument structure.
+ */
+ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
+
+ /*
+ * Maintain the illusion of call-by-value
+ * by restoring vnodes in the argument structure
+ * to their original value.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ if (old_vps[i] != NULLVP) {
+ *(vps_p[i]) = old_vps[i];
+ if (reles & 1)
+ vrele(*(vps_p[i]));
+ }
+ }
+
+ /*
+ * Map the possible out-going vpp
+ * (Assumes that the lower layer always returns
+ * a VREF'ed vpp unless it gets an error.)
+ */
+ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
+ !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
+ !error) {
+ /*
+ * XXX - even though some ops have vpp returned vp's,
+ * several ops actually vrele this before returning.
+ * We must avoid these ops.
+ * (This should go away when these ops are regularized.)
+ */
+ if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
+ goto out;
+ vppp = VOPARG_OFFSETTO(struct vnode***,
+ descp->vdesc_vpp_offset,ap);
+ error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp);
+ }
+
+ out:
+ return (error);
+}
+
+
+/*
+ * We handle getattr only to change the fsid.
+ */
+int
+null_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error;
+ if (error = null_bypass(ap))
+ return (error);
+ /* Requires that arguments be restored. */
+ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+ return (0);
+}
+
+
+int
+null_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our null_node is in the
+ * cache and reusable.
+ *
+ * NEEDSWORK: Someday, consider inactive'ing
+ * the lowervp and then trying to reactivate it
+ * with capabilities (v_id)
+ * like they do in the name lookup cache code.
+ * That's too much work for now.
+ */
+ return (0);
+}
+
+int
+null_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct null_node *xp = VTONULL(vp);
+ struct vnode *lowervp = xp->null_lowervp;
+
+ /*
+ * Note: in vop_reclaim, vp->v_op == dead_vnodeop_p,
+ * so we can't call VOPs on ourself.
+ */
+ /* After this assignment, this node will not be re-used. */
+ xp->null_lowervp = NULL;
+ LIST_REMOVE(xp, null_hash);
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = NULL;
+ vrele (lowervp);
+ return (0);
+}
+
+
+int
+null_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ printf ("\ttag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLVPTOLOWERVP(vp));
+ return (0);
+}
+
+
+/*
+ * XXX - vop_strategy must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+null_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = NULLVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_STRATEGY(bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+
+/*
+ * XXX - like vop_strategy, vop_bwrite must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+null_bwrite(ap)
+ struct vop_bwrite_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = NULLVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_BWRITE(bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+/*
+ * Global vfs data structures
+ */
+int (**null_vnodeop_p)();
+struct vnodeopv_entry_desc null_vnodeop_entries[] = {
+ { &vop_default_desc, null_bypass },
+
+ { &vop_getattr_desc, null_getattr },
+ { &vop_inactive_desc, null_inactive },
+ { &vop_reclaim_desc, null_reclaim },
+ { &vop_print_desc, null_print },
+
+ { &vop_strategy_desc, null_strategy },
+ { &vop_bwrite_desc, null_bwrite },
+
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc null_vnodeop_opv_desc =
+ { &null_vnodeop_p, null_vnodeop_entries };
diff --git a/sys/miscfs/portal/portal.h b/sys/miscfs/portal/portal.h
new file mode 100644
index 00000000000..f61cf5a1563
--- /dev/null
+++ b/sys/miscfs/portal/portal.h
@@ -0,0 +1,74 @@
+/* $NetBSD: portal.h,v 1.6 1995/06/01 22:44:21 jtc Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * from: Id: portal.h,v 1.3 1992/05/30 10:05:24 jsp Exp
+ * @(#)portal.h 8.4 (Berkeley) 1/21/94
+ */
+
+struct portal_args {
+ char *pa_config; /* Config file */
+ int pa_socket; /* Socket to server */
+};
+
+struct portal_cred {
+ int pcr_flag; /* File open mode */
+ uid_t pcr_uid; /* From ucred */
+ gid_t pcr_gid; /* From ucred */
+ short pcr_ngroups; /* From ucred */
+ gid_t pcr_groups[NGROUPS]; /* From ucred */
+};
+
+#ifdef _KERNEL
+struct portalmount {
+ struct vnode *pm_root; /* Root node */
+ struct file *pm_server; /* Held reference to server socket */
+};
+
+struct portalnode {
+ int pt_size; /* Length of Arg */
+ char *pt_arg; /* Arg to send to server */
+ int pt_fileid; /* cookie */
+};
+
+#define VFSTOPORTAL(mp) ((struct portalmount *)((mp)->mnt_data))
+#define VTOPORTAL(vp) ((struct portalnode *)(vp)->v_data)
+
+#define PORTAL_ROOTFILEID 2
+
+extern int (**portal_vnodeop_p)();
+extern struct vfsops portal_vfsops;
+#endif /* _KERNEL */
diff --git a/sys/miscfs/portal/portal_vfsops.c b/sys/miscfs/portal/portal_vfsops.c
new file mode 100644
index 00000000000..e068a24e559
--- /dev/null
+++ b/sys/miscfs/portal/portal_vfsops.c
@@ -0,0 +1,311 @@
+/* $NetBSD: portal_vfsops.c,v 1.13 1995/06/18 14:47:35 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * from: Id: portal_vfsops.c,v 1.5 1992/05/30 10:25:27 jsp Exp
+ * @(#)portal_vfsops.c 8.6 (Berkeley) 1/21/94
+ */
+
+/*
+ * Portal Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/file.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/protosw.h>
+#include <sys/domain.h>
+#include <sys/un.h>
+#include <miscfs/portal/portal.h>
+
+int
+portal_init()
+{
+
+ return (0);
+}
+
+/*
+ * Mount the per-process file descriptors (/dev/fd)
+ */
+int
+portal_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct file *fp;
+ struct portal_args args;
+ struct portalmount *fmp;
+ struct socket *so;
+ struct vnode *rvp;
+ size_t size;
+ int error;
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ if (error = copyin(data, (caddr_t) &args, sizeof(struct portal_args)))
+ return (error);
+
+ if (error = getsock(p->p_fd, args.pa_socket, &fp))
+ return (error);
+ so = (struct socket *) fp->f_data;
+ if (so->so_proto->pr_domain->dom_family != AF_UNIX)
+ return (ESOCKTNOSUPPORT);
+
+ error = getnewvnode(VT_PORTAL, mp, portal_vnodeop_p, &rvp); /* XXX */
+ if (error)
+ return (error);
+ MALLOC(rvp->v_data, void *, sizeof(struct portalnode),
+ M_TEMP, M_WAITOK);
+
+ fmp = (struct portalmount *) malloc(sizeof(struct portalmount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+ rvp->v_type = VDIR;
+ rvp->v_flag |= VROOT;
+ VTOPORTAL(rvp)->pt_arg = 0;
+ VTOPORTAL(rvp)->pt_size = 0;
+ VTOPORTAL(rvp)->pt_fileid = PORTAL_ROOTFILEID;
+ fmp->pm_root = rvp;
+ fmp->pm_server = fp; fp->f_count++;
+
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t)fmp;
+ getnewfsid(mp, makefstype(MOUNT_PORTAL));
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void) copyinstr(args.pa_config, mp->mnt_stat.f_mntfromname,
+ MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+ return (0);
+}
+
+int
+portal_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+int
+portal_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ extern int doforce;
+ struct vnode *rootvp = VFSTOPORTAL(mp)->pm_root;
+ int error, flags = 0;
+
+ if (mntflags & MNT_FORCE) {
+ /* portal can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#ifdef notyet
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, rootvp, flags))
+ return (error);
+
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(rootvp);
+ /*
+ * Shutdown the socket. This will cause the select in the
+ * daemon to wake up, and then the accept will get ECONNABORTED
+ * which it interprets as a request to go and bury itself.
+ */
+ soshutdown((struct socket *) VFSTOPORTAL(mp)->pm_server->f_data, 2);
+ /*
+ * Discard reference to underlying file. Must call closef because
+ * this may be the last reference.
+ */
+ closef(VFSTOPORTAL(mp)->pm_server, (struct proc *) 0);
+ /*
+ * Finally, throw away the portalmount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+portal_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = VFSTOPORTAL(mp)->pm_root;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+portal_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+
+#ifdef COMPAT_09
+ sbp->f_type = 12;
+#else
+ sbp->f_type = 0;
+#endif
+ sbp->f_bsize = DEV_BSIZE;
+ sbp->f_iosize = DEV_BSIZE;
+ sbp->f_blocks = 2; /* 1K to keep df happy */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = 1; /* Allow for "." */
+ sbp->f_ffree = 0; /* See comments above */
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ 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);
+}
+
+int
+portal_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+int
+portal_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_fhtovp(mp, fhp, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+portal_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+struct vfsops portal_vfsops = {
+ MOUNT_PORTAL,
+ portal_mount,
+ portal_start,
+ portal_unmount,
+ portal_root,
+ portal_quotactl,
+ portal_statfs,
+ portal_sync,
+ portal_vget,
+ portal_fhtovp,
+ portal_vptofh,
+ portal_init,
+};
diff --git a/sys/miscfs/portal/portal_vnops.c b/sys/miscfs/portal/portal_vnops.c
new file mode 100644
index 00000000000..d25f65dd434
--- /dev/null
+++ b/sys/miscfs/portal/portal_vnops.c
@@ -0,0 +1,718 @@
+/* $NetBSD: portal_vnops.c,v 1.14 1995/10/07 06:28:55 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * from: Id: portal_vnops.c,v 1.4 1992/05/30 10:05:24 jsp Exp
+ * @(#)portal_vnops.c 8.8 (Berkeley) 1/21/94
+ */
+
+/*
+ * Portal Filesystem
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/proc.h>
+#include <sys/filedesc.h>
+#include <sys/vnode.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <sys/malloc.h>
+#include <sys/namei.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/un.h>
+#include <sys/unpcb.h>
+#include <sys/syscallargs.h>
+
+#include <miscfs/portal/portal.h>
+
+static int portal_fileid = PORTAL_ROOTFILEID+1;
+
+static void
+portal_closefd(p, fd)
+ struct proc *p;
+ int fd;
+{
+ struct sys_close_args /* {
+ syscallarg(int) fd;
+ } */ ua;
+ register_t retval[2];
+ int error;
+
+ SCARG(&ua, fd) = fd;
+ error = sys_close(p, &ua, retval);
+ /*
+ * We should never get an error, and there isn't anything
+ * we could do if we got one, so just print a message.
+ */
+ if (error)
+ printf("portal_closefd: error = %d\n", error);
+}
+
+/*
+ * vp is the current namei directory
+ * cnp is the name to locate in that directory...
+ */
+int
+portal_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname = cnp->cn_nameptr;
+ struct portalnode *pt;
+ int error;
+ struct vnode *fvp = 0;
+ char *path;
+ int size;
+
+ *vpp = NULLVP;
+
+ if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
+ return (EROFS);
+
+ if (cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ /*VOP_LOCK(dvp);*/
+ return (0);
+ }
+
+ error = getnewvnode(VT_PORTAL, dvp->v_mount, portal_vnodeop_p, &fvp);
+ if (error)
+ goto bad;
+ fvp->v_type = VREG;
+ MALLOC(fvp->v_data, void *, sizeof(struct portalnode), M_TEMP,
+ M_WAITOK);
+
+ pt = VTOPORTAL(fvp);
+ /*
+ * Save all of the remaining pathname and
+ * advance the namei next pointer to the end
+ * of the string.
+ */
+ for (size = 0, path = pname; *path; path++)
+ size++;
+ cnp->cn_consume = size - cnp->cn_namelen;
+
+ pt->pt_arg = malloc(size+1, M_TEMP, M_WAITOK);
+ pt->pt_size = size+1;
+ bcopy(pname, pt->pt_arg, pt->pt_size);
+ pt->pt_fileid = portal_fileid++;
+
+ *vpp = fvp;
+ /*VOP_LOCK(fvp);*/
+ return (0);
+
+bad:;
+ if (fvp)
+ vrele(fvp);
+ return (error);
+}
+
+static int
+portal_connect(so, so2)
+ struct socket *so;
+ struct socket *so2;
+{
+ /* from unp_connect, bypassing the namei stuff... */
+ struct socket *so3;
+ struct unpcb *unp2;
+ struct unpcb *unp3;
+
+ if (so2 == 0)
+ return (ECONNREFUSED);
+
+ if (so->so_type != so2->so_type)
+ return (EPROTOTYPE);
+
+ if ((so2->so_options & SO_ACCEPTCONN) == 0)
+ return (ECONNREFUSED);
+
+ if ((so3 = sonewconn(so2, 0)) == 0)
+ return (ECONNREFUSED);
+
+ unp2 = sotounpcb(so2);
+ unp3 = sotounpcb(so3);
+ if (unp2->unp_addr)
+ unp3->unp_addr = m_copy(unp2->unp_addr, 0, (int)M_COPYALL);
+
+ so2 = so3;
+
+
+ return (unp_connect2(so, so2));
+}
+
+int
+portal_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct socket *so = 0;
+ struct portalnode *pt;
+ struct proc *p = ap->a_p;
+ struct vnode *vp = ap->a_vp;
+ int s;
+ struct uio auio;
+ struct iovec aiov[2];
+ int res;
+ struct mbuf *cm = 0;
+ struct cmsghdr *cmsg;
+ int newfds;
+ int *ip;
+ int fd;
+ int error;
+ int len;
+ struct portalmount *fmp;
+ struct file *fp;
+ struct portal_cred pcred;
+
+ /*
+ * Nothing to do when opening the root node.
+ */
+ if (vp->v_flag & VROOT)
+ return (0);
+
+ /*
+ * Can't be opened unless the caller is set up
+ * to deal with the side effects. Check for this
+ * by testing whether the p_dupfd has been set.
+ */
+ if (p->p_dupfd >= 0)
+ return (ENODEV);
+
+ pt = VTOPORTAL(vp);
+ fmp = VFSTOPORTAL(vp->v_mount);
+
+ /*
+ * Create a new socket.
+ */
+ error = socreate(AF_UNIX, &so, SOCK_STREAM, 0);
+ if (error)
+ goto bad;
+
+ /*
+ * Reserve some buffer space
+ */
+ res = pt->pt_size + sizeof(pcred) + 512; /* XXX */
+ error = soreserve(so, res, res);
+ if (error)
+ goto bad;
+
+ /*
+ * Kick off connection
+ */
+ error = portal_connect(so, (struct socket *)fmp->pm_server->f_data);
+ if (error)
+ goto bad;
+
+ /*
+ * Wait for connection to complete
+ */
+ /*
+ * XXX: Since the mount point is holding a reference on the
+ * underlying server socket, it is not easy to find out whether
+ * the server process is still running. To handle this problem
+ * we loop waiting for the new socket to be connected (something
+ * which will only happen if the server is still running) or for
+ * the reference count on the server socket to drop to 1, which
+ * will happen if the server dies. Sleep for 5 second intervals
+ * and keep polling the reference count. XXX.
+ */
+ s = splsoftnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ if (fmp->pm_server->f_count == 1) {
+ error = ECONNREFUSED;
+ splx(s);
+ goto bad;
+ }
+ (void) tsleep((caddr_t) &so->so_timeo, PSOCK, "portalcon", 5 * hz);
+ }
+ splx(s);
+
+ if (so->so_error) {
+ error = so->so_error;
+ goto bad;
+ }
+
+ /*
+ * Set miscellaneous flags
+ */
+ so->so_rcv.sb_timeo = 0;
+ so->so_snd.sb_timeo = 0;
+ so->so_rcv.sb_flags |= SB_NOINTR;
+ so->so_snd.sb_flags |= SB_NOINTR;
+
+
+ pcred.pcr_flag = ap->a_mode;
+ pcred.pcr_uid = ap->a_cred->cr_uid;
+ pcred.pcr_gid = ap->a_cred->cr_gid;
+ pcred.pcr_ngroups = ap->a_cred->cr_ngroups;
+ bcopy(ap->a_cred->cr_groups, pcred.pcr_groups, NGROUPS * sizeof(gid_t));
+ aiov[0].iov_base = (caddr_t) &pcred;
+ aiov[0].iov_len = sizeof(pcred);
+ aiov[1].iov_base = pt->pt_arg;
+ aiov[1].iov_len = pt->pt_size;
+ auio.uio_iov = aiov;
+ auio.uio_iovcnt = 2;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_procp = p;
+ auio.uio_offset = 0;
+ auio.uio_resid = aiov[0].iov_len + aiov[1].iov_len;
+
+ error = sosend(so, (struct mbuf *) 0, &auio,
+ (struct mbuf *) 0, (struct mbuf *) 0, 0);
+ if (error)
+ goto bad;
+
+ len = auio.uio_resid = sizeof(int);
+ do {
+ struct mbuf *m = 0;
+ int flags = MSG_WAITALL;
+ error = soreceive(so, (struct mbuf **) 0, &auio,
+ &m, &cm, &flags);
+ if (error)
+ goto bad;
+
+ /*
+ * Grab an error code from the mbuf.
+ */
+ if (m) {
+ m = m_pullup(m, sizeof(int)); /* Needed? */
+ if (m) {
+ error = *(mtod(m, int *));
+ m_freem(m);
+ } else {
+ error = EINVAL;
+ }
+ } else {
+ if (cm == 0) {
+ error = ECONNRESET; /* XXX */
+#ifdef notdef
+ break;
+#endif
+ }
+ }
+ } while (cm == 0 && auio.uio_resid == len && !error);
+
+ if (cm == 0)
+ goto bad;
+
+ if (auio.uio_resid) {
+ error = 0;
+#ifdef notdef
+ error = EMSGSIZE;
+ goto bad;
+#endif
+ }
+
+ /*
+ * XXX: Break apart the control message, and retrieve the
+ * received file descriptor. Note that more than one descriptor
+ * may have been received, or that the rights chain may have more
+ * than a single mbuf in it. What to do?
+ */
+ cmsg = mtod(cm, struct cmsghdr *);
+ newfds = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof (int);
+ if (newfds == 0) {
+ error = ECONNREFUSED;
+ goto bad;
+ }
+ /*
+ * At this point the rights message consists of a control message
+ * header, followed by a data region containing a vector of
+ * integer file descriptors. The fds were allocated by the action
+ * of receiving the control message.
+ */
+ ip = (int *) (cmsg + 1);
+ fd = *ip++;
+ if (newfds > 1) {
+ /*
+ * Close extra fds.
+ */
+ int i;
+ printf("portal_open: %d extra fds\n", newfds - 1);
+ for (i = 1; i < newfds; i++) {
+ portal_closefd(p, *ip);
+ ip++;
+ }
+ }
+
+ /*
+ * Check that the mode the file is being opened for is a subset
+ * of the mode of the existing descriptor.
+ */
+ fp = p->p_fd->fd_ofiles[fd];
+ if (((ap->a_mode & (FREAD|FWRITE)) | fp->f_flag) != fp->f_flag) {
+ portal_closefd(p, fd);
+ error = EACCES;
+ goto bad;
+ }
+
+ /*
+ * Save the dup fd in the proc structure then return the
+ * special error code (ENXIO) which causes magic things to
+ * happen in vn_open. The whole concept is, well, hmmm.
+ */
+ p->p_dupfd = fd;
+ error = ENXIO;
+
+bad:;
+ /*
+ * And discard the control message.
+ */
+ if (cm) {
+ m_freem(cm);
+ }
+
+ if (so) {
+ soshutdown(so, 2);
+ soclose(so);
+ }
+ return (error);
+}
+
+int
+portal_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct vattr *vap = ap->a_vap;
+ struct timeval tv;
+
+ bzero(vap, sizeof(*vap));
+ vattr_null(vap);
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
+ vap->va_size = DEV_BSIZE;
+ vap->va_blocksize = DEV_BSIZE;
+ microtime(&tv);
+ TIMEVAL_TO_TIMESPEC(&tv, &vap->va_atime);
+ vap->va_mtime = vap->va_atime;
+ vap->va_ctime = vap->va_ctime;
+ vap->va_gen = 0;
+ vap->va_flags = 0;
+ vap->va_rdev = 0;
+ /* vap->va_qbytes = 0; */
+ vap->va_bytes = 0;
+ /* vap->va_qsize = 0; */
+ if (vp->v_flag & VROOT) {
+ vap->va_type = VDIR;
+ vap->va_mode = S_IRUSR|S_IWUSR|S_IXUSR|
+ S_IRGRP|S_IWGRP|S_IXGRP|
+ S_IROTH|S_IWOTH|S_IXOTH;
+ vap->va_nlink = 2;
+ vap->va_fileid = 2;
+ } else {
+ vap->va_type = VREG;
+ vap->va_mode = S_IRUSR|S_IWUSR|
+ S_IRGRP|S_IWGRP|
+ S_IROTH|S_IWOTH;
+ vap->va_nlink = 1;
+ vap->va_fileid = VTOPORTAL(vp)->pt_fileid;
+ }
+ return (0);
+}
+
+int
+portal_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ /*
+ * Can't mess with the root vnode
+ */
+ if (ap->a_vp->v_flag & VROOT)
+ return (EACCES);
+
+ return (0);
+}
+
+/*
+ * Fake readdir, just return empty directory.
+ * It is hard to deal with '.' and '..' so don't bother.
+ */
+int
+portal_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+int
+portal_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+int
+portal_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct portalnode *pt = VTOPORTAL(ap->a_vp);
+
+ if (pt->pt_arg) {
+ free((caddr_t) pt->pt_arg, M_TEMP);
+ pt->pt_arg = 0;
+ }
+ FREE(ap->a_vp->v_data, M_TEMP);
+ ap->a_vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+portal_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ register_t *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Print out the contents of a Portal vnode.
+ */
+/* ARGSUSED */
+int
+portal_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_PORTAL, portal vnode\n");
+ return (0);
+}
+
+/*void*/
+int
+portal_vfree(ap)
+ struct vop_vfree_args /* {
+ struct vnode *a_pvp;
+ ino_t a_ino;
+ int a_mode;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+
+/*
+ * Portal vnode unsupported operation
+ */
+int
+portal_enotsupp()
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Portal "should never get here" operation
+ */
+int
+portal_badop()
+{
+
+ panic("portal: bad op");
+ /* NOTREACHED */
+}
+
+/*
+ * Portal vnode null operation
+ */
+int
+portal_nullop()
+{
+
+ return (0);
+}
+
+#define portal_create ((int (*) __P((struct vop_create_args *)))portal_enotsupp)
+#define portal_mknod ((int (*) __P((struct vop_mknod_args *)))portal_enotsupp)
+#define portal_close ((int (*) __P((struct vop_close_args *)))nullop)
+#define portal_access ((int (*) __P((struct vop_access_args *)))nullop)
+#define portal_read ((int (*) __P((struct vop_read_args *)))portal_enotsupp)
+#define portal_write ((int (*) __P((struct vop_write_args *)))portal_enotsupp)
+#define portal_ioctl ((int (*) __P((struct vop_ioctl_args *)))portal_enotsupp)
+#define portal_select ((int (*) __P((struct vop_select_args *)))portal_enotsupp)
+#define portal_mmap ((int (*) __P((struct vop_mmap_args *)))portal_enotsupp)
+#define portal_fsync ((int (*) __P((struct vop_fsync_args *)))nullop)
+#define portal_seek ((int (*) __P((struct vop_seek_args *)))nullop)
+#define portal_remove ((int (*) __P((struct vop_remove_args *)))portal_enotsupp)
+#define portal_link ((int (*) __P((struct vop_link_args *)))portal_enotsupp)
+#define portal_rename ((int (*) __P((struct vop_rename_args *)))portal_enotsupp)
+#define portal_mkdir ((int (*) __P((struct vop_mkdir_args *)))portal_enotsupp)
+#define portal_rmdir ((int (*) __P((struct vop_rmdir_args *)))portal_enotsupp)
+#define portal_symlink \
+ ((int (*) __P((struct vop_symlink_args *)))portal_enotsupp)
+#define portal_readlink \
+ ((int (*) __P((struct vop_readlink_args *)))portal_enotsupp)
+#define portal_abortop ((int (*) __P((struct vop_abortop_args *)))nullop)
+#define portal_lock ((int (*) __P((struct vop_lock_args *)))nullop)
+#define portal_unlock ((int (*) __P((struct vop_unlock_args *)))nullop)
+#define portal_bmap ((int (*) __P((struct vop_bmap_args *)))portal_badop)
+#define portal_strategy \
+ ((int (*) __P((struct vop_strategy_args *)))portal_badop)
+#define portal_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+#define portal_advlock \
+ ((int (*) __P((struct vop_advlock_args *)))portal_enotsupp)
+#define portal_blkatoff \
+ ((int (*) __P((struct vop_blkatoff_args *)))portal_enotsupp)
+#define portal_valloc ((int(*) __P(( \
+ struct vnode *pvp, \
+ int mode, \
+ struct ucred *cred, \
+ struct vnode **vpp))) portal_enotsupp)
+#define portal_truncate \
+ ((int (*) __P((struct vop_truncate_args *)))portal_enotsupp)
+#define portal_update ((int (*) __P((struct vop_update_args *)))portal_enotsupp)
+#define portal_bwrite ((int (*) __P((struct vop_bwrite_args *)))portal_enotsupp)
+
+int (**portal_vnodeop_p)();
+struct vnodeopv_entry_desc portal_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, portal_lookup }, /* lookup */
+ { &vop_create_desc, portal_create }, /* create */
+ { &vop_mknod_desc, portal_mknod }, /* mknod */
+ { &vop_open_desc, portal_open }, /* open */
+ { &vop_close_desc, portal_close }, /* close */
+ { &vop_access_desc, portal_access }, /* access */
+ { &vop_getattr_desc, portal_getattr }, /* getattr */
+ { &vop_setattr_desc, portal_setattr }, /* setattr */
+ { &vop_read_desc, portal_read }, /* read */
+ { &vop_write_desc, portal_write }, /* write */
+ { &vop_ioctl_desc, portal_ioctl }, /* ioctl */
+ { &vop_select_desc, portal_select }, /* select */
+ { &vop_mmap_desc, portal_mmap }, /* mmap */
+ { &vop_fsync_desc, portal_fsync }, /* fsync */
+ { &vop_seek_desc, portal_seek }, /* seek */
+ { &vop_remove_desc, portal_remove }, /* remove */
+ { &vop_link_desc, portal_link }, /* link */
+ { &vop_rename_desc, portal_rename }, /* rename */
+ { &vop_mkdir_desc, portal_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, portal_rmdir }, /* rmdir */
+ { &vop_symlink_desc, portal_symlink }, /* symlink */
+ { &vop_readdir_desc, portal_readdir }, /* readdir */
+ { &vop_readlink_desc, portal_readlink }, /* readlink */
+ { &vop_abortop_desc, portal_abortop }, /* abortop */
+ { &vop_inactive_desc, portal_inactive }, /* inactive */
+ { &vop_reclaim_desc, portal_reclaim }, /* reclaim */
+ { &vop_lock_desc, portal_lock }, /* lock */
+ { &vop_unlock_desc, portal_unlock }, /* unlock */
+ { &vop_bmap_desc, portal_bmap }, /* bmap */
+ { &vop_strategy_desc, portal_strategy }, /* strategy */
+ { &vop_print_desc, portal_print }, /* print */
+ { &vop_islocked_desc, portal_islocked }, /* islocked */
+ { &vop_pathconf_desc, portal_pathconf }, /* pathconf */
+ { &vop_advlock_desc, portal_advlock }, /* advlock */
+ { &vop_blkatoff_desc, portal_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, portal_valloc }, /* valloc */
+ { &vop_vfree_desc, portal_vfree }, /* vfree */
+ { &vop_truncate_desc, portal_truncate }, /* truncate */
+ { &vop_update_desc, portal_update }, /* update */
+ { &vop_bwrite_desc, portal_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc portal_vnodeop_opv_desc =
+ { &portal_vnodeop_p, portal_vnodeop_entries };
diff --git a/sys/miscfs/procfs/README b/sys/miscfs/procfs/README
new file mode 100644
index 00000000000..2b76d14534e
--- /dev/null
+++ b/sys/miscfs/procfs/README
@@ -0,0 +1,113 @@
+/* $NetBSD: README,v 1.4 1994/06/29 06:34:43 cgd Exp $ */
+
+saute procfs lyonnais
+
+procfs supports two levels of directory. the filesystem root
+directory contains a representation of the system process table.
+this consists of an entry for each active and zombie process, and
+an additional entry "curproc" which always represents the process
+making the lookup request.
+
+each of the sub-directories contains several files. these files
+are used to control and interrogate processes. the files implemented
+are:
+
+ file - xxx. the exec'ed file.
+
+ status - r/o. returns process status.
+
+ ctl - w/o. sends a control message to the process.
+ for example:
+ echo hup > /proc/curproc/note
+ will send a SIGHUP to the shell.
+ whereas
+ echo attach > /proc/1293/ctl
+ would set up process 1293 for debugging.
+ see below for more details.
+
+ mem - r/w. virtual memory image of the process.
+ parts of the address space are readable
+ only if they exist in the target process.
+ a more reasonable alternative might be
+ to return zero pages instead of an error.
+ comments?
+
+ note - w/o. writing a string here sends the
+ equivalent note to the process.
+ [ not implemented. ]
+
+ notepg - w/o. the same as note, but sends to all
+ members of the process group.
+ [ not implemented. ]
+
+ regs - r/w. process register set. this can be read
+ or written any time even if the process
+ is not stopped. since the bsd kernel
+ is single-processor, this implementation
+ will get the "right" register values.
+ a multi-proc kernel would need to do some
+ synchronisation.
+
+this then looks like:
+
+% ls -li /proc
+total 0
+ 9 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 0
+ 17 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 1
+ 89 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 10
+ 25 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 2
+2065 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 257
+2481 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 309
+ 265 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 32
+3129 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 390
+3209 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 400
+3217 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 401
+3273 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 408
+ 393 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 48
+ 409 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 50
+ 465 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 57
+ 481 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 59
+ 537 dr-xr-xr-x 2 root kmem 0 Sep 21 15:06 66
+ 545 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 67
+ 657 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 81
+ 665 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 82
+ 673 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 83
+ 681 dr-xr-xr-x 2 root wheel 0 Sep 21 15:06 84
+3273 dr-xr-xr-x 2 jsp staff 0 Sep 21 15:06 curproc
+% ls -li /proc/curproc
+total 408
+3341 --w------- 1 jsp staff 0 Sep 21 15:06 ctl
+1554 -r-xr-xr-x 1 bin bin 90112 Mar 29 04:52 file
+3339 -rw------- 1 jsp staff 118784 Sep 21 15:06 mem
+3343 --w------- 1 jsp staff 0 Sep 21 15:06 note
+3344 --w------- 1 jsp staff 0 Sep 21 15:06 notepg
+3340 -rw------- 1 jsp staff 0 Sep 21 15:06 regs
+3342 -r--r--r-- 1 jsp staff 0 Sep 21 15:06 status
+% df /proc/curproc /proc/curproc/file
+Filesystem 512-blocks Used Avail Capacity Mounted on
+proc 2 2 0 100% /proc
+/dev/wd0a 16186 13548 1018 93% /
+% cat /proc/curproc/status
+cat 446 439 400 81 12,0 ctty 748620684 270000 0 0 0 20000 nochan 11 20 20 20 0 21 117
+
+
+
+the basic sequence of commands written to "ctl" would be
+
+ attach - this stops the target process and
+ arranges for the sending process
+ to become the debug control process
+ wait - wait for the target process to come to
+ a steady state ready for debugging.
+ step - single step, with no signal delivery.
+ run - continue running, with no signal delivery,
+ until next trap or breakpoint.
+ <signame> - deliver signal <signame> and continue running.
+ detach - continue execution of the target process
+ and remove it from control by the debug process
+
+in a normal debugging environment, where the target is fork/exec'd by
+the debugger, the debugger should fork and the child should stop itself
+(with a self-inflicted SIGSTOP). the parent should do a "wait" then an
+"attach". as before, the child will hit a breakpoint on the first
+instruction in any newly exec'd image.
diff --git a/sys/miscfs/procfs/procfs.h b/sys/miscfs/procfs/procfs.h
new file mode 100644
index 00000000000..37ef12e6f0d
--- /dev/null
+++ b/sys/miscfs/procfs/procfs.h
@@ -0,0 +1,170 @@
+/* $NetBSD: procfs.h,v 1.14 1995/10/09 11:18:51 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs.h 8.7 (Berkeley) 6/15/94
+ */
+
+/*
+ * The different types of node in a procfs filesystem
+ */
+typedef enum {
+ Proot, /* the filesystem root */
+ Pcurproc, /* symbolic link for curproc */
+ Pproc, /* a process-specific sub-directory */
+ Pfile, /* the executable file */
+ Pmem, /* the process's memory image */
+ Pregs, /* the process's register set */
+ Pfpregs, /* the process's FP register set */
+ Pctl, /* process control */
+ Pstatus, /* process status */
+ Pnote, /* process notifier */
+ Pnotepg /* process group notifier */
+} pfstype;
+
+/*
+ * control data for the proc file system.
+ */
+struct pfsnode {
+ struct pfsnode *pfs_next; /* next on list */
+ struct vnode *pfs_vnode; /* vnode associated with this pfsnode */
+ pfstype pfs_type; /* type of procfs node */
+ pid_t pfs_pid; /* associated process */
+ u_short pfs_mode; /* mode bits for stat() */
+ u_long pfs_flags; /* open flags */
+ u_long pfs_fileno; /* unique file id */
+};
+
+#define PROCFS_NOTELEN 64 /* max length of a note (/proc/$pid/note) */
+#define PROCFS_CTLLEN 8 /* max length of a ctl msg (/proc/$pid/ctl */
+
+/*
+ * Kernel stuff follows
+ */
+#ifdef _KERNEL
+#define CNEQ(cnp, s, len) \
+ ((cnp)->cn_namelen == (len) && \
+ (bcmp((s), (cnp)->cn_nameptr, (len)) == 0))
+
+#define UIO_MX 32
+
+#define PROCFS_FILENO(pid, type) \
+ (((type) < Pproc) ? \
+ ((type) + 2) : \
+ ((((pid)+1) << 4) + ((int) (type))))
+
+/*
+ * Convert between pfsnode vnode
+ */
+#define VTOPFS(vp) ((struct pfsnode *)(vp)->v_data)
+#define PFSTOV(pfs) ((pfs)->pfs_vnode)
+
+typedef struct vfs_namemap vfs_namemap_t;
+struct vfs_namemap {
+ const char *nm_name;
+ int nm_val;
+};
+
+int vfs_getuserstr __P((struct uio *, char *, int *));
+vfs_namemap_t *vfs_findname __P((vfs_namemap_t *, char *, int));
+
+#define PFIND(pid) ((pid) ? pfind(pid) : &proc0)
+int procfs_freevp __P((struct vnode *));
+int procfs_allocvp __P((struct mount *, struct vnode **, long, pfstype));
+struct vnode *procfs_findtextvp __P((struct proc *));
+int procfs_donote __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+int procfs_doregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+int procfs_dofpregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+int procfs_domem __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+int procfs_doctl __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+int procfs_dostatus __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+
+/* functions to check whether or not files should be displayed */
+int procfs_validfile __P((struct proc *));
+int procfs_validfpregs __P((struct proc *));
+int procfs_validregs __P((struct proc *));
+
+#define PROCFS_LOCKED 0x01
+#define PROCFS_WANT 0x02
+
+extern int (**procfs_vnodeop_p)();
+extern struct vfsops procfs_vfsops;
+
+/*
+ * Prototypes for procfs vnode ops
+ */
+int procfs_badop(); /* varargs */
+int procfs_rw __P((struct vop_read_args *));
+int procfs_lookup __P((struct vop_lookup_args *));
+#define procfs_create ((int (*) __P((struct vop_create_args *))) procfs_badop)
+#define procfs_mknod ((int (*) __P((struct vop_mknod_args *))) procfs_badop)
+int procfs_open __P((struct vop_open_args *));
+int procfs_close __P((struct vop_close_args *));
+int procfs_access __P((struct vop_access_args *));
+int procfs_getattr __P((struct vop_getattr_args *));
+int procfs_setattr __P((struct vop_setattr_args *));
+#define procfs_read procfs_rw
+#define procfs_write procfs_rw
+int procfs_ioctl __P((struct vop_ioctl_args *));
+#define procfs_select ((int (*) __P((struct vop_select_args *))) procfs_badop)
+#define procfs_mmap ((int (*) __P((struct vop_mmap_args *))) procfs_badop)
+#define procfs_fsync ((int (*) __P((struct vop_fsync_args *))) procfs_badop)
+#define procfs_seek ((int (*) __P((struct vop_seek_args *))) procfs_badop)
+#define procfs_remove ((int (*) __P((struct vop_remove_args *))) procfs_badop)
+#define procfs_link ((int (*) __P((struct vop_link_args *))) procfs_badop)
+#define procfs_rename ((int (*) __P((struct vop_rename_args *))) procfs_badop)
+#define procfs_mkdir ((int (*) __P((struct vop_mkdir_args *))) procfs_badop)
+#define procfs_rmdir ((int (*) __P((struct vop_rmdir_args *))) procfs_badop)
+#define procfs_symlink ((int (*) __P((struct vop_symlink_args *))) procfs_badop)
+int procfs_readdir __P((struct vop_readdir_args *));
+int procfs_readlink __P((struct vop_readlink_args *));
+int procfs_abortop __P((struct vop_abortop_args *));
+int procfs_inactive __P((struct vop_inactive_args *));
+int procfs_reclaim __P((struct vop_reclaim_args *));
+#define procfs_lock ((int (*) __P((struct vop_lock_args *))) nullop)
+#define procfs_unlock ((int (*) __P((struct vop_unlock_args *))) nullop)
+int procfs_bmap __P((struct vop_bmap_args *));
+#define procfs_strategy ((int (*) __P((struct vop_strategy_args *))) procfs_badop)
+int procfs_print __P((struct vop_print_args *));
+#define procfs_islocked ((int (*) __P((struct vop_islocked_args *))) nullop)
+#define procfs_advlock ((int (*) __P((struct vop_advlock_args *))) procfs_badop)
+#define procfs_blkatoff ((int (*) __P((struct vop_blkatoff_args *))) procfs_badop)
+#define procfs_valloc ((int (*) __P((struct vop_valloc_args *))) procfs_badop)
+#define procfs_vfree ((int (*) __P((struct vop_vfree_args *))) nullop)
+#define procfs_truncate ((int (*) __P((struct vop_truncate_args *))) procfs_badop)
+#define procfs_update ((int (*) __P((struct vop_update_args *))) nullop)
+#endif /* _KERNEL */
diff --git a/sys/miscfs/procfs/procfs_ctl.c b/sys/miscfs/procfs/procfs_ctl.c
new file mode 100644
index 00000000000..3993f81ae4c
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_ctl.c
@@ -0,0 +1,297 @@
+/* $NetBSD: procfs_ctl.c,v 1.13 1995/08/13 09:06:02 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs_ctl.c 8.4 (Berkeley) 6/15/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/resource.h>
+#include <sys/resourcevar.h>
+#include <sys/ptrace.h>
+#include <miscfs/procfs/procfs.h>
+
+/*
+ * True iff process (p) is in trace wait state
+ * relative to process (curp)
+ */
+#define TRACE_WAIT_P(curp, p) \
+ ((p)->p_stat == SSTOP && \
+ (p)->p_pptr == (curp) && \
+ ((p)->p_flag & P_TRACED))
+
+#define PROCFS_CTL_ATTACH 1
+#define PROCFS_CTL_DETACH 2
+#define PROCFS_CTL_STEP 3
+#define PROCFS_CTL_RUN 4
+#define PROCFS_CTL_WAIT 5
+
+static vfs_namemap_t ctlnames[] = {
+ /* special /proc commands */
+ { "attach", PROCFS_CTL_ATTACH },
+ { "detach", PROCFS_CTL_DETACH },
+ { "step", PROCFS_CTL_STEP },
+ { "run", PROCFS_CTL_RUN },
+ { "wait", PROCFS_CTL_WAIT },
+ { 0 },
+};
+
+static vfs_namemap_t signames[] = {
+ /* regular signal names */
+ { "hup", SIGHUP }, { "int", SIGINT },
+ { "quit", SIGQUIT }, { "ill", SIGILL },
+ { "trap", SIGTRAP }, { "abrt", SIGABRT },
+ { "iot", SIGIOT }, { "emt", SIGEMT },
+ { "fpe", SIGFPE }, { "kill", SIGKILL },
+ { "bus", SIGBUS }, { "segv", SIGSEGV },
+ { "sys", SIGSYS }, { "pipe", SIGPIPE },
+ { "alrm", SIGALRM }, { "term", SIGTERM },
+ { "urg", SIGURG }, { "stop", SIGSTOP },
+ { "tstp", SIGTSTP }, { "cont", SIGCONT },
+ { "chld", SIGCHLD }, { "ttin", SIGTTIN },
+ { "ttou", SIGTTOU }, { "io", SIGIO },
+ { "xcpu", SIGXCPU }, { "xfsz", SIGXFSZ },
+ { "vtalrm", SIGVTALRM }, { "prof", SIGPROF },
+ { "winch", SIGWINCH }, { "info", SIGINFO },
+ { "usr1", SIGUSR1 }, { "usr2", SIGUSR2 },
+ { 0 },
+};
+
+static int
+procfs_control(curp, p, op)
+ struct proc *curp;
+ struct proc *p;
+ int op;
+{
+ int error;
+
+ /*
+ * Attach - attaches the target process for debugging
+ * by the calling process.
+ */
+ if (op == PROCFS_CTL_ATTACH) {
+ /* check whether already being traced */
+ if (p->p_flag & P_TRACED)
+ return (EBUSY);
+
+ /* can't trace yourself! */
+ if (p->p_pid == curp->p_pid)
+ return (EINVAL);
+
+ /*
+ * Go ahead and set the trace flag.
+ * Save the old parent (it's reset in
+ * _DETACH, and also in kern_exit.c:wait4()
+ * Reparent the process so that the tracing
+ * proc gets to see all the action.
+ * Stop the target.
+ */
+ p->p_flag |= P_TRACED;
+ p->p_xstat = 0; /* XXX ? */
+ if (p->p_pptr != curp) {
+ p->p_oppid = p->p_pptr->p_pid;
+ proc_reparent(p, curp);
+ }
+ psignal(p, SIGSTOP);
+ return (0);
+ }
+
+ /*
+ * Target process must be stopped, owned by (curp) and
+ * be set up for tracing (P_TRACED flag set).
+ * Allow DETACH to take place at any time for sanity.
+ * Allow WAIT any time, of course.
+ */
+ switch (op) {
+ case PROCFS_CTL_DETACH:
+ case PROCFS_CTL_WAIT:
+ break;
+
+ default:
+ if (!TRACE_WAIT_P(curp, p))
+ return (EBUSY);
+ }
+
+ /*
+ * do single-step fixup if needed
+ */
+ FIX_SSTEP(p);
+
+ /*
+ * Don't deliver any signal by default.
+ * To continue with a signal, just send
+ * the signal name to the ctl file
+ */
+ p->p_xstat = 0;
+
+ switch (op) {
+ /*
+ * Detach. Cleans up the target process, reparent it if possible
+ * and set it running once more.
+ */
+ case PROCFS_CTL_DETACH:
+ /* if not being traced, then this is a painless no-op */
+ if ((p->p_flag & P_TRACED) == 0)
+ return (0);
+
+ /* not being traced any more */
+ p->p_flag &= ~P_TRACED;
+
+ /* give process back to original parent */
+ if (p->p_oppid != p->p_pptr->p_pid) {
+ struct proc *pp;
+
+ pp = pfind(p->p_oppid);
+ if (pp)
+ proc_reparent(p, pp);
+ }
+
+ p->p_oppid = 0;
+ p->p_flag &= ~P_WAITED; /* XXX ? */
+ wakeup((caddr_t) curp); /* XXX for CTL_WAIT below ? */
+
+ break;
+
+ /*
+ * Step. Let the target process execute a single instruction.
+ */
+ case PROCFS_CTL_STEP:
+ PHOLD(p);
+ error = process_sstep(p, 1);
+ PRELE(p);
+ if (error)
+ return (error);
+ break;
+
+ /*
+ * Run. Let the target process continue running until a breakpoint
+ * or some other trap.
+ */
+ case PROCFS_CTL_RUN:
+ break;
+
+ /*
+ * Wait for the target process to stop.
+ * If the target is not being traced then just wait
+ * to enter
+ */
+ case PROCFS_CTL_WAIT:
+ error = 0;
+ if (p->p_flag & P_TRACED) {
+ while (error == 0 &&
+ (p->p_stat != SSTOP) &&
+ (p->p_flag & P_TRACED) &&
+ (p->p_pptr == curp)) {
+ error = tsleep((caddr_t) p,
+ PWAIT|PCATCH, "procfsx", 0);
+ }
+ if (error == 0 && !TRACE_WAIT_P(curp, p))
+ error = EBUSY;
+ } else {
+ while (error == 0 && p->p_stat != SSTOP) {
+ error = tsleep((caddr_t) p,
+ PWAIT|PCATCH, "procfs", 0);
+ }
+ }
+ return (error);
+
+ default:
+ panic("procfs_control");
+ }
+
+ if (p->p_stat == SSTOP)
+ setrunnable(p);
+ return (0);
+}
+
+int
+procfs_doctl(curp, p, pfs, uio)
+ struct proc *curp;
+ struct pfsnode *pfs;
+ struct uio *uio;
+ struct proc *p;
+{
+ int xlen;
+ int error;
+ char msg[PROCFS_CTLLEN+1];
+ vfs_namemap_t *nm;
+
+ if (uio->uio_rw != UIO_WRITE)
+ return (EOPNOTSUPP);
+
+ xlen = PROCFS_CTLLEN;
+ error = vfs_getuserstr(uio, msg, &xlen);
+ if (error)
+ return (error);
+
+ /*
+ * Map signal names into signal generation
+ * or debug control. Unknown commands and/or signals
+ * return EOPNOTSUPP.
+ *
+ * Sending a signal while the process is being debugged
+ * also has the side effect of letting the target continue
+ * to run. There is no way to single-step a signal delivery.
+ */
+ error = EOPNOTSUPP;
+
+ nm = vfs_findname(ctlnames, msg, xlen);
+ if (nm) {
+ error = procfs_control(curp, p, nm->nm_val);
+ } else {
+ nm = vfs_findname(signames, msg, xlen);
+ if (nm) {
+ if (TRACE_WAIT_P(curp, p)) {
+ p->p_xstat = nm->nm_val;
+ FIX_SSTEP(p);
+ setrunnable(p);
+ } else {
+ psignal(p, nm->nm_val);
+ }
+ error = 0;
+ }
+ }
+
+ return (error);
+}
diff --git a/sys/miscfs/procfs/procfs_fpregs.c b/sys/miscfs/procfs/procfs_fpregs.c
new file mode 100644
index 00000000000..2ec7f0a5f97
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_fpregs.c
@@ -0,0 +1,107 @@
+/* $NetBSD: procfs_fpregs.c,v 1.4 1995/08/13 09:06:05 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs_fpregs.c 8.2 (Berkeley) 6/15/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_dofpregs(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+#if defined(PT_GETFPREGS) || defined(PT_SETFPREGS)
+ int error;
+ struct fpreg r;
+ char *kv;
+ int kl;
+
+ kl = sizeof(r);
+ kv = (char *) &r;
+
+ kv += uio->uio_offset;
+ kl -= uio->uio_offset;
+ if (kl > uio->uio_resid)
+ kl = uio->uio_resid;
+
+ PHOLD(p);
+
+ if (kl < 0)
+ error = EINVAL;
+ else
+ error = process_read_fpregs(p, &r);
+ if (error == 0)
+ error = uiomove(kv, kl, uio);
+ if (error == 0 && uio->uio_rw == UIO_WRITE) {
+ if (p->p_stat != SSTOP)
+ error = EBUSY;
+ else
+ error = process_write_fpregs(p, &r);
+ }
+
+ PRELE(p);
+
+ uio->uio_offset = 0;
+ return (error);
+#else
+ return (EINVAL);
+#endif
+}
+
+int
+procfs_validfpregs(p)
+ struct proc *p;
+{
+
+#if defined(PT_SETFPREGS) || defined(PT_GETFPREGS)
+ return ((p->p_flag & P_SYSTEM) == 0);
+#else
+ return (0);
+#endif
+}
diff --git a/sys/miscfs/procfs/procfs_mem.c b/sys/miscfs/procfs/procfs_mem.c
new file mode 100644
index 00000000000..439d6f56d0c
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_mem.c
@@ -0,0 +1,301 @@
+/* $NetBSD: procfs_mem.c,v 1.7 1995/01/05 07:10:54 chopps Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993 Sean Eric Fagan
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry and Sean Eric Fagan.
+ *
+ * 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.
+ *
+ * @(#)procfs_mem.c 8.5 (Berkeley) 6/15/94
+ */
+
+/*
+ * This is a lightly hacked and merged version
+ * of sef's pread/pwrite functions
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <miscfs/procfs/procfs.h>
+#include <vm/vm.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+static int
+procfs_rwmem(p, uio)
+ struct proc *p;
+ struct uio *uio;
+{
+ int error;
+ int writing;
+
+ writing = uio->uio_rw == UIO_WRITE;
+
+ /*
+ * Only map in one page at a time. We don't have to, but it
+ * makes things easier. This way is trivial - right?
+ */
+ do {
+ vm_map_t map, tmap;
+ vm_object_t object;
+ vm_offset_t kva;
+ vm_offset_t uva;
+ int page_offset; /* offset into page */
+ vm_offset_t pageno; /* page number */
+ vm_map_entry_t out_entry;
+ vm_prot_t out_prot;
+ vm_page_t m;
+ boolean_t wired, single_use;
+ vm_offset_t off;
+ u_int len;
+ int fix_prot;
+
+ uva = (vm_offset_t) uio->uio_offset;
+ if (uva > VM_MAXUSER_ADDRESS) {
+ error = 0;
+ break;
+ }
+
+ /*
+ * Get the page number of this segment.
+ */
+ pageno = trunc_page(uva);
+ page_offset = uva - pageno;
+
+ /*
+ * How many bytes to copy
+ */
+ len = min(PAGE_SIZE - page_offset, uio->uio_resid);
+
+ /*
+ * The map we want...
+ */
+ map = &p->p_vmspace->vm_map;
+
+ /*
+ * Check the permissions for the area we're interested
+ * in.
+ */
+ fix_prot = 0;
+ if (writing)
+ fix_prot = !vm_map_check_protection(map, pageno,
+ pageno + PAGE_SIZE, VM_PROT_WRITE);
+
+ if (fix_prot) {
+ /*
+ * If the page is not writable, we make it so.
+ * XXX It is possible that a page may *not* be
+ * read/executable, if a process changes that!
+ * We will assume, for now, that a page is either
+ * VM_PROT_ALL, or VM_PROT_READ|VM_PROT_EXECUTE.
+ */
+ error = vm_map_protect(map, pageno,
+ pageno + PAGE_SIZE, VM_PROT_ALL, 0);
+ if (error)
+ break;
+ }
+
+ /*
+ * Now we need to get the page. out_entry, out_prot, wired,
+ * and single_use aren't used. One would think the vm code
+ * would be a *bit* nicer... We use tmap because
+ * vm_map_lookup() can change the map argument.
+ */
+ tmap = map;
+ error = vm_map_lookup(&tmap, pageno,
+ writing ? VM_PROT_WRITE : VM_PROT_READ,
+ &out_entry, &object, &off, &out_prot,
+ &wired, &single_use);
+ /*
+ * We're done with tmap now.
+ */
+ if (!error)
+ vm_map_lookup_done(tmap, out_entry);
+
+ /*
+ * Fault the page in...
+ */
+ if (!error && writing && object->shadow) {
+ m = vm_page_lookup(object, off);
+ if (m == 0 || (m->flags & PG_COPYONWRITE))
+ error = vm_fault(map, pageno,
+ VM_PROT_WRITE, FALSE);
+ }
+
+ /* Find space in kernel_map for the page we're interested in */
+ if (!error) {
+ kva = VM_MIN_KERNEL_ADDRESS;
+ error = vm_map_find(kernel_map, object, off, &kva,
+ PAGE_SIZE, 1);
+ }
+
+ if (!error) {
+ /*
+ * Neither vm_map_lookup() nor vm_map_find() appear
+ * to add a reference count to the object, so we do
+ * that here and now.
+ */
+ vm_object_reference(object);
+
+ /*
+ * Mark the page we just found as pageable.
+ */
+ error = vm_map_pageable(kernel_map, kva,
+ kva + PAGE_SIZE, 0);
+
+ /*
+ * Now do the i/o move.
+ */
+ if (!error)
+ error = uiomove(kva + page_offset, len, uio);
+
+ vm_map_remove(kernel_map, kva, kva + PAGE_SIZE);
+ }
+ if (fix_prot)
+ vm_map_protect(map, pageno, pageno + PAGE_SIZE,
+ VM_PROT_READ|VM_PROT_EXECUTE, 0);
+ } while (error == 0 && uio->uio_resid > 0);
+
+ return (error);
+}
+
+/*
+ * Copy data in and out of the target process.
+ * We do this by mapping the process's page into
+ * the kernel and then doing a uiomove direct
+ * from the kernel address space.
+ */
+int
+procfs_domem(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ return (procfs_rwmem(p, uio));
+}
+
+/*
+ * Given process (p), find the vnode from which
+ * it's text segment is being executed.
+ *
+ * It would be nice to grab this information from
+ * the VM system, however, there is no sure-fire
+ * way of doing that. Instead, fork(), exec() and
+ * wait() all maintain the p_textvp field in the
+ * process proc structure which contains a held
+ * reference to the exec'ed vnode.
+ */
+struct vnode *
+procfs_findtextvp(p)
+ struct proc *p;
+{
+
+ return (p->p_textvp);
+}
+
+
+#ifdef probably_never
+/*
+ * Given process (p), find the vnode from which
+ * it's text segment is being mapped.
+ *
+ * (This is here, rather than in procfs_subr in order
+ * to keep all the VM related code in one place.)
+ */
+struct vnode *
+procfs_findtextvp(p)
+ struct proc *p;
+{
+ int error;
+ vm_object_t object;
+ vm_offset_t pageno; /* page number */
+
+ /* find a vnode pager for the user address space */
+
+ for (pageno = VM_MIN_ADDRESS;
+ pageno < VM_MAXUSER_ADDRESS;
+ pageno += PAGE_SIZE) {
+ vm_map_t map;
+ vm_map_entry_t out_entry;
+ vm_prot_t out_prot;
+ boolean_t wired, single_use;
+ vm_offset_t off;
+
+ map = &p->p_vmspace->vm_map;
+ error = vm_map_lookup(&map, pageno,
+ VM_PROT_READ,
+ &out_entry, &object, &off, &out_prot,
+ &wired, &single_use);
+
+ if (!error) {
+ vm_pager_t pager;
+
+ printf("procfs: found vm object\n");
+ vm_map_lookup_done(map, out_entry);
+ printf("procfs: vm object = %x\n", object);
+
+ /*
+ * At this point, assuming no errors, object
+ * is the VM object mapping UVA (pageno).
+ * Ensure it has a vnode pager, then grab
+ * the vnode from that pager's handle.
+ */
+
+ pager = object->pager;
+ printf("procfs: pager = %x\n", pager);
+ if (pager)
+ printf("procfs: found pager, type = %d\n", pager->pg_type);
+ if (pager && pager->pg_type == PG_VNODE) {
+ struct vnode *vp;
+
+ vp = (struct vnode *) pager->pg_handle;
+ printf("procfs: vp = 0x%x\n", vp);
+ return (vp);
+ }
+ }
+ }
+
+ printf("procfs: text object not found\n");
+ return (0);
+}
+#endif /* probably_never */
diff --git a/sys/miscfs/procfs/procfs_note.c b/sys/miscfs/procfs/procfs_note.c
new file mode 100644
index 00000000000..a57dda9ae56
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_note.c
@@ -0,0 +1,72 @@
+/* $NetBSD: procfs_note.c,v 1.8 1994/06/29 06:34:53 cgd Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs_note.c 8.2 (Berkeley) 1/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/signal.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_donote(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ int xlen;
+ int error;
+ char note[PROCFS_NOTELEN+1];
+
+ if (uio->uio_rw != UIO_WRITE)
+ return (EINVAL);
+
+ xlen = PROCFS_NOTELEN;
+ error = vfs_getuserstr(uio, note, &xlen);
+ if (error)
+ return (error);
+
+ /* send to process's notify function */
+ return (EOPNOTSUPP);
+}
diff --git a/sys/miscfs/procfs/procfs_regs.c b/sys/miscfs/procfs/procfs_regs.c
new file mode 100644
index 00000000000..5d6f8929787
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_regs.c
@@ -0,0 +1,107 @@
+/* $NetBSD: procfs_regs.c,v 1.9 1995/08/13 09:06:07 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs_regs.c 8.4 (Berkeley) 6/15/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ptrace.h>
+#include <machine/reg.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_doregs(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+#if defined(PT_GETREGS) || defined(PT_SETREGS)
+ int error;
+ struct reg r;
+ char *kv;
+ int kl;
+
+ kl = sizeof(r);
+ kv = (char *) &r;
+
+ kv += uio->uio_offset;
+ kl -= uio->uio_offset;
+ if (kl > uio->uio_resid)
+ kl = uio->uio_resid;
+
+ PHOLD(p);
+
+ if (kl < 0)
+ error = EINVAL;
+ else
+ error = process_read_regs(p, &r);
+ if (error == 0)
+ error = uiomove(kv, kl, uio);
+ if (error == 0 && uio->uio_rw == UIO_WRITE) {
+ if (p->p_stat != SSTOP)
+ error = EBUSY;
+ else
+ error = process_write_regs(p, &r);
+ }
+
+ PRELE(p);
+
+ uio->uio_offset = 0;
+ return (error);
+#else
+ return (EINVAL);
+#endif
+}
+
+int
+procfs_validregs(p)
+ struct proc *p;
+{
+
+#if defined(PT_SETREGS) || defined(PT_GETREGS)
+ return ((p->p_flag & P_SYSTEM) == 0);
+#else
+ return (0);
+#endif
+}
diff --git a/sys/miscfs/procfs/procfs_status.c b/sys/miscfs/procfs/procfs_status.c
new file mode 100644
index 00000000000..498052620dc
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_status.c
@@ -0,0 +1,145 @@
+/* $NetBSD: procfs_status.c,v 1.10 1995/06/01 22:44:28 jtc Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs_status.c 8.4 (Berkeley) 6/15/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/resource.h>
+#include <sys/resourcevar.h>
+#include <miscfs/procfs/procfs.h>
+
+int
+procfs_dostatus(curp, p, pfs, uio)
+ struct proc *curp;
+ struct proc *p;
+ struct pfsnode *pfs;
+ struct uio *uio;
+{
+ struct session *sess;
+ struct tty *tp;
+ struct ucred *cr;
+ char *ps;
+ char *sep;
+ int pid, ppid, pgid, sid;
+ int i;
+ int xlen;
+ int error;
+ char psbuf[256]; /* XXX - conservative */
+
+ if (uio->uio_rw != UIO_READ)
+ return (EOPNOTSUPP);
+
+ pid = p->p_pid;
+ ppid = p->p_pptr ? p->p_pptr->p_pid : 0,
+ pgid = p->p_pgrp->pg_id;
+ sess = p->p_pgrp->pg_session;
+ sid = sess->s_leader ? sess->s_leader->p_pid : 0;
+
+/* comm pid ppid pgid sid maj,min ctty,sldr start ut st wmsg uid gid groups ... */
+
+ ps = psbuf;
+ bcopy(p->p_comm, ps, MAXCOMLEN);
+ ps[MAXCOMLEN] = '\0';
+ ps += strlen(ps);
+ ps += sprintf(ps, " %d %d %d %d ", pid, ppid, pgid, sid);
+
+ if ((p->p_flag&P_CONTROLT) && (tp = sess->s_ttyp))
+ ps += sprintf(ps, "%d,%d ", major(tp->t_dev), minor(tp->t_dev));
+ else
+ ps += sprintf(ps, "%d,%d ", -1, -1);
+
+ sep = "";
+ if (sess->s_ttyvp) {
+ ps += sprintf(ps, "%sctty", sep);
+ sep = ",";
+ }
+ if (SESS_LEADER(p)) {
+ ps += sprintf(ps, "%ssldr", sep);
+ sep = ",";
+ }
+ if (*sep != ',')
+ ps += sprintf(ps, "noflags");
+
+ if (p->p_flag & P_INMEM)
+ ps += sprintf(ps, " %d,%d",
+ p->p_stats->p_start.tv_sec,
+ p->p_stats->p_start.tv_usec);
+ else
+ ps += sprintf(ps, " -1,-1");
+
+ {
+ struct timeval ut, st;
+
+ calcru(p, &ut, &st, (void *) 0);
+ ps += sprintf(ps, " %d,%d %d,%d",
+ ut.tv_sec,
+ ut.tv_usec,
+ st.tv_sec,
+ st.tv_usec);
+ }
+
+ ps += sprintf(ps, " %s",
+ (p->p_wchan && p->p_wmesg) ? p->p_wmesg : "nochan");
+
+ cr = p->p_ucred;
+
+ ps += sprintf(ps, " %d", cr->cr_uid);
+ ps += sprintf(ps, " %d", cr->cr_gid);
+ for (i = 0; i < cr->cr_ngroups; i++)
+ ps += sprintf(ps, ",%d", cr->cr_groups[i]);
+ ps += sprintf(ps, "\n");
+
+ xlen = ps - psbuf;
+ xlen -= uio->uio_offset;
+ ps = psbuf + uio->uio_offset;
+ xlen = imin(xlen, uio->uio_resid);
+ if (xlen <= 0)
+ error = 0;
+ else
+ error = uiomove(ps, xlen, uio);
+
+ return (error);
+}
diff --git a/sys/miscfs/procfs/procfs_subr.c b/sys/miscfs/procfs/procfs_subr.c
new file mode 100644
index 00000000000..1390a71c5ee
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_subr.c
@@ -0,0 +1,312 @@
+/* $NetBSD: procfs_subr.c,v 1.13 1994/06/29 06:34:57 cgd Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs_subr.c 8.5 (Berkeley) 6/15/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/malloc.h>
+#include <miscfs/procfs/procfs.h>
+
+static struct pfsnode *pfshead;
+static int pfsvplock;
+
+/*
+ * allocate a pfsnode/vnode pair. the vnode is
+ * referenced, but not locked.
+ *
+ * the pid, pfs_type, and mount point uniquely
+ * identify a pfsnode. the mount point is needed
+ * because someone might mount this filesystem
+ * twice.
+ *
+ * all pfsnodes are maintained on a singly-linked
+ * list. new nodes are only allocated when they cannot
+ * be found on this list. entries on the list are
+ * removed when the vfs reclaim entry is called.
+ *
+ * a single lock is kept for the entire list. this is
+ * needed because the getnewvnode() function can block
+ * waiting for a vnode to become free, in which case there
+ * may be more than one process trying to get the same
+ * vnode. this lock is only taken if we are going to
+ * call getnewvnode, since the kernel itself is single-threaded.
+ *
+ * if an entry is found on the list, then call vget() to
+ * take a reference. this is done because there may be
+ * zero references to it and so it needs to removed from
+ * the vnode free list.
+ */
+int
+procfs_allocvp(mp, vpp, pid, pfs_type)
+ struct mount *mp;
+ struct vnode **vpp;
+ long pid;
+ pfstype pfs_type;
+{
+ struct pfsnode *pfs;
+ struct vnode *vp;
+ struct pfsnode **pp;
+ int error;
+
+loop:
+ for (pfs = pfshead; pfs != 0; pfs = pfs->pfs_next) {
+ vp = PFSTOV(pfs);
+ if (pfs->pfs_pid == pid &&
+ pfs->pfs_type == pfs_type &&
+ vp->v_mount == mp) {
+ if (vget(vp, 0))
+ goto loop;
+ *vpp = vp;
+ return (0);
+ }
+ }
+
+ /*
+ * otherwise lock the vp list while we call getnewvnode
+ * since that can block.
+ */
+ if (pfsvplock & PROCFS_LOCKED) {
+ pfsvplock |= PROCFS_WANT;
+ sleep((caddr_t) &pfsvplock, PINOD);
+ goto loop;
+ }
+ pfsvplock |= PROCFS_LOCKED;
+
+ if (error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp))
+ goto out;
+ vp = *vpp;
+
+ MALLOC(pfs, void *, sizeof(struct pfsnode), M_TEMP, M_WAITOK);
+ vp->v_data = pfs;
+
+ pfs->pfs_next = 0;
+ pfs->pfs_pid = (pid_t) pid;
+ pfs->pfs_type = pfs_type;
+ pfs->pfs_vnode = vp;
+ pfs->pfs_flags = 0;
+ pfs->pfs_fileno = PROCFS_FILENO(pid, pfs_type);
+
+ switch (pfs_type) {
+ case Proot: /* /proc = dr-xr-xr-x */
+ pfs->pfs_mode = (VREAD|VEXEC) |
+ (VREAD|VEXEC) >> 3 |
+ (VREAD|VEXEC) >> 6;
+ vp->v_type = VDIR;
+ vp->v_flag = VROOT;
+ break;
+
+ case Pcurproc: /* /proc/curproc = lr--r--r-- */
+ pfs->pfs_mode = (VREAD) |
+ (VREAD >> 3) |
+ (VREAD >> 6);
+ vp->v_type = VLNK;
+ break;
+
+ case Pproc:
+ pfs->pfs_mode = (VREAD|VEXEC) |
+ (VREAD|VEXEC) >> 3 |
+ (VREAD|VEXEC) >> 6;
+ vp->v_type = VDIR;
+ break;
+
+ case Pfile:
+ case Pmem:
+ case Pregs:
+ case Pfpregs:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ vp->v_type = VREG;
+ break;
+
+ case Pctl:
+ case Pnote:
+ case Pnotepg:
+ pfs->pfs_mode = (VWRITE);
+ vp->v_type = VREG;
+ break;
+
+ case Pstatus:
+ pfs->pfs_mode = (VREAD) |
+ (VREAD >> 3) |
+ (VREAD >> 6);
+ vp->v_type = VREG;
+ break;
+
+ default:
+ panic("procfs_allocvp");
+ }
+
+ /* add to procfs vnode list */
+ for (pp = &pfshead; *pp; pp = &(*pp)->pfs_next)
+ continue;
+ *pp = pfs;
+
+out:
+ pfsvplock &= ~PROCFS_LOCKED;
+
+ if (pfsvplock & PROCFS_WANT) {
+ pfsvplock &= ~PROCFS_WANT;
+ wakeup((caddr_t) &pfsvplock);
+ }
+
+ return (error);
+}
+
+int
+procfs_freevp(vp)
+ struct vnode *vp;
+{
+ struct pfsnode **pfspp;
+ struct pfsnode *pfs = VTOPFS(vp);
+
+ for (pfspp = &pfshead; *pfspp != 0; pfspp = &(*pfspp)->pfs_next) {
+ if (*pfspp == pfs) {
+ *pfspp = pfs->pfs_next;
+ break;
+ }
+ }
+
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+ return (0);
+}
+
+int
+procfs_rw(ap)
+ struct vop_read_args *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct uio *uio = ap->a_uio;
+ struct proc *curp = uio->uio_procp;
+ struct pfsnode *pfs = VTOPFS(vp);
+ struct proc *p;
+
+ p = PFIND(pfs->pfs_pid);
+ if (p == 0)
+ return (EINVAL);
+
+ switch (pfs->pfs_type) {
+ case Pnote:
+ case Pnotepg:
+ return (procfs_donote(curp, p, pfs, uio));
+
+ case Pregs:
+ return (procfs_doregs(curp, p, pfs, uio));
+
+ case Pfpregs:
+ return (procfs_dofpregs(curp, p, pfs, uio));
+
+ case Pctl:
+ return (procfs_doctl(curp, p, pfs, uio));
+
+ case Pstatus:
+ return (procfs_dostatus(curp, p, pfs, uio));
+
+ case Pmem:
+ return (procfs_domem(curp, p, pfs, uio));
+
+ default:
+ return (EOPNOTSUPP);
+ }
+}
+
+/*
+ * Get a string from userland into (buf). Strip a trailing
+ * nl character (to allow easy access from the shell).
+ * The buffer should be *buflenp + 1 chars long. vfs_getuserstr
+ * will automatically add a nul char at the end.
+ *
+ * Returns 0 on success or the following errors
+ *
+ * EINVAL: file offset is non-zero.
+ * EMSGSIZE: message is longer than kernel buffer
+ * EFAULT: user i/o buffer is not addressable
+ */
+int
+vfs_getuserstr(uio, buf, buflenp)
+ struct uio *uio;
+ char *buf;
+ int *buflenp;
+{
+ int xlen;
+ int error;
+
+ if (uio->uio_offset != 0)
+ return (EINVAL);
+
+ xlen = *buflenp;
+
+ /* must be able to read the whole string in one go */
+ if (xlen < uio->uio_resid)
+ return (EMSGSIZE);
+ xlen = uio->uio_resid;
+
+ if (error = uiomove(buf, xlen, uio))
+ return (error);
+
+ /* allow multiple writes without seeks */
+ uio->uio_offset = 0;
+
+ /* cleanup string and remove trailing newline */
+ buf[xlen] = '\0';
+ xlen = strlen(buf);
+ if (xlen > 0 && buf[xlen-1] == '\n')
+ buf[--xlen] = '\0';
+ *buflenp = xlen;
+
+ return (0);
+}
+
+vfs_namemap_t *
+vfs_findname(nm, buf, buflen)
+ vfs_namemap_t *nm;
+ char *buf;
+ int buflen;
+{
+
+ for (; nm->nm_name; nm++)
+ if (bcmp(buf, (char *) nm->nm_name, buflen+1) == 0)
+ return (nm);
+
+ return (0);
+}
diff --git a/sys/miscfs/procfs/procfs_vfsops.c b/sys/miscfs/procfs/procfs_vfsops.c
new file mode 100644
index 00000000000..d20bf8c8a53
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_vfsops.c
@@ -0,0 +1,229 @@
+/* $NetBSD: procfs_vfsops.c,v 1.24 1995/06/18 14:47:39 cgd Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs_vfsops.c 8.5 (Berkeley) 6/15/94
+ */
+
+/*
+ * procfs VFS interface
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/buf.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+#include <sys/signalvar.h>
+#include <sys/vnode.h>
+#include <miscfs/procfs/procfs.h>
+#include <vm/vm.h> /* for PAGE_SIZE */
+
+/*
+ * VFS Operations.
+ *
+ * mount system call
+ */
+/* ARGSUSED */
+procfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ size_t size;
+
+ if (UIO_MX & (UIO_MX-1)) {
+ log(LOG_ERR, "procfs: invalid directory entry size");
+ return (EINVAL);
+ }
+
+ if (mp->mnt_flag & MNT_UPDATE)
+ return (EOPNOTSUPP);
+
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = 0;
+ getnewfsid(mp, makefstype(MOUNT_PROCFS));
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("procfs", mp->mnt_stat.f_mntfromname, sizeof("procfs"));
+ return (0);
+}
+
+/*
+ * unmount system call
+ */
+procfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ int error;
+ extern int doforce;
+ int flags = 0;
+
+ if (mntflags & MNT_FORCE) {
+ /* procfs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ if (error = vflush(mp, 0, flags))
+ return (error);
+
+ return (0);
+}
+
+procfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+
+ return (procfs_allocvp(mp, vpp, 0, Proot));
+}
+
+/* ARGSUSED */
+procfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Get file system statistics.
+ */
+procfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+
+#ifdef COMPAT_09
+ sbp->f_type = 10;
+#else
+ sbp->f_type = 0;
+#endif
+ sbp->f_bsize = PAGE_SIZE;
+ sbp->f_iosize = PAGE_SIZE;
+ sbp->f_blocks = 1; /* avoid divide by zero in some df's */
+ sbp->f_bfree = 0;
+ sbp->f_bavail = 0;
+ sbp->f_files = maxproc; /* approx */
+ sbp->f_ffree = maxproc - nprocs; /* approx */
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ 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);
+}
+
+procfs_quotactl(mp, cmds, uid, arg, p)
+ struct mount *mp;
+ int cmds;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+procfs_sync(mp, waitfor)
+ struct mount *mp;
+ int waitfor;
+{
+
+ return (0);
+}
+
+procfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+procfs_fhtovp(mp, fhp, vpp)
+ struct mount *mp;
+ struct fid *fhp;
+ struct vnode **vpp;
+{
+
+ return (EINVAL);
+}
+
+procfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EINVAL);
+}
+
+procfs_init()
+{
+
+ return (0);
+}
+
+struct vfsops procfs_vfsops = {
+ MOUNT_PROCFS,
+ procfs_mount,
+ procfs_start,
+ procfs_unmount,
+ procfs_root,
+ procfs_quotactl,
+ procfs_statfs,
+ procfs_sync,
+ procfs_vget,
+ procfs_fhtovp,
+ procfs_vptofh,
+ procfs_init,
+};
diff --git a/sys/miscfs/procfs/procfs_vnops.c b/sys/miscfs/procfs/procfs_vnops.c
new file mode 100644
index 00000000000..84aea9e2a61
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_vnops.c
@@ -0,0 +1,884 @@
+/* $NetBSD: procfs_vnops.c,v 1.35 1995/10/09 14:03:38 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1993 Jan-Simon Pendry
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)procfs_vnops.c 8.8 (Berkeley) 6/15/94
+ */
+
+/*
+ * procfs vnode interface
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/file.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/dirent.h>
+#include <sys/resourcevar.h>
+#include <sys/ptrace.h>
+#include <vm/vm.h> /* for PAGE_SIZE */
+#include <machine/reg.h>
+#include <miscfs/procfs/procfs.h>
+
+/*
+ * Vnode Operations.
+ *
+ */
+
+/*
+ * This is a list of the valid names in the
+ * process-specific sub-directories. It is
+ * used in procfs_lookup and procfs_readdir
+ */
+struct proc_target {
+ u_char pt_type;
+ u_char pt_namlen;
+ char *pt_name;
+ pfstype pt_pfstype;
+ int (*pt_valid) __P((struct proc *p));
+} proc_targets[] = {
+#define N(s) sizeof(s)-1, s
+ /* name type validp */
+ { DT_DIR, N("."), Pproc, NULL },
+ { DT_DIR, N(".."), Proot, NULL },
+ { DT_REG, N("file"), Pfile, procfs_validfile },
+ { DT_REG, N("mem"), Pmem, NULL },
+ { DT_REG, N("regs"), Pregs, procfs_validregs },
+ { DT_REG, N("fpregs"), Pfpregs, procfs_validfpregs },
+ { DT_REG, N("ctl"), Pctl, NULL },
+ { DT_REG, N("status"), Pstatus, NULL },
+ { DT_REG, N("note"), Pnote, NULL },
+ { DT_REG, N("notepg"), Pnotepg, NULL },
+#undef N
+};
+static int nproc_targets = sizeof(proc_targets) / sizeof(proc_targets[0]);
+
+static pid_t atopid __P((const char *, u_int));
+
+/*
+ * set things up for doing i/o on
+ * the pfsnode (vp). (vp) is locked
+ * on entry, and should be left locked
+ * on exit.
+ *
+ * for procfs we don't need to do anything
+ * in particular for i/o. all that is done
+ * is to support exclusive open on process
+ * memory images.
+ */
+procfs_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ switch (pfs->pfs_type) {
+ case Pmem:
+ if (PFIND(pfs->pfs_pid) == 0)
+ return (ENOENT); /* was ESRCH, jsp */
+
+ if ((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL) ||
+ (pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))
+ return (EBUSY);
+
+ if (ap->a_mode & FWRITE)
+ pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL);
+
+ return (0);
+
+ default:
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * close the pfsnode (vp) after doing i/o.
+ * (vp) is not locked on entry or exit.
+ *
+ * nothing to do for procfs other than undo
+ * any exclusive open flag (see _open above).
+ */
+procfs_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ switch (pfs->pfs_type) {
+ case Pmem:
+ if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL))
+ pfs->pfs_flags &= ~(FWRITE|O_EXCL);
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * do an ioctl operation on pfsnode (vp).
+ * (vp) is not locked on entry or exit.
+ */
+procfs_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ u_long a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (ENOTTY);
+}
+
+/*
+ * do block mapping for pfsnode (vp).
+ * since we don't use the buffer cache
+ * for procfs this function should never
+ * be called. in any case, it's not clear
+ * what part of the kernel ever makes use
+ * of this function. for sanity, this is the
+ * usual no-op bmap, although returning
+ * (EIO) would be a reasonable alternative.
+ */
+procfs_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ } */ *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * _inactive is called when the pfsnode
+ * is vrele'd and the reference count goes
+ * to zero. (vp) will be on the vnode free
+ * list, so to get it back vget() must be
+ * used.
+ *
+ * for procfs, check if the process is still
+ * alive and if it isn't then just throw away
+ * the vnode by calling vgone(). this may
+ * be overkill and a waste of time since the
+ * chances are that the process will still be
+ * there and PFIND is not free.
+ *
+ * (vp) is not locked on entry or exit.
+ */
+procfs_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ if (PFIND(pfs->pfs_pid) == 0)
+ vgone(ap->a_vp);
+
+ return (0);
+}
+
+/*
+ * _reclaim is called when getnewvnode()
+ * wants to make use of an entry on the vnode
+ * free list. at this time the filesystem needs
+ * to free any private data and remove the node
+ * from any private lists.
+ */
+procfs_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (procfs_freevp(ap->a_vp));
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+procfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ register_t *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * _print is used for debugging.
+ * just print a readable description
+ * of (vp).
+ */
+procfs_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ printf("tag VT_PROCFS, type %s, pid %d, mode %x, flags %x\n",
+ pfs->pfs_type, pfs->pfs_pid, pfs->pfs_mode, pfs->pfs_flags);
+}
+
+/*
+ * _abortop is called when operations such as
+ * rename and create fail. this entry is responsible
+ * for undoing any side-effects caused by the lookup.
+ * this will always include freeing the pathname buffer.
+ */
+procfs_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+
+ if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
+ FREE(ap->a_cnp->cn_pnbuf, M_NAMEI);
+ return (0);
+}
+
+/*
+ * generic entry point for unsupported operations
+ */
+procfs_badop()
+{
+
+ return (EIO);
+}
+
+/*
+ * Invent attributes for pfsnode (vp) and store
+ * them in (vap).
+ * Directories lengths are returned as zero since
+ * any real length would require the genuine size
+ * to be computed, and nothing cares anyway.
+ *
+ * this is relatively minimal for procfs.
+ */
+procfs_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct proc *procp;
+ struct timeval tv;
+ int error;
+
+ /* first check the process still exists */
+ switch (pfs->pfs_type) {
+ case Proot:
+ case Pcurproc:
+ procp = 0;
+ break;
+
+ default:
+ procp = PFIND(pfs->pfs_pid);
+ if (procp == 0)
+ return (ENOENT);
+ }
+
+ error = 0;
+
+ /* start by zeroing out the attributes */
+ VATTR_NULL(vap);
+
+ /* next do all the common fields */
+ vap->va_type = ap->a_vp->v_type;
+ vap->va_mode = pfs->pfs_mode;
+ vap->va_fileid = pfs->pfs_fileno;
+ vap->va_flags = 0;
+ vap->va_blocksize = PAGE_SIZE;
+ vap->va_bytes = vap->va_size = 0;
+
+ /*
+ * Make all times be current TOD.
+ * It would be possible to get the process start
+ * time from the p_stat structure, but there's
+ * no "file creation" time stamp anyway, and the
+ * p_stat structure is not addressible if u. gets
+ * swapped out for that process.
+ */
+ microtime(&tv);
+ TIMEVAL_TO_TIMESPEC(&tv, &vap->va_ctime);
+ vap->va_atime = vap->va_mtime = vap->va_ctime;
+
+ /*
+ * If the process has exercised some setuid or setgid
+ * privilege, then rip away read/write permission so
+ * that only root can gain access.
+ */
+ switch (pfs->pfs_type) {
+ case Pmem:
+ case Pregs:
+ case Pfpregs:
+ if (procp->p_flag & P_SUGID)
+ vap->va_mode &= ~((VREAD|VWRITE)|
+ ((VREAD|VWRITE)>>3)|
+ ((VREAD|VWRITE)>>6));
+ case Pctl:
+ case Pstatus:
+ case Pnote:
+ case Pnotepg:
+ vap->va_nlink = 1;
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+ }
+
+ /*
+ * now do the object specific fields
+ *
+ * The size could be set from struct reg, but it's hardly
+ * worth the trouble, and it puts some (potentially) machine
+ * dependent data into this machine-independent code. If it
+ * becomes important then this function should break out into
+ * a per-file stat function in the corresponding .c file.
+ */
+
+ switch (pfs->pfs_type) {
+ case Proot:
+ /*
+ * Set nlink to 1 to tell fts(3) we don't actually know.
+ */
+ vap->va_nlink = 1;
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_size = vap->va_bytes = DEV_BSIZE;
+ break;
+
+ case Pcurproc: {
+ char buf[16]; /* should be enough */
+ vap->va_nlink = 1;
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ vap->va_size = vap->va_bytes =
+ sprintf(buf, "%ld", (long)curproc->p_pid);
+ break;
+ }
+
+ case Pproc:
+ vap->va_nlink = 2;
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ vap->va_size = vap->va_bytes = DEV_BSIZE;
+ break;
+
+ case Pfile:
+ error = EOPNOTSUPP;
+ break;
+
+ case Pmem:
+ vap->va_bytes = vap->va_size =
+ ctob(procp->p_vmspace->vm_tsize +
+ procp->p_vmspace->vm_dsize +
+ procp->p_vmspace->vm_ssize);
+ break;
+
+#if defined(PT_GETREGS) || defined(PT_SETREGS)
+ case Pregs:
+ vap->va_bytes = vap->va_size = sizeof(struct reg);
+ break;
+#endif
+
+#if defined(PT_GETFPREGS) || defined(PT_SETFPREGS)
+ case Pfpregs:
+ vap->va_bytes = vap->va_size = sizeof(struct fpreg);
+ break;
+#endif
+
+ case Pctl:
+ case Pstatus:
+ case Pnote:
+ case Pnotepg:
+ break;
+
+ default:
+ panic("procfs_getattr");
+ }
+
+ return (error);
+}
+
+procfs_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ /*
+ * just fake out attribute setting
+ * it's not good to generate an error
+ * return, otherwise things like creat()
+ * will fail when they try to set the
+ * file length to 0. worse, this means
+ * that echo $note > /proc/$pid/note will fail.
+ */
+
+ return (0);
+}
+
+/*
+ * implement access checking.
+ *
+ * actually, the check for super-user is slightly
+ * broken since it will allow read access to write-only
+ * objects. this doesn't cause any particular trouble
+ * but does mean that the i/o entry points need to check
+ * that the operation really does make sense.
+ */
+procfs_access(ap)
+ struct vop_access_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vattr va;
+ int error;
+
+ if (error = VOP_GETATTR(ap->a_vp, &va, ap->a_cred, ap->a_p))
+ return (error);
+
+ return (vaccess(va.va_mode, va.va_uid, va.va_gid, ap->a_mode,
+ ap->a_cred));
+}
+
+/*
+ * lookup. this is incredibly complicated in the
+ * general case, however for most pseudo-filesystems
+ * very little needs to be done.
+ *
+ * unless you want to get a migraine, just make sure your
+ * filesystem doesn't do any locking of its own. otherwise
+ * read and inwardly digest ufs_lookup().
+ */
+procfs_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode * a_dvp;
+ struct vnode ** a_vpp;
+ struct componentname * a_cnp;
+ } */ *ap;
+{
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname = cnp->cn_nameptr;
+ struct proc_target *pt;
+ struct vnode *fvp;
+ pid_t pid;
+ struct pfsnode *pfs;
+ struct proc *p;
+ int i;
+
+ *vpp = NULL;
+
+ if (cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME)
+ return (EROFS);
+
+ if (cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ /*VOP_LOCK(dvp);*/
+ return (0);
+ }
+
+ pfs = VTOPFS(dvp);
+ switch (pfs->pfs_type) {
+ case Proot:
+ if (cnp->cn_flags & ISDOTDOT)
+ return (EIO);
+
+ if (CNEQ(cnp, "curproc", 7))
+ return (procfs_allocvp(dvp->v_mount, vpp, 0, Pcurproc));
+
+ pid = atopid(pname, cnp->cn_namelen);
+ if (pid == NO_PID)
+ break;
+
+ p = PFIND(pid);
+ if (p == 0)
+ break;
+
+ return (procfs_allocvp(dvp->v_mount, vpp, pid, Pproc));
+
+ case Pproc:
+ if (cnp->cn_flags & ISDOTDOT)
+ return (procfs_root(dvp->v_mount, vpp));
+
+ p = PFIND(pfs->pfs_pid);
+ if (p == 0)
+ break;
+
+ for (pt = proc_targets, i = 0; i < nproc_targets; pt++, i++) {
+ if (cnp->cn_namelen == pt->pt_namlen &&
+ bcmp(pt->pt_name, pname, cnp->cn_namelen) == 0 &&
+ (pt->pt_valid == NULL || (*pt->pt_valid)(p)))
+ goto found;
+ }
+ break;
+
+ found:
+ if (pt->pt_pfstype == Pfile) {
+ fvp = procfs_findtextvp(p);
+ /* We already checked that it exists. */
+ VREF(fvp);
+ VOP_LOCK(fvp);
+ *vpp = fvp;
+ return (0);
+ }
+
+ return (procfs_allocvp(dvp->v_mount, vpp, pfs->pfs_pid,
+ pt->pt_pfstype));
+
+ default:
+ return (ENOTDIR);
+ }
+
+ return (cnp->cn_nameiop == LOOKUP ? ENOENT : EROFS);
+}
+
+int
+procfs_validfile(p)
+ struct proc *p;
+{
+
+ return (procfs_findtextvp(p) != NULLVP);
+}
+
+/*
+ * readdir returns directory entries from pfsnode (vp).
+ *
+ * the strategy here with procfs is to generate a single
+ * directory entry at a time (struct dirent) and then
+ * copy that out to userland using uiomove. a more efficent
+ * though more complex implementation, would try to minimize
+ * the number of calls to uiomove(). for procfs, this is
+ * hardly worth the added code complexity.
+ *
+ * this should just be done through read()
+ */
+procfs_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ u_long *a_cookies;
+ int a_ncookies;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct dirent d;
+ struct pfsnode *pfs;
+ int i;
+ int error;
+ u_long *cookies = ap->a_cookies;
+ int ncookies = ap->a_ncookies;
+
+ pfs = VTOPFS(ap->a_vp);
+
+ if (uio->uio_resid < UIO_MX)
+ return (EINVAL);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ error = 0;
+ i = uio->uio_offset;
+ bzero((caddr_t)&d, UIO_MX);
+ d.d_reclen = UIO_MX;
+
+ switch (pfs->pfs_type) {
+ /*
+ * this is for the process-specific sub-directories.
+ * all that is needed to is copy out all the entries
+ * from the procent[] table (top of this file).
+ */
+ case Pproc: {
+ struct proc *p;
+ struct proc_target *pt;
+
+ p = PFIND(pfs->pfs_pid);
+ if (p == NULL)
+ break;
+
+ for (pt = &proc_targets[i];
+ uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) {
+ if (pt->pt_valid && (*pt->pt_valid)(p) == 0)
+ continue;
+
+ d.d_fileno = PROCFS_FILENO(pfs->pfs_pid, pt->pt_pfstype);
+ d.d_namlen = pt->pt_namlen;
+ bcopy(pt->pt_name, d.d_name, pt->pt_namlen + 1);
+ d.d_type = pt->pt_type;
+
+ if (error = uiomove((caddr_t)&d, UIO_MX, uio))
+ break;
+ if (ncookies-- > 0)
+ *cookies++ = i + 1;
+ }
+
+ break;
+ }
+
+ /*
+ * this is for the root of the procfs filesystem
+ * what is needed is a special entry for "curproc"
+ * followed by an entry for each process on allproc
+#ifdef PROCFS_ZOMBIE
+ * and zombproc.
+#endif
+ */
+
+ case Proot: {
+#ifdef PROCFS_ZOMBIE
+ int doingzomb = 0;
+#endif
+ int pcnt = i;
+ volatile struct proc *p = allproc.lh_first;
+
+ if (pcnt > 3)
+ pcnt = 3;
+ again:
+ for (; p && uio->uio_resid >= UIO_MX; i++, pcnt++) {
+ switch (i) {
+ case 0: /* `.' */
+ case 1: /* `..' */
+ d.d_fileno = PROCFS_FILENO(0, Proot);
+ d.d_namlen = i + 1;
+ bcopy("..", d.d_name, d.d_namlen);
+ d.d_name[i + 1] = '\0';
+ d.d_type = DT_DIR;
+ break;
+
+ case 2:
+ d.d_fileno = PROCFS_FILENO(0, Pcurproc);
+ d.d_namlen = 7;
+ bcopy("curproc", d.d_name, 8);
+ d.d_type = DT_LNK;
+ break;
+
+ default:
+ while (pcnt < i) {
+ pcnt++;
+ p = p->p_list.le_next;
+ if (!p)
+ goto done;
+ }
+ d.d_fileno = PROCFS_FILENO(p->p_pid, Pproc);
+ d.d_namlen = sprintf(d.d_name, "%ld",
+ (long)p->p_pid);
+ d.d_type = DT_REG;
+ p = p->p_list.le_next;
+ break;
+ }
+
+ if (error = uiomove((caddr_t)&d, UIO_MX, uio))
+ break;
+ if (ncookies-- > 0)
+ *cookies++ = i + 1;
+ }
+ done:
+
+#ifdef PROCFS_ZOMBIE
+ if (p == 0 && doingzomb == 0) {
+ doingzomb = 1;
+ p = zombproc.lh_first;
+ goto again;
+ }
+#endif
+
+ break;
+
+ }
+
+ default:
+ error = ENOTDIR;
+ break;
+ }
+
+ uio->uio_offset = i;
+ return (error);
+}
+
+/*
+ * readlink reads the link of `curproc'
+ */
+procfs_readlink(ap)
+ struct vop_readlink_args *ap;
+{
+ struct uio *uio = ap->a_uio;
+ char buf[16]; /* should be enough */
+ int len;
+
+ if (VTOPFS(ap->a_vp)->pfs_fileno != PROCFS_FILENO(0, Pcurproc))
+ return (EINVAL);
+
+ len = sprintf(buf, "%ld", (long)curproc->p_pid);
+
+ return (uiomove((caddr_t)buf, len, ap->a_uio));
+}
+
+/*
+ * convert decimal ascii to pid_t
+ */
+static pid_t
+atopid(b, len)
+ const char *b;
+ u_int len;
+{
+ pid_t p = 0;
+
+ while (len--) {
+ char c = *b++;
+ if (c < '0' || c > '9')
+ return (NO_PID);
+ p = 10 * p + (c - '0');
+ if (p > PID_MAX)
+ return (NO_PID);
+ }
+
+ return (p);
+}
+
+/*
+ * procfs vnode operations.
+ */
+int (**procfs_vnodeop_p)();
+struct vnodeopv_entry_desc procfs_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, procfs_lookup }, /* lookup */
+ { &vop_create_desc, procfs_create }, /* create */
+ { &vop_mknod_desc, procfs_mknod }, /* mknod */
+ { &vop_open_desc, procfs_open }, /* open */
+ { &vop_close_desc, procfs_close }, /* close */
+ { &vop_access_desc, procfs_access }, /* access */
+ { &vop_getattr_desc, procfs_getattr }, /* getattr */
+ { &vop_setattr_desc, procfs_setattr }, /* setattr */
+ { &vop_read_desc, procfs_read }, /* read */
+ { &vop_write_desc, procfs_write }, /* write */
+ { &vop_ioctl_desc, procfs_ioctl }, /* ioctl */
+ { &vop_select_desc, procfs_select }, /* select */
+ { &vop_mmap_desc, procfs_mmap }, /* mmap */
+ { &vop_fsync_desc, procfs_fsync }, /* fsync */
+ { &vop_seek_desc, procfs_seek }, /* seek */
+ { &vop_remove_desc, procfs_remove }, /* remove */
+ { &vop_link_desc, procfs_link }, /* link */
+ { &vop_rename_desc, procfs_rename }, /* rename */
+ { &vop_mkdir_desc, procfs_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, procfs_rmdir }, /* rmdir */
+ { &vop_symlink_desc, procfs_symlink }, /* symlink */
+ { &vop_readdir_desc, procfs_readdir }, /* readdir */
+ { &vop_readlink_desc, procfs_readlink }, /* readlink */
+ { &vop_abortop_desc, procfs_abortop }, /* abortop */
+ { &vop_inactive_desc, procfs_inactive }, /* inactive */
+ { &vop_reclaim_desc, procfs_reclaim }, /* reclaim */
+ { &vop_lock_desc, procfs_lock }, /* lock */
+ { &vop_unlock_desc, procfs_unlock }, /* unlock */
+ { &vop_bmap_desc, procfs_bmap }, /* bmap */
+ { &vop_strategy_desc, procfs_strategy }, /* strategy */
+ { &vop_print_desc, procfs_print }, /* print */
+ { &vop_islocked_desc, procfs_islocked }, /* islocked */
+ { &vop_pathconf_desc, procfs_pathconf }, /* pathconf */
+ { &vop_advlock_desc, procfs_advlock }, /* advlock */
+ { &vop_blkatoff_desc, procfs_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, procfs_valloc }, /* valloc */
+ { &vop_vfree_desc, procfs_vfree }, /* vfree */
+ { &vop_truncate_desc, procfs_truncate }, /* truncate */
+ { &vop_update_desc, procfs_update }, /* update */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc procfs_vnodeop_opv_desc =
+ { &procfs_vnodeop_p, procfs_vnodeop_entries };
diff --git a/sys/miscfs/specfs/spec_vnops.c b/sys/miscfs/specfs/spec_vnops.c
new file mode 100644
index 00000000000..c67734bfea0
--- /dev/null
+++ b/sys/miscfs/specfs/spec_vnops.c
@@ -0,0 +1,702 @@
+/* $NetBSD: spec_vnops.c,v 1.26.2.1 1995/10/15 05:19:55 mycroft Exp $ */
+
+/*
+ * Copyright (c) 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.
+ *
+ * @(#)spec_vnops.c 8.8 (Berkeley) 11/21/94
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/buf.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/vnode.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <miscfs/specfs/specdev.h>
+
+/* symbolic sleep message strings for devices */
+char devopn[] = "devopn";
+char devio[] = "devio";
+char devwait[] = "devwait";
+char devin[] = "devin";
+char devout[] = "devout";
+char devioc[] = "devioc";
+char devcls[] = "devcls";
+
+int (**spec_vnodeop_p)();
+struct vnodeopv_entry_desc spec_vnodeop_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, spec_close }, /* close */
+ { &vop_access_desc, spec_access }, /* access */
+ { &vop_getattr_desc, spec_getattr }, /* getattr */
+ { &vop_setattr_desc, spec_setattr }, /* setattr */
+ { &vop_read_desc, spec_read }, /* read */
+ { &vop_write_desc, spec_write }, /* write */
+ { &vop_lease_desc, spec_lease_check }, /* lease */
+ { &vop_ioctl_desc, spec_ioctl }, /* ioctl */
+ { &vop_select_desc, spec_select }, /* select */
+ { &vop_mmap_desc, spec_mmap }, /* mmap */
+ { &vop_fsync_desc, spec_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, spec_inactive }, /* inactive */
+ { &vop_reclaim_desc, spec_reclaim }, /* reclaim */
+ { &vop_lock_desc, spec_lock }, /* lock */
+ { &vop_unlock_desc, spec_unlock }, /* unlock */
+ { &vop_bmap_desc, spec_bmap }, /* bmap */
+ { &vop_strategy_desc, spec_strategy }, /* strategy */
+ { &vop_print_desc, spec_print }, /* print */
+ { &vop_islocked_desc, spec_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, spec_vfree }, /* vfree */
+ { &vop_truncate_desc, spec_truncate }, /* truncate */
+ { &vop_update_desc, spec_update }, /* update */
+ { &vop_bwrite_desc, vn_bwrite }, /* bwrite */
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc spec_vnodeop_opv_desc =
+ { &spec_vnodeop_p, spec_vnodeop_entries };
+
+/*
+ * Trivial lookup routine that always fails.
+ */
+int
+spec_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+
+ *ap->a_vpp = NULL;
+ return (ENOTDIR);
+}
+
+/*
+ * Open a special file.
+ */
+/* ARGSUSED */
+spec_open(ap)
+ struct vop_open_args /* {
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct vnode *bvp, *vp = ap->a_vp;
+ dev_t bdev, dev = (dev_t)vp->v_rdev;
+ register int maj = major(dev);
+ int error;
+
+ /*
+ * Don't allow open if fs is mounted -nodev.
+ */
+ if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV))
+ return (ENXIO);
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ if ((u_int)maj >= nchrdev)
+ return (ENXIO);
+ if (ap->a_cred != FSCRED && (ap->a_mode & FWRITE)) {
+ /*
+ * When running in very secure mode, do not allow
+ * opens for writing of any disk character devices.
+ */
+ if (securelevel >= 2 && cdevsw[maj].d_type == D_DISK)
+ return (EPERM);
+ /*
+ * When running in secure mode, do not allow opens
+ * for writing of /dev/mem, /dev/kmem, or character
+ * devices whose corresponding block devices are
+ * currently mounted.
+ */
+ if (securelevel >= 1) {
+ if ((bdev = chrtoblk(dev)) != NODEV &&
+ vfinddev(bdev, VBLK, &bvp) &&
+ bvp->v_usecount > 0 &&
+ (error = vfs_mountedon(bvp)))
+ return (error);
+ if (iskmemdev(dev))
+ return (EPERM);
+ }
+ }
+ if (cdevsw[maj].d_type == D_TTY)
+ vp->v_flag |= VISTTY;
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, ap->a_p);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if ((u_int)maj >= nblkdev)
+ return (ENXIO);
+ /*
+ * When running in very secure mode, do not allow
+ * opens for writing of any disk block devices.
+ */
+ if (securelevel >= 2 && ap->a_cred != FSCRED &&
+ (ap->a_mode & FWRITE) && bdevsw[maj].d_type == D_DISK)
+ return (EPERM);
+ /*
+ * Do not allow opens of block devices that are
+ * currently mounted.
+ */
+ if (error = vfs_mountedon(vp))
+ return (error);
+ return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, ap->a_p));
+ }
+ return (0);
+}
+
+/*
+ * Vnode op for read
+ */
+/* ARGSUSED */
+spec_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ struct buf *bp;
+ daddr_t bn, nextbn;
+ long bsize, bscale, ssize;
+ struct partinfo dpart;
+ int n, on, majordev, (*ioctl)();
+ int error = 0;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_READ)
+ panic("spec_read mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("spec_read proc");
+#endif
+ if (uio->uio_resid == 0)
+ return (0);
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[major(vp->v_rdev)].d_read)
+ (vp->v_rdev, uio, ap->a_ioflag);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ bsize = BLKDEV_IOSIZE;
+ ssize = DEV_BSIZE;
+ if ((majordev = major(vp->v_rdev)) < nblkdev &&
+ (ioctl = bdevsw[majordev].d_ioctl) != NULL &&
+ (*ioctl)(vp->v_rdev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0) {
+ if (dpart.part->p_fstype == FS_BSDFFS &&
+ dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
+ bsize = dpart.part->p_frag *
+ dpart.part->p_fsize;
+ if (dpart.disklab->d_secsize != 0)
+ ssize = dpart.disklab->d_secsize;
+ }
+ bscale = bsize / ssize;
+ do {
+ bn = (uio->uio_offset / ssize) &~ (bscale - 1);
+ on = uio->uio_offset % bsize;
+ n = min((unsigned)(bsize - on), uio->uio_resid);
+ if (vp->v_lastr + bscale == bn) {
+ nextbn = bn + bscale;
+ error = breadn(vp, bn, (int)bsize, &nextbn,
+ (int *)&bsize, 1, NOCRED, &bp);
+ } else
+ error = bread(vp, bn, (int)bsize, NOCRED, &bp);
+ vp->v_lastr = bn;
+ n = min(n, bsize - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ error = uiomove((char *)bp->b_data + on, n, uio);
+ brelse(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+
+ default:
+ panic("spec_read type");
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Vnode op for write
+ */
+/* ARGSUSED */
+spec_write(ap)
+ struct vop_write_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct uio *uio = ap->a_uio;
+ struct proc *p = uio->uio_procp;
+ struct buf *bp;
+ daddr_t bn;
+ long bsize, bscale, ssize;
+ struct partinfo dpart;
+ int n, on, majordev, (*ioctl)();
+ int error = 0;
+
+#ifdef DIAGNOSTIC
+ if (uio->uio_rw != UIO_WRITE)
+ panic("spec_write mode");
+ if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
+ panic("spec_write proc");
+#endif
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ VOP_UNLOCK(vp);
+ error = (*cdevsw[major(vp->v_rdev)].d_write)
+ (vp->v_rdev, uio, ap->a_ioflag);
+ VOP_LOCK(vp);
+ return (error);
+
+ case VBLK:
+ if (uio->uio_resid == 0)
+ return (0);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+ bsize = BLKDEV_IOSIZE;
+ ssize = DEV_BSIZE;
+ if ((majordev = major(vp->v_rdev)) < nblkdev &&
+ (ioctl = bdevsw[majordev].d_ioctl) != NULL &&
+ (*ioctl)(vp->v_rdev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0) {
+ if (dpart.part->p_fstype == FS_BSDFFS &&
+ dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
+ bsize = dpart.part->p_frag *
+ dpart.part->p_fsize;
+ if (dpart.disklab->d_secsize != 0)
+ ssize = dpart.disklab->d_secsize;
+ }
+ bscale = bsize / ssize;
+ do {
+ bn = (uio->uio_offset / ssize) &~ (bscale - 1);
+ on = uio->uio_offset % bsize;
+ n = min((unsigned)(bsize - on), uio->uio_resid);
+ if (n == bsize)
+ bp = getblk(vp, bn, bsize, 0, 0);
+ else
+ error = bread(vp, bn, bsize, NOCRED, &bp);
+ n = min(n, bsize - bp->b_resid);
+ if (error) {
+ brelse(bp);
+ return (error);
+ }
+ error = uiomove((char *)bp->b_data + on, n, uio);
+ if (n + on == bsize)
+ bawrite(bp);
+ else
+ bdwrite(bp);
+ } while (error == 0 && uio->uio_resid > 0 && n != 0);
+ return (error);
+
+ default:
+ panic("spec_write type");
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Device ioctl operation.
+ */
+/* ARGSUSED */
+spec_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ u_long a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ dev_t dev = ap->a_vp->v_rdev;
+ int maj = major(dev);
+
+ switch (ap->a_vp->v_type) {
+
+ case VCHR:
+ return ((*cdevsw[maj].d_ioctl)(dev, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p));
+
+ case VBLK:
+ if (ap->a_command == 0 && (long)ap->a_data == B_TAPE)
+ if (bdevsw[maj].d_type == D_TAPE)
+ return (0);
+ else
+ return (1);
+ return ((*bdevsw[maj].d_ioctl)(dev, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p));
+
+ default:
+ panic("spec_ioctl");
+ /* NOTREACHED */
+ }
+}
+
+/* ARGSUSED */
+spec_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register dev_t dev;
+
+ switch (ap->a_vp->v_type) {
+
+ default:
+ return (1); /* XXX */
+
+ case VCHR:
+ dev = ap->a_vp->v_rdev;
+ return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p);
+ }
+}
+/*
+ * Synch buffers associated with a block device
+ */
+/* ARGSUSED */
+int
+spec_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ register struct buf *bp;
+ struct buf *nbp;
+ int s;
+
+ if (vp->v_type == VCHR)
+ return (0);
+ /*
+ * Flush all dirty buffers associated with a block device.
+ */
+loop:
+ s = splbio();
+ for (bp = vp->v_dirtyblkhd.lh_first; bp; bp = nbp) {
+ nbp = bp->b_vnbufs.le_next;
+ if ((bp->b_flags & B_BUSY))
+ continue;
+ if ((bp->b_flags & B_DELWRI) == 0)
+ panic("spec_fsync: not dirty");
+ bremfree(bp);
+ bp->b_flags |= B_BUSY;
+ splx(s);
+ bawrite(bp);
+ goto loop;
+ }
+ if (ap->a_waitfor == MNT_WAIT) {
+ while (vp->v_numoutput) {
+ vp->v_flag |= VBWAIT;
+ sleep((caddr_t)&vp->v_numoutput, PRIBIO + 1);
+ }
+#ifdef DIAGNOSTIC
+ if (vp->v_dirtyblkhd.lh_first) {
+ splx(s);
+ vprint("spec_fsync: dirty", vp);
+ goto loop;
+ }
+#endif
+ }
+ splx(s);
+ return (0);
+}
+
+/*
+ * Just call the device strategy routine
+ */
+spec_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+
+ (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp);
+ return (0);
+}
+
+/*
+ * This is a noop, simply returning what one has been given.
+ */
+spec_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ } */ *ap;
+{
+
+ if (ap->a_vpp != NULL)
+ *ap->a_vpp = ap->a_vp;
+ if (ap->a_bnp != NULL)
+ *ap->a_bnp = ap->a_bn;
+ return (0);
+}
+
+/*
+ * At the moment we do not do any locking.
+ */
+/* ARGSUSED */
+spec_lock(ap)
+ struct vop_lock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/* ARGSUSED */
+spec_unlock(ap)
+ struct vop_unlock_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return (0);
+}
+
+/*
+ * Device close routine
+ */
+/* ARGSUSED */
+spec_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = ap->a_vp;
+ dev_t dev = vp->v_rdev;
+ int (*devclose) __P((dev_t, int, int, struct proc *));
+ int mode, error;
+
+ switch (vp->v_type) {
+
+ case VCHR:
+ /*
+ * Hack: a tty device that is a controlling terminal
+ * has a reference from the session structure.
+ * We cannot easily tell that a character device is
+ * a controlling terminal, unless it is the closing
+ * process' controlling terminal. In that case,
+ * if the reference count is 2 (this last descriptor
+ * plus the session), release the reference from the session.
+ */
+ if (vcount(vp) == 2 && ap->a_p &&
+ vp == ap->a_p->p_session->s_ttyvp) {
+ vrele(vp);
+ ap->a_p->p_session->s_ttyvp = NULL;
+ }
+ /*
+ * If the vnode is locked, then we are in the midst
+ * of forcably closing the device, otherwise we only
+ * close on last reference.
+ */
+ if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
+ return (0);
+ devclose = cdevsw[major(dev)].d_close;
+ mode = S_IFCHR;
+ break;
+
+ case VBLK:
+ /*
+ * On last close of a block device (that isn't mounted)
+ * we must invalidate any in core blocks, so that
+ * we can, for instance, change floppy disks.
+ */
+ if (error = vinvalbuf(vp, V_SAVE, ap->a_cred, ap->a_p, 0, 0))
+ return (error);
+ /*
+ * We do not want to really close the device if it
+ * is still in use unless we are trying to close it
+ * forcibly. Since every use (buffer, vnode, swap, cmap)
+ * holds a reference to the vnode, and because we mark
+ * any other vnodes that alias this device, when the
+ * sum of the reference counts on all the aliased
+ * vnodes descends to one, we are on last close.
+ */
+ if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
+ return (0);
+ devclose = bdevsw[major(dev)].d_close;
+ mode = S_IFBLK;
+ break;
+
+ default:
+ panic("spec_close: not special");
+ }
+
+ return ((*devclose)(dev, ap->a_fflag, mode, ap->a_p));
+}
+
+/*
+ * Print out the contents of a special device vnode.
+ */
+spec_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ printf("tag VT_NON, dev %d, %d\n", major(ap->a_vp->v_rdev),
+ minor(ap->a_vp->v_rdev));
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+spec_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ register_t *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = LINK_MAX;
+ return (0);
+ case _PC_MAX_CANON:
+ *ap->a_retval = MAX_CANON;
+ return (0);
+ case _PC_MAX_INPUT:
+ *ap->a_retval = MAX_INPUT;
+ return (0);
+ case _PC_PIPE_BUF:
+ *ap->a_retval = PIPE_BUF;
+ return (0);
+ case _PC_CHOWN_RESTRICTED:
+ *ap->a_retval = 1;
+ return (0);
+ case _PC_VDISABLE:
+ *ap->a_retval = _POSIX_VDISABLE;
+ return (0);
+ default:
+ return (EINVAL);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Special device advisory byte-level locks.
+ */
+/* ARGSUSED */
+spec_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+
+ return (EOPNOTSUPP);
+}
+
+/*
+ * Special device failed operation
+ */
+spec_ebadf()
+{
+
+ return (EBADF);
+}
+
+/*
+ * Special device bad operation
+ */
+spec_badop()
+{
+
+ panic("spec_badop called");
+ /* NOTREACHED */
+}
diff --git a/sys/miscfs/specfs/specdev.h b/sys/miscfs/specfs/specdev.h
new file mode 100644
index 00000000000..80af64f3dc3
--- /dev/null
+++ b/sys/miscfs/specfs/specdev.h
@@ -0,0 +1,129 @@
+/* $NetBSD: specdev.h,v 1.9.2.1 1995/10/15 05:19:58 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1990, 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.
+ *
+ * @(#)specdev.h 8.3 (Berkeley) 8/10/94
+ */
+
+/*
+ * This structure defines the information maintained about
+ * special devices. It is allocated in checkalias and freed
+ * in vgone.
+ */
+struct specinfo {
+ struct vnode **si_hashchain;
+ struct vnode *si_specnext;
+ long si_flags;
+ dev_t si_rdev;
+};
+/*
+ * Exported shorthand
+ */
+#define v_rdev v_specinfo->si_rdev
+#define v_hashchain v_specinfo->si_hashchain
+#define v_specnext v_specinfo->si_specnext
+#define v_specflags v_specinfo->si_flags
+
+/*
+ * Flags for specinfo
+ */
+#define SI_MOUNTEDON 0x0001 /* block special device is mounted on */
+
+/*
+ * Special device management
+ */
+#define SPECHSZ 64
+#if ((SPECHSZ&(SPECHSZ-1)) == 0)
+#define SPECHASH(rdev) (((rdev>>5)+(rdev))&(SPECHSZ-1))
+#else
+#define SPECHASH(rdev) (((unsigned)((rdev>>5)+(rdev)))%SPECHSZ)
+#endif
+
+struct vnode *speclisth[SPECHSZ];
+
+/*
+ * Prototypes for special file operations on vnodes.
+ */
+extern int (**spec_vnodeop_p)();
+struct nameidata;
+struct componentname;
+struct ucred;
+struct flock;
+struct buf;
+struct uio;
+
+int spec_badop(),
+ spec_ebadf();
+
+int spec_lookup __P((struct vop_lookup_args *));
+#define spec_create ((int (*) __P((struct vop_create_args *)))spec_badop)
+#define spec_mknod ((int (*) __P((struct vop_mknod_args *)))spec_badop)
+int spec_open __P((struct vop_open_args *));
+int spec_close __P((struct vop_close_args *));
+#define spec_access ((int (*) __P((struct vop_access_args *)))spec_ebadf)
+#define spec_getattr ((int (*) __P((struct vop_getattr_args *)))spec_ebadf)
+#define spec_setattr ((int (*) __P((struct vop_setattr_args *)))spec_ebadf)
+int spec_read __P((struct vop_read_args *));
+int spec_write __P((struct vop_write_args *));
+#define spec_lease_check ((int (*) __P((struct vop_lease_args *)))nullop)
+int spec_ioctl __P((struct vop_ioctl_args *));
+int spec_select __P((struct vop_select_args *));
+#define spec_mmap ((int (*) __P((struct vop_mmap_args *)))spec_badop)
+int spec_fsync __P((struct vop_fsync_args *));
+#define spec_seek ((int (*) __P((struct vop_seek_args *)))spec_badop)
+#define spec_remove ((int (*) __P((struct vop_remove_args *)))spec_badop)
+#define spec_link ((int (*) __P((struct vop_link_args *)))spec_badop)
+#define spec_rename ((int (*) __P((struct vop_rename_args *)))spec_badop)
+#define spec_mkdir ((int (*) __P((struct vop_mkdir_args *)))spec_badop)
+#define spec_rmdir ((int (*) __P((struct vop_rmdir_args *)))spec_badop)
+#define spec_symlink ((int (*) __P((struct vop_symlink_args *)))spec_badop)
+#define spec_readdir ((int (*) __P((struct vop_readdir_args *)))spec_badop)
+#define spec_readlink ((int (*) __P((struct vop_readlink_args *)))spec_badop)
+#define spec_abortop ((int (*) __P((struct vop_abortop_args *)))spec_badop)
+#define spec_inactive ((int (*) __P((struct vop_inactive_args *)))nullop)
+#define spec_reclaim ((int (*) __P((struct vop_reclaim_args *)))nullop)
+int spec_lock __P((struct vop_lock_args *));
+int spec_unlock __P((struct vop_unlock_args *));
+int spec_bmap __P((struct vop_bmap_args *));
+int spec_strategy __P((struct vop_strategy_args *));
+int spec_print __P((struct vop_print_args *));
+#define spec_islocked ((int (*) __P((struct vop_islocked_args *)))nullop)
+int spec_pathconf __P((struct vop_pathconf_args *));
+int spec_advlock __P((struct vop_advlock_args *));
+#define spec_blkatoff ((int (*) __P((struct vop_blkatoff_args *)))spec_badop)
+#define spec_valloc ((int (*) __P((struct vop_valloc_args *)))spec_badop)
+#define spec_reallocblks \
+ ((int (*) __P((struct vop_reallocblks_args *)))spec_badop)
+#define spec_vfree ((int (*) __P((struct vop_vfree_args *)))spec_badop)
+#define spec_truncate ((int (*) __P((struct vop_truncate_args *)))nullop)
+#define spec_update ((int (*) __P((struct vop_update_args *)))nullop)
diff --git a/sys/miscfs/umapfs/umap.h b/sys/miscfs/umapfs/umap.h
new file mode 100644
index 00000000000..52408df2254
--- /dev/null
+++ b/sys/miscfs/umapfs/umap.h
@@ -0,0 +1,92 @@
+/* $NetBSD: umap.h,v 1.5 1995/04/15 01:57:35 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * 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.
+ *
+ * from: @(#)null_vnops.c 1.5 (Berkeley) 7/10/92
+ * @(#)umap.h 8.3 (Berkeley) 1/21/94
+ */
+
+#define MAPFILEENTRIES 64
+#define GMAPFILEENTRIES 16
+#define NOBODY 32767
+#define NULLGROUP 65534
+
+struct umap_args {
+ char *target; /* Target of loopback */
+ int nentries; /* # of entries in user map array */
+ int gnentries; /* # of entries in group map array */
+ u_long (*mapdata)[2]; /* pointer to array of user mappings */
+ u_long (*gmapdata)[2]; /* pointer to array of group mappings */
+};
+
+struct umap_mount {
+ struct mount *umapm_vfs;
+ struct vnode *umapm_rootvp; /* Reference to root umap_node */
+ int info_nentries; /* number of uid mappings */
+ int info_gnentries; /* number of gid mappings */
+ u_long info_mapdata[MAPFILEENTRIES][2]; /* mapping data for
+ user mapping in ficus */
+ u_long info_gmapdata[GMAPFILEENTRIES][2]; /*mapping data for
+ group mapping in ficus */
+};
+
+#ifdef _KERNEL
+/*
+ * A cache of vnode references
+ */
+struct umap_node {
+ LIST_ENTRY(umap_node) umap_hash; /* Hash list */
+ struct vnode *umap_lowervp; /* Aliased vnode - VREFed once */
+ struct vnode *umap_vnode; /* Back pointer to vnode/umap_node */
+};
+
+extern int umap_node_create __P((struct mount *mp, struct vnode *target, struct vnode **vpp));
+extern u_long umap_reverse_findid __P((u_long id, u_long map[][2], int nentries));
+extern void umap_mapids __P((struct mount *v_mount, struct ucred *credp));
+
+#define MOUNTTOUMAPMOUNT(mp) ((struct umap_mount *)((mp)->mnt_data))
+#define VTOUMAP(vp) ((struct umap_node *)(vp)->v_data)
+#define UMAPTOV(xp) ((xp)->umap_vnode)
+#ifdef UMAPFS_DIAGNOSTIC
+extern struct vnode *umap_checkvp __P((struct vnode *vp, char *fil, int lno));
+#define UMAPVPTOLOWERVP(vp) umap_checkvp((vp), __FILE__, __LINE__)
+#else
+#define UMAPVPTOLOWERVP(vp) (VTOUMAP(vp)->umap_lowervp)
+#endif
+
+extern int (**umap_vnodeop_p)();
+extern struct vfsops umap_vfsops;
+#endif /* _KERNEL */
diff --git a/sys/miscfs/umapfs/umap_subr.c b/sys/miscfs/umapfs/umap_subr.c
new file mode 100644
index 00000000000..edbec49cff3
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_subr.c
@@ -0,0 +1,437 @@
+/* $NetBSD: umap_subr.c,v 1.6 1995/06/01 22:44:34 jtc Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * from: Id: lofs_subr.c, v 1.11 1992/05/30 10:05:43 jsp Exp
+ * @(#)umap_subr.c 8.6 (Berkeley) 1/26/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/specfs/specdev.h>
+#include <miscfs/umapfs/umap.h>
+
+#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
+#define NUMAPNODECACHE 16
+
+/*
+ * Null layer cache:
+ * Each cache entry holds a reference to the target vnode
+ * along with a pointer to the alias vnode. When an
+ * entry is added the target vnode is VREF'd. When the
+ * alias is removed the target vnode is vrele'd.
+ */
+
+#define UMAP_NHASH(vp) \
+ (&umap_node_hashtbl[(((u_long)vp)>>LOG2_SIZEVNODE) & umap_node_hash])
+LIST_HEAD(umap_node_hashhead, umap_node) *umap_node_hashtbl;
+u_long umap_node_hash;
+
+/*
+ * Initialise cache headers
+ */
+umapfs_init()
+{
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_init\n"); /* printed during system boot */
+#endif
+ umap_node_hashtbl = hashinit(NUMAPNODECACHE, M_CACHE, &umap_node_hash);
+}
+
+/*
+ * umap_findid is called by various routines in umap_vnodeops.c to
+ * find a user or group id in a map.
+ */
+static u_long
+umap_findid(id, map, nentries)
+ u_long id;
+ u_long map[][2];
+ int nentries;
+{
+ int i;
+
+ /* Find uid entry in map */
+ i = 0;
+ while ((i<nentries) && ((map[i][0]) != id))
+ i++;
+
+ if (i < nentries)
+ return (map[i][1]);
+ else
+ return (-1);
+
+}
+
+/*
+ * umap_reverse_findid is called by umap_getattr() in umap_vnodeops.c to
+ * find a user or group id in a map, in reverse.
+ */
+u_long
+umap_reverse_findid(id, map, nentries)
+ u_long id;
+ u_long map[][2];
+ int nentries;
+{
+ int i;
+
+ /* Find uid entry in map */
+ i = 0;
+ while ((i<nentries) && ((map[i][1]) != id))
+ i++;
+
+ if (i < nentries)
+ return (map[i][0]);
+ else
+ return (-1);
+
+}
+
+/*
+ * Return alias for target vnode if already exists, else 0.
+ */
+static struct vnode *
+umap_node_find(mp, targetvp)
+ struct mount *mp;
+ struct vnode *targetvp;
+{
+ struct umap_node_hashhead *hd;
+ struct umap_node *a;
+ struct vnode *vp;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_find(mp = %x, target = %x)\n", mp, targetvp);
+#endif
+
+ /*
+ * Find hash base, and then search the (two-way) linked
+ * list looking for a umap_node structure which is referencing
+ * the target vnode. If found, the increment the umap_node
+ * reference count (but NOT the target vnode's VREF counter).
+ */
+ hd = UMAP_NHASH(targetvp);
+loop:
+ for (a = hd->lh_first; a != 0; a = a->umap_hash.le_next) {
+ if (a->umap_lowervp == targetvp &&
+ a->umap_vnode->v_mount == mp) {
+ vp = UMAPTOV(a);
+ /*
+ * We need vget for the VXLOCK
+ * stuff, but we don't want to lock
+ * the lower node.
+ */
+ if (vget(vp, 0)) {
+#ifdef UMAPFS_DIAGNOSTIC
+ printf ("umap_node_find: vget failed.\n");
+#endif
+ goto loop;
+ }
+ return (vp);
+ }
+ }
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_find(%x, %x): NOT found\n", mp, targetvp);
+#endif
+
+ return (0);
+}
+
+/*
+ * Make a new umap_node node.
+ * Vp is the alias vnode, lowervp is the target vnode.
+ * Maintain a reference to lowervp.
+ */
+static int
+umap_node_alloc(mp, lowervp, vpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **vpp;
+{
+ struct umap_node_hashhead *hd;
+ struct umap_node *xp;
+ struct vnode *vp, *nvp;
+ int error;
+ extern int (**dead_vnodeop_p)();
+
+ if (error = getnewvnode(VT_UMAP, mp, umap_vnodeop_p, &vp))
+ return (error);
+ vp->v_type = lowervp->v_type;
+
+ MALLOC(xp, struct umap_node *, sizeof(struct umap_node), M_TEMP,
+ M_WAITOK);
+ if (vp->v_type == VBLK || vp->v_type == VCHR) {
+ MALLOC(vp->v_specinfo, struct specinfo *,
+ sizeof(struct specinfo), M_VNODE, M_WAITOK);
+ vp->v_rdev = lowervp->v_rdev;
+ }
+
+ vp->v_data = xp;
+ xp->umap_vnode = vp;
+ xp->umap_lowervp = lowervp;
+ /*
+ * Before we insert our new node onto the hash chains,
+ * check to see if someone else has beaten us to it.
+ * (We could have slept in MALLOC.)
+ */
+ if (nvp = umap_node_find(lowervp)) {
+ *vpp = nvp;
+
+ /* free the substructures we've allocated. */
+ FREE(xp, M_TEMP);
+ if (vp->v_type == VBLK || vp->v_type == VCHR)
+ FREE(vp->v_specinfo, M_VNODE);
+
+ vp->v_type = VBAD; /* node is discarded */
+ vp->v_op = dead_vnodeop_p; /* so ops will still work */
+ vrele(vp); /* get rid of it. */
+ return (0);
+ }
+
+ /*
+ * XXX if it's a device node, it needs to be checkalias()ed.
+ * however, for locking reasons, that's just not possible.
+ * so we have to do most of the dirty work inline. Note that
+ * this is a limited case; we know that there's going to be
+ * an alias, and we know that that alias will be a "real"
+ * device node, i.e. not tagged VT_NON.
+ */
+ if (vp->v_type == VBLK || vp->v_type == VCHR) {
+ struct vnode *cvp, **cvpp;
+
+ cvpp = &speclisth[SPECHASH(vp->v_rdev)];
+loop:
+ for (cvp = *cvpp; cvp; cvp = cvp->v_specnext) {
+ if (vp->v_rdev != cvp->v_rdev ||
+ vp->v_type != cvp->v_type)
+ continue;
+
+ /*
+ * Alias, but not in use, so flush it out.
+ */
+ if (cvp->v_usecount == 0) {
+ vgone(cvp);
+ goto loop;
+ }
+ if (vget(cvp, 0)) /* can't lock; will die! */
+ goto loop;
+ break;
+ }
+
+ vp->v_hashchain = cvpp;
+ vp->v_specnext = *cvpp;
+ vp->v_specflags = 0;
+ *cvpp = vp;
+#ifdef DIAGNOSTIC
+ if (cvp == NULLVP)
+ panic("umap_node_alloc: no alias for device");
+#endif
+ vp->v_flag |= VALIASED;
+ cvp->v_flag |= VALIASED;
+ vrele(cvp);
+ }
+ /* XXX end of transmogrified checkalias() */
+
+ *vpp = vp;
+ VREF(lowervp); /* Extra VREF will be vrele'd in umap_node_create */
+ hd = UMAP_NHASH(lowervp);
+ LIST_INSERT_HEAD(hd, xp, umap_hash);
+ return (0);
+}
+
+
+/*
+ * Try to find an existing umap_node vnode refering
+ * to it, otherwise make a new umap_node vnode which
+ * contains a reference to the target vnode.
+ */
+int
+umap_node_create(mp, targetvp, newvpp)
+ struct mount *mp;
+ struct vnode *targetvp;
+ struct vnode **newvpp;
+{
+ struct vnode *aliasvp;
+
+ if (aliasvp = umap_node_find(mp, targetvp)) {
+ /*
+ * Take another reference to the alias vnode
+ */
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("umap_node_create: exists", ap->umap_vnode);
+#endif
+ /* VREF(aliasvp); */
+ } else {
+ int error;
+
+ /*
+ * Get new vnode.
+ */
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umap_node_create: create new alias vnode\n");
+#endif
+ /*
+ * Make new vnode reference the umap_node.
+ */
+ if (error = umap_node_alloc(mp, targetvp, &aliasvp))
+ return (error);
+
+ /*
+ * aliasvp is already VREF'd by getnewvnode()
+ */
+ }
+
+ vrele(targetvp);
+
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("umap_node_create: alias", aliasvp);
+ vprint("umap_node_create: target", targetvp);
+#endif
+
+ *newvpp = aliasvp;
+ return (0);
+}
+
+#ifdef UMAPFS_DIAGNOSTIC
+int umap_checkvp_barrier = 1;
+struct vnode *
+umap_checkvp(vp, fil, lno)
+ struct vnode *vp;
+ char *fil;
+ int lno;
+{
+ struct umap_node *a = VTOUMAP(vp);
+#if 0
+ /*
+ * Can't do this check because vop_reclaim runs
+ * with funny vop vector.
+ */
+ if (vp->v_op != umap_vnodeop_p) {
+ printf ("umap_checkvp: on non-umap-node\n");
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic("umap_checkvp");
+ }
+#endif
+ if (a->umap_lowervp == NULL) {
+ /* Should never happen */
+ int i; u_long *p;
+ printf("vp = %x, ZERO ptr\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic("umap_checkvp");
+ }
+ if (a->umap_lowervp->v_usecount < 1) {
+ int i; u_long *p;
+ printf("vp = %x, unref'ed lowervp\n", vp);
+ for (p = (u_long *) a, i = 0; i < 8; i++)
+ printf(" %x", p[i]);
+ printf("\n");
+ /* wait for debugger */
+ while (umap_checkvp_barrier) /*WAIT*/ ;
+ panic ("umap with unref'ed lowervp");
+ }
+#if 0
+ printf("umap %x/%d -> %x/%d [%s, %d]\n",
+ a->umap_vnode, a->umap_vnode->v_usecount,
+ a->umap_lowervp, a->umap_lowervp->v_usecount,
+ fil, lno);
+#endif
+ return (a->umap_lowervp);
+}
+#endif
+
+/* umap_mapids maps all of the ids in a credential, both user and group. */
+
+void
+umap_mapids(v_mount, credp)
+ struct mount *v_mount;
+ struct ucred *credp;
+{
+ int i, unentries, gnentries;
+ uid_t uid;
+ gid_t gid;
+ u_long *usermap, *groupmap;
+
+ unentries = MOUNTTOUMAPMOUNT(v_mount)->info_nentries;
+ usermap = &(MOUNTTOUMAPMOUNT(v_mount)->info_mapdata[0][0]);
+ gnentries = MOUNTTOUMAPMOUNT(v_mount)->info_gnentries;
+ groupmap = &(MOUNTTOUMAPMOUNT(v_mount)->info_gmapdata[0][0]);
+
+ /* Find uid entry in map */
+
+ uid = (uid_t) umap_findid(credp->cr_uid, usermap, unentries);
+
+ if (uid != -1)
+ credp->cr_uid = uid;
+ else
+ credp->cr_uid = (uid_t) NOBODY;
+
+#if 1
+ /* cr_gid is the same as cr_groups[0] in 4BSD, but not in NetBSD */
+
+ /* Find gid entry in map */
+
+ gid = (gid_t) umap_findid(credp->cr_gid, groupmap, gnentries);
+
+ if (gid != -1)
+ credp->cr_gid = gid;
+ else
+ credp->cr_gid = NULLGROUP;
+#endif
+
+ /* Now we must map each of the set of groups in the cr_groups
+ structure. */
+
+ i = 0;
+ while (credp->cr_groups[i] != 0) {
+ gid = (gid_t) umap_findid(credp->cr_groups[i],
+ groupmap, gnentries);
+
+ if (gid != -1)
+ credp->cr_groups[i++] = gid;
+ else
+ credp->cr_groups[i++] = NULLGROUP;
+ }
+}
diff --git a/sys/miscfs/umapfs/umap_vfsops.c b/sys/miscfs/umapfs/umap_vfsops.c
new file mode 100644
index 00000000000..09bae9ce8ec
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_vfsops.c
@@ -0,0 +1,414 @@
+/* $NetBSD: umap_vfsops.c,v 1.8 1995/06/18 14:47:44 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * 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.
+ *
+ * from: @(#)null_vfsops.c 1.5 (Berkeley) 7/10/92
+ * @(#)umap_vfsops.c 8.3 (Berkeley) 1/21/94
+ */
+
+/*
+ * Umap Layer
+ * (See mount_umap(8) for a description of this layer.)
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <miscfs/umapfs/umap.h>
+
+/*
+ * Mount umap layer
+ */
+int
+umapfs_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ struct umap_args args;
+ struct vnode *lowerrootvp, *vp;
+ struct vnode *umapm_rootvp;
+ struct umap_mount *amp;
+ size_t size;
+ int error;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ return (EOPNOTSUPP);
+ /* return (VFS_MOUNT(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, path, data, ndp, p));*/
+ }
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct umap_args)))
+ return (error);
+
+ /*
+ * Find lower node
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
+ UIO_USERSPACE, args.target, p);
+ if (error = namei(ndp))
+ return (error);
+
+ /*
+ * Sanity check on lower vnode
+ */
+ lowerrootvp = ndp->ni_vp;
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("vp = %x, check for VDIR...\n", lowerrootvp);
+#endif
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = 0;
+
+ if (lowerrootvp->v_type != VDIR) {
+ vput(lowerrootvp);
+ return (EINVAL);
+ }
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("mp = %x\n", mp);
+#endif
+
+ amp = (struct umap_mount *) malloc(sizeof(struct umap_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Save reference to underlying FS
+ */
+ amp->umapm_vfs = lowerrootvp->v_mount;
+
+ /*
+ * Now copy in the number of entries and maps for umap mapping.
+ */
+ amp->info_nentries = args.nentries;
+ amp->info_gnentries = args.gnentries;
+ error = copyin(args.mapdata, (caddr_t)amp->info_mapdata,
+ 2*sizeof(u_long)*args.nentries);
+ if (error)
+ return (error);
+
+#ifdef UMAP_DIAGNOSTIC
+ printf("umap_mount:nentries %d\n",args.nentries);
+ for (i = 0; i < args.nentries; i++)
+ printf(" %d maps to %d\n", amp->info_mapdata[i][0],
+ amp->info_mapdata[i][1]);
+#endif
+
+ error = copyin(args.gmapdata, (caddr_t)amp->info_gmapdata,
+ 2*sizeof(u_long)*args.nentries);
+ if (error)
+ return (error);
+
+#ifdef UMAP_DIAGNOSTIC
+ printf("umap_mount:gnentries %d\n",args.gnentries);
+ for (i = 0; i < args.gnentries; i++)
+ printf(" group %d maps to %d\n",
+ amp->info_gmapdata[i][0],
+ amp->info_gmapdata[i][1]);
+#endif
+
+
+ /*
+ * Save reference. Each mount also holds
+ * a reference on the root vnode.
+ */
+ error = umap_node_create(mp, lowerrootvp, &vp);
+ /*
+ * Unlock the node (either the lower or the alias)
+ */
+ VOP_UNLOCK(vp);
+ /*
+ * Make sure the node alias worked
+ */
+ if (error) {
+ vrele(lowerrootvp);
+ free(amp, M_UFSMNT); /* XXX */
+ return (error);
+ }
+
+ /*
+ * Keep a held reference to the root vnode.
+ * It is vrele'd in umapfs_unmount.
+ */
+ umapm_rootvp = vp;
+ umapm_rootvp->v_flag |= VROOT;
+ amp->umapm_rootvp = umapm_rootvp;
+ if (UMAPVPTOLOWERVP(umapm_rootvp)->v_mount->mnt_flag & MNT_LOCAL)
+ mp->mnt_flag |= MNT_LOCAL;
+ mp->mnt_data = (qaddr_t) amp;
+ getnewfsid(mp, makefstype(MOUNT_LOFS));
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+ (void) copyinstr(args.target, mp->mnt_stat.f_mntfromname, MNAMELEN - 1,
+ &size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_mount: lower %s, alias at %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem will have been called
+ * when that filesystem was mounted.
+ */
+int
+umapfs_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+ /* return (VFS_START(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, flags, p)); */
+}
+
+/*
+ * Free reference to umap layer
+ */
+int
+umapfs_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct vnode *umapm_rootvp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp;
+ int error;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* lofs can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ /*
+ * Clear out buffer cache. I don't think we
+ * ever get anything cached at this level at the
+ * moment, but who knows...
+ */
+#ifdef notyet
+ mntflushbuf(mp, 0);
+ if (mntinvalbuf(mp, 1))
+ return (EBUSY);
+#endif
+ if (umapm_rootvp->v_usecount > 1)
+ return (EBUSY);
+ if (error = vflush(mp, umapm_rootvp, flags))
+ return (error);
+
+#ifdef UMAPFS_DIAGNOSTIC
+ vprint("alias root of lower", umapm_rootvp);
+#endif
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(umapm_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(umapm_rootvp);
+ /*
+ * Finally, throw away the umap_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+umapfs_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct vnode *vp;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_root(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTOUMAPMOUNT(mp)->umapm_rootvp,
+ UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)
+ );
+#endif
+
+ /*
+ * Return locked reference to root.
+ */
+ vp = MOUNTTOUMAPMOUNT(mp)->umapm_rootvp;
+ VREF(vp);
+ VOP_LOCK(vp);
+ *vpp = vp;
+ return (0);
+}
+
+int
+umapfs_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (VFS_QUOTACTL(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, cmd, uid, arg, p));
+}
+
+int
+umapfs_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct statfs mstat;
+
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_statfs(mp = %x, vp = %x->%x)\n", mp,
+ MOUNTTOUMAPMOUNT(mp)->umapm_rootvp,
+ UMAPVPTOLOWERVP(MOUNTTOUMAPMOUNT(mp)->umapm_rootvp)
+ );
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ error = VFS_STATFS(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, &mstat, p);
+ if (error)
+ return (error);
+
+ /* now copy across the "interesting" information and fake the rest */
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ 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);
+}
+
+int
+umapfs_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ /*
+ * XXX - Assumes no data cached at umap layer.
+ */
+ return (0);
+}
+
+int
+umapfs_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (VFS_VGET(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, ino, vpp));
+}
+
+int
+umapfs_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred**credanonp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+umapfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int umapfs_init __P((void));
+
+struct vfsops umap_vfsops = {
+ MOUNT_UMAP,
+ umapfs_mount,
+ umapfs_start,
+ umapfs_unmount,
+ umapfs_root,
+ umapfs_quotactl,
+ umapfs_statfs,
+ umapfs_sync,
+ umapfs_vget,
+ umapfs_fhtovp,
+ umapfs_vptofh,
+ umapfs_init,
+};
diff --git a/sys/miscfs/umapfs/umap_vnops.c b/sys/miscfs/umapfs/umap_vnops.c
new file mode 100644
index 00000000000..87d9a0d37c9
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_vnops.c
@@ -0,0 +1,496 @@
+/* $NetBSD: umap_vnops.c,v 1.4 1995/04/15 01:57:39 cgd Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * the UCLA Ficus project.
+ *
+ * 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.
+ *
+ * @(#)umap_vnops.c 8.3 (Berkeley) 1/5/94
+ */
+
+/*
+ * Umap Layer
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <miscfs/umapfs/umap.h>
+
+
+int umap_bug_bypass = 0; /* for debugging: enables bypass printf'ing */
+
+/*
+ * This is the 10-Apr-92 bypass routine.
+ * See null_vnops.c:null_bypass for more details.
+ */
+int
+umap_bypass(ap)
+ struct vop_generic_args /* {
+ struct vnodeop_desc *a_desc;
+ <other random data follows, presumably>
+ } */ *ap;
+{
+ extern int (**umap_vnodeop_p)(); /* not extern, really "forward" */
+ struct ucred **credpp = 0, *credp = 0;
+ struct ucred *savecredp, *savecompcredp = 0;
+ struct ucred *compcredp = 0;
+ struct vnode **this_vp_p;
+ int error;
+ struct vnode *old_vps[VDESC_MAX_VPS];
+ struct vnode *vp1 = 0;
+ struct vnode **vps_p[VDESC_MAX_VPS];
+ struct vnode ***vppp;
+ struct vnodeop_desc *descp = ap->a_desc;
+ int reles, i;
+ struct componentname **compnamepp = 0;
+
+ if (umap_bug_bypass)
+ printf ("umap_bypass: %s\n", descp->vdesc_name);
+
+#ifdef SAFETY
+ /*
+ * We require at least one vp.
+ */
+ if (descp->vdesc_vp_offsets == NULL ||
+ descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
+ panic ("umap_bypass: no vp's in map.\n");
+#endif
+
+ /*
+ * Map the vnodes going in.
+ * Later, we'll invoke the operation based on
+ * the first mapped vnode's operation vector.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ vps_p[i] = this_vp_p =
+ VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[i], ap);
+
+ if (i == 0) {
+ vp1 = *vps_p[0];
+ }
+
+ /*
+ * We're not guaranteed that any but the first vnode
+ * are of our type. Check for and don't map any
+ * that aren't. (Must map first vp or vclean fails.)
+ */
+
+ if (i && (*this_vp_p)->v_op != umap_vnodeop_p) {
+ old_vps[i] = NULL;
+ } else {
+ old_vps[i] = *this_vp_p;
+ *(vps_p[i]) = UMAPVPTOLOWERVP(*this_vp_p);
+ if (reles & 1)
+ VREF(*this_vp_p);
+ }
+
+ }
+
+ /*
+ * Fix the credentials. (That's the purpose of this layer.)
+ */
+
+ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
+
+ credpp = VOPARG_OFFSETTO(struct ucred**,
+ descp->vdesc_cred_offset, ap);
+
+ /* Save old values */
+
+ savecredp = *credpp;
+ if (savecredp != NOCRED)
+ *credpp = crdup(savecredp);
+ credp = *credpp;
+
+ if (umap_bug_bypass && credp->cr_uid != 0)
+ printf("umap_bypass: user was %d, group %d\n",
+ credp->cr_uid, credp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp1->v_mount, credp);
+
+ if (umap_bug_bypass && credp->cr_uid != 0)
+ printf("umap_bypass: user now %d, group %d\n",
+ credp->cr_uid, credp->cr_gid);
+ }
+
+ /* BSD often keeps a credential in the componentname structure
+ * for speed. If there is one, it better get mapped, too.
+ */
+
+ if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
+
+ compnamepp = VOPARG_OFFSETTO(struct componentname**,
+ descp->vdesc_componentname_offset, ap);
+
+ savecompcredp = (*compnamepp)->cn_cred;
+ if (savecompcredp != NOCRED)
+ (*compnamepp)->cn_cred = crdup(savecompcredp);
+ compcredp = (*compnamepp)->cn_cred;
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_bypass: component credit user was %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp1->v_mount, compcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_bypass: component credit user now %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+ }
+
+ /*
+ * Call the operation on the lower layer
+ * with the modified argument structure.
+ */
+ error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
+
+ /*
+ * Maintain the illusion of call-by-value
+ * by restoring vnodes in the argument structure
+ * to their original value.
+ */
+ reles = descp->vdesc_flags;
+ for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+ if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
+ break; /* bail out at end of list */
+ if (old_vps[i]) {
+ *(vps_p[i]) = old_vps[i];
+ if (reles & 1)
+ vrele(*(vps_p[i]));
+ };
+ };
+
+ /*
+ * Map the possible out-going vpp
+ * (Assumes that the lower layer always returns
+ * a VREF'ed vpp unless it gets an error.)
+ */
+ if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
+ !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
+ !error) {
+ if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
+ goto out;
+ vppp = VOPARG_OFFSETTO(struct vnode***,
+ descp->vdesc_vpp_offset, ap);
+ error = umap_node_create(old_vps[0]->v_mount, **vppp, *vppp);
+ };
+
+ out:
+ /*
+ * Free duplicate cred structure and restore old one.
+ */
+ if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
+ if (umap_bug_bypass && credp && credp->cr_uid != 0)
+ printf("umap_bypass: returning-user was %d\n",
+ credp->cr_uid);
+
+ if (savecredp != NOCRED) {
+ crfree(credp);
+ *credpp = savecredp;
+ if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
+ printf("umap_bypass: returning-user now %d\n\n",
+ savecredp->cr_uid);
+ }
+ }
+
+ if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
+ if (umap_bug_bypass && compcredp && compcredp->cr_uid != 0)
+ printf("umap_bypass: returning-component-user was %d\n",
+ compcredp->cr_uid);
+
+ if (savecompcredp != NOCRED) {
+ crfree(compcredp);
+ (*compnamepp)->cn_cred = savecompcredp;
+ if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
+ printf("umap_bypass: returning-component-user now %d\n",
+ savecompcredp->cr_uid);
+ }
+ }
+
+ return (error);
+}
+
+
+/*
+ * We handle getattr to change the fsid.
+ */
+int
+umap_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ uid_t uid;
+ gid_t gid;
+ int error, tmpid, nentries, gnentries;
+ u_long (*mapdata)[2];
+ u_long (*gmapdata)[2];
+ struct vnode **vp1p;
+ struct vnodeop_desc *descp = ap->a_desc;
+
+ if (error = umap_bypass(ap))
+ return (error);
+ /* Requires that arguments be restored. */
+ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+
+ /*
+ * Umap needs to map the uid and gid returned by a stat
+ * into the proper values for this site. This involves
+ * finding the returned uid in the mapping information,
+ * translating it into the uid on the other end,
+ * and filling in the proper field in the vattr
+ * structure pointed to by ap->a_vap. The group
+ * is easier, since currently all groups will be
+ * translate to the NULLGROUP.
+ */
+
+ /* Find entry in map */
+
+ uid = ap->a_vap->va_uid;
+ gid = ap->a_vap->va_gid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: mapped uid = %d, mapped gid = %d\n", uid,
+ gid);
+
+ vp1p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[0], ap);
+ nentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_nentries;
+ mapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_mapdata);
+ gnentries = MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gnentries;
+ gmapdata = (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gmapdata);
+
+ /* Reverse map the uid for the vnode. Since it's a reverse
+ map, we can't use umap_mapids() to do it. */
+
+ tmpid = umap_reverse_findid(uid, mapdata, nentries);
+
+ if (tmpid != -1) {
+ ap->a_vap->va_uid = (uid_t) tmpid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: original uid = %d\n", uid);
+ } else
+ ap->a_vap->va_uid = (uid_t) NOBODY;
+
+ /* Reverse map the gid for the vnode. */
+
+ tmpid = umap_reverse_findid(gid, gmapdata, gnentries);
+
+ if (tmpid != -1) {
+ ap->a_vap->va_gid = (gid_t) tmpid;
+ if (umap_bug_bypass)
+ printf("umap_getattr: original gid = %d\n", gid);
+ } else
+ ap->a_vap->va_gid = (gid_t) NULLGROUP;
+
+ return (0);
+}
+
+int
+umap_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our umap_node is in the
+ * cache and reusable.
+ *
+ */
+ return (0);
+}
+
+int
+umap_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct umap_node *xp = VTOUMAP(vp);
+ struct vnode *lowervp = xp->umap_lowervp;
+
+ /* After this assignment, this node will not be re-used. */
+ xp->umap_lowervp = NULL;
+ LIST_REMOVE(xp, umap_hash);
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = NULL;
+ vrele(lowervp);
+ return (0);
+}
+
+int
+umap_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_STRATEGY(ap->a_bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+int
+umap_bwrite(ap)
+ struct vop_bwrite_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
+
+ error = VOP_BWRITE(ap->a_bp);
+
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+
+int
+umap_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ printf("\ttag VT_UMAPFS, vp=%x, lowervp=%x\n", vp, UMAPVPTOLOWERVP(vp));
+ return (0);
+}
+
+int
+umap_rename(ap)
+ 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;
+{
+ int error;
+ struct componentname *compnamep;
+ struct ucred *compcredp, *savecompcredp;
+ struct vnode *vp;
+
+ /*
+ * Rename is irregular, having two componentname structures.
+ * We need to map the cre in the second structure,
+ * and then bypass takes care of the rest.
+ */
+
+ vp = ap->a_fdvp;
+ compnamep = ap->a_tcnp;
+ compcredp = compnamep->cn_cred;
+
+ savecompcredp = compcredp;
+ compcredp = compnamep->cn_cred = crdup(savecompcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_rename: rename component credit user was %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ /* Map all ids in the credential structure. */
+
+ umap_mapids(vp->v_mount, compcredp);
+
+ if (umap_bug_bypass && compcredp->cr_uid != 0)
+ printf("umap_rename: rename component credit user now %d, group %d\n",
+ compcredp->cr_uid, compcredp->cr_gid);
+
+ error = umap_bypass(ap);
+
+ /* Restore the additional mapped componentname cred structure. */
+
+ crfree(compcredp);
+ compnamep->cn_cred = savecompcredp;
+
+ return error;
+}
+
+/*
+ * Global vfs data structures
+ */
+/*
+ * XXX - strategy, bwrite are hand coded currently. They should
+ * go away with a merged buffer/block cache.
+ *
+ */
+int (**umap_vnodeop_p)();
+struct vnodeopv_entry_desc umap_vnodeop_entries[] = {
+ { &vop_default_desc, umap_bypass },
+
+ { &vop_getattr_desc, umap_getattr },
+ { &vop_inactive_desc, umap_inactive },
+ { &vop_reclaim_desc, umap_reclaim },
+ { &vop_print_desc, umap_print },
+ { &vop_rename_desc, umap_rename },
+
+ { &vop_strategy_desc, umap_strategy },
+ { &vop_bwrite_desc, umap_bwrite },
+
+ { (struct vnodeop_desc*) NULL, (int(*)()) NULL }
+};
+struct vnodeopv_desc umap_vnodeop_opv_desc =
+ { &umap_vnodeop_p, umap_vnodeop_entries };
diff --git a/sys/miscfs/union/union.h b/sys/miscfs/union/union.h
new file mode 100644
index 00000000000..71db34cad6f
--- /dev/null
+++ b/sys/miscfs/union/union.h
@@ -0,0 +1,132 @@
+/* $NetBSD: union.h,v 1.8 1995/05/30 18:55:28 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)union.h 8.9 (Berkeley) 12/10/94
+ */
+
+struct union_args {
+ char *target; /* Target of loopback */
+ int mntflags; /* Options on the mount */
+};
+
+#define UNMNT_ABOVE 0x0001 /* Target appears below mount point */
+#define UNMNT_BELOW 0x0002 /* Target appears below mount point */
+#define UNMNT_REPLACE 0x0003 /* Target replaces mount point */
+#define UNMNT_OPMASK 0x0003
+
+struct union_mount {
+ struct vnode *um_uppervp;
+ struct vnode *um_lowervp;
+ struct ucred *um_cred; /* Credentials of user calling mount */
+ int um_cmode; /* cmask from mount process */
+ int um_op; /* Operation mode */
+};
+
+#ifdef _KERNEL
+
+/*
+ * DEFDIRMODE is the mode bits used to create a shadow directory.
+ */
+#define VRWXMODE (VREAD|VWRITE|VEXEC)
+#define VRWMODE (VREAD|VWRITE)
+#define UN_DIRMODE ((VRWXMODE)|(VRWXMODE>>3)|(VRWXMODE>>6))
+#define UN_FILEMODE ((VRWMODE)|(VRWMODE>>3)|(VRWMODE>>6))
+
+/*
+ * A cache of vnode references
+ */
+struct union_node {
+ LIST_ENTRY(union_node) un_cache; /* Hash chain */
+ struct vnode *un_vnode; /* Back pointer */
+ struct vnode *un_uppervp; /* overlaying object */
+ struct vnode *un_lowervp; /* underlying object */
+ struct vnode *un_dirvp; /* Parent dir of uppervp */
+ struct vnode *un_pvp; /* Parent vnode */
+ char *un_path; /* saved component name */
+ int un_hash; /* saved un_path hash value */
+ int un_openl; /* # of opens on lowervp */
+ unsigned int un_flags;
+ struct vnode **un_dircache; /* cached union stack */
+ off_t un_uppersz; /* size of upper object */
+ off_t un_lowersz; /* size of lower object */
+#ifdef DIAGNOSTIC
+ pid_t un_pid;
+#endif
+};
+
+#define UN_WANTED 0x01
+#define UN_LOCKED 0x02
+#define UN_ULOCK 0x04 /* Upper node is locked */
+#define UN_KLOCK 0x08 /* Keep upper node locked on vput */
+#define UN_CACHED 0x10 /* In union cache */
+
+extern int union_allocvp __P((struct vnode **, struct mount *,
+ struct vnode *, struct vnode *,
+ struct componentname *, struct vnode *,
+ struct vnode *, int));
+extern int union_copyfile __P((struct vnode *, struct vnode *,
+ struct ucred *, struct proc *));
+extern int union_copyup __P((struct union_node *, int, struct ucred *,
+ struct proc *));
+extern void union_diruncache __P((struct union_node *));
+extern int union_dowhiteout __P((struct union_node *, struct ucred *,
+ struct proc *));
+extern int union_mkshadow __P((struct union_mount *, struct vnode *,
+ struct componentname *, struct vnode **));
+extern int union_mkwhiteout __P((struct union_mount *, struct vnode *,
+ struct componentname *, char *));
+extern int union_vn_create __P((struct vnode **, struct union_node *,
+ struct proc *));
+extern int union_cn_close __P((struct vnode *, int, struct ucred *,
+ struct proc *));
+extern void union_removed_upper __P((struct union_node *un));
+extern struct vnode *union_lowervp __P((struct vnode *));
+extern void union_newlower __P((struct union_node *, struct vnode *));
+extern void union_newupper __P((struct union_node *, struct vnode *));
+extern void union_newsize __P((struct vnode *, off_t, off_t));
+
+#define MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data))
+#define VTOUNION(vp) ((struct union_node *)(vp)->v_data)
+#define UNIONTOV(un) ((un)->un_vnode)
+#define LOWERVP(vp) (VTOUNION(vp)->un_lowervp)
+#define UPPERVP(vp) (VTOUNION(vp)->un_uppervp)
+#define OTHERVP(vp) (UPPERVP(vp) ? UPPERVP(vp) : LOWERVP(vp))
+
+extern int (**union_vnodeop_p)();
+extern struct vfsops union_vfsops;
+#endif /* _KERNEL */
diff --git a/sys/miscfs/union/union_subr.c b/sys/miscfs/union/union_subr.c
new file mode 100644
index 00000000000..4a74d33cd60
--- /dev/null
+++ b/sys/miscfs/union/union_subr.c
@@ -0,0 +1,1099 @@
+/* $NetBSD: union_subr.c,v 1.17 1995/10/05 06:26:12 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1994 Jan-Simon Pendry
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)union_subr.c 8.16 (Berkeley) 12/10/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/vnode.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <vm/vm.h> /* for vnode_pager_setsize */
+#include <miscfs/union/union.h>
+
+#ifdef DIAGNOSTIC
+#include <sys/proc.h>
+#endif
+
+/* must be power of two, otherwise change UNION_HASH() */
+#define NHASH 32
+
+/* unsigned int ... */
+#define UNION_HASH(u, l) \
+ (((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1))
+
+static LIST_HEAD(unhead, union_node) unhead[NHASH];
+static int unvplock[NHASH];
+
+int
+union_init()
+{
+ int i;
+
+ for (i = 0; i < NHASH; i++)
+ LIST_INIT(&unhead[i]);
+ bzero((caddr_t) unvplock, sizeof(unvplock));
+}
+
+static int
+union_list_lock(ix)
+ int ix;
+{
+
+ if (unvplock[ix] & UN_LOCKED) {
+ unvplock[ix] |= UN_WANTED;
+ sleep((caddr_t) &unvplock[ix], PINOD);
+ return (1);
+ }
+
+ unvplock[ix] |= UN_LOCKED;
+
+ return (0);
+}
+
+static void
+union_list_unlock(ix)
+ int ix;
+{
+
+ unvplock[ix] &= ~UN_LOCKED;
+
+ if (unvplock[ix] & UN_WANTED) {
+ unvplock[ix] &= ~UN_WANTED;
+ wakeup((caddr_t) &unvplock[ix]);
+ }
+}
+
+void
+union_updatevp(un, uppervp, lowervp)
+ struct union_node *un;
+ struct vnode *uppervp;
+ struct vnode *lowervp;
+{
+ int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);
+ int nhash = UNION_HASH(uppervp, lowervp);
+ int docache = (lowervp != NULLVP || uppervp != NULLVP);
+
+ /*
+ * Ensure locking is ordered from lower to higher
+ * to avoid deadlocks.
+ */
+ if (nhash < ohash)
+ while (union_list_lock(nhash))
+ continue;
+ while (union_list_lock(ohash))
+ continue;
+ if (nhash > ohash)
+ while (union_list_lock(nhash))
+ continue;
+
+ if (ohash != nhash || !docache) {
+ if (un->un_flags & UN_CACHED) {
+ un->un_flags &= ~UN_CACHED;
+ LIST_REMOVE(un, un_cache);
+ }
+ }
+
+ if (ohash != nhash)
+ union_list_unlock(ohash);
+
+ if (un->un_lowervp != lowervp) {
+ if (un->un_lowervp) {
+ vrele(un->un_lowervp);
+ if (un->un_path) {
+ free(un->un_path, M_TEMP);
+ un->un_path = 0;
+ }
+ if (un->un_dirvp) {
+ vrele(un->un_dirvp);
+ un->un_dirvp = NULLVP;
+ }
+ }
+ un->un_lowervp = lowervp;
+ un->un_lowersz = VNOVAL;
+ }
+
+ if (un->un_uppervp != uppervp) {
+ if (un->un_uppervp)
+ vrele(un->un_uppervp);
+
+ un->un_uppervp = uppervp;
+ un->un_uppersz = VNOVAL;
+ }
+
+ if (docache && (ohash != nhash)) {
+ LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
+ un->un_flags |= UN_CACHED;
+ }
+
+ union_list_unlock(nhash);
+}
+
+void
+union_newlower(un, lowervp)
+ struct union_node *un;
+ struct vnode *lowervp;
+{
+
+ union_updatevp(un, un->un_uppervp, lowervp);
+}
+
+void
+union_newupper(un, uppervp)
+ struct union_node *un;
+ struct vnode *uppervp;
+{
+
+ union_updatevp(un, uppervp, un->un_lowervp);
+}
+
+/*
+ * Keep track of size changes in the underlying vnodes.
+ * If the size changes, then callback to the vm layer
+ * giving priority to the upper layer size.
+ */
+void
+union_newsize(vp, uppersz, lowersz)
+ struct vnode *vp;
+ off_t uppersz, lowersz;
+{
+ struct union_node *un;
+ off_t sz;
+
+ /* only interested in regular files */
+ if (vp->v_type != VREG)
+ return;
+
+ un = VTOUNION(vp);
+ sz = VNOVAL;
+
+ if ((uppersz != VNOVAL) && (un->un_uppersz != uppersz)) {
+ un->un_uppersz = uppersz;
+ if (sz == VNOVAL)
+ sz = un->un_uppersz;
+ }
+
+ if ((lowersz != VNOVAL) && (un->un_lowersz != lowersz)) {
+ un->un_lowersz = lowersz;
+ if (sz == VNOVAL)
+ sz = un->un_lowersz;
+ }
+
+ if (sz != VNOVAL) {
+#ifdef UNION_DIAGNOSTIC
+ printf("union: %s size now %ld\n",
+ uppersz != VNOVAL ? "upper" : "lower", (long) sz);
+#endif
+ vnode_pager_setsize(vp, sz);
+ }
+}
+
+/*
+ * allocate a union_node/vnode pair. the vnode is
+ * referenced and locked. the new vnode is returned
+ * via (vpp). (mp) is the mountpoint of the union filesystem,
+ * (dvp) is the parent directory where the upper layer object
+ * should exist (but doesn't) and (cnp) is the componentname
+ * information which is partially copied to allow the upper
+ * layer object to be created at a later time. (uppervp)
+ * and (lowervp) reference the upper and lower layer objects
+ * being mapped. either, but not both, can be nil.
+ * if supplied, (uppervp) is locked.
+ * the reference is either maintained in the new union_node
+ * object which is allocated, or they are vrele'd.
+ *
+ * all union_nodes are maintained on a singly-linked
+ * list. new nodes are only allocated when they cannot
+ * be found on this list. entries on the list are
+ * removed when the vfs reclaim entry is called.
+ *
+ * a single lock is kept for the entire list. this is
+ * needed because the getnewvnode() function can block
+ * waiting for a vnode to become free, in which case there
+ * may be more than one process trying to get the same
+ * vnode. this lock is only taken if we are going to
+ * call getnewvnode, since the kernel itself is single-threaded.
+ *
+ * if an entry is found on the list, then call vget() to
+ * take a reference. this is done because there may be
+ * zero references to it and so it needs to removed from
+ * the vnode free list.
+ */
+int
+union_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp, docache)
+ struct vnode **vpp;
+ struct mount *mp;
+ struct vnode *undvp; /* parent union vnode */
+ struct vnode *dvp; /* may be null */
+ struct componentname *cnp; /* may be null */
+ struct vnode *uppervp; /* may be null */
+ struct vnode *lowervp; /* may be null */
+ int docache;
+{
+ int error;
+ struct union_node *un;
+ struct union_node **pp;
+ struct vnode *xlowervp = NULLVP;
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ int hash;
+ int vflag;
+ int try;
+
+ if (uppervp == NULLVP && lowervp == NULLVP)
+ panic("union: unidentifiable allocation");
+
+ if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
+ xlowervp = lowervp;
+ lowervp = NULLVP;
+ }
+
+ /* detect the root vnode (and aliases) */
+ vflag = 0;
+ if ((uppervp == um->um_uppervp) &&
+ ((lowervp == NULLVP) || lowervp == um->um_lowervp)) {
+ if (lowervp == NULLVP) {
+ lowervp = um->um_lowervp;
+ if (lowervp != NULLVP)
+ VREF(lowervp);
+ }
+ vflag = VROOT;
+ }
+
+loop:
+ if (!docache) {
+ un = 0;
+ } else for (try = 0; try < 3; try++) {
+ switch (try) {
+ case 0:
+ if (lowervp == NULLVP)
+ continue;
+ hash = UNION_HASH(uppervp, lowervp);
+ break;
+
+ case 1:
+ if (uppervp == NULLVP)
+ continue;
+ hash = UNION_HASH(uppervp, NULLVP);
+ break;
+
+ case 2:
+ if (lowervp == NULLVP)
+ continue;
+ hash = UNION_HASH(NULLVP, lowervp);
+ break;
+ }
+
+ while (union_list_lock(hash))
+ continue;
+
+ for (un = unhead[hash].lh_first; un != 0;
+ un = un->un_cache.le_next) {
+ if ((un->un_lowervp == lowervp ||
+ un->un_lowervp == NULLVP) &&
+ (un->un_uppervp == uppervp ||
+ un->un_uppervp == NULLVP) &&
+ (UNIONTOV(un)->v_mount == mp)) {
+ if (vget(UNIONTOV(un), 0)) {
+ union_list_unlock(hash);
+ goto loop;
+ }
+ break;
+ }
+ }
+
+ union_list_unlock(hash);
+
+ if (un)
+ break;
+ }
+
+ if (un) {
+ /*
+ * Obtain a lock on the union_node.
+ * uppervp is locked, though un->un_uppervp
+ * may not be. this doesn't break the locking
+ * hierarchy since in the case that un->un_uppervp
+ * is not yet locked it will be vrele'd and replaced
+ * with uppervp.
+ */
+
+ if ((dvp != NULLVP) && (uppervp == dvp)) {
+ /*
+ * Access ``.'', so (un) will already
+ * be locked. Since this process has
+ * the lock on (uppervp) no other
+ * process can hold the lock on (un).
+ */
+#ifdef DIAGNOSTIC
+ if ((un->un_flags & UN_LOCKED) == 0)
+ panic("union: . not locked");
+ else if (curproc && un->un_pid != curproc->p_pid &&
+ un->un_pid > -1 && curproc->p_pid > -1)
+ panic("union: allocvp not lock owner");
+#endif
+ } else {
+ if (un->un_flags & UN_LOCKED) {
+ vrele(UNIONTOV(un));
+ un->un_flags |= UN_WANTED;
+ sleep((caddr_t)un, PINOD);
+ goto loop;
+ }
+ un->un_flags |= UN_LOCKED;
+
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+ }
+
+ /*
+ * At this point, the union_node is locked,
+ * un->un_uppervp may not be locked, and uppervp
+ * is locked or nil.
+ */
+
+ /*
+ * Save information about the upper layer.
+ */
+ if (uppervp != un->un_uppervp) {
+ union_newupper(un, uppervp);
+ } else if (uppervp) {
+ vrele(uppervp);
+ }
+
+ if (un->un_uppervp) {
+ un->un_flags |= UN_ULOCK;
+ un->un_flags &= ~UN_KLOCK;
+ }
+
+ /*
+ * Save information about the lower layer.
+ * This needs to keep track of pathname
+ * and directory information which union_vn_create
+ * might need.
+ */
+ if (lowervp != un->un_lowervp) {
+ union_newlower(un, lowervp);
+ if (cnp && (lowervp != NULLVP)) {
+ un->un_hash = cnp->cn_hash;
+ un->un_path = malloc(cnp->cn_namelen+1,
+ M_TEMP, M_WAITOK);
+ bcopy(cnp->cn_nameptr, un->un_path,
+ cnp->cn_namelen);
+ un->un_path[cnp->cn_namelen] = '\0';
+ VREF(dvp);
+ un->un_dirvp = dvp;
+ }
+ } else if (lowervp) {
+ vrele(lowervp);
+ }
+ *vpp = UNIONTOV(un);
+ return (0);
+ }
+
+ if (docache) {
+ /*
+ * otherwise lock the vp list while we call getnewvnode
+ * since that can block.
+ */
+ hash = UNION_HASH(uppervp, lowervp);
+
+ if (union_list_lock(hash))
+ goto loop;
+ }
+
+ error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp);
+ if (error) {
+ if (uppervp) {
+ if (dvp == uppervp)
+ vrele(uppervp);
+ else
+ vput(uppervp);
+ }
+ if (lowervp)
+ vrele(lowervp);
+
+ goto out;
+ }
+
+ MALLOC((*vpp)->v_data, void *, sizeof(struct union_node),
+ M_TEMP, M_WAITOK);
+
+ (*vpp)->v_flag |= vflag;
+ if (uppervp)
+ (*vpp)->v_type = uppervp->v_type;
+ else
+ (*vpp)->v_type = lowervp->v_type;
+ un = VTOUNION(*vpp);
+ un->un_vnode = *vpp;
+ un->un_uppervp = uppervp;
+ un->un_uppersz = VNOVAL;
+ un->un_lowervp = lowervp;
+ un->un_lowersz = VNOVAL;
+ un->un_pvp = undvp;
+ if (undvp != NULLVP)
+ VREF(undvp);
+ un->un_dircache = 0;
+ un->un_openl = 0;
+ un->un_flags = UN_LOCKED;
+ if (un->un_uppervp)
+ un->un_flags |= UN_ULOCK;
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+ if (cnp && (lowervp != NULLVP)) {
+ un->un_hash = cnp->cn_hash;
+ un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
+ bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
+ un->un_path[cnp->cn_namelen] = '\0';
+ VREF(dvp);
+ un->un_dirvp = dvp;
+ } else {
+ un->un_hash = 0;
+ un->un_path = 0;
+ un->un_dirvp = 0;
+ }
+
+ if (docache) {
+ LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
+ un->un_flags |= UN_CACHED;
+ }
+
+ if (xlowervp)
+ vrele(xlowervp);
+
+out:
+ if (docache)
+ union_list_unlock(hash);
+
+ return (error);
+}
+
+int
+union_freevp(vp)
+ struct vnode *vp;
+{
+ struct union_node *un = VTOUNION(vp);
+
+ if (un->un_flags & UN_CACHED) {
+ un->un_flags &= ~UN_CACHED;
+ LIST_REMOVE(un, un_cache);
+ }
+
+ if (un->un_pvp != NULLVP)
+ vrele(un->un_pvp);
+ if (un->un_uppervp != NULLVP)
+ vrele(un->un_uppervp);
+ if (un->un_lowervp != NULLVP)
+ vrele(un->un_lowervp);
+ if (un->un_dirvp != NULLVP)
+ vrele(un->un_dirvp);
+ if (un->un_path)
+ free(un->un_path, M_TEMP);
+
+ FREE(vp->v_data, M_TEMP);
+ vp->v_data = 0;
+
+ return (0);
+}
+
+/*
+ * copyfile. copy the vnode (fvp) to the vnode (tvp)
+ * using a sequence of reads and writes. both (fvp)
+ * and (tvp) are locked on entry and exit.
+ */
+int
+union_copyfile(fvp, tvp, cred, p)
+ struct vnode *fvp;
+ struct vnode *tvp;
+ struct ucred *cred;
+ struct proc *p;
+{
+ char *buf;
+ struct uio uio;
+ struct iovec iov;
+ int error = 0;
+
+ /*
+ * strategy:
+ * allocate a buffer of size MAXBSIZE.
+ * loop doing reads and writes, keeping track
+ * of the current uio offset.
+ * give up at the first sign of trouble.
+ */
+
+ uio.uio_procp = p;
+ uio.uio_segflg = UIO_SYSSPACE;
+ uio.uio_offset = 0;
+
+ VOP_UNLOCK(fvp); /* XXX */
+ VOP_LEASE(fvp, p, cred, LEASE_READ);
+ VOP_LOCK(fvp); /* XXX */
+ VOP_UNLOCK(tvp); /* XXX */
+ VOP_LEASE(tvp, p, cred, LEASE_WRITE);
+ VOP_LOCK(tvp); /* XXX */
+
+ buf = malloc(MAXBSIZE, M_TEMP, M_WAITOK);
+
+ /* ugly loop follows... */
+ do {
+ off_t offset = uio.uio_offset;
+
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf;
+ iov.iov_len = MAXBSIZE;
+ uio.uio_resid = iov.iov_len;
+ uio.uio_rw = UIO_READ;
+ error = VOP_READ(fvp, &uio, 0, cred);
+
+ if (error == 0) {
+ uio.uio_iov = &iov;
+ uio.uio_iovcnt = 1;
+ iov.iov_base = buf;
+ iov.iov_len = MAXBSIZE - uio.uio_resid;
+ uio.uio_offset = offset;
+ uio.uio_rw = UIO_WRITE;
+ uio.uio_resid = iov.iov_len;
+
+ if (uio.uio_resid == 0)
+ break;
+
+ do {
+ error = VOP_WRITE(tvp, &uio, 0, cred);
+ } while ((uio.uio_resid > 0) && (error == 0));
+ }
+
+ } while (error == 0);
+
+ free(buf, M_TEMP);
+ return (error);
+}
+
+/*
+ * (un) is assumed to be locked on entry and remains
+ * locked on exit.
+ */
+int
+union_copyup(un, docopy, cred, p)
+ struct union_node *un;
+ int docopy;
+ struct ucred *cred;
+ struct proc *p;
+{
+ int error;
+ struct vnode *lvp, *uvp;
+
+ error = union_vn_create(&uvp, un, p);
+ if (error)
+ return (error);
+
+ /* at this point, uppervp is locked */
+ union_newupper(un, uvp);
+ un->un_flags |= UN_ULOCK;
+
+ lvp = un->un_lowervp;
+
+ if (docopy) {
+ /*
+ * XX - should not ignore errors
+ * from VOP_CLOSE
+ */
+ VOP_LOCK(lvp);
+ error = VOP_OPEN(lvp, FREAD, cred, p);
+ if (error == 0) {
+ error = union_copyfile(lvp, uvp, cred, p);
+ VOP_UNLOCK(lvp);
+ (void) VOP_CLOSE(lvp, FREAD, cred, p);
+ }
+#ifdef UNION_DIAGNOSTIC
+ if (error == 0)
+ uprintf("union: copied up %s\n", un->un_path);
+#endif
+
+ }
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(uvp);
+ union_vn_close(uvp, FWRITE, cred, p);
+ VOP_LOCK(uvp);
+ un->un_flags |= UN_ULOCK;
+
+ /*
+ * Subsequent IOs will go to the top layer, so
+ * call close on the lower vnode and open on the
+ * upper vnode to ensure that the filesystem keeps
+ * its references counts right. This doesn't do
+ * the right thing with (cred) and (FREAD) though.
+ * Ignoring error returns is not right, either.
+ */
+ if (error == 0) {
+ int i;
+
+ for (i = 0; i < un->un_openl; i++) {
+ (void) VOP_CLOSE(lvp, FREAD, cred, p);
+ (void) VOP_OPEN(uvp, FREAD, cred, p);
+ }
+ un->un_openl = 0;
+ }
+
+ return (error);
+
+}
+
+static int
+union_relookup(um, dvp, vpp, cnp, cn, path, pathlen)
+ struct union_mount *um;
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+ struct componentname *cn;
+ char *path;
+ int pathlen;
+{
+ int error;
+
+ /*
+ * A new componentname structure must be faked up because
+ * there is no way to know where the upper level cnp came
+ * from or what it is being used for. This must duplicate
+ * some of the work done by NDINIT, some of the work done
+ * by namei, some of the work done by lookup and some of
+ * the work done by VOP_LOOKUP when given a CREATE flag.
+ * Conclusion: Horrible.
+ *
+ * The pathname buffer will be FREEed by VOP_MKDIR.
+ */
+ cn->cn_namelen = pathlen;
+ cn->cn_pnbuf = malloc(cn->cn_namelen+1, M_NAMEI, M_WAITOK);
+ bcopy(path, cn->cn_pnbuf, cn->cn_namelen);
+ cn->cn_pnbuf[cn->cn_namelen] = '\0';
+
+ cn->cn_nameiop = CREATE;
+ cn->cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
+ cn->cn_proc = cnp->cn_proc;
+ if (um->um_op == UNMNT_ABOVE)
+ cn->cn_cred = cnp->cn_cred;
+ else
+ cn->cn_cred = um->um_cred;
+ cn->cn_nameptr = cn->cn_pnbuf;
+ cn->cn_hash = cnp->cn_hash;
+ cn->cn_consume = cnp->cn_consume;
+
+ VREF(dvp);
+ error = relookup(dvp, vpp, cn);
+ if (!error)
+ vrele(dvp);
+
+ return (error);
+}
+
+/*
+ * Create a shadow directory in the upper layer.
+ * The new vnode is returned locked.
+ *
+ * (um) points to the union mount structure for access to the
+ * the mounting process's credentials.
+ * (dvp) is the directory in which to create the shadow directory.
+ * it is unlocked on entry and exit.
+ * (cnp) is the componentname to be created.
+ * (vpp) is the returned newly created shadow directory, which
+ * is returned locked.
+ */
+int
+union_mkshadow(um, dvp, cnp, vpp)
+ struct union_mount *um;
+ struct vnode *dvp;
+ struct componentname *cnp;
+ struct vnode **vpp;
+{
+ int error;
+ struct vattr va;
+ struct proc *p = cnp->cn_proc;
+ struct componentname cn;
+
+ error = union_relookup(um, dvp, vpp, cnp, &cn,
+ cnp->cn_nameptr, cnp->cn_namelen);
+ if (error)
+ return (error);
+
+ if (*vpp) {
+ VOP_ABORTOP(dvp, &cn);
+ VOP_UNLOCK(dvp);
+ vrele(*vpp);
+ *vpp = NULLVP;
+ return (EEXIST);
+ }
+
+ /*
+ * policy: when creating the shadow directory in the
+ * upper layer, create it owned by the user who did
+ * the mount, group from parent directory, and mode
+ * 777 modified by umask (ie mostly identical to the
+ * mkdir syscall). (jsp, kb)
+ */
+
+ VATTR_NULL(&va);
+ va.va_type = VDIR;
+ va.va_mode = um->um_cmode;
+
+ /* VOP_LEASE: dvp is locked */
+ VOP_LEASE(dvp, p, cn.cn_cred, LEASE_WRITE);
+
+ error = VOP_MKDIR(dvp, vpp, &cn, &va);
+ return (error);
+}
+
+/*
+ * Create a whiteout entry in the upper layer.
+ *
+ * (um) points to the union mount structure for access to the
+ * the mounting process's credentials.
+ * (dvp) is the directory in which to create the whiteout.
+ * it is locked on entry and exit.
+ * (cnp) is the componentname to be created.
+ */
+int
+union_mkwhiteout(um, dvp, cnp, path)
+ struct union_mount *um;
+ struct vnode *dvp;
+ struct componentname *cnp;
+ char *path;
+{
+ int error;
+ struct vattr va;
+ struct proc *p = cnp->cn_proc;
+ struct vnode *wvp;
+ struct componentname cn;
+
+ VOP_UNLOCK(dvp);
+ error = union_relookup(um, dvp, &wvp, cnp, &cn, path, strlen(path));
+ if (error) {
+ VOP_LOCK(dvp);
+ return (error);
+ }
+
+ if (wvp) {
+ VOP_ABORTOP(dvp, &cn);
+ vrele(dvp);
+ vrele(wvp);
+ return (EEXIST);
+ }
+
+ /* VOP_LEASE: dvp is locked */
+ VOP_LEASE(dvp, p, p->p_ucred, LEASE_WRITE);
+
+ error = VOP_WHITEOUT(dvp, &cn, CREATE);
+ if (error)
+ VOP_ABORTOP(dvp, &cn);
+
+ vrele(dvp);
+
+ return (error);
+}
+
+/*
+ * union_vn_create: creates and opens a new shadow file
+ * on the upper union layer. this function is similar
+ * in spirit to calling vn_open but it avoids calling namei().
+ * the problem with calling namei is that a) it locks too many
+ * things, and b) it doesn't start at the "right" directory,
+ * whereas relookup is told where to start.
+ */
+int
+union_vn_create(vpp, un, p)
+ struct vnode **vpp;
+ struct union_node *un;
+ struct proc *p;
+{
+ struct vnode *vp;
+ struct ucred *cred = p->p_ucred;
+ struct vattr vat;
+ struct vattr *vap = &vat;
+ int fmode = FFLAGS(O_WRONLY|O_CREAT|O_TRUNC|O_EXCL);
+ int error;
+ int cmode = UN_FILEMODE & ~p->p_fd->fd_cmask;
+ char *cp;
+ struct componentname cn;
+
+ *vpp = NULLVP;
+
+ /*
+ * Build a new componentname structure (for the same
+ * reasons outlines in union_mkshadow).
+ * The difference here is that the file is owned by
+ * the current user, rather than by the person who
+ * did the mount, since the current user needs to be
+ * able to write the file (that's why it is being
+ * copied in the first place).
+ */
+ cn.cn_namelen = strlen(un->un_path);
+ cn.cn_pnbuf = (caddr_t) malloc(cn.cn_namelen, M_NAMEI, M_WAITOK);
+ bcopy(un->un_path, cn.cn_pnbuf, cn.cn_namelen+1);
+ cn.cn_nameiop = CREATE;
+ cn.cn_flags = (LOCKPARENT|HASBUF|SAVENAME|SAVESTART|ISLASTCN);
+ cn.cn_proc = p;
+ cn.cn_cred = p->p_ucred;
+ cn.cn_nameptr = cn.cn_pnbuf;
+ cn.cn_hash = un->un_hash;
+ cn.cn_consume = 0;
+
+ VREF(un->un_dirvp);
+ if (error = relookup(un->un_dirvp, &vp, &cn))
+ return (error);
+ vrele(un->un_dirvp);
+
+ if (vp) {
+ VOP_ABORTOP(un->un_dirvp, &cn);
+ if (un->un_dirvp == vp)
+ vrele(un->un_dirvp);
+ else
+ vput(un->un_dirvp);
+ vrele(vp);
+ return (EEXIST);
+ }
+
+ /*
+ * Good - there was no race to create the file
+ * so go ahead and create it. The permissions
+ * on the file will be 0666 modified by the
+ * current user's umask. Access to the file, while
+ * it is unioned, will require access to the top *and*
+ * bottom files. Access when not unioned will simply
+ * require access to the top-level file.
+ * TODO: confirm choice of access permissions.
+ */
+ VATTR_NULL(vap);
+ vap->va_type = VREG;
+ vap->va_mode = cmode;
+ VOP_LEASE(un->un_dirvp, p, cred, LEASE_WRITE);
+ if (error = VOP_CREATE(un->un_dirvp, &vp, &cn, vap))
+ return (error);
+
+ if (error = VOP_OPEN(vp, fmode, cred, p)) {
+ vput(vp);
+ return (error);
+ }
+
+ vp->v_writecount++;
+ *vpp = vp;
+ return (0);
+}
+
+int
+union_vn_close(vp, fmode, cred, p)
+ struct vnode *vp;
+ int fmode;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ if (fmode & FWRITE)
+ --vp->v_writecount;
+ return (VOP_CLOSE(vp, fmode, cred, p));
+}
+
+void
+union_removed_upper(un)
+ struct union_node *un;
+{
+
+ /*
+ * We do not set the uppervp to NULLVP here, because lowervp
+ * may also be NULLVP, so this routine would end up creating
+ * a bogus union node with no upper or lower VP (that causes
+ * pain in many places that assume at least one VP exists).
+ * Since we've removed this node from the cache hash chains,
+ * it won't be found again. When all current holders
+ * release it, union_inactive() will vgone() it.
+ */
+ union_diruncache(un);
+
+ if (un->un_flags & UN_CACHED) {
+ un->un_flags &= ~UN_CACHED;
+ LIST_REMOVE(un, un_cache);
+ }
+
+ if (un->un_flags & UN_ULOCK) {
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ }
+}
+
+#if 0
+struct vnode *
+union_lowervp(vp)
+ struct vnode *vp;
+{
+ struct union_node *un = VTOUNION(vp);
+
+ if ((un->un_lowervp != NULLVP) &&
+ (vp->v_type == un->un_lowervp->v_type)) {
+ if (vget(un->un_lowervp, 0) == 0)
+ return (un->un_lowervp);
+ }
+
+ return (NULLVP);
+}
+#endif
+
+/*
+ * determine whether a whiteout is needed
+ * during a remove/rmdir operation.
+ */
+int
+union_dowhiteout(un, cred, p)
+ struct union_node *un;
+ struct ucred *cred;
+ struct proc *p;
+{
+ struct vattr va;
+
+ if (un->un_lowervp != NULLVP)
+ return (1);
+
+ if (VOP_GETATTR(un->un_uppervp, &va, cred, p) == 0 &&
+ (va.va_flags & OPAQUE))
+ return (1);
+
+ return (0);
+}
+
+static void
+union_dircache_r(vp, vppp, cntp)
+ struct vnode *vp;
+ struct vnode ***vppp;
+ int *cntp;
+{
+ struct union_node *un;
+
+ if (vp->v_op != union_vnodeop_p) {
+ if (vppp) {
+ VREF(vp);
+ *(*vppp)++ = vp;
+ if (--(*cntp) == 0)
+ panic("union: dircache table too small");
+ } else {
+ (*cntp)++;
+ }
+
+ return;
+ }
+
+ un = VTOUNION(vp);
+ if (un->un_uppervp != NULLVP)
+ union_dircache_r(un->un_uppervp, vppp, cntp);
+ if (un->un_lowervp != NULLVP)
+ union_dircache_r(un->un_lowervp, vppp, cntp);
+}
+
+struct vnode *
+union_dircache(vp)
+ struct vnode *vp;
+{
+ int cnt;
+ struct vnode *nvp = NULLVP;
+ struct vnode **vpp;
+ struct vnode **dircache;
+ int error;
+
+ VOP_LOCK(vp);
+
+ dircache = VTOUNION(vp)->un_dircache;
+ if (dircache == 0) {
+ cnt = 0;
+ union_dircache_r(vp, 0, &cnt);
+ cnt++;
+ dircache = (struct vnode **)
+ malloc(cnt * sizeof(struct vnode *),
+ M_TEMP, M_WAITOK);
+ vpp = dircache;
+ union_dircache_r(vp, &vpp, &cnt);
+ VTOUNION(vp)->un_dircache = dircache;
+ *vpp = NULLVP;
+ vpp = dircache + 1;
+ } else {
+ vpp = dircache;
+ do {
+ if (*vpp++ == VTOUNION(vp)->un_uppervp)
+ break;
+ } while (*vpp != NULLVP);
+ }
+
+ if (*vpp == NULLVP)
+ goto out;
+
+ VOP_LOCK(*vpp);
+ VREF(*vpp);
+ error = union_allocvp(&nvp, vp->v_mount, NULLVP, NULLVP, 0, *vpp, NULLVP, 0);
+ if (!error) {
+ VTOUNION(vp)->un_dircache = 0;
+ VTOUNION(nvp)->un_dircache = dircache;
+ }
+
+out:
+ VOP_UNLOCK(vp);
+ return (nvp);
+}
+
+void
+union_diruncache(un)
+ struct union_node *un;
+{
+ struct vnode **vpp;
+
+ if (un->un_dircache != 0) {
+ for (vpp = un->un_dircache; *vpp != NULLVP; vpp++)
+ vrele(*vpp);
+ free(un->un_dircache, M_TEMP);
+ un->un_dircache = 0;
+ }
+}
diff --git a/sys/miscfs/union/union_vfsops.c b/sys/miscfs/union/union_vfsops.c
new file mode 100644
index 00000000000..f85b9c0d4fc
--- /dev/null
+++ b/sys/miscfs/union/union_vfsops.c
@@ -0,0 +1,540 @@
+/* $NetBSD: union_vfsops.c,v 1.10 1995/06/18 14:47:47 cgd Exp $ */
+
+/*
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)union_vfsops.c 8.13 (Berkeley) 12/10/94
+ */
+
+/*
+ * Union Layer
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/proc.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/filedesc.h>
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
+
+/*
+ * Mount union filesystem
+ */
+int
+union_mount(mp, path, data, ndp, p)
+ struct mount *mp;
+ char *path;
+ caddr_t data;
+ struct nameidata *ndp;
+ struct proc *p;
+{
+ int error = 0;
+ struct union_args args;
+ struct vnode *lowerrootvp = NULLVP;
+ struct vnode *upperrootvp = NULLVP;
+ struct union_mount *um = 0;
+ struct ucred *cred = 0;
+ struct ucred *scred;
+ struct vattr va;
+ char *cp;
+ int len;
+ size_t size;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_mount(mp = %x)\n", mp);
+#endif
+
+ /*
+ * Update is a no-op
+ */
+ if (mp->mnt_flag & MNT_UPDATE) {
+ /*
+ * Need to provide.
+ * 1. a way to convert between rdonly and rdwr mounts.
+ * 2. support for nfs exports.
+ */
+ error = EOPNOTSUPP;
+ goto bad;
+ }
+
+ /*
+ * Get argument
+ */
+ if (error = copyin(data, (caddr_t)&args, sizeof(struct union_args)))
+ goto bad;
+
+ lowerrootvp = mp->mnt_vnodecovered;
+ VREF(lowerrootvp);
+
+ /*
+ * Find upper node.
+ */
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
+ UIO_USERSPACE, args.target, p);
+
+ if (error = namei(ndp))
+ goto bad;
+
+ upperrootvp = ndp->ni_vp;
+ vrele(ndp->ni_dvp);
+ ndp->ni_dvp = NULL;
+
+ if (upperrootvp->v_type != VDIR) {
+ error = EINVAL;
+ goto bad;
+ }
+
+ um = (struct union_mount *) malloc(sizeof(struct union_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+
+ /*
+ * Keep a held reference to the target vnodes.
+ * They are vrele'd in union_unmount.
+ *
+ * Depending on the _BELOW flag, the filesystems are
+ * viewed in a different order. In effect, this is the
+ * same as providing a mount under option to the mount syscall.
+ */
+
+ um->um_op = args.mntflags & UNMNT_OPMASK;
+ switch (um->um_op) {
+ case UNMNT_ABOVE:
+ um->um_lowervp = lowerrootvp;
+ um->um_uppervp = upperrootvp;
+ break;
+
+ case UNMNT_BELOW:
+ um->um_lowervp = upperrootvp;
+ um->um_uppervp = lowerrootvp;
+ break;
+
+ case UNMNT_REPLACE:
+ vrele(lowerrootvp);
+ lowerrootvp = NULLVP;
+ um->um_uppervp = upperrootvp;
+ um->um_lowervp = lowerrootvp;
+ break;
+
+ default:
+ error = EINVAL;
+ goto bad;
+ }
+
+ /*
+ * Unless the mount is readonly, ensure that the top layer
+ * supports whiteout operations
+ */
+ if ((mp->mnt_flag & MNT_RDONLY) == 0) {
+ error = VOP_WHITEOUT(um->um_uppervp, (struct componentname *) 0, LOOKUP);
+ if (error)
+ goto bad;
+ }
+
+ um->um_cred = p->p_ucred;
+ crhold(um->um_cred);
+ um->um_cmode = UN_DIRMODE &~ p->p_fd->fd_cmask;
+
+ /*
+ * Depending on what you think the MNT_LOCAL flag might mean,
+ * you may want the && to be || on the conditional below.
+ * At the moment it has been defined that the filesystem is
+ * only local if it is all local, ie the MNT_LOCAL flag implies
+ * that the entire namespace is local. If you think the MNT_LOCAL
+ * flag implies that some of the files might be stored locally
+ * then you will want to change the conditional.
+ */
+ if (um->um_op == UNMNT_ABOVE) {
+ if (((um->um_lowervp == NULLVP) ||
+ (um->um_lowervp->v_mount->mnt_flag & MNT_LOCAL)) &&
+ (um->um_uppervp->v_mount->mnt_flag & MNT_LOCAL))
+ mp->mnt_flag |= MNT_LOCAL;
+ }
+
+ /*
+ * Copy in the upper layer's RDONLY flag. This is for the benefit
+ * of lookup() which explicitly checks the flag, rather than asking
+ * the filesystem for it's own opinion. This means, that an update
+ * mount of the underlying filesystem to go from rdonly to rdwr
+ * will leave the unioned view as read-only.
+ */
+ mp->mnt_flag |= (um->um_uppervp->v_mount->mnt_flag & MNT_RDONLY);
+
+ mp->mnt_data = (qaddr_t)um;
+ getnewfsid(mp, makefstype(MOUNT_UNION));
+
+ (void) copyinstr(path, mp->mnt_stat.f_mntonname, MNAMELEN - 1, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+
+ switch (um->um_op) {
+ case UNMNT_ABOVE:
+ cp = "<above>:";
+ break;
+ case UNMNT_BELOW:
+ cp = "<below>:";
+ break;
+ case UNMNT_REPLACE:
+ cp = "";
+ break;
+ }
+ len = strlen(cp);
+ bcopy(cp, mp->mnt_stat.f_mntfromname, len);
+
+ cp = mp->mnt_stat.f_mntfromname + len;
+ len = MNAMELEN - len;
+
+ (void) copyinstr(args.target, cp, len - 1, &size);
+ bzero(cp + size, len - size);
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_mount: from %s, on %s\n",
+ mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
+#endif
+ return (0);
+
+bad:
+ if (um)
+ free(um, M_UFSMNT);
+ if (cred)
+ crfree(cred);
+ if (upperrootvp)
+ vrele(upperrootvp);
+ if (lowerrootvp)
+ vrele(lowerrootvp);
+ return (error);
+}
+
+/*
+ * VFS start. Nothing needed here - the start routine
+ * on the underlying filesystem(s) will have been called
+ * when that filesystem was mounted.
+ */
+int
+union_start(mp, flags, p)
+ struct mount *mp;
+ int flags;
+ struct proc *p;
+{
+
+ return (0);
+}
+
+/*
+ * Free reference to union layer
+ */
+int
+union_unmount(mp, mntflags, p)
+ struct mount *mp;
+ int mntflags;
+ struct proc *p;
+{
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ struct vnode *um_rootvp;
+ int error;
+ int freeing;
+ int flags = 0;
+ extern int doforce;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_unmount(mp = %x)\n", mp);
+#endif
+
+ if (mntflags & MNT_FORCE) {
+ /* union can never be rootfs so don't check for it */
+ if (!doforce)
+ return (EINVAL);
+ flags |= FORCECLOSE;
+ }
+
+ if (error = union_root(mp, &um_rootvp))
+ return (error);
+
+ /*
+ * Keep flushing vnodes from the mount list.
+ * This is needed because of the un_pvp held
+ * reference to the parent vnode.
+ * If more vnodes have been freed on a given pass,
+ * the try again. The loop will iterate at most
+ * (d) times, where (d) is the maximum tree depth
+ * in the filesystem.
+ */
+ for (freeing = 0; vflush(mp, um_rootvp, flags) != 0;) {
+ struct vnode *vp;
+ int n;
+
+ /* count #vnodes held on mount list */
+ for (n = 0, vp = mp->mnt_vnodelist.lh_first;
+ vp != NULLVP;
+ vp = vp->v_mntvnodes.le_next)
+ n++;
+
+ /* if this is unchanged then stop */
+ if (n == freeing)
+ break;
+
+ /* otherwise try once more time */
+ freeing = n;
+ }
+
+ /* At this point the root vnode should have a single reference */
+ if (um_rootvp->v_usecount > 1) {
+ vput(um_rootvp);
+ return (EBUSY);
+ }
+
+#ifdef UNION_DIAGNOSTIC
+ vprint("union root", um_rootvp);
+#endif
+ /*
+ * Discard references to upper and lower target vnodes.
+ */
+ if (um->um_lowervp)
+ vrele(um->um_lowervp);
+ vrele(um->um_uppervp);
+ crfree(um->um_cred);
+ /*
+ * Release reference on underlying root vnode
+ */
+ vput(um_rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(um_rootvp);
+ /*
+ * Finally, throw away the union_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ mp->mnt_data = 0;
+ return (0);
+}
+
+int
+union_root(mp, vpp)
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ int error;
+ int loselock;
+
+ /*
+ * Return locked reference to root.
+ */
+ VREF(um->um_uppervp);
+ if ((um->um_op == UNMNT_BELOW) &&
+ VOP_ISLOCKED(um->um_uppervp)) {
+ loselock = 1;
+ } else {
+ VOP_LOCK(um->um_uppervp);
+ loselock = 0;
+ }
+ if (um->um_lowervp)
+ VREF(um->um_lowervp);
+ error = union_allocvp(vpp, mp,
+ (struct vnode *) 0,
+ (struct vnode *) 0,
+ (struct componentname *) 0,
+ um->um_uppervp,
+ um->um_lowervp,
+ 1);
+
+ if (error) {
+ if (!loselock)
+ VOP_UNLOCK(um->um_uppervp);
+ vrele(um->um_uppervp);
+ if (um->um_lowervp)
+ vrele(um->um_lowervp);
+ } else {
+ if (loselock)
+ VTOUNION(*vpp)->un_flags &= ~UN_ULOCK;
+ }
+
+ return (error);
+}
+
+int
+union_quotactl(mp, cmd, uid, arg, p)
+ struct mount *mp;
+ int cmd;
+ uid_t uid;
+ caddr_t arg;
+ struct proc *p;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_statfs(mp, sbp, p)
+ struct mount *mp;
+ struct statfs *sbp;
+ struct proc *p;
+{
+ int error;
+ struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
+ struct statfs mstat;
+ int lbsize;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_statfs(mp = %x, lvp = %x, uvp = %x)\n", mp,
+ um->um_lowervp,
+ um->um_uppervp);
+#endif
+
+ bzero(&mstat, sizeof(mstat));
+
+ if (um->um_lowervp) {
+ error = VFS_STATFS(um->um_lowervp->v_mount, &mstat, p);
+ if (error)
+ return (error);
+ }
+
+ /* now copy across the "interesting" information and fake the rest */
+#if 0
+ sbp->f_type = mstat.f_type;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+#endif
+ lbsize = mstat.f_bsize;
+ sbp->f_blocks = mstat.f_blocks;
+ sbp->f_bfree = mstat.f_bfree;
+ sbp->f_bavail = mstat.f_bavail;
+ sbp->f_files = mstat.f_files;
+ sbp->f_ffree = mstat.f_ffree;
+
+ error = VFS_STATFS(um->um_uppervp->v_mount, &mstat, p);
+ if (error)
+ return (error);
+
+ sbp->f_type = 0;
+ sbp->f_flags = mstat.f_flags;
+ sbp->f_bsize = mstat.f_bsize;
+ sbp->f_iosize = mstat.f_iosize;
+
+ /*
+ * if the lower and upper blocksizes differ, then frig the
+ * block counts so that the sizes reported by df make some
+ * kind of sense. none of this makes sense though.
+ */
+
+ if (mstat.f_bsize != lbsize) {
+ sbp->f_blocks = sbp->f_blocks * lbsize / mstat.f_bsize;
+ sbp->f_bfree = sbp->f_bfree * lbsize / mstat.f_bsize;
+ sbp->f_bavail = sbp->f_bavail * lbsize / mstat.f_bsize;
+ }
+ sbp->f_blocks += mstat.f_blocks;
+ sbp->f_bfree += mstat.f_bfree;
+ sbp->f_bavail += mstat.f_bavail;
+ sbp->f_files += mstat.f_files;
+ sbp->f_ffree += mstat.f_ffree;
+
+ if (sbp != &mp->mnt_stat) {
+ bcopy(&mp->mnt_stat.f_fsid, &sbp->f_fsid, sizeof(sbp->f_fsid));
+ 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);
+}
+
+int
+union_sync(mp, waitfor, cred, p)
+ struct mount *mp;
+ int waitfor;
+ struct ucred *cred;
+ struct proc *p;
+{
+
+ /*
+ * XXX - Assumes no data cached at union layer.
+ */
+ return (0);
+}
+
+int
+union_vget(mp, ino, vpp)
+ struct mount *mp;
+ ino_t ino;
+ struct vnode **vpp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_fhtovp(mp, fidp, nam, vpp, exflagsp, credanonp)
+ struct mount *mp;
+ struct fid *fidp;
+ struct mbuf *nam;
+ struct vnode **vpp;
+ int *exflagsp;
+ struct ucred **credanonp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int
+union_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+
+ return (EOPNOTSUPP);
+}
+
+int union_init __P((void));
+
+struct vfsops union_vfsops = {
+ MOUNT_UNION,
+ union_mount,
+ union_start,
+ union_unmount,
+ union_root,
+ union_quotactl,
+ union_statfs,
+ union_sync,
+ union_vget,
+ union_fhtovp,
+ union_vptofh,
+ union_init,
+};
diff --git a/sys/miscfs/union/union_vnops.c b/sys/miscfs/union/union_vnops.c
new file mode 100644
index 00000000000..d921b568f8c
--- /dev/null
+++ b/sys/miscfs/union/union_vnops.c
@@ -0,0 +1,1606 @@
+/* $NetBSD: union_vnops.c,v 1.25 1995/07/13 13:19:18 mycroft Exp $ */
+
+/*
+ * Copyright (c) 1992, 1993, 1994 The Regents of the University of California.
+ * Copyright (c) 1992, 1993, 1994 Jan-Simon Pendry.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * 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.
+ *
+ * @(#)union_vnops.c 8.22 (Berkeley) 12/10/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/vnode.h>
+#include <sys/mount.h>
+#include <sys/namei.h>
+#include <sys/malloc.h>
+#include <sys/buf.h>
+#include <sys/queue.h>
+#include <miscfs/union/union.h>
+
+#define FIXUP(un) { \
+ if (((un)->un_flags & UN_ULOCK) == 0) { \
+ union_fixup(un); \
+ } \
+}
+
+static void
+union_fixup(un)
+ struct union_node *un;
+{
+
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
+}
+
+static int
+union_lookup1(udvp, dvpp, vpp, cnp)
+ struct vnode *udvp;
+ struct vnode **dvpp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+{
+ int error;
+ struct vnode *tdvp;
+ struct vnode *dvp;
+ struct mount *mp;
+
+ dvp = *dvpp;
+
+ /*
+ * If stepping up the directory tree, check for going
+ * back across the mount point, in which case do what
+ * lookup would do by stepping back down the mount
+ * hierarchy.
+ */
+ if (cnp->cn_flags & ISDOTDOT) {
+ while ((dvp != udvp) && (dvp->v_flag & VROOT)) {
+ /*
+ * Don't do the NOCROSSMOUNT check
+ * at this level. By definition,
+ * union fs deals with namespaces, not
+ * filesystems.
+ */
+ tdvp = dvp;
+ *dvpp = dvp = dvp->v_mount->mnt_vnodecovered;
+ vput(tdvp);
+ VREF(dvp);
+ VOP_LOCK(dvp);
+ }
+ }
+
+ error = VOP_LOOKUP(dvp, &tdvp, cnp);
+ if (error)
+ return (error);
+
+ /*
+ * The parent directory will have been unlocked, unless lookup
+ * found the last component. In which case, re-lock the node
+ * here to allow it to be unlocked again (phew) in union_lookup.
+ */
+ if (dvp != tdvp && !(cnp->cn_flags & ISLASTCN))
+ VOP_LOCK(dvp);
+
+ dvp = tdvp;
+
+ /*
+ * Lastly check if the current node is a mount point in
+ * which case walk up the mount hierarchy making sure not to
+ * bump into the root of the mount tree (ie. dvp != udvp).
+ */
+ while (dvp != udvp && (dvp->v_type == VDIR) &&
+ (mp = dvp->v_mountedhere)) {
+
+ if (mp->mnt_flag & MNT_MLOCK) {
+ mp->mnt_flag |= MNT_MWAIT;
+ sleep((caddr_t) mp, PVFS);
+ continue;
+ }
+
+ if (error = VFS_ROOT(mp, &tdvp)) {
+ vput(dvp);
+ return (error);
+ }
+
+ vput(dvp);
+ dvp = tdvp;
+ }
+
+ *vpp = dvp;
+ return (0);
+}
+
+int
+union_lookup(ap)
+ struct vop_lookup_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ int uerror, lerror;
+ struct vnode *uppervp, *lowervp;
+ struct vnode *upperdvp, *lowerdvp;
+ struct vnode *dvp = ap->a_dvp;
+ struct union_node *dun = VTOUNION(dvp);
+ struct componentname *cnp = ap->a_cnp;
+ int lockparent = cnp->cn_flags & LOCKPARENT;
+ int rdonly = cnp->cn_flags & RDONLY;
+ struct union_mount *um = MOUNTTOUNIONMOUNT(dvp->v_mount);
+ struct ucred *saved_cred;
+ int iswhiteout;
+ struct vattr va;
+
+#ifdef notyet
+ if (cnp->cn_namelen == 3 &&
+ cnp->cn_nameptr[2] == '.' &&
+ cnp->cn_nameptr[1] == '.' &&
+ cnp->cn_nameptr[0] == '.') {
+ dvp = *ap->a_vpp = LOWERVP(ap->a_dvp);
+ if (dvp == NULLVP)
+ return (ENOENT);
+ VREF(dvp);
+ VOP_LOCK(dvp);
+ if (!lockparent || !(cnp->cn_flags & ISLASTCN))
+ VOP_UNLOCK(ap->a_dvp);
+ return (0);
+ }
+#endif
+
+ cnp->cn_flags |= LOCKPARENT;
+
+ upperdvp = dun->un_uppervp;
+ lowerdvp = dun->un_lowervp;
+ uppervp = NULLVP;
+ lowervp = NULLVP;
+ iswhiteout = 0;
+
+ /*
+ * do the lookup in the upper level.
+ * if that level comsumes additional pathnames,
+ * then assume that something special is going
+ * on and just return that vnode.
+ */
+ if (upperdvp != NULLVP) {
+ FIXUP(dun);
+ uerror = union_lookup1(um->um_uppervp, &upperdvp,
+ &uppervp, cnp);
+ /*if (uppervp == upperdvp)
+ dun->un_flags |= UN_KLOCK;*/
+
+ if (cnp->cn_consume != 0) {
+ *ap->a_vpp = uppervp;
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ return (uerror);
+ }
+ if (uerror == ENOENT || uerror == EJUSTRETURN) {
+ if (cnp->cn_flags & ISWHITEOUT) {
+ iswhiteout = 1;
+ } else if (lowerdvp != NULLVP) {
+ lerror = VOP_GETATTR(upperdvp, &va,
+ cnp->cn_cred, cnp->cn_proc);
+ if (lerror == 0 && (va.va_flags & OPAQUE))
+ iswhiteout = 1;
+ }
+ }
+ } else {
+ uerror = ENOENT;
+ }
+
+ /*
+ * in a similar way to the upper layer, do the lookup
+ * in the lower layer. this time, if there is some
+ * component magic going on, then vput whatever we got
+ * back from the upper layer and return the lower vnode
+ * instead.
+ */
+ if (lowerdvp != NULLVP && !iswhiteout) {
+ int nameiop;
+
+ VOP_LOCK(lowerdvp);
+
+ /*
+ * Only do a LOOKUP on the bottom node, since
+ * we won't be making changes to it anyway.
+ */
+ nameiop = cnp->cn_nameiop;
+ cnp->cn_nameiop = LOOKUP;
+ if (um->um_op == UNMNT_BELOW) {
+ saved_cred = cnp->cn_cred;
+ cnp->cn_cred = um->um_cred;
+ }
+ lerror = union_lookup1(um->um_lowervp, &lowerdvp,
+ &lowervp, cnp);
+ if (um->um_op == UNMNT_BELOW)
+ cnp->cn_cred = saved_cred;
+ cnp->cn_nameiop = nameiop;
+
+ if (lowervp != lowerdvp)
+ VOP_UNLOCK(lowerdvp);
+
+ if (cnp->cn_consume != 0) {
+ if (uppervp != NULLVP) {
+ if (uppervp == upperdvp)
+ vrele(uppervp);
+ else
+ vput(uppervp);
+ uppervp = NULLVP;
+ }
+ *ap->a_vpp = lowervp;
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+ return (lerror);
+ }
+ } else {
+ lerror = ENOENT;
+ if ((cnp->cn_flags & ISDOTDOT) && dun->un_pvp != NULLVP) {
+ lowervp = LOWERVP(dun->un_pvp);
+ if (lowervp != NULLVP) {
+ VREF(lowervp);
+ VOP_LOCK(lowervp);
+ lerror = 0;
+ }
+ }
+ }
+
+ if (!lockparent)
+ cnp->cn_flags &= ~LOCKPARENT;
+
+ /*
+ * at this point, we have uerror and lerror indicating
+ * possible errors with the lookups in the upper and lower
+ * layers. additionally, uppervp and lowervp are (locked)
+ * references to existing vnodes in the upper and lower layers.
+ *
+ * there are now three cases to consider.
+ * 1. if both layers returned an error, then return whatever
+ * error the upper layer generated.
+ *
+ * 2. if the top layer failed and the bottom layer succeeded
+ * then two subcases occur.
+ * a. the bottom vnode is not a directory, in which
+ * case just return a new union vnode referencing
+ * an empty top layer and the existing bottom layer.
+ * b. the bottom vnode is a directory, in which case
+ * create a new directory in the top-level and
+ * continue as in case 3.
+ *
+ * 3. if the top layer succeeded then return a new union
+ * vnode referencing whatever the new top layer and
+ * whatever the bottom layer returned.
+ */
+
+ *ap->a_vpp = NULLVP;
+
+ /* case 1. */
+ if ((uerror != 0) && (lerror != 0)) {
+ return (uerror);
+ }
+
+ /* case 2. */
+ if (uerror != 0 /* && (lerror == 0) */ ) {
+ if (lowervp->v_type == VDIR) { /* case 2b. */
+ dun->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(upperdvp);
+ uerror = union_mkshadow(um, upperdvp, cnp, &uppervp);
+ VOP_LOCK(upperdvp);
+ dun->un_flags |= UN_ULOCK;
+
+ if (uerror) {
+ if (lowervp != NULLVP) {
+ vput(lowervp);
+ lowervp = NULLVP;
+ }
+ return (uerror);
+ }
+ }
+ }
+
+ if (lowervp != NULLVP)
+ VOP_UNLOCK(lowervp);
+
+ error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
+ uppervp, lowervp, 1);
+
+ if (error) {
+ if (uppervp != NULLVP)
+ vput(uppervp);
+ if (lowervp != NULLVP)
+ vrele(lowervp);
+ } else {
+ if (*ap->a_vpp != dvp)
+ if (!lockparent || !(cnp->cn_flags & ISLASTCN))
+ VOP_UNLOCK(dvp);
+ }
+
+ return (error);
+}
+
+int
+union_create(ap)
+ struct vop_create_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp;
+
+ if ((dvp = un->un_uppervp) != NULLVP) {
+ int error;
+ struct vnode *vp;
+ struct mount *mp;
+
+ FIXUP(un);
+
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ mp = ap->a_dvp->v_mount;
+ vput(ap->a_dvp);
+ error = VOP_CREATE(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ error = union_allocvp(
+ ap->a_vpp,
+ mp,
+ NULLVP,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP,
+ 1);
+ if (error)
+ vput(vp);
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_whiteout(ap)
+ struct vop_whiteout_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ int a_flags;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+
+ if (un->un_uppervp == NULLVP)
+ return (EOPNOTSUPP);
+
+ FIXUP(un);
+ return (VOP_WHITEOUT(un->un_uppervp, ap->a_cnp, ap->a_flags));
+}
+
+int
+union_mknod(ap)
+ struct vop_mknod_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp;
+
+ if ((dvp = un->un_uppervp) != NULLVP) {
+ int error;
+ struct vnode *vp;
+ struct mount *mp;
+
+ FIXUP(un);
+
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ mp = ap->a_dvp->v_mount;
+ vput(ap->a_dvp);
+ error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ if (vp != NULLVP) {
+ error = union_allocvp(
+ ap->a_vpp,
+ mp,
+ NULLVP,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP,
+ 1);
+ if (error)
+ vput(vp);
+ }
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_open(ap)
+ struct vop_open_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *tvp;
+ int mode = ap->a_mode;
+ struct ucred *cred = ap->a_cred;
+ struct proc *p = ap->a_p;
+ int error;
+
+ /*
+ * If there is an existing upper vp then simply open that.
+ */
+ tvp = un->un_uppervp;
+ if (tvp == NULLVP) {
+ /*
+ * If the lower vnode is being opened for writing, then
+ * copy the file contents to the upper vnode and open that,
+ * otherwise can simply open the lower vnode.
+ */
+ tvp = un->un_lowervp;
+ if ((ap->a_mode & FWRITE) && (tvp->v_type == VREG)) {
+ error = union_copyup(un, (mode&O_TRUNC) == 0, cred, p);
+ if (error == 0)
+ error = VOP_OPEN(un->un_uppervp, mode, cred, p);
+ return (error);
+ }
+
+ /*
+ * Just open the lower vnode
+ */
+ un->un_openl++;
+ VOP_LOCK(tvp);
+ error = VOP_OPEN(tvp, mode, cred, p);
+ VOP_UNLOCK(tvp);
+
+ return (error);
+ }
+
+ FIXUP(un);
+
+ error = VOP_OPEN(tvp, mode, cred, p);
+
+ return (error);
+}
+
+int
+union_close(ap)
+ struct vop_close_args /* {
+ struct vnode *a_vp;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp;
+
+ if ((vp = un->un_uppervp) == NULLVP) {
+#ifdef UNION_DIAGNOSTIC
+ if (un->un_openl <= 0)
+ panic("union: un_openl cnt");
+#endif
+ --un->un_openl;
+ vp = un->un_lowervp;
+ }
+
+#ifdef DIAGNOSTIC
+ /*
+ * We should never encounter a vnode with both upper and
+ * lower vnodes NULL.
+ */
+ if (vp == NULLVP) {
+ vprint("empty union vnode", vp);
+ panic("union_close empty vnode");
+ }
+#endif
+
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_close), ap));
+}
+
+/*
+ * Check access permission on the union vnode.
+ * The access check being enforced is to check
+ * against both the underlying vnode, and any
+ * copied vnode. This ensures that no additional
+ * file permissions are given away simply because
+ * the user caused an implicit file copy.
+ */
+int
+union_access(ap)
+ struct vop_access_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ int a_mode;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ int error = EACCES;
+ struct vnode *vp;
+
+ if ((vp = un->un_uppervp) != NULLVP) {
+ FIXUP(un);
+ return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
+ }
+
+ if ((vp = un->un_lowervp) != NULLVP) {
+ VOP_LOCK(vp);
+ error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
+ if (error == 0) {
+ struct union_mount *um = MOUNTTOUNIONMOUNT(ap->a_vp->v_mount);
+
+ if (um->um_op == UNMNT_BELOW)
+ error = VOP_ACCESS(vp, ap->a_mode,
+ um->um_cred, ap->a_p);
+ }
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ }
+
+ return (error);
+}
+
+/*
+ * We handle getattr only to change the fsid and
+ * track object sizes
+ */
+int
+union_getattr(ap)
+ struct vop_getattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error;
+ struct union_node *un = VTOUNION(ap->a_vp);
+ struct vnode *vp = un->un_uppervp;
+ struct vattr *vap;
+ struct vattr va;
+
+
+ /*
+ * Some programs walk the filesystem hierarchy by counting
+ * links to directories to avoid stat'ing all the time.
+ * This means the link count on directories needs to be "correct".
+ * The only way to do that is to call getattr on both layers
+ * and fix up the link count. The link count will not necessarily
+ * be accurate but will be large enough to defeat the tree walkers.
+ */
+
+ vap = ap->a_vap;
+
+ if ((vp = un->un_uppervp) != NULLVP) {
+ /*
+ * It's not clear whether VOP_GETATTR is to be
+ * called with the vnode locked or not. stat() calls
+ * it with (vp) locked, and fstat calls it with
+ * (vp) unlocked.
+ * In the mean time, compensate here by checking
+ * the union_node's lock flag.
+ */
+ if (un->un_flags & UN_LOCKED)
+ FIXUP(un);
+
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ if (error)
+ return (error);
+ union_newsize(ap->a_vp, vap->va_size, VNOVAL);
+ }
+
+ if (vp == NULLVP) {
+ vp = un->un_lowervp;
+ } else if (vp->v_type == VDIR) {
+ vp = un->un_lowervp;
+ vap = &va;
+ } else {
+ vp = NULLVP;
+ }
+
+ if (vp != NULLVP) {
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ if (error)
+ return (error);
+ union_newsize(ap->a_vp, VNOVAL, vap->va_size);
+ }
+
+ if ((vap != ap->a_vap) && (vap->va_type == VDIR))
+ ap->a_vap->va_nlink += vap->va_nlink;
+
+ ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
+ return (0);
+}
+
+int
+union_setattr(ap)
+ struct vop_setattr_args /* {
+ struct vnode *a_vp;
+ struct vattr *a_vap;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+ int error;
+
+ /*
+ * Handle case of truncating lower object to zero size,
+ * by creating a zero length upper object. This is to
+ * handle the case of open with O_TRUNC and O_CREAT.
+ */
+ if ((un->un_uppervp == NULLVP) &&
+ /* assert(un->un_lowervp != NULLVP) */
+ (un->un_lowervp->v_type == VREG)) {
+ error = union_copyup(un, (ap->a_vap->va_size != 0),
+ ap->a_cred, ap->a_p);
+ if (error)
+ return (error);
+ }
+
+ /*
+ * Try to set attributes in upper layer,
+ * otherwise return read-only filesystem error.
+ */
+ if (un->un_uppervp != NULLVP) {
+ FIXUP(un);
+ error = VOP_SETATTR(un->un_uppervp, ap->a_vap,
+ ap->a_cred, ap->a_p);
+ if ((error == 0) && (ap->a_vap->va_size != VNOVAL))
+ union_newsize(ap->a_vp, ap->a_vap->va_size, VNOVAL);
+ } else {
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+int
+union_read(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_READ(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ /*
+ * XXX
+ * perhaps the size of the underlying object has changed under
+ * our feet. take advantage of the offset information present
+ * in the uio structure.
+ */
+ if (error == 0) {
+ struct union_node *un = VTOUNION(ap->a_vp);
+ off_t cur = ap->a_uio->uio_offset;
+
+ if (vp == un->un_uppervp) {
+ if (cur > un->un_uppersz)
+ union_newsize(ap->a_vp, cur, VNOVAL);
+ } else {
+ if (cur > un->un_lowersz)
+ union_newsize(ap->a_vp, VNOVAL, cur);
+ }
+ }
+
+ return (error);
+}
+
+int
+union_write(ap)
+ struct vop_read_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ int a_ioflag;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp;
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ vp = UPPERVP(ap->a_vp);
+ if (vp == NULLVP)
+ panic("union: missing upper layer in write");
+
+ FIXUP(un);
+ error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+
+ /*
+ * the size of the underlying object may be changed by the
+ * write.
+ */
+ if (error == 0) {
+ off_t cur = ap->a_uio->uio_offset;
+
+ if (cur > un->un_uppersz)
+ union_newsize(ap->a_vp, cur, VNOVAL);
+ }
+
+ return (error);
+}
+
+union_lease(ap)
+ struct vop_lease_args /* {
+ struct vnode *a_vp;
+ struct proc *a_p;
+ struct ucred *a_cred;
+ int a_flag;
+ } */ *ap;
+{
+ register struct vnode *vp = OTHERVP(ap->a_vp);
+
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_lease), ap));
+}
+
+int
+union_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ u_long a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = OTHERVP(ap->a_vp);
+
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_ioctl), ap));
+}
+
+int
+union_select(ap)
+ struct vop_select_args /* {
+ struct vnode *a_vp;
+ int a_which;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = OTHERVP(ap->a_vp);
+
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_select), ap));
+}
+
+int
+union_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ register struct vnode *vp = OTHERVP(ap->a_vp);
+
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_mmap), ap));
+}
+
+int
+union_fsync(ap)
+ struct vop_fsync_args /* {
+ struct vnode *a_vp;
+ struct ucred *a_cred;
+ int a_waitfor;
+ struct proc *a_p;
+ } */ *ap;
+{
+ int error = 0;
+ struct vnode *targetvp = OTHERVP(ap->a_vp);
+
+ if (targetvp != NULLVP) {
+ int dolock = (targetvp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(targetvp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_FSYNC(targetvp, ap->a_cred,
+ ap->a_waitfor, ap->a_p);
+ if (dolock)
+ VOP_UNLOCK(targetvp);
+ }
+
+ return (error);
+}
+
+int
+union_seek(ap)
+ struct vop_seek_args /* {
+ struct vnode *a_vp;
+ off_t a_oldoff;
+ off_t a_newoff;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ register struct vnode *vp = OTHERVP(ap->a_vp);
+
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_seek), ap));
+}
+
+int
+union_remove(ap)
+ struct vop_remove_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct union_node *dun = VTOUNION(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (dun->un_uppervp == NULLVP)
+ panic("union remove: null upper vnode");
+
+ if (un->un_uppervp != NULLVP) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+ struct componentname *cnp = ap->a_cnp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ FIXUP(un);
+ VREF(vp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+
+ if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
+ cnp->cn_flags |= DOWHITEOUT;
+ error = VOP_REMOVE(dvp, vp, cnp);
+ if (!error)
+ union_removed_upper(un);
+ } else {
+ FIXUP(dun);
+ error = union_mkwhiteout(
+ MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
+ dun->un_uppervp, ap->a_cnp, un->un_path);
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ }
+
+ return (error);
+}
+
+int
+union_link(ap)
+ struct vop_link_args /* {
+ struct vnode *a_vp;
+ struct vnode *a_tdvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error = 0;
+ struct union_node *un;
+ struct vnode *vp;
+ struct vnode *tdvp;
+
+ un = VTOUNION(ap->a_vp);
+
+ if (ap->a_vp->v_op != ap->a_tdvp->v_op) {
+ tdvp = ap->a_tdvp;
+ } else {
+ struct union_node *tdun = VTOUNION(ap->a_tdvp);
+ if (tdun->un_uppervp == NULLVP) {
+ VOP_LOCK(ap->a_tdvp);
+ if (un->un_uppervp == tdun->un_dirvp) {
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ }
+ error = union_copyup(tdun, 1, ap->a_cnp->cn_cred,
+ ap->a_cnp->cn_proc);
+ if (un->un_uppervp == tdun->un_dirvp) {
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
+ }
+ VOP_UNLOCK(ap->a_tdvp);
+ }
+ tdvp = tdun->un_uppervp;
+ }
+
+ vp = un->un_uppervp;
+ if (vp == NULLVP)
+ error = EROFS;
+
+ if (error) {
+ vput(ap->a_vp);
+ return (error);
+ }
+
+ FIXUP(un);
+ VREF(vp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+
+ return (VOP_LINK(vp, tdvp, ap->a_cnp));
+}
+
+int
+union_rename(ap)
+ 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;
+{
+ int error;
+
+ struct vnode *fdvp = ap->a_fdvp;
+ struct vnode *fvp = ap->a_fvp;
+ struct vnode *tdvp = ap->a_tdvp;
+ struct vnode *tvp = ap->a_tvp;
+
+ if (fdvp->v_op == union_vnodeop_p) { /* always true */
+ struct union_node *un = VTOUNION(fdvp);
+ if (un->un_uppervp == NULLVP) {
+ /*
+ * this should never happen in normal
+ * operation but might if there was
+ * a problem creating the top-level shadow
+ * directory.
+ */
+ error = EXDEV;
+ goto bad;
+ }
+
+ fdvp = un->un_uppervp;
+ VREF(fdvp);
+ vrele(ap->a_fdvp);
+ }
+
+ if (fvp->v_op == union_vnodeop_p) { /* always true */
+ struct union_node *un = VTOUNION(fvp);
+ if (un->un_uppervp == NULLVP) {
+ /* XXX: should do a copyup */
+ error = EXDEV;
+ goto bad;
+ }
+
+ if (un->un_lowervp != NULLVP)
+ ap->a_fcnp->cn_flags |= DOWHITEOUT;
+
+ fvp = un->un_uppervp;
+ VREF(fvp);
+ vrele(ap->a_fvp);
+ }
+
+ if (tdvp->v_op == union_vnodeop_p) {
+ struct union_node *un = VTOUNION(tdvp);
+ if (un->un_uppervp == NULLVP) {
+ /*
+ * this should never happen in normal
+ * operation but might if there was
+ * a problem creating the top-level shadow
+ * directory.
+ */
+ error = EXDEV;
+ goto bad;
+ }
+
+ tdvp = un->un_uppervp;
+ VREF(tdvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_tdvp);
+ }
+
+ if (tvp != NULLVP && tvp->v_op == union_vnodeop_p) {
+ struct union_node *un = VTOUNION(tvp);
+
+ tvp = un->un_uppervp;
+ if (tvp != NULLVP) {
+ VREF(tvp);
+ un->un_flags |= UN_KLOCK;
+ }
+ vput(ap->a_tvp);
+ }
+
+ return (VOP_RENAME(fdvp, fvp, ap->a_fcnp, tdvp, tvp, ap->a_tcnp));
+
+bad:
+ vrele(fdvp);
+ vrele(fvp);
+ vput(tdvp);
+ if (tvp != NULLVP)
+ vput(tvp);
+
+ return (error);
+}
+
+int
+union_mkdir(ap)
+ struct vop_mkdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp != NULLVP) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ VOP_UNLOCK(ap->a_dvp);
+ error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error) {
+ vrele(ap->a_dvp);
+ return (error);
+ }
+
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP,
+ 1);
+ vrele(ap->a_dvp);
+ if (error)
+ vput(vp);
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+int
+union_rmdir(ap)
+ struct vop_rmdir_args /* {
+ struct vnode *a_dvp;
+ struct vnode *a_vp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct union_node *dun = VTOUNION(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (dun->un_uppervp == NULLVP)
+ panic("union rmdir: null upper vnode");
+
+ if (un->un_uppervp != NULLVP) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+ struct componentname *cnp = ap->a_cnp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ FIXUP(un);
+ VREF(vp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+
+ if (union_dowhiteout(un, cnp->cn_cred, cnp->cn_proc))
+ cnp->cn_flags |= DOWHITEOUT;
+ error = VOP_RMDIR(dvp, vp, cnp);
+ if (!error)
+ union_removed_upper(un);
+ } else {
+ FIXUP(dun);
+ error = union_mkwhiteout(
+ MOUNTTOUNIONMOUNT(UNIONTOV(dun)->v_mount),
+ dun->un_uppervp, ap->a_cnp, un->un_path);
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ }
+
+ return (error);
+}
+
+int
+union_symlink(ap)
+ struct vop_symlink_args /* {
+ struct vnode *a_dvp;
+ struct vnode **a_vpp;
+ struct componentname *a_cnp;
+ struct vattr *a_vap;
+ char *a_target;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ struct vnode *dvp = un->un_uppervp;
+
+ if (dvp != NULLVP) {
+ int error;
+ struct vnode *vp;
+ struct mount *mp = ap->a_dvp->v_mount;
+
+ FIXUP(un);
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_SYMLINK(dvp, &vp, ap->a_cnp,
+ ap->a_vap, ap->a_target);
+ *ap->a_vpp = NULLVP;
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+/*
+ * union_readdir works in concert with getdirentries and
+ * readdir(3) to provide a list of entries in the unioned
+ * directories. getdirentries is responsible for walking
+ * down the union stack. readdir(3) is responsible for
+ * eliminating duplicate names from the returned data stream.
+ */
+int
+union_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnodeop_desc *a_desc;
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ int *a_eofflag;
+ u_long *a_cookies;
+ int a_ncookies;
+ } */ *ap;
+{
+ register struct union_node *un = VTOUNION(ap->a_vp);
+ register struct vnode *vp;
+
+ if ((vp = un->un_uppervp) == NULLVP)
+ return (0);
+
+ FIXUP(un);
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_readdir), ap));
+}
+
+int
+union_readlink(ap)
+ struct vop_readlink_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ ap->a_vp = vp;
+ error = VCALL(vp, VOFFSET(vop_readlink), ap);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_abortop(ap)
+ struct vop_abortop_args /* {
+ struct vnode *a_dvp;
+ struct componentname *a_cnp;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_dvp);
+ struct union_node *un = VTOUNION(ap->a_dvp);
+ int islocked = un->un_flags & UN_LOCKED;
+ int dolock = (vp == LOWERVP(ap->a_dvp));
+
+ if (islocked) {
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_dvp));
+ }
+ ap->a_dvp = vp;
+ error = VCALL(vp, VOFFSET(vop_abortop), ap);
+ if (islocked && dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_inactive(ap)
+ struct vop_inactive_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ /*
+ * Do nothing (and _don't_ bypass).
+ * Wait to vrele lowervp until reclaim,
+ * so that until then our union_node is in the
+ * cache and reusable.
+ *
+ * NEEDSWORK: Someday, consider inactive'ing
+ * the lowervp and then trying to reactivate it
+ * with capabilities (v_id)
+ * like they do in the name lookup cache code.
+ * That's too much work for now.
+ */
+
+#ifdef UNION_DIAGNOSTIC
+ if (un->un_flags & UN_LOCKED)
+ panic("union: inactivating locked node");
+ if (un->un_flags & UN_ULOCK)
+ panic("union: inactivating w/locked upper node");
+#endif
+
+ union_diruncache(un);
+
+ if ((un->un_flags & UN_CACHED) == 0)
+ vgone(ap->a_vp);
+
+ return (0);
+}
+
+int
+union_reclaim(ap)
+ struct vop_reclaim_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ union_freevp(ap->a_vp);
+
+ return (0);
+}
+
+int
+union_lock(ap)
+ struct vop_lock_args *ap;
+{
+ struct vnode *vp = ap->a_vp;
+ struct union_node *un;
+
+start:
+ while (vp->v_flag & VXLOCK) {
+ vp->v_flag |= VXWANT;
+ sleep((caddr_t)vp, PINOD);
+ }
+
+ un = VTOUNION(vp);
+
+ if (un->un_uppervp != NULLVP) {
+ if (((un->un_flags & UN_ULOCK) == 0) &&
+ (vp->v_usecount != 0)) {
+ VOP_LOCK(un->un_uppervp);
+ un->un_flags |= UN_ULOCK;
+ }
+#ifdef DIAGNOSTIC
+ if (un->un_flags & UN_KLOCK) {
+ vprint("dangling upper lock", vp);
+ panic("union: dangling upper lock");
+ }
+#endif
+ }
+
+ if (un->un_flags & UN_LOCKED) {
+#ifdef DIAGNOSTIC
+ if (curproc && un->un_pid == curproc->p_pid &&
+ un->un_pid > -1 && curproc->p_pid > -1)
+ panic("union: locking against myself");
+#endif
+ un->un_flags |= UN_WANTED;
+ sleep((caddr_t)un, PINOD);
+ goto start;
+ }
+
+#ifdef DIAGNOSTIC
+ if (curproc)
+ un->un_pid = curproc->p_pid;
+ else
+ un->un_pid = -1;
+#endif
+
+ un->un_flags |= UN_LOCKED;
+ return (0);
+}
+
+int
+union_unlock(ap)
+ struct vop_lock_args *ap;
+{
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+#ifdef DIAGNOSTIC
+ if ((un->un_flags & UN_LOCKED) == 0)
+ panic("union: unlock unlocked node");
+ if (curproc && un->un_pid != curproc->p_pid &&
+ curproc->p_pid > -1 && un->un_pid > -1)
+ panic("union: unlocking other process's union node");
+#endif
+
+ un->un_flags &= ~UN_LOCKED;
+
+ if ((un->un_flags & (UN_ULOCK|UN_KLOCK)) == UN_ULOCK)
+ VOP_UNLOCK(un->un_uppervp);
+
+ un->un_flags &= ~(UN_ULOCK|UN_KLOCK);
+
+ if (un->un_flags & UN_WANTED) {
+ un->un_flags &= ~UN_WANTED;
+ wakeup((caddr_t)un);
+ }
+
+#ifdef DIAGNOSTIC
+ un->un_pid = 0;
+#endif
+
+ return (0);
+}
+
+int
+union_bmap(ap)
+ struct vop_bmap_args /* {
+ struct vnode *a_vp;
+ daddr_t a_bn;
+ struct vnode **a_vpp;
+ daddr_t *a_bnp;
+ int *a_runp;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ ap->a_vp = vp;
+ error = VCALL(vp, VOFFSET(vop_bmap), ap);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_print(ap)
+ struct vop_print_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+ struct vnode *vp = ap->a_vp;
+
+ printf("\ttag VT_UNION, vp=%x, uppervp=%x, lowervp=%x\n",
+ vp, UPPERVP(vp), LOWERVP(vp));
+ if (UPPERVP(vp))
+ vprint("uppervp", UPPERVP(vp));
+ if (LOWERVP(vp))
+ vprint("lowervp", LOWERVP(vp));
+ return (0);
+}
+
+int
+union_islocked(ap)
+ struct vop_islocked_args /* {
+ struct vnode *a_vp;
+ } */ *ap;
+{
+
+ return ((VTOUNION(ap->a_vp)->un_flags & UN_LOCKED) ? 1 : 0);
+}
+
+int
+union_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ register_t *a_retval;
+ } */ *ap;
+{
+ int error;
+ struct vnode *vp = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ ap->a_vp = vp;
+ error = VCALL(vp, VOFFSET(vop_pathconf), ap);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_advlock(ap)
+ struct vop_advlock_args /* {
+ struct vnode *a_vp;
+ caddr_t a_id;
+ int a_op;
+ struct flock *a_fl;
+ int a_flags;
+ } */ *ap;
+{
+ register struct vnode *vp = OTHERVP(ap->a_vp);
+
+ ap->a_vp = vp;
+ return (VCALL(vp, VOFFSET(vop_advlock), ap));
+}
+
+
+/*
+ * XXX - vop_strategy must be hand coded because it has no
+ * vnode in its arguments.
+ * This goes away with a merged VM/buffer cache.
+ */
+int
+union_strategy(ap)
+ struct vop_strategy_args /* {
+ struct buf *a_bp;
+ } */ *ap;
+{
+ struct buf *bp = ap->a_bp;
+ int error;
+ struct vnode *savedvp;
+
+ savedvp = bp->b_vp;
+ bp->b_vp = OTHERVP(bp->b_vp);
+
+#ifdef DIAGNOSTIC
+ if (bp->b_vp == NULLVP)
+ panic("union_strategy: nil vp");
+ if (((bp->b_flags & B_READ) == 0) &&
+ (bp->b_vp == LOWERVP(savedvp)))
+ panic("union_strategy: writing to lowervp");
+#endif
+
+ error = VOP_STRATEGY(bp);
+ bp->b_vp = savedvp;
+
+ return (error);
+}
+
+/*
+ * Global vfs data structures
+ */
+int (**union_vnodeop_p)();
+struct vnodeopv_entry_desc union_vnodeop_entries[] = {
+ { &vop_default_desc, vn_default_error },
+ { &vop_lookup_desc, union_lookup }, /* lookup */
+ { &vop_create_desc, union_create }, /* create */
+ { &vop_whiteout_desc, union_whiteout }, /* whiteout */
+ { &vop_mknod_desc, union_mknod }, /* mknod */
+ { &vop_open_desc, union_open }, /* open */
+ { &vop_close_desc, union_close }, /* close */
+ { &vop_access_desc, union_access }, /* access */
+ { &vop_getattr_desc, union_getattr }, /* getattr */
+ { &vop_setattr_desc, union_setattr }, /* setattr */
+ { &vop_read_desc, union_read }, /* read */
+ { &vop_write_desc, union_write }, /* write */
+ { &vop_lease_desc, union_lease }, /* lease */
+ { &vop_ioctl_desc, union_ioctl }, /* ioctl */
+ { &vop_select_desc, union_select }, /* select */
+ { &vop_mmap_desc, union_mmap }, /* mmap */
+ { &vop_fsync_desc, union_fsync }, /* fsync */
+ { &vop_seek_desc, union_seek }, /* seek */
+ { &vop_remove_desc, union_remove }, /* remove */
+ { &vop_link_desc, union_link }, /* link */
+ { &vop_rename_desc, union_rename }, /* rename */
+ { &vop_mkdir_desc, union_mkdir }, /* mkdir */
+ { &vop_rmdir_desc, union_rmdir }, /* rmdir */
+ { &vop_symlink_desc, union_symlink }, /* symlink */
+ { &vop_readdir_desc, union_readdir }, /* readdir */
+ { &vop_readlink_desc, union_readlink }, /* readlink */
+ { &vop_abortop_desc, union_abortop }, /* abortop */
+ { &vop_inactive_desc, union_inactive }, /* inactive */
+ { &vop_reclaim_desc, union_reclaim }, /* reclaim */
+ { &vop_lock_desc, union_lock }, /* lock */
+ { &vop_unlock_desc, union_unlock }, /* unlock */
+ { &vop_bmap_desc, union_bmap }, /* bmap */
+ { &vop_strategy_desc, union_strategy }, /* strategy */
+ { &vop_print_desc, union_print }, /* print */
+ { &vop_islocked_desc, union_islocked }, /* islocked */
+ { &vop_pathconf_desc, union_pathconf }, /* pathconf */
+ { &vop_advlock_desc, union_advlock }, /* advlock */
+#ifdef notdef
+ { &vop_blkatoff_desc, union_blkatoff }, /* blkatoff */
+ { &vop_valloc_desc, union_valloc }, /* valloc */
+ { &vop_vfree_desc, union_vfree }, /* vfree */
+ { &vop_truncate_desc, union_truncate }, /* truncate */
+ { &vop_update_desc, union_update }, /* update */
+ { &vop_bwrite_desc, union_bwrite }, /* bwrite */
+#endif
+ { (struct vnodeop_desc*)NULL, (int(*)())NULL }
+};
+struct vnodeopv_desc union_vnodeop_opv_desc =
+ { &union_vnodeop_p, union_vnodeop_entries };