summaryrefslogtreecommitdiffstats
path: root/sys/miscfs
diff options
context:
space:
mode:
authorrgrimes <rgrimes@FreeBSD.org>1994-05-24 10:09:53 +0000
committerrgrimes <rgrimes@FreeBSD.org>1994-05-24 10:09:53 +0000
commitd66ac7fb8d399b38baf5f99aaf56caaa58cb7fe8 (patch)
tree7b1a8eb5b08af4b9f7bac45ad41398687df2c351 /sys/miscfs
parent27464aaa8e6ad0a90df705f3dd8ea4c48ffefd04 (diff)
parent8fb65ce818b3e3c6f165b583b910af24000768a5 (diff)
downloadFreeBSD-src-d66ac7fb8d399b38baf5f99aaf56caaa58cb7fe8.zip
FreeBSD-src-d66ac7fb8d399b38baf5f99aaf56caaa58cb7fe8.tar.gz
This commit was generated by cvs2svn to compensate for changes in r1541,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'sys/miscfs')
-rw-r--r--sys/miscfs/deadfs/dead_vnops.c354
-rw-r--r--sys/miscfs/fdesc/fdesc.h82
-rw-r--r--sys/miscfs/fdesc/fdesc_vfsops.c288
-rw-r--r--sys/miscfs/fdesc/fdesc_vnops.c974
-rw-r--r--sys/miscfs/fifofs/fifo.h85
-rw-r--r--sys/miscfs/fifofs/fifo_vnops.c494
-rw-r--r--sys/miscfs/kernfs/kernfs.h56
-rw-r--r--sys/miscfs/kernfs/kernfs_vfsops.c329
-rw-r--r--sys/miscfs/kernfs/kernfs_vnops.c759
-rw-r--r--sys/miscfs/nullfs/null.h75
-rw-r--r--sys/miscfs/nullfs/null_subr.c293
-rw-r--r--sys/miscfs/nullfs/null_vfsops.c366
-rw-r--r--sys/miscfs/nullfs/null_vnops.c462
-rw-r--r--sys/miscfs/portal/portal.h72
-rw-r--r--sys/miscfs/portal/portal_vfsops.c313
-rw-r--r--sys/miscfs/portal/portal_vnops.c707
-rw-r--r--sys/miscfs/procfs/README113
-rw-r--r--sys/miscfs/procfs/procfs.h186
-rw-r--r--sys/miscfs/procfs/procfs_ctl.c302
-rw-r--r--sys/miscfs/procfs/procfs_fpregs.c87
-rw-r--r--sys/miscfs/procfs/procfs_mem.c302
-rw-r--r--sys/miscfs/procfs/procfs_note.c73
-rw-r--r--sys/miscfs/procfs/procfs_regs.c87
-rw-r--r--sys/miscfs/procfs/procfs_status.c145
-rw-r--r--sys/miscfs/procfs/procfs_subr.c314
-rw-r--r--sys/miscfs/procfs/procfs_vfsops.c243
-rw-r--r--sys/miscfs/procfs/procfs_vnops.c814
-rw-r--r--sys/miscfs/specfs/spec_vnops.c689
-rw-r--r--sys/miscfs/specfs/specdev.h127
-rw-r--r--sys/miscfs/umapfs/umap.h92
-rw-r--r--sys/miscfs/umapfs/umap_subr.c397
-rw-r--r--sys/miscfs/umapfs/umap_vfsops.c407
-rw-r--r--sys/miscfs/umapfs/umap_vnops.c488
-rw-r--r--sys/miscfs/union/README7
-rw-r--r--sys/miscfs/union/libc.opendir.c225
-rw-r--r--sys/miscfs/union/union.h117
-rw-r--r--sys/miscfs/union/union_subr.c744
-rw-r--r--sys/miscfs/union/union_vfsops.c550
-rw-r--r--sys/miscfs/union/union_vnops.c1495
39 files changed, 13713 insertions, 0 deletions
diff --git a/sys/miscfs/deadfs/dead_vnops.c b/sys/miscfs/deadfs/dead_vnops.c
new file mode 100644
index 0000000..9d04652
--- /dev/null
+++ b/sys/miscfs/deadfs/dead_vnops.c
@@ -0,0 +1,354 @@
+/*
+ * 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.1 (Berkeley) 6/10/93
+ */
+
+#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 character devices, EIO for others
+ */
+ if (ap->a_vp->v_type != VCHR)
+ 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;
+ int 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 0000000..4c682e7
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc.h
@@ -0,0 +1,82 @@
+/*
+ * 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.5 (Berkeley) 1/21/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 {
+ struct fdescnode *fd_forw; /* Hash chain */
+ struct fdescnode *fd_back;
+ 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 0000000..80c543d
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc_vfsops.c
@@ -0,0 +1,288 @@
+/*
+ * 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;
+ u_int 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;
+ /* XXX -- don't mark as local to work around fts() problems */
+ /*mp->mnt_flag |= MNT_LOCAL;*/
+ mp->mnt_data = (qaddr_t) fmp;
+ getnewfsid(mp, 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);
+
+ sbp->f_type = MOUNT_FDESC;
+ sbp->f_flags = 0;
+ 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);
+ }
+ 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 = {
+ 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 0000000..00d8675
--- /dev/null
+++ b/sys/miscfs/fdesc/fdesc_vnops.c
@@ -0,0 +1,974 @@
+/*
+ * 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.9 (Berkeley) 1/21/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 3
+#define FD_NHASH(ix) ((ix) & NFDCACHE)
+
+/*
+ * Cache head
+ */
+struct fdcache {
+ struct fdescnode *fc_forw;
+ struct fdescnode *fc_back;
+};
+
+static struct fdcache fdcache[NFDCACHE];
+
+/*
+ * Initialise cache headers
+ */
+fdesc_init()
+{
+ struct fdcache *fc;
+
+ devctty = makedev(nchrdev, 0);
+
+ for (fc = fdcache; fc < fdcache + NFDCACHE; fc++)
+ fc->fc_forw = fc->fc_back = (struct fdescnode *) fc;
+}
+
+/*
+ * Compute hash list for given target vnode
+ */
+static struct fdcache *
+fdesc_hash(ix)
+ int ix;
+{
+
+ return (&fdcache[FD_NHASH(ix)]);
+}
+
+int
+fdesc_allocvp(ftype, ix, mp, vpp)
+ fdntype ftype;
+ int ix;
+ struct mount *mp;
+ struct vnode **vpp;
+{
+ struct fdcache *fc;
+ struct fdescnode *fd;
+ int error = 0;
+
+loop:
+ fc = fdesc_hash(ix);
+ for (fd = fc->fc_forw; fd != (struct fdescnode *) fc; fd = fd->fd_forw) {
+ 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;
+ fc = fdesc_hash(ix);
+ insque(fd, fc);
+
+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;
+ int error = 0;
+
+ 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 */
+ error = ENODEV;
+ break;
+
+ case Fctty:
+ error = cttyopen(devctty, ap->a_mode, 0, ap->a_p);
+ break;
+ }
+
+ return (error);
+}
+
+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) {
+ /*
+ * don't allow directories to show up because
+ * that causes loops in the namespace.
+ */
+ vap->va_type = VFIFO;
+ }
+ 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 16
+
+static struct dirtmp {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_short d_namlen;
+ char d_name[8];
+} rootent[] = {
+ { FD_DEVFD, UIO_MX, 2, "fd" },
+ { FD_STDIN, UIO_MX, 5, "stdin" },
+ { FD_STDOUT, UIO_MX, 6, "stdout" },
+ { FD_STDERR, UIO_MX, 6, "stderr" },
+ { FD_CTTY, UIO_MX, 3, "tty" },
+ { 0 }
+};
+
+int
+fdesc_readdir(ap)
+ struct vop_readdir_args /* {
+ struct vnode *a_vp;
+ struct uio *a_uio;
+ struct ucred *a_cred;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct filedesc *fdp;
+ int i;
+ int error;
+
+ switch (VTOFDESC(ap->a_vp)->fd_type) {
+ case Fctty:
+ return (0);
+
+ case Fdesc:
+ return (ENOTDIR);
+
+ default:
+ break;
+ }
+
+ fdp = uio->uio_procp->p_fd;
+
+ if (VTOFDESC(ap->a_vp)->fd_type == Froot) {
+ struct dirent d;
+ struct dirent *dp = &d;
+ struct dirtmp *dt;
+
+ i = uio->uio_offset / UIO_MX;
+ error = 0;
+
+ while (uio->uio_resid > 0) {
+ dt = &rootent[i];
+ if (dt->d_fileno == 0) {
+ /**eofflagp = 1;*/
+ break;
+ }
+ i++;
+
+ switch (dt->d_fileno) {
+ case FD_CTTY:
+ if (cttyvp(uio->uio_procp) == NULL)
+ continue;
+ break;
+
+ case FD_STDIN:
+ case FD_STDOUT:
+ case FD_STDERR:
+ if ((dt->d_fileno-FD_STDIN) >= fdp->fd_nfiles)
+ continue;
+ if (fdp->fd_ofiles[dt->d_fileno-FD_STDIN] == NULL)
+ continue;
+ break;
+ }
+ bzero((caddr_t) dp, UIO_MX);
+ dp->d_fileno = dt->d_fileno;
+ dp->d_namlen = dt->d_namlen;
+ dp->d_type = DT_UNKNOWN;
+ dp->d_reclen = dt->d_reclen;
+ bcopy(dt->d_name, dp->d_name, dp->d_namlen+1);
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ }
+ uio->uio_offset = i * UIO_MX;
+ return (error);
+ }
+
+ i = uio->uio_offset / UIO_MX;
+ error = 0;
+ while (uio->uio_resid > 0) {
+ if (i >= fdp->fd_nfiles)
+ break;
+
+ if (fdp->fd_ofiles[i] != NULL) {
+ struct dirent d;
+ struct dirent *dp = &d;
+
+ bzero((caddr_t) dp, UIO_MX);
+
+ dp->d_namlen = sprintf(dp->d_name, "%d", i);
+ dp->d_reclen = UIO_MX;
+ dp->d_type = DT_UNKNOWN;
+ dp->d_fileno = i + FD_STDIN;
+ /*
+ * And ship to userland
+ */
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ }
+ i++;
+ }
+
+ uio->uio_offset = i * UIO_MX;
+ 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;
+ int 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;
+
+ remque(VTOFDESC(vp));
+ 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;
+ int *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 0000000..e89186d
--- /dev/null
+++ b/sys/miscfs/fifofs/fifo.h
@@ -0,0 +1,85 @@
+/*
+ * 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.2 (Berkeley) 2/2/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 *));
+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 0000000..bad33a4
--- /dev/null
+++ b/sys/miscfs/fifofs/fifo_vnops.c
@@ -0,0 +1,494 @@
+/*
+ * 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.2 (Berkeley) 1/4/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_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 ((ap->a_mode & (FREAD|FWRITE)) == (FREAD|FWRITE))
+ return (EINVAL);
+ if ((fip = vp->v_fifoinfo) == NULL) {
+ MALLOC(fip, struct fifoinfo *, sizeof(*fip), M_VNODE, M_WAITOK);
+ vp->v_fifoinfo = fip;
+ if (error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0)) {
+ free(fip, M_VNODE);
+ vp->v_fifoinfo = NULL;
+ return (error);
+ }
+ fip->fi_readsock = rso;
+ if (error = socreate(AF_UNIX, &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;
+ }
+ error = 0;
+ if (ap->a_mode & FREAD) {
+ fip->fi_readers++;
+ if (fip->fi_readers == 1) {
+ fip->fi_writesock->so_state &= ~SS_CANTSENDMORE;
+ if (fip->fi_writers > 0)
+ wakeup((caddr_t)&fip->fi_writers);
+ }
+ if (ap->a_mode & O_NONBLOCK)
+ return (0);
+ while (fip->fi_writers == 0) {
+ VOP_UNLOCK(vp);
+ error = tsleep((caddr_t)&fip->fi_readers,
+ PCATCH | PSOCK, openstr, 0);
+ VOP_LOCK(vp);
+ if (error)
+ break;
+ }
+ } else {
+ fip->fi_writers++;
+ if (fip->fi_readers == 0 && (ap->a_mode & O_NONBLOCK)) {
+ error = ENXIO;
+ } else {
+ if (fip->fi_writers == 1) {
+ fip->fi_readsock->so_state &= ~SS_CANTRCVMORE;
+ if (fip->fi_readers > 0)
+ wakeup((caddr_t)&fip->fi_readers);
+ }
+ while (fip->fi_readers == 0) {
+ VOP_UNLOCK(vp);
+ error = tsleep((caddr_t)&fip->fi_writers,
+ PCATCH | PSOCK, openstr, 0);
+ VOP_LOCK(vp);
+ if (error)
+ break;
+ }
+ }
+ }
+ if (error)
+ 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, (int *)0,
+ (struct mbuf **)0, (struct mbuf **)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;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+ struct file filetmp;
+
+ if (ap->a_command == FIONBIO)
+ return (0);
+ if (ap->a_fflag & FREAD)
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
+ else
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
+ return (soo_ioctl(&filetmp, ap->a_command, ap->a_data, ap->a_p));
+}
+
+/* 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;
+
+ if (ap->a_fflags & FREAD)
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_readsock;
+ else
+ filetmp.f_data = (caddr_t)ap->a_vp->v_fifoinfo->fi_writesock;
+ return (soo_select(&filetmp, ap->a_which, ap->a_p));
+}
+
+/*
+ * 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 & FWRITE) {
+ fip->fi_writers--;
+ if (fip->fi_writers == 0)
+ socantrcvmore(fip->fi_readsock);
+ } else {
+ fip->fi_readers--;
+ if (fip->fi_readers == 0)
+ socantsendmore(fip->fi_writesock);
+ }
+ 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;
+ int *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 0000000..75ddecc
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs.h
@@ -0,0 +1,56 @@
+/*
+ * 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.4 (Berkeley) 1/21/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 struct vnode *rrootvp;
+#endif /* KERNEL */
diff --git a/sys/miscfs/kernfs/kernfs_vfsops.c b/sys/miscfs/kernfs/kernfs_vfsops.c
new file mode 100644
index 0000000..b68d76e
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs_vfsops.c
@@ -0,0 +1,329 @@
+/*
+ * 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.4 (Berkeley) 1/21/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>
+
+struct vnode *rrootvp;
+
+/*
+ * Create a vnode for a character device.
+ */
+int
+cdevvp(dev, vpp)
+ dev_t dev;
+ struct vnode **vpp;
+{
+ register struct vnode *vp;
+ struct vnode *nvp;
+ int error;
+
+ if (dev == NODEV)
+ return (0);
+ error = getnewvnode(VT_NON, (struct mount *)0, spec_vnodeop_p, &nvp);
+ if (error) {
+ *vpp = 0;
+ return (error);
+ }
+ vp = nvp;
+ vp->v_type = VCHR;
+ if (nvp = checkalias(vp, dev, (struct mount *)0)) {
+ vput(vp);
+ vp = nvp;
+ }
+ *vpp = vp;
+ return (0);
+}
+
+kernfs_init()
+{
+ int cmaj;
+ int bmaj = major(rootdev);
+ int error = ENXIO;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_init\n"); /* printed during system boot */
+#endif
+
+ for (cmaj = 0; cmaj < nchrdev; cmaj++) {
+ if (cdevsw[cmaj].d_open == bdevsw[bmaj].d_open) {
+ dev_t cdev = makedev(cmaj, minor(rootdev));
+ error = cdevvp(cdev, &rrootvp);
+ if (error == 0)
+ break;
+ }
+ }
+
+ if (error) {
+ printf("kernfs: no raw boot device\n");
+ rrootvp = 0;
+ }
+}
+
+/*
+ * 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;
+ u_int 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);
+
+ error = getnewvnode(VT_KERNFS, mp, kernfs_vnodeop_p, &rvp); /* XXX */
+ if (error)
+ return (error);
+
+ MALLOC(fmp, struct kernfs_mount *, sizeof(struct kernfs_mount),
+ M_UFSMNT, M_WAITOK); /* XXX */
+ 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, 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
+ 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
+ /*
+ * Release reference on underlying root vnode
+ */
+ vrele(rootvp);
+ /*
+ * And blow it away for future re-use
+ */
+ vgone(rootvp);
+ /*
+ * Finally, throw away the kernfs_mount structure
+ */
+ free(mp->mnt_data, M_UFSMNT); /* XXX */
+ 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
+
+ sbp->f_type = MOUNT_KERNFS;
+ sbp->f_flags = 0;
+ 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);
+ }
+ 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 = {
+ 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 0000000..10b7d7c
--- /dev/null
+++ b/sys/miscfs/kernfs/kernfs_vnops.c
@@ -0,0 +1,759 @@
+/*
+ * 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.6 (Berkeley) 2/10/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 <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 {
+ 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
+ int kt_tag;
+ int kt_rw;
+ int kt_vtype;
+} kern_targets[] = {
+/* NOTE: The name must be less than UIO_MX-16 chars in length */
+ /* name data tag ro/rw */
+ { ".", 0, KTT_NULL, VREAD, VDIR },
+ { "..", 0, KTT_NULL, VREAD, VDIR },
+ { "boottime", &boottime.tv_sec, KTT_INT, VREAD, VREG },
+ { "copyright", copyright, KTT_STRING, VREAD, VREG },
+ { "hostname", 0, KTT_HOSTNAME, VREAD|VWRITE, VREG },
+ { "hz", &hz, KTT_INT, VREAD, VREG },
+ { "loadavg", 0, KTT_AVENRUN, VREAD, VREG },
+ { "pagesize", &cnt.v_page_size, KTT_INT, VREAD, VREG },
+ { "physmem", &physmem, KTT_INT, VREAD, VREG },
+#if 0
+ { "root", 0, KTT_NULL, VREAD, VDIR },
+#endif
+ { "rootdev", 0, KTT_NULL, VREAD, VBLK },
+ { "rrootdev", 0, KTT_NULL, VREAD, VCHR },
+ { "time", 0, KTT_TIME, VREAD, VREG },
+ { "version", version, KTT_STRING, VREAD, VREG },
+};
+
+static int nkern_targets = sizeof(kern_targets) / sizeof(kern_targets[0]);
+
+static int
+kernfs_xread(kt, buf, len, lenp)
+ struct kern_target *kt;
+ char *buf;
+ int len;
+ int *lenp;
+{
+ switch (kt->kt_tag) {
+ case KTT_TIME: {
+ struct timeval tv;
+ microtime(&tv);
+ sprintf(buf, "%d %d\n", tv.tv_sec, tv.tv_usec);
+ break;
+ }
+
+ case KTT_INT: {
+ int *ip = kt->kt_data;
+ sprintf(buf, "%d\n", *ip);
+ break;
+ }
+
+ case KTT_STRING: {
+ char *cp = kt->kt_data;
+ int xlen = strlen(cp) + 1;
+
+ if (xlen >= len)
+ return (EINVAL);
+
+ bcopy(cp, buf, xlen);
+ break;
+ }
+
+ case KTT_HOSTNAME: {
+ char *cp = hostname;
+ int xlen = hostnamelen;
+
+ if (xlen >= (len-2))
+ return (EINVAL);
+
+ bcopy(cp, buf, xlen);
+ buf[xlen] = '\n';
+ buf[xlen+1] = '\0';
+ break;
+ }
+
+ case KTT_AVENRUN:
+ sprintf(buf, "%ld %ld %ld %ld\n",
+ averunnable.ldavg[0],
+ averunnable.ldavg[1],
+ averunnable.ldavg[2],
+ averunnable.fscale);
+ break;
+
+ default:
+ return (EINVAL);
+ }
+
+ *lenp = strlen(buf);
+ return (0);
+}
+
+static 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 vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode *fvp;
+ int error, i;
+ char *pname;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup(%x)\n", ap);
+ printf("kernfs_lookup(dp = %x, vpp = %x, cnp = %x)\n", dvp, vpp, ap->a_cnp);
+#endif
+ pname = cnp->cn_nameptr;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup(%s)\n", pname);
+#endif
+ 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
+
+ /*
+ * /kern/rootdev is the root device
+ */
+ if (cnp->cn_namelen == 7 && bcmp(pname, "rootdev", 7) == 0) {
+ *vpp = rootvp;
+ VREF(rootvp);
+ VOP_LOCK(rootvp);
+ return (0);
+ }
+
+ /*
+ * /kern/rrootdev is the raw root device
+ */
+ if (cnp->cn_namelen == 8 && bcmp(pname, "rrootdev", 8) == 0) {
+ if (rrootvp) {
+ *vpp = rrootvp;
+ VREF(rrootvp);
+ VOP_LOCK(rrootvp);
+ return (0);
+ }
+ error = ENXIO;
+ goto bad;
+ }
+
+ error = ENOENT;
+
+ for (i = 0; i < nkern_targets; i++) {
+ struct kern_target *kt = &kern_targets[i];
+ if (cnp->cn_namelen == strlen(kt->kt_name) &&
+ bcmp(kt->kt_name, pname, cnp->cn_namelen) == 0) {
+ error = 0;
+ break;
+ }
+ }
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: i = %d, error = %d\n", i, error);
+#endif
+
+ if (error)
+ goto bad;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: allocate new vnode\n");
+#endif
+ error = getnewvnode(VT_KERNFS, dvp->v_mount, kernfs_vnodeop_p, &fvp);
+ if (error)
+ goto bad;
+ MALLOC(fvp->v_data, void *, sizeof(struct kernfs_node), M_TEMP, M_WAITOK);
+ VTOKERN(fvp)->kf_kt = &kern_targets[i];
+ fvp->v_type = VTOKERN(fvp)->kf_kt->kt_vtype;
+ *vpp = fvp;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: newvp = %x\n", fvp);
+#endif
+ return (0);
+
+bad:;
+ *vpp = NULL;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_lookup: error = %d\n", error);
+#endif
+ return (error);
+}
+
+kernfs_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;
+
+ /*
+ * Can always open the root (modulo perms)
+ */
+ if (vp->v_flag & VROOT)
+ return (0);
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_open, mode = %x, file = %s\n",
+ ap->a_mode, VTOKERN(vp)->kf_kt->kt_name);
+#endif
+
+ if ((ap->a_mode & FWRITE) && !(VTOKERN(vp)->kf_kt->kt_rw & VWRITE))
+ return (EOPNOTSUPP);
+
+ return (0);
+}
+
+static 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;
+ struct ucred *cred = ap->a_cred;
+ mode_t mode = ap->a_mode;
+
+ if (mode & VEXEC) {
+ if (vp->v_flag & VROOT)
+ return (0);
+ return (EACCES);
+ }
+
+ if (cred->cr_uid == 0) {
+ if ((vp->v_flag & VROOT) == 0) {
+ struct kern_target *kt = VTOKERN(vp)->kf_kt;
+
+ if ((mode & VWRITE) && !(kt->kt_rw & VWRITE))
+ return (EROFS);
+ }
+ return (0);
+ }
+
+ if (mode & VWRITE)
+ return (EACCES);
+
+ return (0);
+}
+
+
+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;
+ int error = 0;
+ char strbuf[KSTRING];
+
+ 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_qsize = 0; */
+ vap->va_blocksize = DEV_BSIZE;
+ microtime(&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;
+
+ 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;
+#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_rw & VWRITE ? WRITE_MODE : READ_MODE);
+ vap->va_nlink = 1;
+ vap->va_fileid = 3 + (kt - kern_targets) / sizeof(*kt);
+ error = kernfs_xread(kt, strbuf, sizeof(strbuf), &nbytes);
+ vap->va_size = nbytes;
+ }
+
+ vp->v_type = vap->va_type;
+#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);
+}
+
+static 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];
+ int off = uio->uio_offset;
+ int error, len;
+ char *cp;
+
+ if (vp->v_flag & VROOT)
+ return (EOPNOTSUPP);
+
+ kt = VTOKERN(vp)->kf_kt;
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kern_read %s\n", kt->kt_name);
+#endif
+
+ len = 0;
+ error = kernfs_xread(kt, strbuf, sizeof(strbuf), &len);
+ if (error)
+ return (error);
+ cp = strbuf + off;
+ len -= off;
+ return (uiomove(cp, len, uio));
+}
+
+static 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_flag & VROOT)
+ return (0);
+
+ kt = VTOKERN(vp)->kf_kt;
+
+ if (uio->uio_offset != 0)
+ return (EINVAL);
+
+ xlen = min(uio->uio_resid, KSTRING-1);
+ error = uiomove(strbuf, xlen, uio);
+ if (error)
+ 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;
+ } */ *ap;
+{
+ struct uio *uio = ap->a_uio;
+ int i;
+ int error;
+
+ i = uio->uio_offset / UIO_MX;
+ error = 0;
+ while (uio->uio_resid > 0 && i < nkern_targets) {
+ struct dirent d;
+ struct dirent *dp = &d;
+ struct kern_target *kt = &kern_targets[i];
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_readdir: i = %d\n", i);
+#endif
+
+ bzero((caddr_t) dp, UIO_MX);
+
+ dp->d_namlen = strlen(kt->kt_name);
+ bcopy(kt->kt_name, dp->d_name, dp->d_namlen+1);
+
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_readdir: name = %s, len = %d\n",
+ dp->d_name, dp->d_namlen);
+#endif
+ /*
+ * Fill in the remaining fields
+ */
+ dp->d_reclen = UIO_MX;
+ dp->d_fileno = i + 3;
+ dp->d_type = DT_UNKNOWN; /* XXX */
+ /*
+ * And ship to userland
+ */
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ i++;
+ }
+
+ uio->uio_offset = i * UIO_MX;
+
+ return (error);
+}
+
+kernfs_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;
+#ifdef KERNFS_DIAGNOSTIC
+ printf("kernfs_inactive(%x)\n", vp);
+#endif
+ 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;
+ int *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 0000000..14286ff
--- /dev/null
+++ b/sys/miscfs/nullfs/null.h
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ *
+ * @(#)null.h 8.2 (Berkeley) 1/21/94
+ *
+ * $Id: lofs.h,v 1.8 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+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 {
+ struct null_node *null_forw; /* Hash chain */
+ struct null_node *null_back;
+ 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 0000000..a31723f
--- /dev/null
+++ b/sys/miscfs/nullfs/null_subr.c
@@ -0,0 +1,293 @@
+/*
+ * 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.
+ *
+ * @(#)null_subr.c 8.4 (Berkeley) 1/21/94
+ *
+ * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+#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>
+
+#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
+#define NNULLNODECACHE 16
+#define NULL_NHASH(vp) ((((u_long)vp)>>LOG2_SIZEVNODE) & (NNULLNODECACHE-1))
+
+/*
+ * 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.
+ */
+
+/*
+ * Cache head
+ */
+struct null_node_cache {
+ struct null_node *ac_forw;
+ struct null_node *ac_back;
+};
+
+static struct null_node_cache null_node_cache[NNULLNODECACHE];
+
+/*
+ * Initialise cache headers
+ */
+nullfs_init()
+{
+ struct null_node_cache *ac;
+#ifdef NULLFS_DIAGNOSTIC
+ printf("nullfs_init\n"); /* printed during system boot */
+#endif
+
+ for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++)
+ ac->ac_forw = ac->ac_back = (struct null_node *) ac;
+}
+
+/*
+ * Compute hash list for given lower vnode
+ */
+static struct null_node_cache *
+null_node_hash(lowervp)
+struct vnode *lowervp;
+{
+
+ return (&null_node_cache[NULL_NHASH(lowervp)]);
+}
+
+/*
+ * 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_cache *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_node_hash(lowervp);
+loop:
+ for (a = hd->ac_forw; a != (struct null_node *) hd; a = a->null_forw) {
+ 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, lofsvp 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_cache *hd;
+ struct null_node *xp;
+ struct vnode *othervp, *vp;
+ int error;
+
+ if (error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp))
+ return (error);
+ vp = *vpp;
+
+ MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
+ vp->v_type = lowervp->v_type;
+ xp->null_vnode = vp;
+ vp->v_data = xp;
+ 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 (othervp = null_node_find(lowervp)) {
+ FREE(xp, M_TEMP);
+ vp->v_type = VBAD; /* node is discarded */
+ vp->v_usecount = 0; /* XXX */
+ *vpp = othervp;
+ return 0;
+ };
+ VREF(lowervp); /* Extra VREF will be vrele'd in null_node_create */
+ hd = null_node_hash(lowervp);
+ insque(xp, hd);
+ 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 ");
+ vprint ("null_node_create: lower ");
+ printf ("null_node_create: lower has 0 usecount.\n");
+ 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 0000000..b0d2df7
--- /dev/null
+++ b/sys/miscfs/nullfs/null_vfsops.c
@@ -0,0 +1,366 @@
+/*
+ * 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.
+ *
+ * @(#)null_vfsops.c 8.2 (Berkeley) 1/21/94
+ *
+ * @(#)lofs_vfsops.c 1.2 (Berkeley) 6/18/92
+ * $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $
+ */
+
+/*
+ * 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;
+ u_int 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, 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);
+ }
+ 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 VFS_FHTOVP(MOUNTTONULLMOUNT(mp)->nullm_vfs, fidp, nam, vpp, exflagsp,credanonp);
+}
+
+int
+nullfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ return VFS_VPTOFH(NULLVPTOLOWERVP(vp), fhp);
+}
+
+int nullfs_init __P((void));
+
+struct vfsops null_vfsops = {
+ 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 0000000..115ff6f
--- /dev/null
+++ b/sys/miscfs/nullfs/null_vnops.c
@@ -0,0 +1,462 @@
+/*
+ * 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 jsp $
+ * ...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)->v_op != null_vnodeop_p) {
+ old_vps[i] = NULL;
+ } 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]) {
+ *(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;
+ remque(xp);
+ 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 0000000..38d7ee0
--- /dev/null
+++ b/sys/miscfs/portal/portal.h
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ *
+ * @(#)portal.h 8.4 (Berkeley) 1/21/94
+ *
+ * $Id: portal.h,v 1.3 1992/05/30 10:05:24 jsp Exp jsp $
+ */
+
+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 */
+ 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 0000000..39e8563
--- /dev/null
+++ b/sys/miscfs/portal/portal_vfsops.c
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ *
+ * @(#)portal_vfsops.c 8.6 (Berkeley) 1/21/94
+ *
+ * $Id: portal_vfsops.c,v 1.5 1992/05/30 10:25:27 jsp Exp jsp $
+ */
+
+/*
+ * 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;
+ u_int 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, 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);
+
+#ifdef notdef
+ bzero(mp->mnt_stat.f_mntfromname, MNAMELEN);
+ bcopy("portal", mp->mnt_stat.f_mntfromname, sizeof("portal"));
+#endif
+
+ 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;
+{
+
+ sbp->f_type = MOUNT_PORTAL;
+ sbp->f_flags = 0;
+ 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);
+ }
+ 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 = {
+ 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 0000000..5e17026
--- /dev/null
+++ b/sys/miscfs/portal/portal_vnops.c
@@ -0,0 +1,707 @@
+/*
+ * 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.
+ *
+ * @(#)portal_vnops.c 8.8 (Berkeley) 1/21/94
+ *
+ * $Id: portal_vnops.c,v 1.4 1992/05/30 10:05:24 jsp Exp jsp $
+ */
+
+/*
+ * 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 <miscfs/portal/portal.h>
+
+static int portal_fileid = PORTAL_ROOTFILEID+1;
+
+static void
+portal_closefd(p, fd)
+ struct proc *p;
+ int fd;
+{
+ int error;
+ struct {
+ int fd;
+ } ua;
+ int rc;
+
+ ua.fd = fd;
+ error = close(p, &ua, &rc);
+ /*
+ * 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;
+{
+ char *pname = ap->a_cnp->cn_nameptr;
+ struct portalnode *pt;
+ int error;
+ struct vnode *fvp = 0;
+ char *path;
+ int size;
+
+ if (ap->a_cnp->cn_namelen == 1 && *pname == '.') {
+ *ap->a_vpp = ap->a_dvp;
+ VREF(ap->a_dvp);
+ /*VOP_LOCK(ap->a_dvp);*/
+ return (0);
+ }
+
+
+ error = getnewvnode(VT_PORTAL, ap->a_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++;
+ ap->a_cnp->cn_consume = size - ap->a_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++;
+
+ *ap->a_vpp = fvp;
+ /*VOP_LOCK(fvp);*/
+ return (0);
+
+bad:;
+ if (fvp) {
+ vrele(fvp);
+ }
+ *ap->a_vpp = NULL;
+ 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 = splnet();
+ 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_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;
+
+ 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(&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;
+ int *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 0000000..38811b3
--- /dev/null
+++ b/sys/miscfs/procfs/README
@@ -0,0 +1,113 @@
+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.
+
+$Id: README,v 3.1 1993/12/15 09:40:17 jsp Exp $
diff --git a/sys/miscfs/procfs/procfs.h b/sys/miscfs/procfs/procfs.h
new file mode 100644
index 0000000..f7b8fa3
--- /dev/null
+++ b/sys/miscfs/procfs/procfs.h
@@ -0,0 +1,186 @@
+/*
+ * 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.6 (Berkeley) 2/3/94
+ *
+ * From:
+ * $Id: procfs.h,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * The different types of node in a procfs filesystem
+ */
+typedef enum {
+ Proot, /* the filesystem root */
+ 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))
+
+/*
+ * Format of a directory entry in /proc, ...
+ * This must map onto struct dirent (see <dirent.h>)
+ */
+#define PROCFS_NAMELEN 8
+struct pfsdent {
+ u_long d_fileno;
+ u_short d_reclen;
+ u_char d_type;
+ u_char d_namlen;
+ char d_name[PROCFS_NAMELEN];
+};
+#define UIO_MX sizeof(struct pfsdent)
+#define PROCFS_FILENO(pid, type) \
+ (((type) == Proot) ? \
+ 2 : \
+ ((((pid)+1) << 3) + ((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;
+};
+
+extern int vfs_getuserstr __P((struct uio *, char *, int *));
+extern vfs_namemap_t *vfs_findname __P((vfs_namemap_t *, char *, int));
+
+/* <machine/reg.h> */
+struct reg;
+struct fpreg;
+
+#define PFIND(pid) ((pid) ? pfind(pid) : &proc0)
+extern int procfs_freevp __P((struct vnode *));
+extern int procfs_allocvp __P((struct mount *, struct vnode **, long, pfstype));
+extern struct vnode *procfs_findtextvp __P((struct proc *));
+extern int procfs_sstep __P((struct proc *));
+extern void procfs_fix_sstep __P((struct proc *));
+extern int procfs_read_regs __P((struct proc *, struct reg *));
+extern int procfs_write_regs __P((struct proc *, struct reg *));
+extern int procfs_read_fpregs __P((struct proc *, struct fpreg *));
+extern int procfs_write_fpregs __P((struct proc *, struct fpreg *));
+extern int procfs_donote __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_doregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_dofpregs __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_domem __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_doctl __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+extern int procfs_dostatus __P((struct proc *, struct proc *, struct pfsnode *pfsp, struct uio *uio));
+
+#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 *));
+#define procfs_readlink ((int (*) __P((struct vop_readlink_args *))) procfs_badop)
+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 0000000..a42a03c
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_ctl.c
@@ -0,0 +1,302 @@
+/*
+ * 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.3 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_ctl.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#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>
+
+/*
+ * 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))
+
+#ifdef notdef
+#define FIX_SSTEP(p) { \
+ procfs_fix_sstep(p); \
+ } \
+}
+#else
+#define FIX_SSTEP(p)
+#endif
+
+#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:
+ procfs_sstep(p);
+ 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 0000000..6d850a6
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_fpregs.c
@@ -0,0 +1,87 @@
+/*
+ * 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.1 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_regs.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#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 <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;
+{
+ 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;
+
+ if (kl < 0)
+ error = EINVAL;
+ else
+ error = procfs_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 = procfs_write_fpregs(p, &r);
+ }
+
+ uio->uio_offset = 0;
+ return (error);
+}
diff --git a/sys/miscfs/procfs/procfs_mem.c b/sys/miscfs/procfs/procfs_mem.c
new file mode 100644
index 0000000..039983d
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_mem.c
@@ -0,0 +1,302 @@
+/*
+ * 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.4 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_mem.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * 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)
+ 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;
+{
+ int error;
+
+ if (uio->uio_resid == 0)
+ return (0);
+
+ error = procfs_rwmem(p, uio);
+
+ return (error);
+}
+
+/*
+ * 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 0000000..bf2f160
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_note.c
@@ -0,0 +1,73 @@
+/*
+ * 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
+ *
+ * From:
+ * $Id: procfs_note.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#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 0000000..fa95fef
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_regs.c
@@ -0,0 +1,87 @@
+/*
+ * 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.3 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_regs.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#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 <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;
+{
+ 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;
+
+ if (kl < 0)
+ error = EINVAL;
+ else
+ error = procfs_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 = procfs_write_regs(p, &r);
+ }
+
+ uio->uio_offset = 0;
+ return (error);
+}
diff --git a/sys/miscfs/procfs/procfs_status.c b/sys/miscfs/procfs/procfs_status.c
new file mode 100644
index 0000000..d88aaab
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_status.c
@@ -0,0 +1,145 @@
+/*
+ * 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.3 (Berkeley) 2/17/94
+ *
+ * From:
+ * $Id: procfs_status.c,v 3.1 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#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 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, 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 = min(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 0000000..b371af1
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_subr.c
@@ -0,0 +1,314 @@
+/*
+ * 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.4 (Berkeley) 1/27/94
+ *
+ * From:
+ * $Id: procfs_subr.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+#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;
+{
+ int error;
+ struct pfsnode *pfs;
+ struct pfsnode **pp;
+
+loop:
+ for (pfs = pfshead; pfs != 0; pfs = pfs->pfs_next) {
+ if (pfs->pfs_pid == pid &&
+ pfs->pfs_type == pfs_type &&
+ PFSTOV(pfs)->v_mount == mp) {
+ if (vget(pfs->pfs_vnode, 0))
+ goto loop;
+ *vpp = pfs->pfs_vnode;
+ 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;
+
+ error = getnewvnode(VT_PROCFS, mp, procfs_vnodeop_p, vpp);
+ if (error)
+ goto out;
+
+ MALLOC((*vpp)->v_data, void *, sizeof(struct pfsnode),
+ M_TEMP, M_WAITOK);
+
+ pfs = VTOPFS(*vpp);
+ pfs->pfs_next = 0;
+ pfs->pfs_pid = (pid_t) pid;
+ pfs->pfs_type = pfs_type;
+ pfs->pfs_vnode = *vpp;
+ 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;
+ break;
+
+ case Pproc:
+ pfs->pfs_mode = (VREAD|VEXEC) |
+ (VREAD|VEXEC) >> 3 |
+ (VREAD|VEXEC) >> 6;
+ break;
+
+ case Pfile:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pmem:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pregs:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pfpregs:
+ pfs->pfs_mode = (VREAD|VWRITE);
+ break;
+
+ case Pctl:
+ pfs->pfs_mode = (VWRITE);
+ break;
+
+ case Pstatus:
+ pfs->pfs_mode = (VREAD) |
+ (VREAD >> 3) |
+ (VREAD >> 6);
+ break;
+
+ case Pnote:
+ pfs->pfs_mode = (VWRITE);
+ break;
+
+ case Pnotepg:
+ pfs->pfs_mode = (VWRITE);
+ 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;
+
+ error = uiomove(buf, xlen, uio);
+ if (error)
+ 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 0000000..3938ca1
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_vfsops.c
@@ -0,0 +1,243 @@
+/*
+ * 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.4 (Berkeley) 1/21/94
+ *
+ * From:
+ * $Id: procfs_vfsops.c,v 3.1 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * 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;
+{
+ u_int 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, MOUNT_PROCFS);
+
+ (void) copyinstr(path, (caddr_t)mp->mnt_stat.f_mntonname, MNAMELEN, &size);
+ bzero(mp->mnt_stat.f_mntonname + size, MNAMELEN - size);
+
+ size = sizeof("procfs") - 1;
+ bcopy("procfs", mp->mnt_stat.f_mntfromname, size);
+ bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
+
+ 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;
+{
+ struct pfsnode *pfs;
+ struct vnode *vp;
+ int error;
+
+ error = procfs_allocvp(mp, &vp, (pid_t) 0, Proot);
+ if (error)
+ return (error);
+
+ vp->v_type = VDIR;
+ vp->v_flag = VROOT;
+ pfs = VTOPFS(vp);
+
+ *vpp = vp;
+ return (0);
+}
+
+/*
+ */
+/* 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;
+{
+ sbp->f_type = MOUNT_PROCFS;
+ 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);
+ }
+
+ 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 = {
+ 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 0000000..4e1ee00
--- /dev/null
+++ b/sys/miscfs/procfs/procfs_vnops.c
@@ -0,0 +1,814 @@
+/*
+ * 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.6 (Berkeley) 2/7/94
+ *
+ * From:
+ * $Id: procfs_vnops.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
+ */
+
+/*
+ * 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 <miscfs/procfs/procfs.h>
+#include <vm/vm.h> /* for PAGE_SIZE */
+
+/*
+ * 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
+ */
+static struct pfsnames {
+ u_short d_namlen;
+ char d_name[PROCFS_NAMELEN];
+ pfstype d_pfstype;
+} procent[] = {
+#define N(s) sizeof(s)-1, s
+ /* namlen, nam, type */
+ { N("file"), Pfile },
+ { N("mem"), Pmem },
+ { N("regs"), Pregs },
+ { N("fpregs"), Pfpregs },
+ { N("ctl"), Pctl },
+ { N("status"), Pstatus },
+ { N("note"), Pnote },
+ { N("notepg"), Pnotepg },
+#undef N
+};
+#define Nprocent (sizeof(procent)/sizeof(procent[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 *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 *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 *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 *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 *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 *ap;
+{
+ int error;
+
+ error = procfs_freevp(ap->a_vp);
+ return (error);
+}
+
+/*
+ * Return POSIX pathconf information applicable to special devices.
+ */
+procfs_pathconf(ap)
+ struct vop_pathconf_args /* {
+ struct vnode *a_vp;
+ int a_name;
+ int *a_retval;
+ } */ *ap;
+{
+
+ switch (ap->a_name) {
+ case _PC_LINK_MAX:
+ *ap->a_retval = 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 *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+
+ printf("tag VT_PROCFS, pid %d, mode %x, flags %x\n",
+ 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 *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 *ap;
+{
+ struct pfsnode *pfs = VTOPFS(ap->a_vp);
+ struct vattr *vap = ap->a_vap;
+ struct proc *procp;
+ int error;
+
+ /* first check the process still exists */
+ switch (pfs->pfs_type) {
+ case Proot:
+ 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;
+
+ /*
+ * 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 Pregs:
+ case Pfpregs:
+ case Pmem:
+ if (procp->p_flag & P_SUGID)
+ vap->va_mode &= ~((VREAD|VWRITE)|
+ ((VREAD|VWRITE)>>3)|
+ ((VREAD|VWRITE)>>6));
+ break;
+ }
+
+ /*
+ * 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(&vap->va_ctime);
+ vap->va_atime = vap->va_mtime = vap->va_ctime;
+
+ /*
+ * 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:
+ vap->va_nlink = 2;
+ vap->va_uid = 0;
+ vap->va_gid = 0;
+ break;
+
+ case Pproc:
+ vap->va_nlink = 2;
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+
+ case Pfile:
+ error = EOPNOTSUPP;
+ break;
+
+ case Pmem:
+ vap->va_nlink = 1;
+ vap->va_bytes = vap->va_size =
+ ctob(procp->p_vmspace->vm_tsize +
+ procp->p_vmspace->vm_dsize +
+ procp->p_vmspace->vm_ssize);
+ vap->va_uid = procp->p_ucred->cr_uid;
+ vap->va_gid = procp->p_ucred->cr_gid;
+ break;
+
+ case Pregs:
+ case Pfpregs:
+ 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;
+
+ default:
+ panic("procfs_getattr");
+ }
+
+ return (error);
+}
+
+procfs_setattr(ap)
+ struct vop_setattr_args *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.
+ *
+ * something very similar to this code is duplicated
+ * throughout the 4bsd kernel and should be moved
+ * into kern/vfs_subr.c sometime.
+ *
+ * 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 *ap;
+{
+ struct vattr *vap;
+ struct vattr vattr;
+ int error;
+
+ /*
+ * If you're the super-user,
+ * you always get access.
+ */
+ if (ap->a_cred->cr_uid == (uid_t) 0)
+ return (0);
+ vap = &vattr;
+ if (error = VOP_GETATTR(ap->a_vp, vap, ap->a_cred, ap->a_p))
+ return (error);
+
+ /*
+ * Access check is based on only one of owner, group, public.
+ * If not owner, then check group. If not a member of the
+ * group, then check public access.
+ */
+ if (ap->a_cred->cr_uid != vap->va_uid) {
+ gid_t *gp;
+ int i;
+
+ (ap->a_mode) >>= 3;
+ gp = ap->a_cred->cr_groups;
+ for (i = 0; i < ap->a_cred->cr_ngroups; i++, gp++)
+ if (vap->va_gid == *gp)
+ goto found;
+ ap->a_mode >>= 3;
+found:
+ ;
+ }
+
+ if ((vap->va_mode & ap->a_mode) == ap->a_mode)
+ return (0);
+
+ return (EACCES);
+}
+
+/*
+ * 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 *ap;
+{
+ struct componentname *cnp = ap->a_cnp;
+ struct vnode **vpp = ap->a_vpp;
+ struct vnode *dvp = ap->a_dvp;
+ char *pname = cnp->cn_nameptr;
+ int error = 0;
+ pid_t pid;
+ struct vnode *nvp;
+ struct pfsnode *pfs;
+ struct proc *procp;
+ pfstype pfs_type;
+ int i;
+
+ if (cnp->cn_namelen == 1 && *pname == '.') {
+ *vpp = dvp;
+ VREF(dvp);
+ /*VOP_LOCK(dvp);*/
+ return (0);
+ }
+
+ *vpp = NULL;
+
+ pfs = VTOPFS(dvp);
+ switch (pfs->pfs_type) {
+ case Proot:
+ if (cnp->cn_flags & ISDOTDOT)
+ return (EIO);
+
+ if (CNEQ(cnp, "curproc", 7))
+ pid = cnp->cn_proc->p_pid;
+ else
+ pid = atopid(pname, cnp->cn_namelen);
+ if (pid == NO_PID)
+ return (ENOENT);
+
+ procp = PFIND(pid);
+ if (procp == 0)
+ return (ENOENT);
+
+ error = procfs_allocvp(dvp->v_mount, &nvp, pid, Pproc);
+ if (error)
+ return (error);
+
+ nvp->v_type = VDIR;
+ pfs = VTOPFS(nvp);
+
+ *vpp = nvp;
+ return (0);
+
+ case Pproc:
+ if (cnp->cn_flags & ISDOTDOT) {
+ error = procfs_root(dvp->v_mount, vpp);
+ return (error);
+ }
+
+ procp = PFIND(pfs->pfs_pid);
+ if (procp == 0)
+ return (ENOENT);
+
+ for (i = 0; i < Nprocent; i++) {
+ struct pfsnames *dp = &procent[i];
+
+ if (cnp->cn_namelen == dp->d_namlen &&
+ bcmp(pname, dp->d_name, dp->d_namlen) == 0) {
+ pfs_type = dp->d_pfstype;
+ goto found;
+ }
+ }
+ return (ENOENT);
+
+ found:
+ if (pfs_type == Pfile) {
+ nvp = procfs_findtextvp(procp);
+ if (nvp) {
+ VREF(nvp);
+ VOP_LOCK(nvp);
+ } else {
+ error = ENXIO;
+ }
+ } else {
+ error = procfs_allocvp(dvp->v_mount, &nvp,
+ pfs->pfs_pid, pfs_type);
+ if (error)
+ return (error);
+
+ nvp->v_type = VREG;
+ pfs = VTOPFS(nvp);
+ }
+ *vpp = nvp;
+ return (error);
+
+ default:
+ return (ENOTDIR);
+ }
+}
+
+/*
+ * readdir returns directory entries from pfsnode (vp).
+ *
+ * the strategy here with procfs is to generate a single
+ * directory entry at a time (struct pfsdent) 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 *ap;
+{
+ struct uio *uio = ap->a_uio;
+ struct pfsdent d;
+ struct pfsdent *dp = &d;
+ struct pfsnode *pfs;
+ int error;
+ int count;
+ int i;
+
+ pfs = VTOPFS(ap->a_vp);
+
+ if (uio->uio_resid < UIO_MX)
+ return (EINVAL);
+ if (uio->uio_offset & (UIO_MX-1))
+ return (EINVAL);
+ if (uio->uio_offset < 0)
+ return (EINVAL);
+
+ error = 0;
+ count = 0;
+ i = uio->uio_offset / 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: {
+ while (uio->uio_resid >= UIO_MX) {
+ struct pfsnames *dt;
+
+ if (i >= Nprocent)
+ break;
+
+ dt = &procent[i];
+
+ dp->d_reclen = UIO_MX;
+ dp->d_fileno = PROCFS_FILENO(pfs->pfs_pid, dt->d_pfstype);
+ dp->d_type = DT_REG;
+ dp->d_namlen = dt->d_namlen;
+ bcopy(dt->d_name, dp->d_name, sizeof(dt->d_name)-1);
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ count += UIO_MX;
+ i++;
+ }
+
+ 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: {
+ int pcnt;
+#ifdef PROCFS_ZOMBIE
+ int doingzomb = 0;
+#endif
+ volatile struct proc *p;
+
+ p = allproc;
+
+#define PROCFS_XFILES 1 /* number of other entries, like "curproc" */
+ pcnt = PROCFS_XFILES;
+
+ while (p && uio->uio_resid >= UIO_MX) {
+ bzero((char *) dp, UIO_MX);
+ dp->d_type = DT_DIR;
+ dp->d_reclen = UIO_MX;
+
+ switch (i) {
+ case 0:
+ /* ship out entry for "curproc" */
+ dp->d_fileno = PROCFS_FILENO(PID_MAX+1, Pproc);
+ dp->d_namlen = sprintf(dp->d_name, "curproc");
+ break;
+
+ default:
+ if (pcnt >= i) {
+ dp->d_fileno = PROCFS_FILENO(p->p_pid, Pproc);
+ dp->d_namlen = sprintf(dp->d_name, "%ld", (long) p->p_pid);
+ }
+
+ p = p->p_next;
+
+#ifdef PROCFS_ZOMBIE
+ if (p == 0 && doingzomb == 0) {
+ doingzomb = 1;
+ p = zombproc;
+ }
+#endif
+
+ if (pcnt++ < i)
+ continue;
+
+ break;
+ }
+ error = uiomove((caddr_t) dp, UIO_MX, uio);
+ if (error)
+ break;
+ count += UIO_MX;
+ i++;
+ }
+
+ break;
+
+ }
+
+ default:
+ error = ENOTDIR;
+ break;
+ }
+
+ uio->uio_offset = i * UIO_MX;
+
+ return (error);
+}
+
+/*
+ * 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 0000000..111c517
--- /dev/null
+++ b/sys/miscfs/specfs/spec_vnops.c
@@ -0,0 +1,689 @@
+/*
+ * 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.6 (Berkeley) 4/9/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_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, spec_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 && isdisk(dev, VCHR))
+ 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);
+ }
+ }
+ 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) && isdisk(dev, VBLK))
+ 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;
+ struct partinfo dpart;
+ int n, on, majordev, (*ioctl)();
+ int error = 0;
+ dev_t dev;
+
+#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_offset < 0)
+ return (EINVAL);
+ bsize = BLKDEV_IOSIZE;
+ dev = vp->v_rdev;
+ if ((majordev = major(dev)) < nblkdev &&
+ (ioctl = bdevsw[majordev].d_ioctl) != NULL &&
+ (*ioctl)(dev, DIOCGPART, (caddr_t)&dpart, FREAD, p) == 0 &&
+ 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;
+ bscale = bsize / DEV_BSIZE;
+ do {
+ bn = (uio->uio_offset / DEV_BSIZE) &~ (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);
+ if (n + on == bsize)
+ bp->b_flags |= B_AGE;
+ 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;
+ int bsize, blkmask;
+ struct partinfo dpart;
+ register int n, on;
+ 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;
+ if ((*bdevsw[major(vp->v_rdev)].d_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;
+ }
+ blkmask = (bsize / DEV_BSIZE) - 1;
+ do {
+ bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask;
+ 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) {
+ bp->b_flags |= B_AGE;
+ 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;
+ int 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;
+
+ switch (ap->a_vp->v_type) {
+
+ case VCHR:
+ return ((*cdevsw[major(dev)].d_ioctl)(dev, ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_p));
+
+ case VBLK:
+ if (ap->a_command == 0 && (int)ap->a_data == B_TAPE)
+ if (bdevsw[major(dev)].d_flags & B_TAPE)
+ return (0);
+ else
+ return (1);
+ return ((*bdevsw[major(dev)].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) {
+ 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;
+ int *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 0000000..a13b66e
--- /dev/null
+++ b/sys/miscfs/specfs/specdev.h
@@ -0,0 +1,127 @@
+/*
+ * 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.2 (Berkeley) 2/2/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 *));
+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)
+#define spec_bwrite ((int (*) __P((struct vop_bwrite_args *)))nullop)
diff --git a/sys/miscfs/umapfs/umap.h b/sys/miscfs/umapfs/umap.h
new file mode 100644
index 0000000..9f4d1e7
--- /dev/null
+++ b/sys/miscfs/umapfs/umap.h
@@ -0,0 +1,92 @@
+/*
+ * 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.h 8.3 (Berkeley) 1/21/94
+ *
+ * @(#)null_vnops.c 1.5 (Berkeley) 7/10/92
+ */
+
+#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 {
+ struct umap_node *umap_forw; /* Hash chain */
+ struct umap_node *umap_back;
+ 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 0000000..6f1f077
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_subr.c
@@ -0,0 +1,397 @@
+/*
+ * 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.
+ *
+ * @(#)umap_subr.c 8.6 (Berkeley) 1/26/94
+ *
+ * $Id: lofs_subr.c, v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
+ */
+
+#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>
+
+#define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
+#define NUMAPNODECACHE 16
+#define UMAP_NHASH(vp) ((((u_long) vp)>>LOG2_SIZEVNODE) & (NUMAPNODECACHE-1))
+
+/*
+ * 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.
+ */
+
+/*
+ * Cache head
+ */
+struct umap_node_cache {
+ struct umap_node *ac_forw;
+ struct umap_node *ac_back;
+};
+
+static struct umap_node_cache umap_node_cache[NUMAPNODECACHE];
+
+/*
+ * Initialise cache headers
+ */
+umapfs_init()
+{
+ struct umap_node_cache *ac;
+#ifdef UMAPFS_DIAGNOSTIC
+ printf("umapfs_init\n"); /* printed during system boot */
+#endif
+
+ for (ac = umap_node_cache; ac < umap_node_cache + NUMAPNODECACHE; ac++)
+ ac->ac_forw = ac->ac_back = (struct umap_node *) ac;
+}
+
+/*
+ * Compute hash list for given target vnode
+ */
+static struct umap_node_cache *
+umap_node_hash(targetvp)
+ struct vnode *targetvp;
+{
+
+ return (&umap_node_cache[UMAP_NHASH(targetvp)]);
+}
+
+/*
+ * 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_cache *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_node_hash(targetvp);
+
+ loop:
+ for (a = hd->ac_forw; a != (struct umap_node *) hd; a = a->umap_forw) {
+ 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, lofsvp is the target vnode.
+ * Maintain a reference to (targetvp).
+ */
+static int
+umap_node_alloc(mp, lowervp, vpp)
+ struct mount *mp;
+ struct vnode *lowervp;
+ struct vnode **vpp;
+{
+ struct umap_node_cache *hd;
+ struct umap_node *xp;
+ struct vnode *othervp, *vp;
+ int error;
+
+ if (error = getnewvnode(VT_UMAP, mp, umap_vnodeop_p, vpp))
+ return (error);
+ vp = *vpp;
+
+ MALLOC(xp, struct umap_node *, sizeof(struct umap_node),
+ M_TEMP, M_WAITOK);
+ vp->v_type = lowervp->v_type;
+ xp->umap_vnode = vp;
+ vp->v_data = xp;
+ 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 (othervp = umap_node_find(lowervp)) {
+ FREE(xp, M_TEMP);
+ vp->v_type = VBAD; /* node is discarded */
+ vp->v_usecount = 0; /* XXX */
+ *vpp = othervp;
+ return (0);
+ }
+ VREF(lowervp); /* Extra VREF will be vrele'd in umap_node_create */
+ hd = umap_node_hash(lowervp);
+ insque(xp, hd);
+ 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;
+ u_long *groupmap, *usermap;
+ uid_t uid;
+ gid_t gid;
+
+ 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;
+
+#ifdef notdef
+ /* cr_gid is the same as cr_groups[0] in 4BSD */
+
+ /* 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 0000000..2480a85
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_vfsops.c
@@ -0,0 +1,407 @@
+/*
+ * 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_vfsops.c 8.3 (Berkeley) 1/21/94
+ *
+ * @(#)null_vfsops.c 1.5 (Berkeley) 7/10/92
+ */
+
+/*
+ * 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;
+ u_int 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, 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);
+ }
+ 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 (VFS_FHTOVP(MOUNTTOUMAPMOUNT(mp)->umapm_vfs, fidp, nam, vpp, exflagsp,credanonp));
+}
+
+int
+umapfs_vptofh(vp, fhp)
+ struct vnode *vp;
+ struct fid *fhp;
+{
+ return (VFS_VPTOFH(UMAPVPTOLOWERVP(vp), fhp));
+}
+
+int umapfs_init __P((void));
+
+struct vfsops umap_vfsops = {
+ 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 0000000..287804e
--- /dev/null
+++ b/sys/miscfs/umapfs/umap_vnops.c
@@ -0,0 +1,488 @@
+/*
+ * 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);
+ (*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);
+
+ compcredp = (*compnamepp)->cn_cred;
+ savecompcredp = compcredp;
+ compcredp = (*compnamepp)->cn_cred = crdup(savecompcredp);
+
+ 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);
+
+ crfree(credp);
+ (*credpp) = savecredp;
+ if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
+ printf("umap_bypass: returning-user now %d\n\n",
+ (*credpp)->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);
+
+ crfree(compcredp);
+ (*compnamepp)->cn_cred = savecompcredp;
+ if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
+ printf("umap_bypass: returning-component-user now %d\n",
+ compcredp->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;
+{
+ short uid, gid;
+ int error, tmpid, nentries, gnentries;
+ u_long (*mapdata)[2], (*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;
+ remque(xp);
+ 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/README b/sys/miscfs/union/README
new file mode 100644
index 0000000..14a4769
--- /dev/null
+++ b/sys/miscfs/union/README
@@ -0,0 +1,7 @@
+If you plan on using union mounts, then you should consider replacing
+"libc/gen/opendir.c" in the C library with the file "libc.opendir.c"
+in this directory. The replacement version of opendir() automatically
+removes duplicate names when a union stack is encountered. You will
+then need to rebuild the C library and all commands.
+
+@(#)README 8.1 (Berkeley) 2/15/94
diff --git a/sys/miscfs/union/libc.opendir.c b/sys/miscfs/union/libc.opendir.c
new file mode 100644
index 0000000..99ed58b
--- /dev/null
+++ b/sys/miscfs/union/libc.opendir.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char orig_sccsid[] = "@(#)opendir.c 8.2 (Berkeley) 2/12/94";
+static char sccsid[] = "@(#)libc.opendir.c 8.1 (Berkeley) 2/15/94";
+#endif /* LIBC_SCCS and not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * open a directory.
+ */
+DIR *
+opendir(name)
+ const char *name;
+{
+ DIR *dirp;
+ int fd;
+ int incr;
+ struct statfs sfb;
+
+ if ((fd = open(name, 0)) == -1)
+ return (NULL);
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1 ||
+ (dirp = (DIR *)malloc(sizeof(DIR))) == NULL) {
+ close(fd);
+ return (NULL);
+ }
+
+ /*
+ * If CLBYTES is an exact multiple of DIRBLKSIZ, use a CLBYTES
+ * buffer that it cluster boundary aligned.
+ * Hopefully this can be a big win someday by allowing page
+ * trades trade to user space to be done by getdirentries()
+ */
+ if ((CLBYTES % DIRBLKSIZ) == 0)
+ incr = CLBYTES;
+ else
+ incr = DIRBLKSIZ;
+
+#ifdef MOUNT_UNION
+ /*
+ * Determine whether this directory is the top of a union stack.
+ */
+ if (fstatfs(fd, &sfb) < 0) {
+ free(dirp);
+ close(fd);
+ return (NULL);
+ }
+
+ if (sfb.f_type == MOUNT_UNION) {
+ int len = 0;
+ int space = 0;
+ char *buf = 0;
+ char *ddptr = 0;
+ int n;
+ struct dirent **dpv;
+
+ /*
+ * The strategy here is to read all the directory
+ * entries into a buffer, sort the buffer, and
+ * remove duplicate entries by setting the inode
+ * number to zero.
+ */
+
+ /*
+ * Fixup dd_loc to be non-zero to fake out readdir
+ */
+ dirp->dd_loc = sizeof(void *);
+
+ do {
+ /*
+ * Always make at least DIRBLKSIZ bytes
+ * available to getdirentries
+ */
+ if (space < DIRBLKSIZ) {
+ space += incr;
+ len += incr;
+ buf = realloc(buf, len);
+ if (buf == NULL) {
+ free(dirp);
+ close(fd);
+ return (NULL);
+ }
+ ddptr = buf + (len - space) + dirp->dd_loc;
+ }
+
+ n = getdirentries(fd, ddptr, space, &dirp->dd_seek);
+ if (n > 0) {
+ ddptr += n;
+ space -= n;
+ }
+ } while (n > 0);
+
+ /*
+ * There is now a buffer full of (possibly) duplicate
+ * names.
+ */
+ dirp->dd_buf = buf;
+
+ /*
+ * Go round this loop twice...
+ *
+ * Scan through the buffer, counting entries.
+ * On the second pass, save pointers to each one.
+ * Then sort the pointers and remove duplicate names.
+ */
+ for (dpv = 0;;) {
+ n = 0;
+ ddptr = buf + dirp->dd_loc;
+ while (ddptr < buf + len) {
+ struct dirent *dp;
+
+ dp = (struct dirent *) ddptr;
+ if ((int)dp & 03)
+ break;
+ if ((dp->d_reclen <= 0) ||
+ (dp->d_reclen > (buf + len + 1 - ddptr)))
+ break;
+ ddptr += dp->d_reclen;
+ if (dp->d_fileno) {
+ if (dpv)
+ dpv[n] = dp;
+ n++;
+ }
+ }
+
+ if (dpv) {
+ struct dirent *xp;
+
+ /*
+ * If and when whiteouts happen,
+ * this sort would need to be stable.
+ */
+ heapsort(dpv, n, sizeof(*dpv), alphasort);
+
+ dpv[n] = NULL;
+ xp = NULL;
+
+ /*
+ * Scan through the buffer in sort order,
+ * zapping the inode number of any
+ * duplicate names.
+ */
+ for (n = 0; dpv[n]; n++) {
+ struct dirent *dp = dpv[n];
+
+ if ((xp == NULL) ||
+ strcmp(dp->d_name, xp->d_name))
+ xp = dp;
+ else
+ dp->d_fileno = 0;
+ }
+
+ free(dpv);
+ break;
+ } else {
+ dpv = malloc((n+1) * sizeof(struct dirent *));
+ if (dpv == NULL)
+ break;
+ }
+ }
+
+ dirp->dd_len = len;
+ dirp->dd_size = ddptr - dirp->dd_buf;
+ } else
+#endif /* MOUNT_UNION */
+ {
+ dirp->dd_len = incr;
+ dirp->dd_buf = malloc(dirp->dd_len);
+ if (dirp->dd_buf == NULL) {
+ free(dirp);
+ close (fd);
+ return (NULL);
+ }
+ dirp->dd_seek = 0;
+ dirp->dd_loc = 0;
+ }
+
+ dirp->dd_fd = fd;
+
+ /*
+ * Set up seek point for rewinddir.
+ */
+ dirp->dd_rewind = telldir(dirp);
+
+ return (dirp);
+}
diff --git a/sys/miscfs/union/union.h b/sys/miscfs/union/union.h
new file mode 100644
index 0000000..463218a
--- /dev/null
+++ b/sys/miscfs/union/union.h
@@ -0,0 +1,117 @@
+/*
+ * 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.2 (Berkeley) 2/17/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 */
+ char *un_path; /* saved component name */
+ int un_hash; /* saved un_path hash value */
+ int un_openl; /* # of opens on lowervp */
+ int un_flags;
+#ifdef DIAGNOSTIC
+ pid_t un_pid;
+#endif
+};
+
+#define UN_WANT 0x01
+#define UN_LOCKED 0x02
+#define UN_ULOCK 0x04 /* Upper node is locked */
+#define UN_KLOCK 0x08 /* Keep upper node locked on vput */
+
+extern int union_allocvp __P((struct vnode **, struct mount *,
+ struct vnode *, struct vnode *,
+ struct componentname *, struct vnode *,
+ struct vnode *));
+extern int union_copyfile __P((struct proc *, struct ucred *,
+ struct vnode *, struct vnode *));
+extern int union_mkshadow __P((struct union_mount *, struct vnode *,
+ struct componentname *, struct vnode **));
+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 *));
+
+#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 0000000..77947d1
--- /dev/null
+++ b/sys/miscfs/union/union_subr.c
@@ -0,0 +1,744 @@
+/*
+ * 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.4 (Berkeley) 2/17/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.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 <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_WANT;
+ 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_WANT) {
+ unvplock[ix] &= ~UN_WANT;
+ 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);
+
+ if (ohash != nhash) {
+ /*
+ * Ensure locking is ordered from lower to higher
+ * to avoid deadlocks.
+ */
+ if (nhash < ohash) {
+ int t = ohash;
+ ohash = nhash;
+ nhash = t;
+ }
+
+ while (union_list_lock(ohash))
+ continue;
+
+ while (union_list_lock(nhash))
+ continue;
+
+ LIST_REMOVE(un, un_cache);
+ union_list_unlock(ohash);
+ } else {
+ while (union_list_lock(nhash))
+ continue;
+ }
+
+ 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;
+ }
+
+ if (un->un_uppervp != uppervp) {
+ if (un->un_uppervp)
+ vrele(un->un_uppervp);
+
+ un->un_uppervp = uppervp;
+ }
+
+ if (ohash != nhash)
+ LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);
+
+ 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);
+}
+
+/*
+ * 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)
+ struct vnode **vpp;
+ struct mount *mp;
+ struct vnode *undvp;
+ 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 error;
+ struct union_node *un;
+ struct union_node **pp;
+ struct vnode *xlowervp = NULLVP;
+ int hash;
+ int try;
+
+ if (uppervp == NULLVP && lowervp == NULLVP)
+ panic("union: unidentifiable allocation");
+
+ if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {
+ xlowervp = lowervp;
+ lowervp = NULLVP;
+ }
+
+loop:
+ 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_WANT;
+ sleep((caddr_t) &un->un_flags, 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) &&
+ (lowervp->v_type == VREG)) {
+ 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);
+ }
+
+ /*
+ * 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);
+
+ 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_lowervp = lowervp;
+ 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) && (lowervp->v_type == VREG)) {
+ 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;
+ }
+
+ LIST_INSERT_HEAD(&unhead[hash], un, un_cache);
+
+ if (xlowervp)
+ vrele(xlowervp);
+
+out:
+ union_list_unlock(hash);
+
+ return (error);
+}
+
+int
+union_freevp(vp)
+ struct vnode *vp;
+{
+ struct union_node *un = VTOUNION(vp);
+
+ LIST_REMOVE(un, un_cache);
+
+ if (un->un_uppervp)
+ vrele(un->un_uppervp);
+ if (un->un_lowervp)
+ vrele(un->un_lowervp);
+ if (un->un_dirvp)
+ 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(p, cred, fvp, tvp)
+ struct proc *p;
+ struct ucred *cred;
+ struct vnode *fvp;
+ struct vnode *tvp;
+{
+ 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 */
+ LEASE_CHECK(fvp, p, cred, LEASE_READ);
+ VOP_LOCK(fvp); /* XXX */
+ VOP_UNLOCK(tvp); /* XXX */
+ LEASE_CHECK(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);
+}
+
+/*
+ * 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;
+
+ /*
+ * 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)
+ */
+
+ /*
+ * 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_pnbuf = malloc(cnp->cn_namelen+1, M_NAMEI, M_WAITOK);
+ bcopy(cnp->cn_nameptr, cn.cn_pnbuf, cnp->cn_namelen);
+ cn.cn_pnbuf[cnp->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_namelen = cnp->cn_namelen;
+ cn.cn_hash = cnp->cn_hash;
+ cn.cn_consume = cnp->cn_consume;
+
+ VREF(dvp);
+ if (error = relookup(dvp, vpp, &cn))
+ return (error);
+ vrele(dvp);
+
+ if (*vpp) {
+ VOP_ABORTOP(dvp, &cn);
+ VOP_UNLOCK(dvp);
+ vrele(*vpp);
+ *vpp = NULLVP;
+ return (EEXIST);
+ }
+
+ VATTR_NULL(&va);
+ va.va_type = VDIR;
+ va.va_mode = um->um_cmode;
+
+ /* LEASE_CHECK: dvp is locked */
+ LEASE_CHECK(dvp, p, p->p_ucred, LEASE_WRITE);
+
+ error = VOP_MKDIR(dvp, vpp, &cn, &va);
+ 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;
+ LEASE_CHECK(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));
+}
+
+void
+union_removed_upper(un)
+ struct union_node *un;
+{
+ if (un->un_flags & UN_ULOCK) {
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ }
+
+ union_newupper(un, NULLVP);
+}
+
+struct vnode *
+union_lowervp(vp)
+ struct vnode *vp;
+{
+ struct union_node *un = VTOUNION(vp);
+
+ if (un->un_lowervp && (vp->v_type == un->un_lowervp->v_type)) {
+ if (vget(un->un_lowervp, 0))
+ return (NULLVP);
+ }
+
+ return (un->un_lowervp);
+}
diff --git a/sys/miscfs/union/union_vfsops.c b/sys/miscfs/union/union_vfsops.c
new file mode 100644
index 0000000..9fa2746
--- /dev/null
+++ b/sys/miscfs/union/union_vfsops.c
@@ -0,0 +1,550 @@
+/*
+ * 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.7 (Berkeley) 3/5/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;
+ struct ucred *cred = 0;
+ struct ucred *scred;
+ struct vattr va;
+ char *cp;
+ int len;
+ u_int 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;
+ }
+
+ /*
+ * Take a copy of the process's credentials. This isn't
+ * quite right since the euid will always be zero and we
+ * want to get the "real" users credentials. So fix up
+ * the uid field after taking the copy.
+ */
+ cred = crdup(p->p_ucred);
+ cred->cr_uid = p->p_cred->p_ruid;
+
+ /*
+ * Ensure the *real* user has write permission on the
+ * mounted-on directory. This allows the mount_union
+ * command to be made setuid root so allowing anyone
+ * to do union mounts onto any directory on which they
+ * have write permission and which they also own.
+ */
+ error = VOP_GETATTR(mp->mnt_vnodecovered, &va, cred, p);
+ if (error)
+ goto bad;
+ if ((va.va_uid != cred->cr_uid) &&
+ (cred->cr_uid != 0)) {
+ error = EACCES;
+ goto bad;
+ }
+ error = VOP_ACCESS(mp->mnt_vnodecovered, VWRITE, cred, p);
+ if (error)
+ 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. Use the real process credentials,
+ * not the effective ones since this will have come
+ * through a setuid process (mount_union). All this
+ * messing around with permissions is entirely bogus
+ * and should be removed by allowing any user straight
+ * past the mount system call.
+ */
+ scred = p->p_ucred;
+ p->p_ucred = cred;
+ NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT,
+ UIO_USERSPACE, args.target, p);
+ p->p_ucred = scred;
+
+ 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;
+ }
+
+ um->um_cred = 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);
+
+ /*
+ * This is a user mount. Privilege check for unmount
+ * will be done in union_unmount.
+ */
+ mp->mnt_flag |= MNT_USER;
+
+ mp->mnt_data = (qaddr_t) um;
+ getnewfsid(mp, 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 (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 flags = 0;
+ extern int doforce;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_unmount(mp = %x)\n", mp);
+#endif
+
+ /* only the mounter, or superuser can unmount */
+ if ((p->p_cred->p_ruid != um->um_cred->cr_uid) &&
+ (error = suser(p->p_ucred, &p->p_acflag)))
+ return (error);
+
+ 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);
+ if (um_rootvp->v_usecount > 1) {
+ vput(um_rootvp);
+ return (EBUSY);
+ }
+ if (error = vflush(mp, um_rootvp, flags)) {
+ vput(um_rootvp);
+ return (error);
+ }
+
+#ifdef UNION_DIAGNOSTIC
+ vprint("alias root of lower", 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;
+
+#ifdef UNION_DIAGNOSTIC
+ printf("union_root(mp = %x, lvp = %x, uvp = %x)\n", mp,
+ um->um_lowervp,
+ um->um_uppervp);
+#endif
+
+ /*
+ * 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);
+
+ if (error) {
+ if (!loselock)
+ VOP_UNLOCK(um->um_uppervp);
+ vrele(um->um_uppervp);
+ if (um->um_lowervp)
+ vrele(um->um_lowervp);
+ } else {
+ (*vpp)->v_flag |= VROOT;
+ 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 = MOUNT_UNION;
+ 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);
+ }
+ 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 = {
+ 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 0000000..96327b0
--- /dev/null
+++ b/sys/miscfs/union/union_vnops.c
@@ -0,0 +1,1495 @@
+/*
+ * 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.6 (Berkeley) 2/17/94
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/file.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 <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, dvp, vpp, cnp)
+ struct vnode *udvp;
+ struct vnode *dvp;
+ struct vnode **vpp;
+ struct componentname *cnp;
+{
+ int error;
+ struct vnode *tdvp;
+ struct mount *mp;
+
+ /*
+ * 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) {
+ for (;;) {
+ /*
+ * Don't do the NOCROSSMOUNT check
+ * at this level. By definition,
+ * union fs deals with namespaces, not
+ * filesystems.
+ */
+ if ((dvp->v_flag & VROOT) == 0)
+ break;
+
+ tdvp = dvp;
+ 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;
+
+ cnp->cn_flags |= LOCKPARENT;
+
+ upperdvp = dun->un_uppervp;
+ lowerdvp = dun->un_lowervp;
+ uppervp = NULLVP;
+ lowervp = NULLVP;
+
+ /*
+ * 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) {
+ 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);
+ }
+ } 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) {
+ 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) {
+ 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 (!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) {
+ vput(lowervp);
+ lowervp = NULLVP;
+ }
+ return (uerror);
+ }
+ }
+ }
+
+ if (lowervp)
+ VOP_UNLOCK(lowervp);
+
+ error = union_allocvp(ap->a_vpp, dvp->v_mount, dvp, upperdvp, cnp,
+ uppervp, lowervp);
+
+ if (error) {
+ if (uppervp)
+ vput(uppervp);
+ if (lowervp)
+ 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 = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ 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,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ if (error)
+ vput(vp);
+ return (error);
+ }
+
+ vput(ap->a_dvp);
+ return (EROFS);
+}
+
+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 = un->un_uppervp;
+
+ if (dvp) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_MKNOD(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ if (vp) {
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ 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)) {
+ struct vnode *vp;
+ int i;
+
+ /*
+ * Open the named file in the upper layer. Note that
+ * the file may have come into existence *since* the
+ * lookup was done, since the upper layer may really
+ * be a loopback mount of some other filesystem...
+ * so open the file with exclusive create and barf if
+ * it already exists.
+ * XXX - perhaps should re-lookup the node (once more
+ * with feeling) and simply open that. Who knows.
+ */
+ error = union_vn_create(&vp, un, p);
+ if (error)
+ return (error);
+
+ /* at this point, uppervp is locked */
+ union_newupper(un, vp);
+ un->un_flags |= UN_ULOCK;
+
+ /*
+ * Now, if the file is being opened with truncation,
+ * then the (new) upper vnode is ready to fly,
+ * otherwise the data from the lower vnode must be
+ * copied to the upper layer first. This only works
+ * for regular files (check is made above).
+ */
+ if ((mode & O_TRUNC) == 0) {
+ /*
+ * XXX - should not ignore errors
+ * from VOP_CLOSE
+ */
+ VOP_LOCK(tvp);
+ error = VOP_OPEN(tvp, FREAD, cred, p);
+ if (error == 0) {
+ error = union_copyfile(p, cred,
+ tvp, un->un_uppervp);
+ VOP_UNLOCK(tvp);
+ (void) VOP_CLOSE(tvp, FREAD);
+ } else {
+ VOP_UNLOCK(tvp);
+ }
+
+#ifdef UNION_DIAGNOSTIC
+ if (!error)
+ uprintf("union: copied up %s\n",
+ un->un_path);
+#endif
+ }
+
+ un->un_flags &= ~UN_ULOCK;
+ VOP_UNLOCK(un->un_uppervp);
+ union_vn_close(un->un_uppervp, FWRITE, cred, p);
+ VOP_LOCK(un->un_uppervp);
+ 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 righ, either.
+ */
+ for (i = 0; i < un->un_openl; i++) {
+ (void) VOP_CLOSE(tvp, FREAD);
+ (void) VOP_OPEN(un->un_uppervp, FREAD, cred, p);
+ }
+ un->un_openl = 0;
+
+ 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 (un->un_uppervp) {
+ vp = un->un_uppervp;
+ } else {
+#ifdef UNION_DIAGNOSTIC
+ if (un->un_openl <= 0)
+ panic("union: un_openl cnt");
+#endif
+ --un->un_openl;
+ vp = un->un_lowervp;
+ }
+
+ return (VOP_CLOSE(vp, ap->a_fflag, ap->a_cred, ap->a_p));
+}
+
+/*
+ * 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) {
+ FIXUP(un);
+ return (VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p));
+ }
+
+ if (vp = un->un_lowervp) {
+ VOP_LOCK(vp);
+ error = VOP_ACCESS(vp, ap->a_mode, ap->a_cred, ap->a_p);
+ if (error == 0) {
+ struct union_mount *um = MOUNTTOUNIONMOUNT(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.
+ */
+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;
+
+ vp = un->un_uppervp;
+ if (vp != NULLVP) {
+ FIXUP(un);
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ if (error)
+ return (error);
+ }
+
+ 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) {
+ VOP_LOCK(vp);
+ error = VOP_GETATTR(vp, vap, ap->a_cred, ap->a_p);
+ VOP_UNLOCK(vp);
+ if (error)
+ return (error);
+ }
+
+ if ((vap != ap->a_vap) && (vap->va_type == VDIR))
+ ap->a_vap->va_nlink += vap->va_nlink;
+
+ 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) &&
+ (ap->a_vap->va_size == 0)) {
+ struct vnode *vp;
+
+ error = union_vn_create(&vp, un, ap->a_p);
+ if (error)
+ return (error);
+
+ /* at this point, uppervp is locked */
+ union_newupper(un, vp);
+
+ VOP_UNLOCK(vp);
+ union_vn_close(un->un_uppervp, FWRITE, ap->a_cred, ap->a_p);
+ VOP_LOCK(vp);
+ un->un_flags |= UN_ULOCK;
+ }
+
+ /*
+ * 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);
+ } 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);
+
+ 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 = OTHERVP(ap->a_vp);
+ int dolock = (vp == LOWERVP(ap->a_vp));
+
+ if (dolock)
+ VOP_LOCK(vp);
+ else
+ FIXUP(VTOUNION(ap->a_vp));
+ error = VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, ap->a_cred);
+ if (dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_ioctl(ap)
+ struct vop_ioctl_args /* {
+ struct vnode *a_vp;
+ int a_command;
+ caddr_t a_data;
+ int a_fflag;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (VOP_IOCTL(OTHERVP(ap->a_vp), ap->a_command, ap->a_data,
+ ap->a_fflag, ap->a_cred, ap->a_p));
+}
+
+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;
+{
+
+ return (VOP_SELECT(OTHERVP(ap->a_vp), ap->a_which, ap->a_fflags,
+ ap->a_cred, ap->a_p));
+}
+
+int
+union_mmap(ap)
+ struct vop_mmap_args /* {
+ struct vnode *a_vp;
+ int a_fflags;
+ struct ucred *a_cred;
+ struct proc *a_p;
+ } */ *ap;
+{
+
+ return (VOP_MMAP(OTHERVP(ap->a_vp), ap->a_fflags,
+ ap->a_cred, ap->a_p));
+}
+
+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) {
+ 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;
+{
+
+ return (VOP_SEEK(OTHERVP(ap->a_vp), ap->a_oldoff, ap->a_newoff, ap->a_cred));
+}
+
+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 && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ 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);
+
+ error = VOP_REMOVE(dvp, vp, ap->a_cnp);
+ if (!error)
+ union_removed_upper(un);
+
+ /*
+ * XXX: should create a whiteout here
+ */
+ } else {
+ /*
+ * XXX: should create a whiteout here
+ */
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ error = EROFS;
+ }
+
+ 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;
+ struct union_node *dun = VTOUNION(ap->a_vp);
+ struct union_node *un = VTOUNION(ap->a_tdvp);
+
+ if (dun->un_uppervp && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ FIXUP(dun);
+ VREF(dvp);
+ dun->un_flags |= UN_KLOCK;
+ vput(ap->a_vp);
+ FIXUP(un);
+ VREF(vp);
+ vrele(ap->a_tdvp);
+
+ error = VOP_LINK(dvp, vp, ap->a_cnp);
+ } else {
+ /*
+ * XXX: need to copy to upper layer
+ * and do the link there.
+ */
+ vput(ap->a_vp);
+ vrele(ap->a_tdvp);
+ error = EROFS;
+ }
+
+ return (error);
+}
+
+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) {
+ error = EROFS;
+ goto bad;
+ }
+
+ FIXUP(un);
+ 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) {
+ error = EROFS;
+ goto bad;
+ }
+
+ FIXUP(un);
+ 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) {
+ error = EROFS;
+ goto bad;
+ }
+
+ tdvp = un->un_uppervp;
+ VREF(tdvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_tdvp);
+ }
+
+ if (tvp && tvp->v_op == union_vnodeop_p) {
+ struct union_node *un = VTOUNION(tvp);
+ if (un->un_uppervp == NULLVP) {
+ error = EROFS;
+ goto bad;
+ }
+
+ tvp = un->un_uppervp;
+ 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)
+ 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) {
+ int error;
+ struct vnode *vp;
+
+ FIXUP(un);
+ VREF(dvp);
+ un->un_flags |= UN_KLOCK;
+ vput(ap->a_dvp);
+ error = VOP_MKDIR(dvp, &vp, ap->a_cnp, ap->a_vap);
+ if (error)
+ return (error);
+
+ error = union_allocvp(
+ ap->a_vpp,
+ ap->a_dvp->v_mount,
+ ap->a_dvp,
+ NULLVP,
+ ap->a_cnp,
+ vp,
+ NULLVP);
+ 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 && un->un_uppervp) {
+ struct vnode *dvp = dun->un_uppervp;
+ struct vnode *vp = un->un_uppervp;
+
+ 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);
+
+ error = VOP_RMDIR(dvp, vp, ap->a_cnp);
+ if (!error)
+ union_removed_upper(un);
+
+ /*
+ * XXX: should create a whiteout here
+ */
+ } else {
+ /*
+ * XXX: should create a whiteout here
+ */
+ vput(ap->a_dvp);
+ vput(ap->a_vp);
+ error = EROFS;
+ }
+
+ 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) {
+ 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;
+ } */ *ap;
+{
+ int error = 0;
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (un->un_uppervp) {
+ FIXUP(un);
+ error = VOP_READDIR(un->un_uppervp, ap->a_uio, ap->a_cred);
+ }
+
+ return (error);
+}
+
+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));
+ error = VOP_READLINK(vp, ap->a_uio, ap->a_cred);
+ 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));
+ }
+ error = VOP_ABORTOP(vp, ap->a_cnp);
+ if (islocked && dolock)
+ VOP_UNLOCK(vp);
+
+ return (error);
+}
+
+int
+union_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 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
+ struct union_node *un = VTOUNION(ap->a_vp);
+
+ if (un->un_flags & UN_LOCKED)
+ panic("union: inactivating locked node");
+#endif
+
+ 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) {
+ if ((un->un_flags & UN_ULOCK) == 0) {
+ un->un_flags |= UN_ULOCK;
+ VOP_LOCK(un->un_uppervp);
+ }
+#ifdef DIAGNOSTIC
+ if (un->un_flags & UN_KLOCK)
+ 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_WANT;
+ sleep((caddr_t) &un->un_flags, 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_WANT) {
+ un->un_flags &= ~UN_WANT;
+ wakeup((caddr_t) &un->un_flags);
+ }
+
+#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));
+ error = VOP_BMAP(vp, ap->a_bn, ap->a_vpp, ap->a_bnp, ap->a_runp);
+ 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));
+ 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;
+ int *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));
+ error = VOP_PATHCONF(vp, ap->a_name, ap->a_retval);
+ 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;
+{
+
+ return (VOP_ADVLOCK(OTHERVP(ap->a_vp), ap->a_id, ap->a_op,
+ ap->a_fl, ap->a_flags));
+}
+
+
+/*
+ * 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_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_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 };
OpenPOWER on IntegriCloud