add memory filesystem initialization
[unix-history] / usr / src / sys / kern / sys_generic.c
index 30b6ed0..27ba545 100644 (file)
-/*     sys_generic.c   5.3     82/07/24        */
-
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/dir.h"
-#include "../h/user.h"
-#include "../h/tty.h"
-#include "../h/fcntl.h"
-#include "../h/file.h"
-#include "../h/inode.h"
-#include "../h/buf.h"
-#include "../h/proc.h"
-#include "../h/inline.h"
-#include "../h/conf.h"
-#include "../h/socket.h"
-#include "../h/socketvar.h"
-#include "../h/cmap.h"
-#include "../h/vlimit.h"
-#include "../h/fs.h"
-#ifdef MUSH
-#include "../h/quota.h"
-#include "../h/share.h"
-#else
-#define        CHARGE(nothing)
+/*
+ * Copyright (c) 1982, 1986, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley.  The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ *     @(#)sys_generic.c       7.12 (Berkeley) %G%
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "syscontext.h"
+#include "ioctl.h"
+#include "file.h"
+#include "proc.h"
+#include "uio.h"
+#include "kernel.h"
+#include "stat.h"
+#include "malloc.h"
+#ifdef KTRACE
+#include "ktrace.h"
 #endif
 #endif
-#include "../h/descrip.h"
 
 /*
  * Read system call.
  */
 read()
 {
 
 /*
  * Read system call.
  */
 read()
 {
-       register struct file *fp;
-       register struct inode *ip;
        register struct a {
                int     fdes;
                char    *cbuf;
                unsigned count;
        register struct a {
                int     fdes;
                char    *cbuf;
                unsigned count;
-       } *uap;
+       } *uap = (struct a *)u.u_ap;
+       register struct file *fp;
+       struct uio auio;
+       struct iovec aiov;
+       long cnt, error = 0;
+#ifdef KTRACE
+       struct iovec ktriov;
+#endif
 
 
-       uap = (struct a *)u.u_ap;
-       if ((int)uap->count < 0) {
-               u.u_error = EINVAL;
-               return;
+       if (((unsigned)uap->fdes) >= NOFILE ||
+           (fp = u.u_ofile[uap->fdes]) == NULL ||
+           (fp->f_flag & FREAD) == 0)
+               RETURN (EBADF);
+       if (uap->count < 0)
+               RETURN (EINVAL);
+       aiov.iov_base = (caddr_t)uap->cbuf;
+       aiov.iov_len = uap->count;
+       auio.uio_iov = &aiov;
+       auio.uio_iovcnt = 1;
+       auio.uio_resid = uap->count;
+       auio.uio_rw = UIO_READ;
+       auio.uio_segflg = UIO_USERSPACE;
+#ifdef KTRACE
+       /*
+        * if tracing, save a copy of iovec
+        */
+       if (KTRPOINT(u.u_procp, KTR_GENIO))
+               ktriov = aiov;
+#endif
+       cnt = uap->count;
+       if (setjmp(&u.u_qsave)) {
+               if (auio.uio_resid == cnt) {
+                       if ((u.u_sigintr & sigmask(u.u_procp->p_cursig)) != 0)
+                               error = EINTR;
+                       else
+                               u.u_eosys = RESTARTSYS;
+               }
+       } else
+               error = (*fp->f_ops->fo_read)(fp, &auio, fp->f_cred);
+       cnt -= auio.uio_resid;
+#ifdef KTRACE
+       if (KTRPOINT(u.u_procp, KTR_GENIO))
+               ktrgenio(u.u_procp->p_tracep, uap->fdes, UIO_READ, &ktriov, cnt);
+#endif
+       u.u_r.r_val1 = cnt;
+       RETURN (error);
+}
+
+readv()
+{
+       register struct a {
+               int     fdes;
+               struct  iovec *iovp;
+               unsigned iovcnt;
+       } *uap = (struct a *)u.u_ap;
+       register struct file *fp;
+       struct uio auio;
+       register struct iovec *iov;
+       struct iovec aiov[UIO_SMALLIOV];
+       long i, cnt, error = 0;
+#ifdef KTRACE
+       struct iovec *ktriov = NULL;
+#endif
+
+       if (((unsigned)uap->fdes) >= NOFILE ||
+           (fp = u.u_ofile[uap->fdes]) == NULL ||
+           (fp->f_flag & FREAD) == 0)
+               RETURN (EBADF);
+       if (uap->iovcnt > UIO_SMALLIOV) {
+               if (uap->iovcnt > UIO_MAXIOV)
+                       RETURN (EINVAL);
+               MALLOC(iov, struct iovec *, 
+                     sizeof(struct iovec) * uap->iovcnt, M_IOV, M_WAITOK);
+       } else
+               iov = aiov;
+       auio.uio_iov = iov;
+       auio.uio_iovcnt = uap->iovcnt;
+       auio.uio_rw = UIO_READ;
+       auio.uio_segflg = UIO_USERSPACE;
+       if (error = copyin((caddr_t)uap->iovp, (caddr_t)iov,
+           uap->iovcnt * sizeof (struct iovec)))
+               goto done;
+       auio.uio_resid = 0;
+       for (i = 0; i < uap->iovcnt; i++) {
+               if (iov->iov_len < 0) {
+                       error = EINVAL;
+                       goto done;
+               }
+               auio.uio_resid += iov->iov_len;
+               if (auio.uio_resid < 0) {
+                       error = EINVAL;
+                       goto done;
+               }
+               iov++;
        }
        }
-       GETF(fp, uap->fdes);
-       if ((fp->f_flag&FREAD) == 0) {
-               u.u_error = EBADF;
-               return;
+#ifdef KTRACE
+       /*
+        * if tracing, save a copy of iovec
+        */
+       if (KTRPOINT(u.u_procp, KTR_GENIO))  {
+               int iovlen = auio.uio_iovcnt * sizeof (struct iovec);
+
+               MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
+               bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
        }
        }
-       u.u_base = (caddr_t)uap->cbuf;
-       u.u_count = uap->count;
-       u.u_segflg = 0;
-       if ((u.u_procp->p_flag&SNUSIG) && setjmp(u.u_qsav)) {
-               if (u.u_count == uap->count)
-                       u.u_eosys = RESTARTSYS;
-       } else if (fp->f_type == DTYPE_SOCKET)
-               u.u_error = soreceive(fp->f_socket, (struct sockaddr *)0);
-       else {
-               ip = fp->f_inode;
-               u.u_offset = fp->f_offset;
-               if ((ip->i_mode&IFMT) == IFREG) {
-                       ilock(ip);
-                       readi(ip);
-                       iunlock(ip);
-               } else
-                       readi(ip);
-               fp->f_offset += uap->count - u.u_count;
+#endif
+       cnt = auio.uio_resid;
+       if (setjmp(&u.u_qsave)) {
+               if (auio.uio_resid == cnt) {
+                       if ((u.u_sigintr & sigmask(u.u_procp->p_cursig)) != 0)
+                               error = EINTR;
+                       else
+                               u.u_eosys = RESTARTSYS;
+               }
+       } else
+               error = (*fp->f_ops->fo_read)(fp, &auio, fp->f_cred);
+       cnt -= auio.uio_resid;
+#ifdef KTRACE
+       if (ktriov != NULL) {
+               ktrgenio(u.u_procp->p_tracep, uap->fdes, UIO_READ, ktriov, cnt);
+               FREE(ktriov, M_TEMP);
        }
        }
-       u.u_r.r_val1 = uap->count - u.u_count;
+#endif
+       u.u_r.r_val1 = cnt;
+done:
+       if (uap->iovcnt > UIO_SMALLIOV)
+               FREE(iov, M_IOV);
+       RETURN (error);
 }
 
 /*
 }
 
 /*
@@ -75,378 +175,430 @@ read()
  */
 write()
 {
  */
 write()
 {
-       register struct file *fp;
-       register struct inode *ip;
        register struct a {
                int     fdes;
                char    *cbuf;
                unsigned count;
        register struct a {
                int     fdes;
                char    *cbuf;
                unsigned count;
-       } *uap;
-
-       uap = (struct a *)u.u_ap;
-       if ((int)uap->count < 0) {
-               u.u_error = EINVAL;
-               return;
-       }
-       GETF(fp, uap->fdes);
-       if ((fp->f_flag&FWRITE) == 0) {
-               u.u_error = EBADF;
-               return;
-       }
-       u.u_base = (caddr_t)uap->cbuf;
-       u.u_count = uap->count;
-       u.u_segflg = 0;
-       if ((u.u_procp->p_flag&SNUSIG) && setjmp(u.u_qsav)) {
-               if (u.u_count == uap->count)
-                       u.u_eosys = RESTARTSYS;
-       } else if (fp->f_type == DTYPE_SOCKET)
-               u.u_error = sosend(fp->f_socket, (struct sockaddr *)0);
-       else {
-               ip = fp->f_inode;
-               if (fp->f_flag & O_APPEND)
-                       fp->f_offset = ip->i_size;
-               u.u_offset = fp->f_offset;
-               if ((ip->i_mode&IFMT) == IFREG) {
-                       ilock(ip);
-                       writei(ip);
-                       iunlock(ip);
-               } else
-                       writei(ip);
-               fp->f_offset += uap->count - u.u_count;
-       }
-       u.u_r.r_val1 = uap->count - u.u_count;
-}
-
-readv()
-{
+       } *uap = (struct a *)u.u_ap;
+       register struct file *fp;
+       struct uio auio;
+       struct iovec aiov;
+       long cnt, error = 0;
+#ifdef KTRACE
+       struct iovec ktriov;
+#endif
 
 
+       if (((unsigned)uap->fdes) >= NOFILE ||
+           (fp = u.u_ofile[uap->fdes]) == NULL ||
+           (fp->f_flag & FWRITE) == 0)
+               RETURN (EBADF);
+       if (uap->count < 0)
+               RETURN (EINVAL);
+       aiov.iov_base = (caddr_t)uap->cbuf;
+       aiov.iov_len = uap->count;
+       auio.uio_iov = &aiov;
+       auio.uio_iovcnt = 1;
+       auio.uio_resid = uap->count;
+       auio.uio_rw = UIO_WRITE;
+       auio.uio_segflg = UIO_USERSPACE;
+#ifdef KTRACE
+       /*
+        * if tracing, save a copy of iovec
+        */
+       if (KTRPOINT(u.u_procp, KTR_GENIO))
+               ktriov = aiov;
+#endif
+       cnt = uap->count;
+       if (setjmp(&u.u_qsave)) {
+               if (auio.uio_resid == cnt) {
+                       if ((u.u_sigintr & sigmask(u.u_procp->p_cursig)) != 0)
+                               error = EINTR;
+                       else
+                               u.u_eosys = RESTARTSYS;
+               }
+       } else
+               error = (*fp->f_ops->fo_write)(fp, &auio, fp->f_cred);
+       cnt -= auio.uio_resid;
+#ifdef KTRACE
+       if (KTRPOINT(u.u_procp, KTR_GENIO))
+               ktrgenio(u.u_procp->p_tracep, uap->fdes, UIO_WRITE,
+                   &ktriov, cnt);
+#endif
+       u.u_r.r_val1 = cnt;
+       RETURN (error);
 }
 
 writev()
 {
 }
 
 writev()
 {
+       register struct a {
+               int     fdes;
+               struct  iovec *iovp;
+               unsigned iovcnt;
+       } *uap = (struct a *)u.u_ap;
+       register struct file *fp;
+       struct uio auio;
+       register struct iovec *iov;
+       struct iovec aiov[UIO_SMALLIOV];
+       long i, cnt, error = 0;
+#ifdef KTRACE
+       struct iovec *ktriov = NULL;
+#endif
 
 
+       if (((unsigned)uap->fdes) >= NOFILE ||
+           (fp = u.u_ofile[uap->fdes]) == NULL ||
+           (fp->f_flag & FWRITE) == 0)
+               RETURN (EBADF);
+       if (uap->iovcnt > UIO_SMALLIOV) {
+               if (uap->iovcnt > UIO_MAXIOV)
+                       RETURN (EINVAL);
+               MALLOC(iov, struct iovec *, 
+                     sizeof(struct iovec) * uap->iovcnt, M_IOV, M_WAITOK);
+       } else
+               iov = aiov;
+       auio.uio_iov = iov;
+       auio.uio_iovcnt = uap->iovcnt;
+       auio.uio_rw = UIO_WRITE;
+       auio.uio_segflg = UIO_USERSPACE;
+       if (error = copyin((caddr_t)uap->iovp, (caddr_t)iov,
+           uap->iovcnt * sizeof (struct iovec)))
+               goto done;
+       auio.uio_resid = 0;
+       for (i = 0; i < uap->iovcnt; i++) {
+               if (iov->iov_len < 0) {
+                       error = EINVAL;
+                       goto done;
+               }
+               auio.uio_resid += iov->iov_len;
+               if (auio.uio_resid < 0) {
+                       error = EINVAL;
+                       goto done;
+               }
+               iov++;
+       }
+#ifdef KTRACE
+       /*
+        * if tracing, save a copy of iovec
+        */
+       if (KTRPOINT(u.u_procp, KTR_GENIO))  {
+               int iovlen = auio.uio_iovcnt * sizeof (struct iovec);
+
+               MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK);
+               bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen);
+       }
+#endif
+       cnt = auio.uio_resid;
+       if (setjmp(&u.u_qsave)) {
+               if (auio.uio_resid == cnt) {
+                       if ((u.u_sigintr & sigmask(u.u_procp->p_cursig)) != 0)
+                               error = EINTR;
+                       else
+                               u.u_eosys = RESTARTSYS;
+               }
+       } else
+               error = (*fp->f_ops->fo_write)(fp, &auio, fp->f_cred);
+       cnt -= auio.uio_resid;
+#ifdef KTRACE
+       if (ktriov != NULL) {
+               ktrgenio(u.u_procp->p_tracep, uap->fdes, UIO_WRITE,
+                   ktriov, cnt);
+               FREE(ktriov, M_TEMP);
+       }
+#endif
+       u.u_r.r_val1 = cnt;
+done:
+       if (uap->iovcnt > UIO_SMALLIOV)
+               FREE(iov, M_IOV);
+       RETURN (error);
 }
 
 /*
  * Ioctl system call
 }
 
 /*
  * Ioctl system call
- * Check legality, execute common code, and switch out to individual
- * device routine.
  */
 ioctl()
 {
        register struct file *fp;
  */
 ioctl()
 {
        register struct file *fp;
-       register struct inode *ip;
-       register struct a {
+       struct a {
                int     fdes;
                int     cmd;
                caddr_t cmarg;
                int     fdes;
                int     cmd;
                caddr_t cmarg;
-       } *uap;
-       register dev_t dev;
-       register fmt;
+       } *uap = (struct a *)u.u_ap;
+       register int com;
+       register u_int size;
+       caddr_t memp = 0;
+#define STK_PARAMS     128
+       char stkbuf[STK_PARAMS];
+       caddr_t data = stkbuf;
 
 
-       uap = (struct a *)u.u_ap;
-       if ((fp = getf(uap->fdes)) == NULL)
-               return;
+       if ((unsigned)uap->fdes >= NOFILE ||
+           (fp = u.u_ofile[uap->fdes]) == NULL)
+               RETURN (EBADF);
        if ((fp->f_flag & (FREAD|FWRITE)) == 0) {
                u.u_error = EBADF;
                return;
        }
        if ((fp->f_flag & (FREAD|FWRITE)) == 0) {
                u.u_error = EBADF;
                return;
        }
-       if (uap->cmd==FIOCLEX) {
-               u.u_pofile[uap->fdes] |= EXCLOSE;
+       com = uap->cmd;
+
+       if (com == FIOCLEX) {
+               u.u_pofile[uap->fdes] |= UF_EXCLOSE;
                return;
        }
                return;
        }
-       if (uap->cmd==FIONCLEX) {
-               u.u_pofile[uap->fdes] &= ~EXCLOSE;
+       if (com == FIONCLEX) {
+               u.u_pofile[uap->fdes] &= ~UF_EXCLOSE;
                return;
        }
                return;
        }
-       if (fp->f_type == DTYPE_SOCKET) {
-               soioctl(fp->f_socket, uap->cmd, uap->cmarg);
+
+       /*
+        * Interpret high order word to find
+        * amount of data to be copied to/from the
+        * user's address space.
+        */
+       size = IOCPARM_LEN(com);
+       if (size > IOCPARM_MAX) {
+               u.u_error = ENOTTY;
                return;
        }
                return;
        }
-       ip = fp->f_inode;
-       fmt = ip->i_mode & IFMT;
-       if (fmt != IFCHR) {
-               if (uap->cmd==FIONREAD && (fmt == IFREG || fmt == IFDIR)) {
-                       off_t nread = ip->i_size - fp->f_offset;
-
-                       if (copyout((caddr_t)&nread, uap->cmarg, sizeof(off_t)))
-                               u.u_error = EFAULT;
-               } else if (uap->cmd == FIONBIO || uap->cmd == FIOASYNC)
-                       return;
-               else
-                       u.u_error = ENOTTY;
-               return;
+       if (size > sizeof (stkbuf)) {
+               memp = (caddr_t)malloc((u_long)IOCPARM_LEN(com), M_IOCTLOPS,
+                   M_WAITOK);
+               data = memp;
        }
        }
-       dev = ip->i_rdev;
-       u.u_r.r_val1 = 0;
-       if ((u.u_procp->p_flag&SNUSIG) && setjmp(u.u_qsav)) {
-               u.u_eosys = RESTARTSYS;
-               return;
+       if (com&IOC_IN) {
+               if (size) {
+                       u.u_error = copyin(uap->cmarg, data, (u_int)size);
+                       if (u.u_error) {
+                               if (memp)
+                                       free(memp, M_IOCTLOPS);
+                               return;
+                       }
+               } else
+                       *(caddr_t *)data = uap->cmarg;
+       } else if ((com&IOC_OUT) && size)
+               /*
+                * Zero the buffer so the user always
+                * gets back something deterministic.
+                */
+               bzero(data, size);
+       else if (com&IOC_VOID)
+               *(caddr_t *)data = uap->cmarg;
+
+       switch (com) {
+
+       case FIONBIO:
+               u.u_error = fset(fp, FNDELAY, *(int *)data);
+               break;
+
+       case FIOASYNC:
+               u.u_error = fset(fp, FASYNC, *(int *)data);
+               break;
+
+       case FIOSETOWN:
+               u.u_error = fsetown(fp, *(int *)data);
+               break;
+
+       case FIOGETOWN:
+               u.u_error = fgetown(fp, (int *)data);
+               break;
+       default:
+               if (setjmp(&u.u_qsave))
+                       u.u_error = EINTR;
+               else
+                       u.u_error = (*fp->f_ops->fo_ioctl)(fp, com, data);
+               /*
+                * Copy any data to user, size was
+                * already set and checked above.
+                */
+               if (u.u_error == 0 && (com&IOC_OUT) && size)
+                       u.u_error = copyout(data, uap->cmarg, (u_int)size);
+               break;
        }
        }
-       (*cdevsw[major(dev)].d_ioctl)(dev, uap->cmd, uap->cmarg, 0);
+       if (memp)
+               free(memp, M_IOCTLOPS);
 }
 
 }
 
