/* kern_descrip.c 5.2 82/07/24 */ #include "../h/param.h" #include "../h/systm.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/inode.h" #include "../h/proc.h" #include "../h/conf.h" #include "../h/file.h" #include "../h/inline.h" #include "../h/socket.h" #include "../h/socketvar.h" #include "../h/mount.h" #include "../h/descrip.h" /* * Descriptor management. */ /* * TODO: * getf should be renamed * ufalloc side effects are gross */ /* * System calls on descriptors. */ dstd() { u.u_r.r_val1 = NOFILE; } dup() { register struct a { int i; } *uap = (struct a *) u.u_ap; register struct file *fp; register int j; if (uap->i &~ 077) { uap->i &= 077; dup2(); return; } /* XXX */ fp = getf(uap->i); if (fp == 0) return; j = ufalloc(); if (j < 0) return; u.u_ofile[j] = fp; fp->f_count++; } dup2() { register struct a { int i, j; } *uap = (struct a *) u.u_ap; register struct file *fp; fp = getf(uap->i); if (fp == 0) return; if (uap->j < 0 || uap->j >= NOFILE) { u.u_error = EBADF; return; } u.u_r.r_val1 = uap->j; if (uap->i == uap->j) return; if (u.u_ofile[uap->j]) { closef(u.u_ofile[uap->j], 0); if (u.u_error) return; /* u.u_ofile[uap->j] = 0; */ } u.u_ofile[uap->j] = fp; fp->f_count++; } close() { register struct a { int i; } *uap = (struct a *)u.u_ap; register struct file *fp; fp = getf(uap->i); if (fp == 0) return; u.u_ofile[uap->i] = 0; closef(fp, 0); /* WHAT IF u.u_error ? */ } dtype() { register struct a { int d; struct dtype *dtypeb; } *uap = (struct a *)u.u_ap; register struct file *fp; struct dtype adtype; fp = getf(uap->d); if (fp == 0) return; adtype.dt_type = 0; /* XXX */ adtype.dt_protocol = 0; /* XXX */ if (copyout((caddr_t)&adtype, (caddr_t)uap->dtypeb, sizeof (struct dtype)) < 0) { u.u_error = EFAULT; return; } } dwrap() { register struct a { int d; struct dtype *dtypeb; } *uap = (struct a *)u.u_ap; register struct file *fp; struct dtype adtype; fp = getf(uap->d); if (fp == 0) return; if (copyin((caddr_t)uap->dtypeb, (caddr_t)&adtype, sizeof (struct dtype)) < 0) { u.u_error = EFAULT; return; } /* DO WRAP */ } dselect() { } dnblock() { register struct a { int d; int how; } *uap = (struct a *)u.u_ap; /* XXX */ } dsignal() { register struct a { int d; int how; } *uap = (struct a *)u.u_ap; /* XXX */ } int nselcoll; /* * Select system call. */ oselect() { register struct uap { int nfd; fd_set *rp, *wp; int timo; } *ap = (struct uap *)u.u_ap; fd_set rd, wr; int nfds = 0, readable = 0, writeable = 0; time_t t = time; int s, tsel, ncoll, rem; if (ap->nfd > NOFILE) ap->nfd = NOFILE; if (ap->nfd < 0) { u.u_error = EBADF; return; } if (ap->rp && copyin((caddr_t)ap->rp,(caddr_t)&rd,sizeof(fd_set))) return; if (ap->wp && copyin((caddr_t)ap->wp,(caddr_t)&wr,sizeof(fd_set))) return; retry: ncoll = nselcoll; u.u_procp->p_flag |= SSEL; if (ap->rp) readable = selscan(ap->nfd, rd, &nfds, FREAD); if (ap->wp) writeable = selscan(ap->nfd, wr, &nfds, FWRITE); if (u.u_error) goto done; if (readable || writeable) goto done; rem = (ap->timo+999)/1000 - (time - t); if (ap->timo == 0 || rem <= 0) goto done; s = spl6(); if ((u.u_procp->p_flag & SSEL) == 0 || nselcoll != ncoll) { u.u_procp->p_flag &= ~SSEL; splx(s); goto retry; } u.u_procp->p_flag &= ~SSEL; tsel = tsleep((caddr_t)&selwait, PZERO+1, rem); splx(s); switch (tsel) { case TS_OK: goto retry; case TS_SIG: u.u_error = EINTR; return; case TS_TIME: break; } done: rd.fds_bits[0] = readable; wr.fds_bits[0] = writeable; s = sizeof (fd_set); if (s * NBBY > ap->nfd) s = (ap->nfd + NBBY - 1) / NBBY; u.u_r.r_val1 = nfds; if (ap->rp) (void) copyout((caddr_t)&rd, (caddr_t)ap->rp, sizeof(fd_set)); if (ap->wp) (void) copyout((caddr_t)&wr, (caddr_t)ap->wp, sizeof(fd_set)); } selscan(nfd, fds, nfdp, flag) int nfd; fd_set fds; int *nfdp, flag; { struct file *fp; struct inode *ip; register int bits; int i, able, res = 0; bits = fds.fds_bits[0]; while (i = ffs(bits)) { if (i > nfd) break; bits &= ~(1<<(i-1)); fp = u.u_ofile[i-1]; if (fp == NULL) { u.u_error = EBADF; return (0); } if (fp->f_type == DTYPE_SOCKET) able = soselect(fp->f_socket, flag); else { ip = fp->f_inode; switch (ip->i_mode & IFMT) { case IFCHR: able = (*cdevsw[major(ip->i_rdev)].d_select) (ip->i_rdev, flag); break; case IFBLK: case IFREG: case IFDIR: able = 1; break; } } if (able) { res |= (1<<(i-1)); (*nfdp)++; } } return (res); } /*ARGSUSED*/ seltrue(dev, flag) dev_t dev; int flag; { return (1); } selwakeup(p, coll) register struct proc *p; int coll; { int s; if (coll) { nselcoll++; wakeup((caddr_t)&selwait); } if (p) { if (p->p_wchan == (caddr_t)&selwait) setrun(p); else { s = spl6(); if (p->p_flag & SSEL) p->p_flag &= ~SSEL; splx(s); } } } /* * Allocate a user file descriptor. */ ufalloc() { register i; for (i=0; if_count == 0) goto slot; for (fp = file; fp < lastf; fp++) if (fp->f_count == 0) goto slot; tablefull("file"); u.u_error = ENFILE; return (NULL); slot: u.u_ofile[i] = fp; fp->f_count++; fp->f_offset = 0; fp->f_inode = 0; lastf = fp + 1; return (fp); } /* * Convert a user supplied file descriptor into a pointer * to a file structure. Only task is to check range of the descriptor. * Critical paths should use the GETF macro, defined in inline.h. */ struct file * getf(f) register int f; { register struct file *fp; if ((unsigned)f >= NOFILE || (fp = u.u_ofile[f]) == NULL) { u.u_error = EBADF; return (NULL); } return (fp); } /* * Internal form of close. * Decrement reference count on * file structure. * Also make sure the pipe protocol * does not constipate. * * Decrement reference count on the inode following * removal to the referencing file structure. * Call device handler on last close. * Nouser indicates that the user isn't available to present * errors to. */ closef(fp, nouser) register struct file *fp; { register struct inode *ip; register struct mount *mp; int flag, mode; dev_t dev; register int (*cfunc)(); if (fp == NULL) return; if (fp->f_count > 1) { fp->f_count--; return; } if (fp->f_type == DTYPE_SOCKET) { u.u_error = 0; /* XXX */ soclose(fp->f_socket, nouser); if (nouser == 0 && u.u_error) return; fp->f_socket = 0; fp->f_count = 0; return; } flag = fp->f_flag; ip = fp->f_inode; dev = (dev_t)ip->i_rdev; mode = ip->i_mode & IFMT; ilock(ip); iput(ip); fp->f_count = 0; switch (mode) { case IFCHR: cfunc = cdevsw[major(dev)].d_close; #ifdef EFS /* * Every close() must call the driver if the * extended file system is being used -- not * just the last close. Pass along the file * pointer for reference later. */ if (major(dev) == efs_major) { (*cfunc)(dev, flag, fp, nouser); return; } #endif break; case IFBLK: /* * We don't want to really close the device if it is mounted */ for (mp = mount; mp < &mount[NMOUNT]; mp++) if (mp->m_bufp != NULL && mp->m_dev == dev) return; cfunc = bdevsw[major(dev)].d_close; break; default: return; } for (fp = file; fp < fileNFILE; fp++) { if (fp->f_type == DTYPE_SOCKET) /* XXX */ continue; if (fp->f_count && (ip = fp->f_inode) && ip->i_rdev == dev && (ip->i_mode&IFMT) == mode) return; } if (mode == IFBLK) { /* * On last close of a block device (that isn't mounted) * we must invalidate any in core blocks */ bflush(dev); binval(dev); } (*cfunc)(dev, flag, fp); } #ifdef CAD /* * chfile -- change all references to the inode named by * device/inum to the file referred to by fd. * Used by init to remove all references to the device. */ chfile() { register struct file *fp; register struct inode *from; register struct inode *to; off_t offset; dev_t dev; int rw; struct a { int device; /* actually dev_t */ int inum; /* actually ino_t */ int fd; } *uap; if (!suser()) { u.u_error = EPERM; return; } uap = (struct a *) u.u_ap; fp = getf(uap->fd); if (fp == NULL) { u.u_error = EBADF; return; } if (fp->f_type == DTYPE_SOCKET) { u.u_error = EINVAL; return; } for (from = &inode[0]; from < &inode[ninode]; from++) if (from->i_number == (ino_t)uap->inum && from->i_dev == (dev_t)uap->device) break; if (from >= &inode[ninode]) { u.u_error = ENXIO; return; } offset = fp->f_offset; to = fp->f_inode; from->i_count++; for (fp = &file[0]; fp < &file[nfile]; fp++) { if (fp->f_count > 0 && fp->f_inode == from) { fp->f_inode = to; to->i_count++; fp->f_offset = offset; rw |= fp->f_flag & FWRITE; iput(from); } } /* * This inode is no longer referenced. * Switch out to the appropriate close * routine, if required */ dev = (dev_t)from->i_un.i_rdev; switch(from->i_mode & IFMT) { case IFCHR: (*cdevsw[major(dev)].d_close)(dev, rw); break; case IFBLK: (*bdevsw[major(dev)].d_close)(dev, rw); break; default: break; } iput(from); } #endif