BSD 4_4_Lite1 release
[unix-history] / usr / src / sys / miscfs / specfs / spec_vnops.c
index 172f96e..111c517 100644 (file)
@@ -1,10 +1,36 @@
 /*
 /*
- * Copyright (c) 1989 The Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1989, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  *
- * %sccs.include.redist.c%
+ * 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.
  *
  *
- *     @(#)spec_vnops.c        7.44 (Berkeley) %G%
+ * 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/param.h>
 #include <sys/mount.h>
 #include <sys/namei.h>
 #include <sys/vnode.h>
 #include <sys/mount.h>
 #include <sys/namei.h>
 #include <sys/vnode.h>
-#include <sys/specdev.h>
 #include <sys/stat.h>
 #include <sys/errno.h>
 #include <sys/ioctl.h>
 #include <sys/file.h>
 #include <sys/disklabel.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";
 
 /* symbolic sleep message strings for devices */
 char   devopn[] = "devopn";
@@ -38,20 +64,20 @@ struct vnodeopv_entry_desc spec_vnodeop_entries[] = {
        { &vop_lookup_desc, spec_lookup },              /* lookup */
        { &vop_create_desc, spec_create },              /* create */
        { &vop_mknod_desc, spec_mknod },                /* mknod */
        { &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_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_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_read_desc, spec_read },                  /* read */
        { &vop_write_desc, spec_write },                /* write */
        { &vop_ioctl_desc, spec_ioctl },                /* ioctl */
        { &vop_select_desc, spec_select },              /* select */
        { &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_mmap_desc, spec_mmap },                  /* mmap */
        { &vop_fsync_desc, spec_fsync },                /* fsync */
        { &vop_fsync_desc, spec_fsync },                /* fsync */
-       { &vop_seek_desc, spec_seek },          /* seek */
+       { &vop_seek_desc, spec_seek },                  /* seek */
        { &vop_remove_desc, spec_remove },              /* remove */
        { &vop_remove_desc, spec_remove },              /* remove */
-       { &vop_link_desc, spec_link },          /* link */
+       { &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_rename_desc, spec_rename },              /* rename */
        { &vop_mkdir_desc, spec_mkdir },                /* mkdir */
        { &vop_rmdir_desc, spec_rmdir },                /* rmdir */
@@ -61,15 +87,15 @@ struct vnodeopv_entry_desc spec_vnodeop_entries[] = {
        { &vop_abortop_desc, spec_abortop },            /* abortop */
        { &vop_inactive_desc, spec_inactive },          /* inactive */
        { &vop_reclaim_desc, spec_reclaim },            /* reclaim */
        { &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_lock_desc, spec_lock },                  /* lock */
        { &vop_unlock_desc, spec_unlock },              /* unlock */
        { &vop_unlock_desc, spec_unlock },              /* unlock */
-       { &vop_bmap_desc, spec_bmap },          /* bmap */
+       { &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_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_advlock_desc, spec_advlock },            /* advlock */
        { &vop_blkatoff_desc, spec_blkatoff },          /* blkatoff */
-       { &vop_vget_desc, spec_vget },          /* vget */
        { &vop_valloc_desc, spec_valloc },              /* valloc */
        { &vop_vfree_desc, spec_vfree },                /* vfree */
        { &vop_truncate_desc, spec_truncate },          /* truncate */
        { &vop_valloc_desc, spec_valloc },              /* valloc */
        { &vop_vfree_desc, spec_vfree },                /* vfree */
        { &vop_truncate_desc, spec_truncate },          /* truncate */
@@ -84,8 +110,12 @@ struct vnodeopv_desc spec_vnodeop_opv_desc =
  * Trivial lookup routine that always fails.
  */
 int
  * Trivial lookup routine that always fails.
  */
 int
-spec_lookup (ap)
-       struct vop_lookup_args *ap;
+spec_lookup(ap)
+       struct vop_lookup_args /* {
+               struct vnode *a_dvp;
+               struct vnode **a_vpp;
+               struct componentname *a_cnp;
+       } */ *ap;
 {
 
        *ap->a_vpp = NULL;
 {
 
        *ap->a_vpp = NULL;
@@ -93,37 +123,76 @@ spec_lookup (ap)
 }
 
 /*
 }
 
 /*
- * Open a special file: Don't allow open if fs is mounted -nodev,
- * and don't allow opens of block devices that are currently mounted.
- * Otherwise, call device driver open function.
+ * Open a special file.
  */
 /* ARGSUSED */
  */
 /* ARGSUSED */
-spec_open (ap)
-       struct vop_open_args *ap;
+spec_open(ap)
+       struct vop_open_args /* {
+               struct vnode *a_vp;
+               int  a_mode;
+               struct ucred *a_cred;
+               struct proc *a_p;
+       } */ *ap;
 {
 {
-       USES_VOP_LOCK;
-       USES_VOP_UNLOCK;
-       dev_t dev = (dev_t)ap->a_vp->v_rdev;
+       struct vnode *bvp, *vp = ap->a_vp;
+       dev_t bdev, dev = (dev_t)vp->v_rdev;
        register int maj = major(dev);
        int error;
 
        register int maj = major(dev);
        int error;
 
-       if (ap->a_vp->v_mount && (ap->a_vp->v_mount->mnt_flag & MNT_NODEV))
+       /*
+        * Don't allow open if fs is mounted -nodev.
+        */
+       if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV))
                return (ENXIO);
 
                return (ENXIO);
 
-       switch (ap->a_vp->v_type) {
+       switch (vp->v_type) {
 
        case VCHR:
                if ((u_int)maj >= nchrdev)
                        return (ENXIO);
 
        case VCHR:
                if ((u_int)maj >= nchrdev)
                        return (ENXIO);
-               VOP_UNLOCK(ap->a_vp);
+               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);
                error = (*cdevsw[maj].d_open)(dev, ap->a_mode, S_IFCHR, ap->a_p);
-               VOP_LOCK(ap->a_vp);
+               VOP_LOCK(vp);
                return (error);
 
        case VBLK:
                if ((u_int)maj >= nblkdev)
                        return (ENXIO);
                return (error);
 
        case VBLK:
                if ((u_int)maj >= nblkdev)
                        return (ENXIO);
-               if (error = ufs_mountedon(ap->a_vp))
+               /*
+                * 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 (error);
                return ((*bdevsw[maj].d_open)(dev, ap->a_mode, S_IFBLK, ap->a_p));
        }
@@ -134,70 +203,76 @@ spec_open (ap)
  * Vnode op for read
  */
 /* ARGSUSED */
  * Vnode op for read
  */
 /* ARGSUSED */
-spec_read (ap)
-       struct vop_read_args *ap;
+spec_read(ap)
+       struct vop_read_args /* {
+               struct vnode *a_vp;
+               struct uio *a_uio;
+               int  a_ioflag;
+               struct ucred *a_cred;
+       } */ *ap;
 {
 {
-       USES_VOP_LOCK;
-       USES_VOP_UNLOCK;
-       struct proc *p = ap->a_uio->uio_procp;
+       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;
        struct buf *bp;
        daddr_t bn, nextbn;
        long bsize, bscale;
        struct partinfo dpart;
-       register int n, on;
+       int n, on, majordev, (*ioctl)();
        int error = 0;
        int error = 0;
+       dev_t dev;
 
 #ifdef DIAGNOSTIC
 
 #ifdef DIAGNOSTIC
-       if (ap->a_uio->uio_rw != UIO_READ)
+       if (uio->uio_rw != UIO_READ)
                panic("spec_read mode");
                panic("spec_read mode");
-       if (ap->a_uio->uio_segflg == UIO_USERSPACE && ap->a_uio->uio_procp != curproc)
+       if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
                panic("spec_read proc");
 #endif
                panic("spec_read proc");
 #endif
-       if (ap->a_uio->uio_resid == 0)
+       if (uio->uio_resid == 0)
                return (0);
 
                return (0);
 
-       switch (ap->a_vp->v_type) {
+       switch (vp->v_type) {
 
        case VCHR:
 
        case VCHR:
-               VOP_UNLOCK(ap->a_vp);
-               error = (*cdevsw[major(ap->a_vp->v_rdev)].d_read)
-                       (ap->a_vp->v_rdev, ap->a_uio, ap->a_ioflag);
-               VOP_LOCK(ap->a_vp);
+               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:
                return (error);
 
        case VBLK:
-               if (ap->a_uio->uio_offset < 0)
+               if (uio->uio_offset < 0)
                        return (EINVAL);
                bsize = BLKDEV_IOSIZE;
                        return (EINVAL);
                bsize = BLKDEV_IOSIZE;
-               if ((*bdevsw[major(ap->a_vp->v_rdev)].d_ioctl)(ap->a_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;
-               }
+               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 {
                bscale = bsize / DEV_BSIZE;
                do {
-                       bn = (ap->a_uio->uio_offset / DEV_BSIZE) &~ (bscale - 1);
-                       on = ap->a_uio->uio_offset % bsize;
-                       n = MIN((unsigned)(bsize - on), ap->a_uio->uio_resid);
-                       if (ap->a_vp->v_lastr + bscale == bn) {
+                       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;
                                nextbn = bn + bscale;
-                               error = breadn(ap->a_vp, bn, (int)bsize, &nextbn,
+                               error = breadn(vp, bn, (int)bsize, &nextbn,
                                        (int *)&bsize, 1, NOCRED, &bp);
                        } else
                                        (int *)&bsize, 1, NOCRED, &bp);
                        } else
-                               error = bread(ap->a_vp, bn, (int)bsize, NOCRED, &bp);
-                       ap->a_vp->v_lastr = bn;
-                       n = MIN(n, bsize - bp->b_resid);
+                               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);
                        }
                        if (error) {
                                brelse(bp);
                                return (error);
                        }
-                       error = uiomove(bp->b_un.b_addr + on, n, ap->a_uio);
+                       error = uiomove((char *)bp->b_data + on, n, uio);
                        if (n + on == bsize)
                                bp->b_flags |= B_AGE;
                        brelse(bp);
                        if (n + on == bsize)
                                bp->b_flags |= B_AGE;
                        brelse(bp);
-               } while (error == 0 && ap->a_uio->uio_resid > 0 && n != 0);
+               } while (error == 0 && uio->uio_resid > 0 && n != 0);
                return (error);
 
        default:
                return (error);
 
        default:
@@ -210,12 +285,17 @@ spec_read (ap)
  * Vnode op for write
  */
 /* ARGSUSED */
  * Vnode op for write
  */
 /* ARGSUSED */
-spec_write (ap)
-       struct vop_write_args *ap;
+spec_write(ap)
+       struct vop_write_args /* {
+               struct vnode *a_vp;
+               struct uio *a_uio;
+               int  a_ioflag;
+               struct ucred *a_cred;
+       } */ *ap;
 {
 {
-       USES_VOP_LOCK;
-       USES_VOP_UNLOCK;
-       struct proc *p = ap->a_uio->uio_procp;
+       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 buf *bp;
        daddr_t bn;
        int bsize, blkmask;
@@ -224,28 +304,28 @@ spec_write (ap)
        int error = 0;
 
 #ifdef DIAGNOSTIC
        int error = 0;
 
 #ifdef DIAGNOSTIC
-       if (ap->a_uio->uio_rw != UIO_WRITE)
+       if (uio->uio_rw != UIO_WRITE)
                panic("spec_write mode");
                panic("spec_write mode");
-       if (ap->a_uio->uio_segflg == UIO_USERSPACE && ap->a_uio->uio_procp != curproc)
+       if (uio->uio_segflg == UIO_USERSPACE && uio->uio_procp != curproc)
                panic("spec_write proc");
 #endif
 
                panic("spec_write proc");
 #endif
 
-       switch (ap->a_vp->v_type) {
+       switch (vp->v_type) {
 
        case VCHR:
 
        case VCHR:
-               VOP_UNLOCK(ap->a_vp);
-               error = (*cdevsw[major(ap->a_vp->v_rdev)].d_write)
-                       (ap->a_vp->v_rdev, ap->a_uio, ap->a_ioflag);
-               VOP_LOCK(ap->a_vp);
+               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:
                return (error);
 
        case VBLK:
-               if (ap->a_uio->uio_resid == 0)
+               if (uio->uio_resid == 0)
                        return (0);
                        return (0);
-               if (ap->a_uio->uio_offset < 0)
+               if (uio->uio_offset < 0)
                        return (EINVAL);
                bsize = BLKDEV_IOSIZE;
                        return (EINVAL);
                bsize = BLKDEV_IOSIZE;
-               if ((*bdevsw[major(ap->a_vp->v_rdev)].d_ioctl)(ap->a_vp->v_rdev, DIOCGPART,
+               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)
                    (caddr_t)&dpart, FREAD, p) == 0) {
                        if (dpart.part->p_fstype == FS_BSDFFS &&
                            dpart.part->p_frag != 0 && dpart.part->p_fsize != 0)
@@ -254,25 +334,25 @@ spec_write (ap)
                }
                blkmask = (bsize / DEV_BSIZE) - 1;
                do {
                }
                blkmask = (bsize / DEV_BSIZE) - 1;
                do {
-                       bn = (ap->a_uio->uio_offset / DEV_BSIZE) &~ blkmask;
-                       on = ap->a_uio->uio_offset % bsize;
-                       n = MIN((unsigned)(bsize - on), ap->a_uio->uio_resid);
+                       bn = (uio->uio_offset / DEV_BSIZE) &~ blkmask;
+                       on = uio->uio_offset % bsize;
+                       n = min((unsigned)(bsize - on), uio->uio_resid);
                        if (n == bsize)
                        if (n == bsize)
-                               bp = getblk(ap->a_vp, bn, bsize);
+                               bp = getblk(vp, bn, bsize, 0, 0);
                        else
                        else
-                               error = bread(ap->a_vp, bn, bsize, NOCRED, &bp);
-                       n = MIN(n, bsize - bp->b_resid);
+                               error = bread(vp, bn, bsize, NOCRED, &bp);
+                       n = min(n, bsize - bp->b_resid);
                        if (error) {
                                brelse(bp);
                                return (error);
                        }
                        if (error) {
                                brelse(bp);
                                return (error);
                        }
-                       error = uiomove(bp->b_un.b_addr + on, n, ap->a_uio);
+                       error = uiomove((char *)bp->b_data + on, n, uio);
                        if (n + on == bsize) {
                                bp->b_flags |= B_AGE;
                                bawrite(bp);
                        } else
                                bdwrite(bp);
                        if (n + on == bsize) {
                                bp->b_flags |= B_AGE;
                                bawrite(bp);
                        } else
                                bdwrite(bp);
-               } while (error == 0 && ap->a_uio->uio_resid > 0 && n != 0);
+               } while (error == 0 && uio->uio_resid > 0 && n != 0);
                return (error);
 
        default:
                return (error);
 
        default:
@@ -285,8 +365,15 @@ spec_write (ap)
  * Device ioctl operation.
  */
 /* ARGSUSED */
  * Device ioctl operation.
  */
 /* ARGSUSED */
-spec_ioctl (ap)
-       struct vop_ioctl_args *ap;
+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;
 
 {
        dev_t dev = ap->a_vp->v_rdev;
 
@@ -312,8 +399,14 @@ spec_ioctl (ap)
 }
 
 /* ARGSUSED */
 }
 
 /* ARGSUSED */
-spec_select (ap)
-       struct vop_select_args *ap;
+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;
 
 {
        register dev_t dev;
 
@@ -327,12 +420,66 @@ spec_select (ap)
                return (*cdevsw[major(dev)].d_select)(dev, ap->a_which, ap->a_p);
        }
 }
                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
  */
 
 /*
  * Just call the device strategy routine
  */
-spec_strategy (ap)
-       struct vop_strategy_args *ap;
+spec_strategy(ap)
+       struct vop_strategy_args /* {
+               struct buf *a_bp;
+       } */ *ap;
 {
 
        (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp);
 {
 
        (*bdevsw[major(ap->a_bp->b_dev)].d_strategy)(ap->a_bp);
@@ -342,8 +489,13 @@ spec_strategy (ap)
 /*
  * This is a noop, simply returning what one has been given.
  */
 /*
  * This is a noop, simply returning what one has been given.
  */
-spec_bmap (ap)
-       struct vop_bmap_args *ap;
+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)
 {
 
        if (ap->a_vpp != NULL)
@@ -357,16 +509,20 @@ spec_bmap (ap)
  * At the moment we do not do any locking.
  */
 /* ARGSUSED */
  * At the moment we do not do any locking.
  */
 /* ARGSUSED */
-spec_lock (ap)
-       struct vop_lock_args *ap;
+spec_lock(ap)
+       struct vop_lock_args /* {
+               struct vnode *a_vp;
+       } */ *ap;
 {
 
        return (0);
 }
 
 /* ARGSUSED */
 {
 
        return (0);
 }
 
 /* ARGSUSED */
-spec_unlock (ap)
-       struct vop_unlock_args *ap;
+spec_unlock(ap)
+       struct vop_unlock_args /* {
+               struct vnode *a_vp;
+       } */ *ap;
 {
 
        return (0);
 {
 
        return (0);
@@ -376,22 +532,42 @@ spec_unlock (ap)
  * Device close routine
  */
 /* ARGSUSED */
  * Device close routine
  */
 /* ARGSUSED */
-spec_close (ap)
-       struct vop_close_args *ap;
+spec_close(ap)
+       struct vop_close_args /* {
+               struct vnode *a_vp;
+               int  a_fflag;
+               struct ucred *a_cred;
+               struct proc *a_p;
+       } */ *ap;
 {
 {
-       dev_t dev = ap->a_vp->v_rdev;
+       register struct vnode *vp = ap->a_vp;
+       dev_t dev = vp->v_rdev;
        int (*devclose) __P((dev_t, int, int, struct proc *));
        int (*devclose) __P((dev_t, int, int, struct proc *));
-       int mode;
+       int mode, error;
 
 
-       switch (ap->a_vp->v_type) {
+       switch (vp->v_type) {
 
        case VCHR:
 
        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 the vnode is locked, then we are in the midst
                 * of forcably closing the device, otherwise we only
                 * close on last reference.
                 */
-               if (vcount(ap->a_vp) > 1 && (ap->a_vp->v_flag & VXLOCK) == 0)
+               if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
                        return (0);
                devclose = cdevsw[major(dev)].d_close;
                mode = S_IFCHR;
                        return (0);
                devclose = cdevsw[major(dev)].d_close;
                mode = S_IFCHR;
@@ -403,9 +579,8 @@ spec_close (ap)
                 * we must invalidate any in core blocks, so that
                 * we can, for instance, change floppy disks.
                 */
                 * we must invalidate any in core blocks, so that
                 * we can, for instance, change floppy disks.
                 */
-               vflushbuf(ap->a_vp, 0);
-               if (vinvalbuf(ap->a_vp, 1))
-                       return (0);
+               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
                /*
                 * We do not want to really close the device if it
                 * is still in use unless we are trying to close it
@@ -415,7 +590,7 @@ spec_close (ap)
                 * sum of the reference counts on all the aliased
                 * vnodes descends to one, we are on last close.
                 */
                 * sum of the reference counts on all the aliased
                 * vnodes descends to one, we are on last close.
                 */
-               if (vcount(ap->a_vp) > 1 && (ap->a_vp->v_flag & VXLOCK) == 0)
+               if (vcount(vp) > 1 && (vp->v_flag & VXLOCK) == 0)
                        return (0);
                devclose = bdevsw[major(dev)].d_close;
                mode = S_IFBLK;
                        return (0);
                devclose = bdevsw[major(dev)].d_close;
                mode = S_IFBLK;
@@ -431,20 +606,64 @@ spec_close (ap)
 /*
  * Print out the contents of a special device vnode.
  */
 /*
  * Print out the contents of a special device vnode.
  */
-spec_print (ap)
-       struct vop_print_args *ap;
+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));
 }
 
 {
 
        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 */
 /*
  * Special device advisory byte-level locks.
  */
 /* ARGSUSED */
-spec_advlock (ap)
-       struct vop_advlock_args *ap;
+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);
 {
 
        return (EOPNOTSUPP);