+int    unselect();
+int    nselcoll;
+
 /*
 /*
- * Do nothing specific version of line
- * discipline specific ioctl command.
+ * Select system call.
  */
  */
-/*ARGSUSED*/
-nullioctl(tp, cmd, addr)
-       struct tty *tp;
-       caddr_t addr;
+select()
 {
 {
+       register struct uap  {
+               int     nd;
+               fd_set  *in, *ou, *ex;
+               struct  timeval *tv;
+       } *uap = (struct uap *)u.u_ap;
+       fd_set ibits[3], obits[3];
+       struct timeval atv;
+       int s, ncoll, ni;
+       label_t lqsave;
 
 
-       return (cmd);
-}
+       bzero((caddr_t)ibits, sizeof(ibits));
+       bzero((caddr_t)obits, sizeof(obits));
+       if (uap->nd > NOFILE)
+               uap->nd = NOFILE;       /* forgiving, if slightly wrong */
+       ni = howmany(uap->nd, NFDBITS);
 
 
-/*
- * Read the file corresponding to
- * the inode pointed at by the argument.
- * The actual read arguments are found
- * in the variables:
- *     u_base          core address for destination
- *     u_offset        byte offset in file
- *     u_count         number of bytes to read
- *     u_segflg        read to kernel/user/user I
- */
-readi(ip)
-       register struct inode *ip;
-{
-       struct buf *bp;
-       struct fs *fs;
-       dev_t dev;
-       daddr_t lbn, bn;
-       off_t diff;
-       register int on, type;
-       register unsigned n;
-       int size;
-       long bsize;
-       extern int mem_no;
-
-       if (u.u_count == 0)
-               return;
-       dev = (dev_t)ip->i_rdev;
-       if (u.u_offset < 0 && ((ip->i_mode&IFMT) != IFCHR ||
-           mem_no != major(dev))) {
-               u.u_error = EINVAL;
-               return;
+#define        getbits(name, x) \
+       if (uap->name) { \
+               u.u_error = copyin((caddr_t)uap->name, (caddr_t)&ibits[x], \
+                   (unsigned)(ni * sizeof(fd_mask))); \
+               if (u.u_error) \
+                       goto done; \
        }
        }
-       ip->i_flag |= IACC;
-       type = ip->i_mode&IFMT;
-       if (type == IFCHR) {
-               register c = u.u_count;
-               (*cdevsw[major(dev)].d_read)(dev);
-               CHARGE(sc_tio * (c - u.u_count));
-               return;
-       }
-       if (type != IFBLK) {
-               dev = ip->i_dev;
-               fs = ip->i_fs;
-               bsize = fs->fs_bsize;
-       } else
-               bsize = BLKDEV_IOSIZE;
-       do {
-               lbn = u.u_offset / bsize;
-               on = u.u_offset % bsize;
-               n = MIN((unsigned)(bsize - on), u.u_count);
-               if (type != IFBLK) {
-                       diff = ip->i_size - u.u_offset;
-                       if (diff <= 0)
-                               return;
-                       if (diff < n)
-                               n = diff;
-                       bn = fsbtodb(fs, bmap(ip, lbn, B_READ));
-                       if (u.u_error)
-                               return;
-                       size = blksize(fs, ip, lbn);
-               } else {
-                       size = bsize;
-                       bn = lbn * (BLKDEV_IOSIZE/DEV_BSIZE);
-                       rablock = bn + (BLKDEV_IOSIZE/DEV_BSIZE);
-                       rasize = bsize;
+       getbits(in, 0);
+       getbits(ou, 1);
+       getbits(ex, 2);
+#undef getbits
+
+       if (uap->tv) {
+               u.u_error = copyin((caddr_t)uap->tv, (caddr_t)&atv,
+                       sizeof (atv));
+               if (u.u_error)
+                       goto done;
+               if (itimerfix(&atv)) {
+                       u.u_error = EINVAL;
+                       goto done;
                }
                }
-               if ((long)bn<0) {
-                       bp = geteblk(size);
-                       clrbuf(bp);
-               } else if (ip->i_lastr + 1 == lbn)
-                       bp = breada(dev, bn, size, rablock, rasize);
-               else
-                       bp = bread(dev, bn, size);
-               ip->i_lastr = lbn;
-               n = MIN(n, size - bp->b_resid);
-               if (n != 0) {
-#ifdef UNFAST
-                       iomove(bp->b_un.b_addr + on, n, B_READ);
-#else
-                       if (u.u_segflg != 1) {
-                               if (copyout(bp->b_un.b_addr+on, u.u_base, n)) {
-                                       u.u_error = EFAULT;
-                                       goto bad;
-                               }
-                       } else
-                               bcopy(bp->b_un.b_addr + on, u.u_base, n);
-                       u.u_base += n;
-                       u.u_offset += n;
-                       u.u_count -= n;
-bad:
-                       ;
-#endif
+               s = splhigh(); timevaladd(&atv, &time); splx(s);
+       }
+retry:
+       ncoll = nselcoll;
+       u.u_procp->p_flag |= SSEL;
+       u.u_r.r_val1 = selscan(ibits, obits, uap->nd);
+       if (u.u_error || u.u_r.r_val1)
+               goto done;
+       s = splhigh();
+       /* this should be timercmp(&time, &atv, >=) */
+       if (uap->tv && (time.tv_sec > atv.tv_sec ||
+           time.tv_sec == atv.tv_sec && time.tv_usec >= atv.tv_usec)) {
+               splx(s);
+               goto done;
+       }
+       if ((u.u_procp->p_flag & SSEL) == 0 || nselcoll != ncoll) {
+               splx(s);
+               goto retry;
+       }
+       u.u_procp->p_flag &= ~SSEL;
+       if (uap->tv) {
+               lqsave = u.u_qsave;
+               if (setjmp(&u.u_qsave)) {
+                       untimeout(unselect, (caddr_t)u.u_procp);
+                       u.u_error = EINTR;
+                       splx(s);
+                       goto done;
                }
                }
-               if (n + on == bsize || u.u_offset == ip->i_size)
-                       bp->b_flags |= B_AGE;
-               brelse(bp);
-       } while (u.u_error == 0 && u.u_count != 0 && n != 0);
-}
-
-/*
- * Write the file corresponding to
- * the inode pointed at by the argument.
- * The actual write arguments are found
- * in the variables:
- *     u_base          core address for source
- *     u_offset        byte offset in file
- *     u_count         number of bytes to write
- *     u_segflg        write to kernel/user/user I
- */
-writei(ip)
-       register struct inode *ip;
-{
-       struct buf *bp;
-       register struct fs *fs;
-       dev_t dev;
-       daddr_t lbn, bn;
-       register int on, type;
-       register unsigned n;
-       long bsize;
-       int size, i, count;
-       extern int mem_no;
-
-       dev = (dev_t)ip->i_rdev;
-       if (u.u_offset < 0 && ((ip->i_mode&IFMT) != IFCHR ||
-           mem_no != major(dev)) ) {
-               u.u_error = EINVAL;
-               return;
+               timeout(unselect, (caddr_t)u.u_procp, hzto(&atv));
        }
        }
-       type = ip->i_mode & IFMT;
-       if (type == IFCHR) {
-               ip->i_flag |= IUPD|ICHG;
-               CHARGE(sc_tio * u.u_count);
-               (*cdevsw[major(dev)].d_write)(dev);
-               return;
+       sleep((caddr_t)&selwait, PZERO+1);
+       if (uap->tv) {
+               u.u_qsave = lqsave;
+               untimeout(unselect, (caddr_t)u.u_procp);
        }
        }
-       if (u.u_count == 0)
-               return;
-       if ((ip->i_mode & IFMT) == IFREG &&
-           u.u_offset + u.u_count > u.u_limit[LIM_FSIZE]) {
-               psignal(u.u_procp, SIGXFSZ);
-               u.u_error = EMFILE;
-               return;
+       splx(s);
+       goto retry;
+done:
+       u.u_procp->p_flag &= ~SSEL;
+#define        putbits(name, x) \
+       if (uap->name) { \
+               int error = copyout((caddr_t)&obits[x], (caddr_t)uap->name, \
+                   (unsigned)(ni * sizeof(fd_mask))); \
+               if (error) \
+                       u.u_error = error; \
        }
        }
-       if (type!=IFBLK) {
-               dev = ip->i_dev;
-               fs = ip->i_fs;
-               bsize = fs->fs_bsize;
-       } else {
-               bsize = BLKDEV_IOSIZE;
+       if (u.u_error == 0) {
+               putbits(in, 0);
+               putbits(ou, 1);
+               putbits(ex, 2);
+#undef putbits
        }
        }
-       do {
-               lbn = u.u_offset / bsize;
-               on = u.u_offset % bsize;
-               n = MIN((unsigned)(bsize - on), u.u_count);
-               if (type != IFBLK) {
-                       bn = fsbtodb(fs, bmap(ip, lbn, B_WRITE, (int)(on + n)));
-                       if ((long)bn<0)
-                               return;
-                       if(u.u_offset + n > ip->i_size &&
-                          (type == IFDIR || type == IFREG || type == IFLNK))
-                               ip->i_size = u.u_offset + n;
-                       size = blksize(fs, ip, lbn);
-               } else {
-                       size = bsize;
-                       bn = lbn * (BLKDEV_IOSIZE/DEV_BSIZE);
+}
+
+unselect(p)
+       register struct proc *p;
+{
+       register int s = splhigh();
+
+       switch (p->p_stat) {
+
+       case SSLEEP:
+               setrun(p);
+               break;
+
+       case SSTOP:
+               unsleep(p);
+               break;
+       }
+       splx(s);
+}
+
+selscan(ibits, obits, nfd)
+       fd_set *ibits, *obits;
+{
+       register int which, i, j;
+       register fd_mask bits;
+       int flag;
+       struct file *fp;
+       int n = 0;
+
+       for (which = 0; which < 3; which++) {
+               switch (which) {
+
+               case 0:
+                       flag = FREAD; break;
+
+               case 1:
+                       flag = FWRITE; break;
+
+               case 2:
+                       flag = 0; break;
                }
                }
-               if (bn) {
-                       count = howmany(size, DEV_BSIZE);
-                       for (i = 0; i < count; i += CLSIZE) {
-                               if (mfind(dev, bn + i))
-                                       munhash(dev, bn + i);
+               for (i = 0; i < nfd; i += NFDBITS) {
+                       bits = ibits[which].fds_bits[i/NFDBITS];
+                       while ((j = ffs(bits)) && i + --j < nfd) {
+                               bits &= ~(1 << j);
+                               fp = u.u_ofile[i + j];
+                               if (fp == NULL) {
+                                       u.u_error = EBADF;
+                                       break;
+                               }
+                               if ((*fp->f_ops->fo_select)(fp, flag)) {
+                                       FD_SET(i + j, &obits[which]);
+                                       n++;
+                               }
                        }
                }
                        }
                }
-               if(n == bsize) 
-                       bp = getblk(dev, bn, size);
-               else
-                       bp = bread(dev, bn, size);
-#ifdef UNFAST
-               iomove(bp->b_un.b_addr + on, n, B_WRITE);
-#else
-               if (u.u_segflg != 1) {
-                       if (copyin(u.u_base, bp->b_un.b_addr + on, n)) {
-                               u.u_error = EFAULT;
-                               goto bad;
-                       }
-               } else
-                       bcopy(u.u_base, bp->b_un.b_addr + on, n);
-               u.u_base += n;
-               u.u_offset += n;
-               u.u_count -= n;
-bad:
-               ;
-#endif
-               if (u.u_error != 0)
-                       brelse(bp);
-               else {
-                       if ((ip->i_mode&IFMT) == IFDIR)
-                               /*
-                                * Writing to clear a directory entry.
-                                * Must insure the write occurs before
-                                * the inode is freed, or may end up
-                                * pointing at a new (different) file
-                                * if inode is quickly allocated again
-                                * and system crashes.
-                                */
-                               bwrite(bp);
-                       else if (n + on == bsize) {
-                               bp->b_flags |= B_AGE;
-                               bawrite(bp);
-                       } else
-                               bdwrite(bp);
-               }
-               ip->i_flag |= IUPD|ICHG;
-               if (u.u_ruid != 0)
-                       ip->i_mode &= ~(ISUID|ISGID);
-       } while (u.u_error == 0 && u.u_count != 0);
+       }
+       return (n);
 }
 
 }
 
