int mmap_exec(u_int k, int envid, int execonly) { /* munmap all non-MAP_INHERIT regions */ struct Mmap *m2, *m = mmap_list.lh_first; if (!execonly) return 0; while (m) if (m->mmap_flags & MAP_INHERIT) { /* not implemented - need to set up a region of vm to mmap data */ assert(0); m = m->mmap_link.le_next; } else { m2 = m->mmap_link.le_next; assert(msync(m->mmap_addr, m->mmap_len, 0) == 0); if (m->mmap_filp) { lock_filp(m->mmap_filp); filp_refcount_dec(m->mmap_filp); if (filp_refcount_get(m->mmap_filp) == 0) { unlock_filp(m->mmap_filp); close_filp(m->mmap_filp); } else unlock_filp(m->mmap_filp); } m = m2; } return 0; }
/*===========================================================================* * do_lseek * *===========================================================================*/ int do_lseek() { /* Perform the lseek(ls_fd, offset, whence) system call. */ register struct filp *rfilp; int r = OK, seekfd, seekwhence; off_t offset; u64_t pos, newpos; seekfd = job_m_in.ls_fd; seekwhence = job_m_in.whence; offset = (off_t) job_m_in.offset_lo; /* Check to see if the file descriptor is valid. */ if ( (rfilp = get_filp(seekfd, VNODE_READ)) == NULL) return(err_code); /* No lseek on pipes. */ if (S_ISFIFO(rfilp->filp_vno->v_mode)) { unlock_filp(rfilp); return(ESPIPE); } /* The value of 'whence' determines the start position to use. */ switch(seekwhence) { case SEEK_SET: pos = cvu64(0); break; case SEEK_CUR: pos = rfilp->filp_pos; break; case SEEK_END: pos = cvul64(rfilp->filp_vno->v_size); break; default: unlock_filp(rfilp); return(EINVAL); } if (offset >= 0) newpos = add64ul(pos, offset); else newpos = sub64ul(pos, -offset); /* Check for overflow. */ if (ex64hi(newpos) != 0) { r = EOVERFLOW; } else if ((off_t) ex64lo(newpos) < 0) { /* no negative file size */ r = EOVERFLOW; } else { /* insert the new position into the output message */ m_out.reply_l1 = ex64lo(newpos); if (cmp64(newpos, rfilp->filp_pos) != 0) { rfilp->filp_pos = newpos; /* Inhibit read ahead request */ r = req_inhibread(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr); } } unlock_filp(rfilp); return(r); }
/*===========================================================================* * actual_llseek * *===========================================================================*/ int actual_llseek(struct fproc *rfp, message *m_out, int seekfd, int seekwhence, u64_t offset) { /* Perform the llseek(ls_fd, offset, whence) system call. */ register struct filp *rfilp; u64_t pos, newpos; int r = OK; long off_hi = ex64hi(offset); /* Check to see if the file descriptor is valid. */ if ( (rfilp = get_filp2(rfp, seekfd, VNODE_READ)) == NULL) { return(err_code); } /* No lseek on pipes. */ if (S_ISFIFO(rfilp->filp_vno->v_mode)) { unlock_filp(rfilp); return(ESPIPE); } /* The value of 'whence' determines the start position to use. */ switch(seekwhence) { case SEEK_SET: pos = cvu64(0); break; case SEEK_CUR: pos = rfilp->filp_pos; break; case SEEK_END: pos = cvul64(rfilp->filp_vno->v_size); break; default: unlock_filp(rfilp); return(EINVAL); } newpos = pos + offset; /* Check for overflow. */ if ((off_hi > 0) && cmp64(newpos, pos) < 0) r = EINVAL; else if ((off_hi < 0) && cmp64(newpos, pos) > 0) r = EINVAL; else { /* insert the new position into the output message */ m_out->reply_l1 = ex64lo(newpos); m_out->reply_l2 = ex64hi(newpos); if (cmp64(newpos, rfilp->filp_pos) != 0) { rfilp->filp_pos = newpos; /* Inhibit read ahead request */ r = req_inhibread(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr); } } unlock_filp(rfilp); return(r); }
int dupvm(struct fproc *rfp, int pfd, int *vmfd, struct filp **newfilp) { int result, procfd; struct filp *f = NULL; struct fproc *vmf = &fproc[VM_PROC_NR]; *newfilp = NULL; if ((f = get_filp2(rfp, pfd, VNODE_READ)) == NULL) { printf("VFS dupvm: get_filp2 failed\n"); return EBADF; } if(!f->filp_vno->v_vmnt->m_haspeek) { unlock_filp(f); printf("VFS dupvm: no peek available\n"); return EINVAL; } assert(f->filp_vno); assert(f->filp_vno->v_vmnt); if (!S_ISREG(f->filp_vno->v_mode) && !S_ISBLK(f->filp_vno->v_mode)) { printf("VFS: mmap regular/blockdev only; dev 0x%x ino %d has mode 0%o\n", (int) f->filp_vno->v_dev, (int) f->filp_vno->v_inode_nr, (int) f->filp_vno->v_mode); unlock_filp(f); return EINVAL; } /* get free FD in VM */ if((result=get_fd(vmf, 0, 0, &procfd, NULL)) != OK) { unlock_filp(f); printf("VFS dupvm: getfd failed\n"); return result; } *vmfd = procfd; f->filp_count++; assert(f->filp_count > 0); vmf->fp_filp[procfd] = f; /* mmap FD's are inuse */ FD_SET(procfd, &vmf->fp_filp_inuse); *newfilp = f; return OK; }
/*===========================================================================* * actual_lseek * *===========================================================================*/ int actual_lseek(struct fproc *rfp, int seekfd, int seekwhence, off_t offset, off_t *newposp) { register struct filp *rfilp; int r = OK; off_t pos, newpos; /* Check to see if the file descriptor is valid. */ if ( (rfilp = get_filp2(rfp, seekfd, VNODE_READ)) == NULL) { return(err_code); } /* No lseek on pipes. */ if (S_ISFIFO(rfilp->filp_vno->v_mode)) { unlock_filp(rfilp); return(ESPIPE); } /* The value of 'whence' determines the start position to use. */ switch(seekwhence) { case SEEK_SET: pos = 0; break; case SEEK_CUR: pos = rfilp->filp_pos; break; case SEEK_END: pos = rfilp->filp_vno->v_size; break; default: unlock_filp(rfilp); return(EINVAL); } newpos = pos + offset; /* Check for overflow. */ if ((offset > 0) && (newpos <= pos)) { r = EOVERFLOW; } else if ((offset < 0) && (newpos >= pos)) { r = EOVERFLOW; } else { if (newposp != NULL) *newposp = newpos; if (newpos != rfilp->filp_pos) { rfilp->filp_pos = newpos; /* Inhibit read ahead request */ r = req_inhibread(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr); } } unlock_filp(rfilp); return(r); }
/*===========================================================================* * do_dup * *===========================================================================*/ int do_dup() { /* Perform the dup(fd) or dup2(fd,fd2) system call. These system calls are * obsolete. In fact, it is not even possible to invoke them using the * current library because the library routines call fcntl(). They are * provided to permit old binary programs to continue to run. */ int rfd, rfd2; struct filp *f; int r = OK; scratch(fp).file.fd_nr = job_m_in.fd; rfd2 = job_m_in.fd2; /* Is the file descriptor valid? */ rfd = scratch(fp).file.fd_nr & ~DUP_MASK; /* kill off dup2 bit, if on */ if ((f = get_filp(rfd, VNODE_READ)) == NULL) return(err_code); /* Distinguish between dup and dup2. */ if (!(scratch(fp).file.fd_nr & DUP_MASK)) { /* bit not on */ /* dup(fd) */ r = get_fd(0, 0, &rfd2, NULL); } else { /* dup2(old_fd, new_fd) */ if (rfd2 < 0 || rfd2 >= OPEN_MAX) { r = EBADF; } else if (rfd == rfd2) { /* ignore the call: dup2(x, x) */ r = rfd2; } else { /* All is fine, close new_fd if necessary */ unlock_filp(f); /* or it might deadlock on do_close */ (void) close_fd(fp, rfd2); /* cannot fail */ f = get_filp(rfd, VNODE_READ); /* lock old_fd again */ } } if (r == OK) { /* Success. Set up new file descriptors. */ f->filp_count++; fp->fp_filp[rfd2] = f; FD_SET(rfd2, &fp->fp_filp_inuse); r = rfd2; } unlock_filp(f); return(r); }
int dupvm(struct fproc *rfp, int pfd, int *vmfd, struct filp **newfilp) { int result, procfd; struct filp *f = NULL; struct fproc *vmf = fproc_addr(VM_PROC_NR); *newfilp = NULL; if ((f = get_filp2(rfp, pfd, VNODE_READ)) == NULL) { printf("VFS dupvm: get_filp2 failed\n"); return EBADF; } if(!(f->filp_vno->v_vmnt->m_fs_flags & RES_HASPEEK)) { unlock_filp(f); #if 0 /* Noisy diagnostic for mmap() by ld.so */ printf("VFS dupvm: no peek available\n"); #endif return EINVAL; } assert(f->filp_vno); assert(f->filp_vno->v_vmnt); if (!S_ISREG(f->filp_vno->v_mode) && !S_ISBLK(f->filp_vno->v_mode)) { printf("VFS: mmap regular/blockdev only; dev 0x%llx ino %llu has mode 0%o\n", f->filp_vno->v_dev, f->filp_vno->v_inode_nr, f->filp_vno->v_mode); unlock_filp(f); return EINVAL; } /* get free FD in VM */ if((result=get_fd(vmf, 0, 0, &procfd, NULL)) != OK) { unlock_filp(f); printf("VFS dupvm: getfd failed\n"); return result; } *vmfd = procfd; f->filp_count++; assert(f->filp_count > 0); vmf->fp_filp[procfd] = f; *newfilp = f; return OK; }
/*===========================================================================* * do_llseek * *===========================================================================*/ PUBLIC int do_llseek() { /* Perform the llseek(ls_fd, offset, whence) system call. */ register struct filp *rfilp; u64_t pos, newpos; int r = OK; /* Check to see if the file descriptor is valid. */ if ( (rfilp = get_filp(m_in.ls_fd, VNODE_READ)) == NULL) return(err_code); /* No lseek on pipes. */ if (rfilp->filp_vno->v_pipe == I_PIPE) { unlock_filp(rfilp); return(ESPIPE); } /* The value of 'whence' determines the start position to use. */ switch(m_in.whence) { case SEEK_SET: pos = cvu64(0); break; case SEEK_CUR: pos = rfilp->filp_pos; break; case SEEK_END: pos = cvul64(rfilp->filp_vno->v_size); break; default: unlock_filp(rfilp); return(EINVAL); } newpos = add64(pos, make64(m_in.offset_lo, m_in.offset_high)); /* Check for overflow. */ if (( (long) m_in.offset_high > 0) && cmp64(newpos, pos) < 0) r = EINVAL; else if (( (long) m_in.offset_high < 0) && cmp64(newpos, pos) > 0) r = EINVAL; else { rfilp->filp_pos = newpos; /* insert the new position into the output message */ m_out.reply_l1 = ex64lo(newpos); m_out.reply_l2 = ex64hi(newpos); if (cmp64(newpos, rfilp->filp_pos) != 0) { /* Inhibit read ahead request */ r = req_inhibread(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr); } } unlock_filp(rfilp); return(r); }
/*===========================================================================* * do_pending_pipe * *===========================================================================*/ static void do_pending_pipe(void) { int r, op; struct filp *f; tll_access_t locktype; f = fp->fp_filp[fp->fp_fd]; assert(f != NULL); locktype = (job_call_nr == VFS_READ) ? VNODE_READ : VNODE_WRITE; op = (job_call_nr == VFS_READ) ? READING : WRITING; lock_filp(f, locktype); r = rw_pipe(op, who_e, f, fp->fp_io_buffer, fp->fp_io_nbytes); if (r != SUSPEND) { /* Do we have results to report? */ /* Process is writing, but there is no reader. Send a SIGPIPE signal. * This should match the corresponding code in read_write(). */ if (r == EPIPE && op == WRITING) { if (!(f->filp_flags & O_NOSIGPIPE)) sys_kill(fp->fp_endpoint, SIGPIPE); } replycode(fp->fp_endpoint, r); } unlock_filp(f); }
/*===========================================================================* * do_fsync * *===========================================================================*/ PUBLIC int do_fsync() { /* Perform the fsync() system call. */ struct filp *rfilp; struct vmnt *vmp; dev_t dev; int r = OK; if ((rfilp = get_filp(m_in.m1_i1, VNODE_READ)) == NULL) return(err_code); dev = rfilp->filp_vno->v_dev; for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) { if (vmp->m_dev != NO_DEV && vmp->m_dev == dev && vmp->m_fs_e != NONE && vmp->m_root_node != NULL) { if ((r = lock_vmnt(vmp, VMNT_EXCL)) != OK) break; req_sync(vmp->m_fs_e); unlock_vmnt(vmp); } } unlock_filp(rfilp); return(r); }
/*===========================================================================* * do_getdents * *===========================================================================*/ int do_getdents(void) { /* Perform the getdents(fd, buf, size) system call. */ int r = OK; off_t new_pos; register struct filp *rfilp; scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_readwrite.fd; scratch(fp).io.io_buffer = job_m_in.m_lc_vfs_readwrite.buf; scratch(fp).io.io_nbytes = job_m_in.m_lc_vfs_readwrite.len; /* Is the file descriptor valid? */ if ( (rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_READ)) == NULL) return(err_code); if (!(rfilp->filp_mode & R_BIT)) r = EBADF; else if (!S_ISDIR(rfilp->filp_vno->v_mode)) r = EBADF; if (r == OK) { r = req_getdents(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr, rfilp->filp_pos, scratch(fp).io.io_buffer, scratch(fp).io.io_nbytes, &new_pos, 0); if (r > 0) rfilp->filp_pos = new_pos; } unlock_filp(rfilp); return(r); }
/*===========================================================================* * do_fsync * *===========================================================================*/ int do_fsync(void) { /* Perform the fsync() system call. */ struct filp *rfilp; struct vmnt *vmp; dev_t dev; int r = OK; scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_fsync.fd; if ((rfilp = get_filp(scratch(fp).file.fd_nr, VNODE_READ)) == NULL) return(err_code); dev = rfilp->filp_vno->v_dev; unlock_filp(rfilp); for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) { if (vmp->m_dev != dev) continue; if ((r = lock_vmnt(vmp, VMNT_READ)) != OK) break; if (vmp->m_dev != NO_DEV && vmp->m_dev == dev && vmp->m_fs_e != NONE && vmp->m_root_node != NULL) { req_sync(vmp->m_fs_e); } unlock_vmnt(vmp); } return(r); }
/*===========================================================================* * do_pending_pipe * *===========================================================================*/ static void *do_pending_pipe(void *arg) { int r, op; struct job my_job; struct filp *f; tll_access_t locktype; my_job = *((struct job *) arg); fp = my_job.j_fp; lock_proc(fp, 1 /* force lock */); f = scratch(fp).file.filp; assert(f != NULL); scratch(fp).file.filp = NULL; locktype = (job_call_nr == READ) ? VNODE_READ : VNODE_WRITE; op = (job_call_nr == READ) ? READING : WRITING; lock_filp(f, locktype); r = rw_pipe(op, who_e, f, scratch(fp).io.io_buffer, scratch(fp).io.io_nbytes); if (r != SUSPEND) /* Do we have results to report? */ reply(fp->fp_endpoint, r); unlock_filp(f); thread_cleanup(fp); return(NULL); }
/*===========================================================================* * do_fstat * *===========================================================================*/ int do_fstat() { /* Perform the fstat(fd, buf) system call. */ register struct filp *rfilp; int r, pipe_pos = 0, old_stat = 0, rfd; vir_bytes statbuf; statbuf = (vir_bytes) job_m_in.buffer; rfd = job_m_in.fd; if (job_call_nr == PREV_FSTAT) old_stat = 1; /* Is the file descriptor valid? */ if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code); /* If we read from a pipe, send position too */ if (rfilp->filp_vno->v_pipe == I_PIPE) { if (rfilp->filp_mode & R_BIT) if (ex64hi(rfilp->filp_pos) != 0) { panic("do_fstat: bad position in pipe"); } pipe_pos = ex64lo(rfilp->filp_pos); } r = req_stat(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr, who_e, statbuf, pipe_pos, old_stat); unlock_filp(rfilp); return(r); }
static int cdev_ioctl(struct file *filp, unsigned int request, char *argp) { int status; dev_t device = filp->f_dev; unlock_filp(filp); signals_off(); status = (*cdevsw[major(device)].d_ioctl)(device, request, argp, GENFLAG(filp), curproc); if (status == 0) { #if 1 if (request == TIOCSCTTY) { {int ret = proc_controlt(-1,-1,(int)filp); assert(ret == 0);} } #endif } else { errno = status; status = -1; } signals_on(); /* HBXX - Race condition, can receive a signal during the return and next unlock_filp() */ lock_filp(filp); return status; }
void mmap_exit(void *arg) { /* munmap all regions */ struct Mmap *m2, *m = mmap_list.lh_first; while (m) { m2 = m->mmap_link.le_next; assert(msync(m->mmap_addr, m->mmap_len, 0) == 0); if (m->mmap_filp) { lock_filp(m->mmap_filp); filp_refcount_dec(m->mmap_filp); if (filp_refcount_get(m->mmap_filp) == 0) { unlock_filp(m->mmap_filp); close_filp(m->mmap_filp); } else unlock_filp(m->mmap_filp); } m = m2; } }
/*===========================================================================* * do_fchdir * *===========================================================================*/ int do_fchdir() { /* Change directory on already-opened fd. */ struct filp *rfilp; int r, rfd; rfd = job_m_in.fd; /* Is the file descriptor valid? */ if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code); r = change_into(&fp->fp_wd, rfilp->filp_vno); unlock_filp(rfilp); return(r); }
/*===========================================================================* * actual_read_write_peek * *===========================================================================*/ int actual_read_write_peek(struct fproc *rfp, int rw_flag, int io_fd, vir_bytes io_buf, size_t io_nbytes) { /* Perform read(fd, buffer, nbytes) or write(fd, buffer, nbytes) call. */ struct filp *f; tll_access_t locktype; int r; int ro = 1; if(rw_flag == WRITING) ro = 0; scratch(rfp).file.fd_nr = io_fd; scratch(rfp).io.io_buffer = io_buf; scratch(rfp).io.io_nbytes = io_nbytes; locktype = rw_flag == WRITING ? VNODE_WRITE : VNODE_READ; if ((f = get_filp2(rfp, scratch(rfp).file.fd_nr, locktype)) == NULL) return(err_code); assert(f->filp_count > 0); if (((f->filp_mode) & (ro ? R_BIT : W_BIT)) == 0) { unlock_filp(f); return(EBADF); } if (scratch(rfp).io.io_nbytes == 0) { unlock_filp(f); return(0); /* so char special files need not check for 0*/ } r = read_write(rfp, rw_flag, f, scratch(rfp).io.io_buffer, scratch(rfp).io.io_nbytes, who_e); unlock_filp(f); return(r); }
static inline void close_filp(struct file *filp) { int fd; for (fd = NR_OPEN - 1; fd >= 0; fd--) if (__current->fd[fd] == NULL) { __current->fd[fd] = filp; break; } assert(fd >= 0); __current->cloexec_flag[fd] = 0; lock_filp(filp); filp_refcount_inc(filp); unlock_filp(filp); close(fd); }
int mmap_fork(u_int k, int envid, int NewPid) { /* increase the refcounts */ struct Mmap *m = mmap_list.lh_first; while (m) { if (m->mmap_filp) { lock_filp(m->mmap_filp); filp_refcount_inc(m->mmap_filp); unlock_filp(m->mmap_filp); } m = m->mmap_link.le_next; } return 0; }
static int cdev_write(struct file *filp, char *buffer, int nbyte, int blocking) { int status; dev_t dev = filp->f_dev; struct uio uio; struct iovec iov[4]; char buf[CLALLOCSZ*8]; int i; demand(filp, bogus filp); unlock_filp(filp); signals_off(); DPRINTF(CLU_LEVEL, ("cdev_write: filp: %08x offset: %qd nbyte: %d\n", (int)filp, filp->f_pos, nbyte)); assert(nbyte <= CLALLOCSZ*8); memcpy(buf,buffer,nbyte); iov[0].iov_base = buf; iov[0].iov_len = nbyte; uio.uio_iov = iov; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = nbyte; uio.uio_rw = UIO_WRITE; k0printf("Write: %d: ",uio.uio_resid); for (i = 0; i < uio.uio_resid; i++) k0printf(">%d (%c)",(unsigned int)buf[i],buf[i]); EnterCritical(); status = (*cdevsw[major(dev)].d_write)(dev, &uio, GENFLAG(filp)); ExitCritical(); k0printf("Read: %d: ",nbyte - uio.uio_resid); if (status == 0) { status = nbyte - uio.uio_resid; } else { errno = status; status = -1; } signals_on(); /* HBXX - Race condition, can receive a signal during the return and next unlock_filp() */ lock_filp(filp); return status; }
static int cdev_read(struct file *filp, char *buffer, int nbyte, int blocking) { int status; dev_t dev = filp->f_dev; struct uio uio; struct iovec iov[4]; demand(filp, bogus filp); DPRINTF(CLU_LEVEL, ("cdev_read: filp: %08x offset: %qd nbyte: %d\n", (int)filp, filp->f_pos, nbyte)); /* if (nbyte > CLALLOCSZ) {fprintf(stderr,"ncdev_read, warn large nbyte\n");} */ iov[0].iov_base = buffer; iov[0].iov_len = nbyte; uio.uio_iov = iov; uio.uio_iovcnt = 1; uio.uio_offset = 0; uio.uio_resid = nbyte; uio.uio_rw = UIO_READ; signals_off(); unlock_filp(filp); EnterCritical(); status = (*cdevsw[major(dev)].d_read)(dev, &uio, GENFLAG(filp)); ExitCritical(); signals_on(); k0printf("Read: %d: ",nbyte - uio.uio_resid); if (status == 0) { status = nbyte - uio.uio_resid; } else { errno = status; status = -1; } /* HBXX - Race condition, can receive a signal during the return and next unlock_filp() */ lock_filp(filp); #if 0 if (status >= 0) { extern void pr_uio(); kprintf("read(%d,%d) ",major(dev),minor(dev)); pr_uio(&uio); } #endif return status; }
/*===========================================================================* * do_fstatvfs * *===========================================================================*/ int do_fstatvfs() { /* Perform the fstat(fd, buf) system call. */ register struct filp *rfilp; int r, rfd; vir_bytes statbuf; rfd = job_m_in.fd; statbuf = (vir_bytes) job_m_in.name2; /* Is the file descriptor valid? */ if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code); r = req_statvfs(rfilp->filp_vno->v_fs_e, who_e, statbuf); unlock_filp(rfilp); return(r); }
/*===========================================================================* * do_pending_pipe * *===========================================================================*/ static void do_pending_pipe(void) { vir_bytes buf; size_t nbytes, cum_io; int r, op, fd; struct filp *f; tll_access_t locktype; assert(fp->fp_blocked_on == FP_BLOCKED_ON_NONE); /* * We take all our needed resumption state from the m_in message, which is * filled by unblock(). Since this is an internal resumption, there is no * need to perform extensive checks on the message fields. */ fd = job_m_in.m_lc_vfs_readwrite.fd; buf = job_m_in.m_lc_vfs_readwrite.buf; nbytes = job_m_in.m_lc_vfs_readwrite.len; cum_io = job_m_in.m_lc_vfs_readwrite.cum_io; f = fp->fp_filp[fd]; assert(f != NULL); locktype = (job_call_nr == VFS_READ) ? VNODE_READ : VNODE_WRITE; op = (job_call_nr == VFS_READ) ? READING : WRITING; lock_filp(f, locktype); r = rw_pipe(op, who_e, f, job_call_nr, fd, buf, nbytes, cum_io); if (r != SUSPEND) { /* Do we have results to report? */ /* Process is writing, but there is no reader. Send a SIGPIPE signal. * This should match the corresponding code in read_write(). */ if (r == EPIPE && op == WRITING) { if (!(f->filp_flags & O_NOSIGPIPE)) sys_kill(fp->fp_endpoint, SIGPIPE); } replycode(fp->fp_endpoint, r); } unlock_filp(f); }
/*===========================================================================* * do_fstatvfs * *===========================================================================*/ int do_fstatvfs(void) { /* Perform the fstatvfs1(fd, buf, flags) system call. */ register struct filp *rfilp; int r, rfd, flags; vir_bytes statbuf; rfd = job_m_in.m_lc_vfs_statvfs1.fd; statbuf = job_m_in.m_lc_vfs_statvfs1.buf; flags = job_m_in.m_lc_vfs_statvfs1.flags; /* Is the file descriptor valid? */ if ((rfilp = get_filp(rfd, VNODE_READ)) == NULL) return(err_code); r = fill_statvfs(rfilp->filp_vno->v_vmnt, who_e, statbuf, flags); unlock_filp(rfilp); return(r); }
/*===========================================================================* * select_cancel_filp * *===========================================================================*/ static void select_cancel_filp(struct filp *f) { /* Reduce number of select users of this filp */ assert(f); assert(f->filp_selectors >= 0); if (f->filp_selectors == 0) return; if (f->filp_count == 0) return; select_lock_filp(f, f->filp_select_ops); f->filp_selectors--; if (f->filp_selectors == 0) { /* No one selecting on this filp anymore, forget about select state */ f->filp_select_ops = 0; f->filp_select_flags = 0; f->filp_pipe_select_ops = 0; } unlock_filp(f); }
/*===========================================================================* * do_select * *===========================================================================*/ int do_select(void) { /* Implement the select(nfds, readfds, writefds, errorfds, timeout) system * call. First we copy the arguments and verify their sanity. Then we check * whether there are file descriptors that satisfy the select call right of the * bat. If so, or if there are no ready file descriptors but the process * requested to return immediately, we return the result. Otherwise we set a * timeout and wait for either the file descriptors to become ready or the * timer to go off. If no timeout value was provided, we wait indefinitely. */ int r, nfds, do_timeout = 0, fd, s; struct timeval timeout; struct selectentry *se; vir_bytes vtimeout; nfds = job_m_in.SEL_NFDS; vtimeout = (vir_bytes) job_m_in.SEL_TIMEOUT; /* Sane amount of file descriptors? */ if (nfds < 0 || nfds > OPEN_MAX) return(EINVAL); /* Find a slot to store this select request */ for (s = 0; s < MAXSELECTS; s++) if (selecttab[s].requestor == NULL) /* Unused slot */ break; if (s >= MAXSELECTS) return(ENOSPC); se = &selecttab[s]; wipe_select(se); /* Clear results of previous usage */ se->requestor = fp; se->req_endpt = who_e; se->vir_readfds = (fd_set *) job_m_in.SEL_READFDS; se->vir_writefds = (fd_set *) job_m_in.SEL_WRITEFDS; se->vir_errorfds = (fd_set *) job_m_in.SEL_ERRORFDS; /* Copy fdsets from the process */ if ((r = copy_fdsets(se, nfds, FROM_PROC)) != OK) { se->requestor = NULL; return(r); } /* Did the process set a timeout value? If so, retrieve it. */ if (vtimeout != 0) { do_timeout = 1; r = sys_vircopy(who_e, (vir_bytes) vtimeout, SELF, (vir_bytes) &timeout, sizeof(timeout)); if (r != OK) { se->requestor = NULL; return(r); } } /* No nonsense in the timeval */ if (do_timeout && (timeout.tv_sec < 0 || timeout.tv_usec < 0)) { se->requestor = NULL; return(EINVAL); } /* If there is no timeout, we block forever. Otherwise, we block up to the * specified time interval. */ if (!do_timeout) /* No timeout value set */ se->block = 1; else if (do_timeout && (timeout.tv_sec > 0 || timeout.tv_usec > 0)) se->block = 1; else /* timeout set as (0,0) - this effects a poll */ se->block = 0; se->expiry = 0; /* no timer set (yet) */ /* Verify that file descriptors are okay to select on */ for (fd = 0; fd < nfds; fd++) { struct filp *f; unsigned int type, ops; /* Because the select() interface implicitly includes file descriptors * you might not want to select on, we have to figure out whether we're * interested in them. Typically, these file descriptors include fd's * inherited from the parent proc and file descriptors that have been * close()d, but had a lower fd than one in the current set. */ if (!(ops = tab2ops(fd, se))) continue; /* No operations set; nothing to do for this fd */ /* Get filp belonging to this fd */ f = se->filps[fd] = get_filp(fd, VNODE_READ); if (f == NULL) { if (err_code == EBADF) r = err_code; else /* File descriptor is 'ready' to return EIO */ r = EINTR; se->requestor = NULL; return(r); } /* Check file types. According to POSIX 2008: * "The pselect() and select() functions shall support regular files, * terminal and pseudo-terminal devices, FIFOs, pipes, and sockets. The * behavior of pselect() and select() on file descriptors that refer to * other types of file is unspecified." * * In our case, terminal and pseudo-terminal devices are handled by the * TTY major and sockets by either INET major (socket type AF_INET) or * PFS major (socket type AF_UNIX). PFS acts as an FS when it handles * pipes and as a driver when it handles sockets. Additionally, we * support select on the LOG major to handle kernel logging, which is * beyond the POSIX spec. */ se->type[fd] = -1; for (type = 0; type < SEL_FDS; type++) { if (fdtypes[type].type_match(f)) { se->type[fd] = type; se->nfds = fd+1; se->filps[fd]->filp_selectors++; break; } } unlock_filp(f); if (se->type[fd] == -1) { /* Type not found */ se->requestor = NULL; return(EBADF); } } /* Check all file descriptors in the set whether one is 'ready' now */ for (fd = 0; fd < nfds; fd++) { int ops, r; struct filp *f; /* Again, check for involuntarily selected fd's */ if (!(ops = tab2ops(fd, se))) continue; /* No operations set; nothing to do for this fd */ /* Test filp for select operations if not already done so. e.g., * processes sharing a filp and both doing a select on that filp. */ f = se->filps[fd]; if ((f->filp_select_ops & ops) != ops) { int wantops; wantops = (f->filp_select_ops |= ops); r = do_select_request(se, fd, &wantops); if (r != OK && r != SUSPEND) break; /* Error or bogus return code; abort */ /* The select request above might have turned on/off some * operations because they were 'ready' or not meaningful. * Either way, we might have a result and we need to store them * in the select table entry. */ if (wantops & ops) ops2tab(wantops, fd, se); } } if ((se->nreadyfds > 0 || !se->block) && !is_deferred(se)) { /* fd's were found that were ready to go right away, and/or * we were instructed not to block at all. Must return * immediately. */ r = copy_fdsets(se, se->nfds, TO_PROC); select_cancel_all(se); se->requestor = NULL; if (r != OK) return(r); else if (se->error != OK) return(se->error); return(se->nreadyfds); } /* Convert timeval to ticks and set the timer. If it fails, undo * all, return error. */ if (do_timeout) { int ticks; /* Open Group: * "If the requested timeout interval requires a finer * granularity than the implementation supports, the * actual timeout interval shall be rounded up to the next * supported value." */ #define USECPERSEC 1000000 while(timeout.tv_usec >= USECPERSEC) { /* this is to avoid overflow with *system_hz below */ timeout.tv_usec -= USECPERSEC; timeout.tv_sec++; } ticks = timeout.tv_sec * system_hz + (timeout.tv_usec * system_hz + USECPERSEC-1) / USECPERSEC; se->expiry = ticks; set_timer(&se->timer, ticks, select_timeout_check, s); } /* process now blocked */ suspend(FP_BLOCKED_ON_SELECT); return(SUSPEND); }
/*===========================================================================* * common_open * *===========================================================================*/ int common_open(char path[PATH_MAX], int oflags, mode_t omode) { /* Common code from do_creat and do_open. */ int b, r, exist = TRUE, major_dev; dev_t dev; mode_t bits; struct filp *filp, *filp2; struct vnode *vp; struct vmnt *vmp; struct dmap *dp; struct lookup resolve; /* Remap the bottom two bits of oflags. */ bits = (mode_t) mode_map[oflags & O_ACCMODE]; if (!bits) return(EINVAL); /* See if file descriptor and filp slots are available. */ if ((r = get_fd(0, bits, &(scratch(fp).file.fd_nr), &filp)) != OK) return(r); lookup_init(&resolve, path, PATH_NOFLAGS, &vmp, &vp); /* If O_CREATE is set, try to make the file. */ if (oflags & O_CREAT) { omode = I_REGULAR | (omode & ALLPERMS & fp->fp_umask); vp = new_node(&resolve, oflags, omode); r = err_code; if (r == OK) exist = FALSE; /* We just created the file */ else if (r != EEXIST) { /* other error */ if (vp) unlock_vnode(vp); unlock_filp(filp); return(r); } else exist = !(oflags & O_EXCL);/* file exists, if the O_EXCL flag is set this is an error */ } else { /* Scan path name */ resolve.l_vmnt_lock = VMNT_READ; resolve.l_vnode_lock = VNODE_OPCL; if ((vp = eat_path(&resolve, fp)) == NULL) { unlock_filp(filp); return(err_code); } if (vmp != NULL) unlock_vmnt(vmp); } /* Claim the file descriptor and filp slot and fill them in. */ fp->fp_filp[scratch(fp).file.fd_nr] = filp; FD_SET(scratch(fp).file.fd_nr, &fp->fp_filp_inuse); filp->filp_count = 1; filp->filp_vno = vp; filp->filp_flags = oflags; /* Only do the normal open code if we didn't just create the file. */ if (exist) { /* Check protections. */ if ((r = forbidden(fp, vp, bits)) == OK) { /* Opening reg. files, directories, and special files differ */ switch (vp->v_mode & S_IFMT) { case S_IFREG: /* Truncate regular file if O_TRUNC. */ if (oflags & O_TRUNC) { if ((r = forbidden(fp, vp, W_BIT)) != OK) break; truncate_vnode(vp, 0); } break; case S_IFDIR: /* Directories may be read but not written. */ r = (bits & W_BIT ? EISDIR : OK); break; case S_IFCHR: /* Invoke the driver for special processing. */ dev = (dev_t) vp->v_sdev; /* TTY needs to know about the O_NOCTTY flag. */ r = dev_open(dev, who_e, bits | (oflags & O_NOCTTY)); if (r == SUSPEND) suspend(FP_BLOCKED_ON_DOPEN); else vp = filp->filp_vno; /* Might be updated by * dev_open/clone_opcl */ break; case S_IFBLK: lock_bsf(); /* Invoke the driver for special processing. */ dev = (dev_t) vp->v_sdev; r = bdev_open(dev, bits); if (r != OK) { unlock_bsf(); break; } major_dev = major(vp->v_sdev); dp = &dmap[major_dev]; if (dp->dmap_driver == NONE) { printf("VFS: block driver disappeared!\n"); unlock_bsf(); r = ENXIO; break; } /* Check whether the device is mounted or not. If so, * then that FS is responsible for this device. * Otherwise we default to ROOT_FS. */ vp->v_bfs_e = ROOT_FS_E; /* By default */ for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) if (vmp->m_dev == vp->v_sdev && !(vmp->m_flags & VMNT_FORCEROOTBSF)) { vp->v_bfs_e = vmp->m_fs_e; } /* Send the driver label to the file system that will * handle the block I/O requests (even when its label * and endpoint are known already), but only when it is * the root file system. Other file systems will * already have it anyway. */ if (vp->v_bfs_e != ROOT_FS_E) { unlock_bsf(); break; } if (req_newdriver(vp->v_bfs_e, vp->v_sdev, dp->dmap_label) != OK) { printf("VFS: error sending driver label\n"); bdev_close(dev); r = ENXIO; } unlock_bsf(); break; case S_IFIFO: /* Create a mapped inode on PFS which handles reads and writes to this named pipe. */ tll_upgrade(&vp->v_lock); r = map_vnode(vp, PFS_PROC_NR); if (r == OK) { if (vp->v_ref_count == 1) { vp->v_pipe_rd_pos = 0; vp->v_pipe_wr_pos = 0; if (vp->v_size != 0) r = truncate_vnode(vp, 0); } oflags |= O_APPEND; /* force append mode */ filp->filp_flags = oflags; } if (r == OK) { r = pipe_open(vp, bits, oflags); } if (r != ENXIO) { /* See if someone else is doing a rd or wt on * the FIFO. If so, use its filp entry so the * file position will be automatically shared. */ b = (bits & R_BIT ? R_BIT : W_BIT); filp->filp_count = 0; /* don't find self */ if ((filp2 = find_filp(vp, b)) != NULL) { /* Co-reader or writer found. Use it.*/ fp->fp_filp[scratch(fp).file.fd_nr] = filp2; filp2->filp_count++; filp2->filp_vno = vp; filp2->filp_flags = oflags; /* v_count was incremented after the vnode * has been found. i_count was incremented * incorrectly in FS, not knowing that we * were going to use an existing filp * entry. Correct this error. */ unlock_vnode(vp); put_vnode(vp); } else { /* Nobody else found. Restore filp. */ filp->filp_count = 1; } } break; } } } unlock_filp(filp); /* If error, release inode. */ if (r != OK) { if (r != SUSPEND) { fp->fp_filp[scratch(fp).file.fd_nr] = NULL; FD_CLR(scratch(fp).file.fd_nr, &fp->fp_filp_inuse); filp->filp_count = 0; filp->filp_vno = NULL; put_vnode(vp); } } else { r = scratch(fp).file.fd_nr; } return(r); }
/*===========================================================================* * free_proc * *===========================================================================*/ static void free_proc(int flags) { int i; register struct fproc *rfp; register struct filp *rfilp; register struct vnode *vp; dev_t dev; if (fp->fp_endpoint == NONE) panic("free_proc: already free"); if (fp_is_blocked(fp)) unpause(); /* Loop on file descriptors, closing any that are open. */ for (i = 0; i < OPEN_MAX; i++) { (void) close_fd(fp, i); } /* Release root and working directories. */ if (fp->fp_rd) { put_vnode(fp->fp_rd); fp->fp_rd = NULL; } if (fp->fp_wd) { put_vnode(fp->fp_wd); fp->fp_wd = NULL; } /* The rest of these actions is only done when processes actually exit. */ if (!(flags & FP_EXITING)) return; fp->fp_flags |= FP_EXITING; /* Check if any process is SUSPENDed on this driver. * If a driver exits, unmap its entries in the dmap table. * (unmapping has to be done after the first step, because the * dmap table is used in the first step.) */ unsuspend_by_endpt(fp->fp_endpoint); dmap_unmap_by_endpt(fp->fp_endpoint); worker_stop_by_endpt(fp->fp_endpoint); /* Unblock waiting threads */ vmnt_unmap_by_endpt(fp->fp_endpoint); /* Invalidate open files if this * was an active FS */ /* If a session leader exits and it has a controlling tty, then revoke * access to its controlling tty from all other processes using it. */ if ((fp->fp_flags & FP_SESLDR) && fp->fp_tty != 0) { dev = fp->fp_tty; for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { if(rfp->fp_pid == PID_FREE) continue; if (rfp->fp_tty == dev) rfp->fp_tty = 0; for (i = 0; i < OPEN_MAX; i++) { if ((rfilp = rfp->fp_filp[i]) == NULL) continue; if (rfilp->filp_mode == FILP_CLOSED) continue; vp = rfilp->filp_vno; if (!S_ISCHR(vp->v_mode)) continue; if (vp->v_sdev != dev) continue; lock_filp(rfilp, VNODE_READ); (void) cdev_close(dev); /* Ignore any errors. */ /* FIXME: missing select check */ rfilp->filp_mode = FILP_CLOSED; unlock_filp(rfilp); } } } /* Exit done. Mark slot as free. */ fp->fp_endpoint = NONE; fp->fp_pid = PID_FREE; fp->fp_flags = FP_NOFLAGS; }
/*===========================================================================* * do_fcntl * *===========================================================================*/ int do_fcntl(void) { /* Perform the fcntl(fd, cmd, ...) system call. */ register struct filp *f; int new_fd, fl, r = OK, fcntl_req, fcntl_argx; tll_access_t locktype; scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_fcntl.fd; scratch(fp).io.io_buffer = job_m_in.m_lc_vfs_fcntl.arg_ptr; scratch(fp).io.io_nbytes = job_m_in.m_lc_vfs_fcntl.cmd; fcntl_req = job_m_in.m_lc_vfs_fcntl.cmd; fcntl_argx = job_m_in.m_lc_vfs_fcntl.arg_int; /* Is the file descriptor valid? */ locktype = (fcntl_req == F_FREESP) ? VNODE_WRITE : VNODE_READ; if ((f = get_filp(scratch(fp).file.fd_nr, locktype)) == NULL) return(err_code); switch (fcntl_req) { case F_DUPFD: /* This replaces the old dup() system call. */ if (fcntl_argx < 0 || fcntl_argx >= OPEN_MAX) r = EINVAL; else if ((r = get_fd(fp, fcntl_argx, 0, &new_fd, NULL)) == OK) { f->filp_count++; fp->fp_filp[new_fd] = f; r = new_fd; } break; case F_GETFD: /* Get close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */ r = 0; if (FD_ISSET(scratch(fp).file.fd_nr, &fp->fp_cloexec_set)) r = FD_CLOEXEC; break; case F_SETFD: /* Set close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */ if (fcntl_argx & FD_CLOEXEC) FD_SET(scratch(fp).file.fd_nr, &fp->fp_cloexec_set); else FD_CLR(scratch(fp).file.fd_nr, &fp->fp_cloexec_set); break; case F_GETFL: /* Get file status flags (O_NONBLOCK and O_APPEND). */ fl = f->filp_flags & (O_NONBLOCK | O_APPEND | O_ACCMODE); r = fl; break; case F_SETFL: /* Set file status flags (O_NONBLOCK and O_APPEND). */ fl = O_NONBLOCK | O_APPEND; f->filp_flags = (f->filp_flags & ~fl) | (fcntl_argx & fl); break; case F_GETLK: case F_SETLK: case F_SETLKW: /* Set or clear a file lock. */ r = lock_op(f, fcntl_req); break; case F_FREESP: { /* Free a section of a file */ off_t start, end, offset; struct flock flock_arg; /* Check if it's a regular file. */ if (!S_ISREG(f->filp_vno->v_mode)) r = EINVAL; else if (!(f->filp_mode & W_BIT)) r = EBADF; else { /* Copy flock data from userspace. */ r = sys_datacopy_wrapper(who_e, scratch(fp).io.io_buffer, SELF, (vir_bytes) &flock_arg, sizeof(flock_arg)); } if (r != OK) break; /* Convert starting offset to signed. */ offset = (off_t) flock_arg.l_start; /* Figure out starting position base. */ switch(flock_arg.l_whence) { case SEEK_SET: start = 0; break; case SEEK_CUR: start = f->filp_pos; break; case SEEK_END: start = f->filp_vno->v_size; break; default: r = EINVAL; } if (r != OK) break; /* Check for overflow or underflow. */ if (offset > 0 && start + offset < start) r = EINVAL; else if (offset < 0 && start + offset > start) r = EINVAL; else { start += offset; if (start < 0) r = EINVAL; } if (r != OK) break; if (flock_arg.l_len != 0) { if (start >= f->filp_vno->v_size) r = EINVAL; else if ((end = start + flock_arg.l_len) <= start) r = EINVAL; else if (end > f->filp_vno->v_size) end = f->filp_vno->v_size; } else { end = 0; } if (r != OK) break; r = req_ftrunc(f->filp_vno->v_fs_e, f->filp_vno->v_inode_nr,start,end); if (r == OK && flock_arg.l_len == 0) f->filp_vno->v_size = start; break; } case F_GETNOSIGPIPE: r = !!(f->filp_flags & O_NOSIGPIPE); break; case F_SETNOSIGPIPE: if (fcntl_argx) f->filp_flags |= O_NOSIGPIPE; else f->filp_flags &= ~O_NOSIGPIPE; break; case F_FLUSH_FS_CACHE: { struct vnode *vn = f->filp_vno; mode_t mode = f->filp_vno->v_mode; if (!super_user) { r = EPERM; } else if (S_ISBLK(mode)) { /* Block device; flush corresponding device blocks. */ r = req_flush(vn->v_bfs_e, vn->v_sdev); } else if (S_ISREG(mode) || S_ISDIR(mode)) { /* Directory or regular file; flush hosting FS blocks. */ r = req_flush(vn->v_fs_e, vn->v_dev); } else { /* Remaining cases.. Meaning unclear. */ r = ENODEV; } break; } default: r = EINVAL; } unlock_filp(f); return(r); }