-/*
- * Move n bytes at byte location
- * &bp->b_un.b_addr[o] to/from (flag) the
- * user/kernel (u.segflg) area starting at u.base.
- * Update all the arguments by the number
- * of bytes moved.
- */
-iomove(cp, n, flag)
-       register caddr_t cp;
-       register unsigned n;
+/*ARGSUSED*/
+seltrue(dev, flag)
+       dev_t dev;
+       int flag;
 {
 {
-       register int t;
 
 
-       if (n==0)
-               return;
-       if (u.u_segflg != 1) {
-               if (flag==B_WRITE)
-                       t = copyin(u.u_base, (caddr_t)cp, n);
-               else
-                       t = copyout((caddr_t)cp, u.u_base, n);
-               if (t) {
-                       u.u_error = EFAULT;
-                       return;
-               }
-       } else
-               if (flag == B_WRITE)
-                       bcopy(u.u_base, (caddr_t)cp, n);
-               else
-                       bcopy((caddr_t)cp, u.u_base, n);
-       u.u_base += n;
-       u.u_offset += n;
-       u.u_count -= n;
+       return (1);
+}
+
+selwakeup(p, coll)
+       register struct proc *p;
+       int coll;
+{
+
+       if (coll) {
+               nselcoll++;
+               wakeup((caddr_t)&selwait);
+       }
+       if (p) {
+               int s = splhigh();
+               if (p->p_wchan == (caddr_t)&selwait) {
+                       if (p->p_stat == SSLEEP)
+                               setrun(p);
+                       else
+                               unsleep(p);
+               } else if (p->p_flag & SSEL)
+                       p->p_flag &= ~SSEL;
+               splx(s);
+       }
 }
 }