/*===========================================================================* * 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); }
/*===========================================================================* * 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_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_lseek * *===========================================================================*/ PUBLIC int do_lseek() { /* Perform the lseek(ls_fd, offset, whence) system call. */ register struct filp *rfilp; register off_t pos; /* Check to see if the file descriptor is valid. */ if ( (rfilp = get_filp(m_in.ls_fd)) == NIL_FILP) return(err_code); /* No lseek on pipes. */ if (rfilp->filp_ino->i_pipe == I_PIPE) return(ESPIPE); /* The value of 'whence' determines the start position to use. */ switch(m_in.whence) { case SEEK_SET: pos = 0; break; case SEEK_CUR: pos = rfilp->filp_pos; break; case SEEK_END: pos = rfilp->filp_ino->i_size; break; default: return(EINVAL); } /* Check for overflow. */ if (((long)m_in.offset > 0) && ((long)(pos + m_in.offset) < (long)pos)) return(EINVAL); if (((long)m_in.offset < 0) && ((long)(pos + m_in.offset) > (long)pos)) return(EINVAL); pos = pos + m_in.offset; if (pos != rfilp->filp_pos) rfilp->filp_ino->i_seek = ISEEK; /* inhibit read ahead */ rfilp->filp_pos = pos; m_out.reply_l1 = pos; /* insert the long into the output message */ return(OK); }
/*===========================================================================* * do_dup * *===========================================================================*/ PUBLIC 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. */ register int rfd; register struct filp *f; struct filp *dummy; int r; /* Is the file descriptor valid? */ rfd = m_in.fd & ~DUP_MASK; /* kill off dup2 bit, if on */ if ((f = get_filp(rfd)) == NULL) return(err_code); /* Distinguish between dup and dup2. */ if (m_in.fd == rfd) { /* bit not on */ /* dup(fd) */ if ((r = get_fd(0, 0, &m_in.fd2, &dummy)) != OK) return(r); } else { /* dup2(fd, fd2) */ if (m_in.fd2 < 0 || m_in.fd2 >= OPEN_MAX) return(EBADF); if (rfd == m_in.fd2) return(m_in.fd2); /* ignore the call: dup2(x, x) */ m_in.fd = m_in.fd2; /* prepare to close fd2 */ (void) do_close(); /* cannot fail */ } /* Success. Set up new file descriptors. */ f->filp_count++; fp->fp_filp[m_in.fd2] = f; FD_SET(m_in.fd2, &fp->fp_filp_inuse); return(m_in.fd2); }
/*===========================================================================* * 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_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); }
/*===========================================================================* * scall_llseek * *===========================================================================*/ int scall_llseek(void) { /* Perform the llseek(fd, offset, whence) system call. */ register struct filp *rfilp; loff_t pos, newpos; int r; /* Check to see if the file descriptor is valid. */ if ((rfilp = get_filp(m_in.ls_fd)) == NIL_FILP) return -EBADF; /* No lseek on pipes. */ if (rfilp->filp_vno->v_pipe == I_PIPE) return -ESPIPE; /* The value of 'whence' determines the start position to use. */ switch(m_in.whence) { case SEEK_SET: pos = (loff_t)0; break; case SEEK_CUR: pos = rfilp->filp_pos; break; case SEEK_END: pos = (loff_t)rfilp->filp_vno->v_size; break; default: return -EINVAL; } newpos = pos + (((loff_t)m_in.offset_high << 32) | m_in.offset_lo); /* Check for overflow. */ if (((long)m_in.offset_high > 0) && newpos < pos) return -EINVAL; if (((long)m_in.offset_high < 0) && newpos > pos) return -EINVAL; if (newpos != rfilp->filp_pos) { /* Inhibit read ahead request */ r = req_inhibread(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr); if (r != 0) return r; } rfilp->filp_pos = newpos; /* Copy the result to user space */ if (sys_datacopy(ENDPT_SELF, (vir_bytes)&newpos, who_e, (vir_bytes)m_in.result_addr, sizeof(newpos))) return -EFAULT; return 0; }
/*===========================================================================* * do_fchdir * *===========================================================================*/ PUBLIC int do_fchdir() { /* Change directory on already-opened fd. */ struct filp *rfilp; /* Is the file descriptor valid? */ if ( (rfilp = get_filp(m_in.fd)) == NIL_FILP) return(err_code); dup_inode(rfilp->filp_ino); return change_into(&fp->fp_workdir, rfilp->filp_ino); }
/*===========================================================================* * do_fchdir * *===========================================================================*/ PUBLIC int do_fchdir() { /* Change directory on already-opened fd. */ struct filp *rfilp; /* Is the file descriptor valid? */ if ((rfilp = get_filp(m_in.fd)) == NULL) return(err_code); dup_vnode(rfilp->filp_vno); /* Change into expects a reference. */ return change_into(&fp->fp_wd, rfilp->filp_vno); }
/*===========================================================================* * scall_lseek * *===========================================================================*/ int scall_lseek() { /* Perform the lseek(ls_fd, offset, whence) system call. */ register struct filp *rfilp; int r; long offset; u64_t pos, newpos; /* Check to see if the file descriptor is valid. */ if ( (rfilp = get_filp(m_in.ls_fd)) == NIL_FILP) return(err_code); /* No lseek on pipes. */ if (rfilp->filp_vno->v_pipe == I_PIPE) 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: return(-EINVAL); } offset= m_in.offset_lo; if (offset >= 0) newpos= add64ul(pos, offset); else newpos= sub64ul(pos, -offset); /* Check for overflow. */ if (ex64hi(newpos) != 0) return -EINVAL; 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); if (r != 0) return r; } rfilp->filp_pos = newpos; return ex64lo(newpos); }
/*===========================================================================* * 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); }
/*===========================================================================* * do_ftruncate * *===========================================================================*/ int do_ftruncate() { /* As with do_truncate(), truncate_vnode() does the actual work. */ int r; struct filp *rfilp; /* File is already opened; get a vnode pointer from filp */ if ((rfilp = get_filp(m_in.m_data1)) == NIL_FILP) return(err_code); if (!(rfilp->filp_mode & W_BIT)) return(-EBADF); return truncate_vnode(rfilp->filp_vno, m_in.m_data4); }
/*===========================================================================* * do_ftruncate * *===========================================================================*/ PUBLIC int do_ftruncate() { /* As with do_truncate(), truncate_inode() does the actual work. */ int r; struct filp *rfilp; if ( (rfilp = get_filp(m_in.m2_i1)) == NIL_FILP) return err_code; if (!(rfilp->filp_mode & W_BIT)) return EBADF; return truncate_vn(rfilp->filp_vno, m_in.m2_l1); }
/*===========================================================================* * 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); }
/*===========================================================================* * 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_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_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); }
/*===========================================================================* * do_fcntl * *===========================================================================*/ int do_fcntl() { /* Perform the fcntl(fd, request, ...) 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.fd; scratch(fp).io.io_buffer = job_m_in.buffer; scratch(fp).io.io_nbytes = job_m_in.nbytes; /* a.k.a. m_in.request */ fcntl_req = job_m_in.request; fcntl_argx = job_m_in.addr; /* 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(fcntl_argx, 0, &new_fd, NULL)) == OK) { f->filp_count++; fp->fp_filp[new_fd] = f; FD_SET(new_fd, &fp->fp_filp_inuse); 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 | O_REOPEN; 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; struct flock flock_arg; signed long offset; /* 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(who_e, (vir_bytes) scratch(fp).io.io_buffer, SELF, (vir_bytes) &flock_arg, sizeof(flock_arg)); if (r != OK) break; /* Convert starting offset to signed. */ offset = (signed long) flock_arg.l_start; /* Figure out starting position base. */ switch(flock_arg.l_whence) { case SEEK_SET: start = 0; break; case SEEK_CUR: if (ex64hi(f->filp_pos) != 0) panic("do_fcntl: position in file too high"); start = ex64lo(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: /* POSIX: return value other than -1 is flag is set, else -1 */ r = -1; if (f->filp_flags & O_NOSIGPIPE) r = 0; break; case F_SETNOSIGPIPE: fl = (O_NOSIGPIPE); f->filp_flags = (f->filp_flags & ~fl) | (fcntl_argx & fl); break; default: r = EINVAL; } unlock_filp(f); return(r); }
/*===========================================================================* * 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); }
/*===========================================================================* * do_fcntl * *===========================================================================*/ PUBLIC int do_fcntl() { /* Perform the fcntl(fd, request, ...) system call. */ register struct filp *f; int new_fd, r, fl; struct filp *dummy; /* Is the file descriptor valid? */ if ((f = get_filp(m_in.fd)) == NULL) return(err_code); switch (m_in.request) { case F_DUPFD: /* This replaces the old dup() system call. */ if (m_in.addr < 0 || m_in.addr >= OPEN_MAX) return(EINVAL); if ((r = get_fd(m_in.addr, 0, &new_fd, &dummy)) != OK) return(r); f->filp_count++; fp->fp_filp[new_fd] = f; return(new_fd); case F_GETFD: /* Get close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */ return( FD_ISSET(m_in.fd, &fp->fp_cloexec_set) ? FD_CLOEXEC : 0); case F_SETFD: /* Set close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */ if(m_in.addr & FD_CLOEXEC) FD_SET(m_in.fd, &fp->fp_cloexec_set); else FD_CLR(m_in.fd, &fp->fp_cloexec_set); return(OK); case F_GETFL: /* Get file status flags (O_NONBLOCK and O_APPEND). */ fl = f->filp_flags & (O_NONBLOCK | O_APPEND | O_ACCMODE); return(fl); case F_SETFL: /* Set file status flags (O_NONBLOCK and O_APPEND). */ fl = O_NONBLOCK | O_APPEND | O_REOPEN; f->filp_flags = (f->filp_flags & ~fl) | (m_in.addr & fl); return(OK); case F_GETLK: case F_SETLK: case F_SETLKW: /* Set or clear a file lock. */ r = lock_op(f, m_in.request); return(r); case F_FREESP: { /* Free a section of a file. Preparation is done here, actual freeing * in freesp_inode(). */ off_t start, end; struct flock flock_arg; signed long offset; /* Check if it's a regular file. */ if((f->filp_vno->v_mode & I_TYPE) != I_REGULAR) return(EINVAL); if (!(f->filp_mode & W_BIT)) return(EBADF); /* Copy flock data from userspace. */ if((r = sys_datacopy(who_e, (vir_bytes) m_in.name1, SELF, (vir_bytes) &flock_arg, (phys_bytes) sizeof(flock_arg))) != OK) return(r); /* Convert starting offset to signed. */ offset = (signed long) flock_arg.l_start; /* Figure out starting position base. */ switch(flock_arg.l_whence) { case SEEK_SET: start = 0; break; case SEEK_CUR: if (ex64hi(f->filp_pos) != 0) panic("do_fcntl: position in file too high"); start = ex64lo(f->filp_pos); break; case SEEK_END: start = f->filp_vno->v_size; break; default: return EINVAL; } /* Check for overflow or underflow. */ if(offset > 0 && start + offset < start) return EINVAL; if(offset < 0 && start + offset > start) return EINVAL; start += offset; if(start < 0) return EINVAL; if(flock_arg.l_len != 0) { if(start >= f->filp_vno->v_size) return EINVAL; end = start + flock_arg.l_len; if(end <= start) return EINVAL; if(end > f->filp_vno->v_size) end = f->filp_vno->v_size; } else { end = 0; } 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; return(r); } default: return(EINVAL); } }
/*===========================================================================* * do_close * *===========================================================================*/ PUBLIC int do_close() { /* Perform the close(fd) system call. */ register struct filp *rfilp; register struct inode *rip; struct file_lock *flp; int rw, mode_word, lock_count; dev_t dev; /* First locate the inode that belongs to the file descriptor. */ if ( (rfilp = get_filp(m_in.fd)) == NIL_FILP) return(err_code); rip = rfilp->filp_ino; /* 'rip' points to the inode */ if (rfilp->filp_count - 1 == 0 && rfilp->filp_mode != FILP_CLOSED) { /* Check to see if the file is special. */ mode_word = rip->i_mode & I_TYPE; if (mode_word == I_CHAR_SPECIAL || mode_word == I_BLOCK_SPECIAL) { dev = (dev_t) rip->i_zone[0]; if (mode_word == I_BLOCK_SPECIAL) { /* Invalidate cache entries unless special is mounted * or ROOT */ if (!mounted(rip)) { (void) do_sync(); /* purge cache */ invalidate(dev); } } /* Do any special processing on device close. */ dev_close(dev); } } /* If the inode being closed is a pipe, release everyone hanging on it. */ if (rip->i_pipe == I_PIPE) { rw = (rfilp->filp_mode & R_BIT ? WRITE : READ); release(rip, rw, NR_PROCS); } /* If a write has been done, the inode is already marked as DIRTY. */ if (--rfilp->filp_count == 0) { if (rip->i_pipe == I_PIPE && rip->i_count > 1) { /* Save the file position in the i-node in case needed later. * The read and write positions are saved separately. The * last 3 zones in the i-node are not used for (named) pipes. */ if (rfilp->filp_mode == R_BIT) rip->i_zone[V2_NR_DZONES+0] = (zone_t) rfilp->filp_pos; else rip->i_zone[V2_NR_DZONES+1] = (zone_t) rfilp->filp_pos; } put_inode(rip); } fp->fp_cloexec &= ~(1L << m_in.fd); /* turn off close-on-exec bit */ fp->fp_filp[m_in.fd] = NIL_FILP; FD_CLR(m_in.fd, &fp->fp_filp_inuse); /* Check to see if the file is locked. If so, release all locks. */ if (nr_locks == 0) return(OK); lock_count = nr_locks; /* save count of locks */ for (flp = &file_lock[0]; flp < &file_lock[NR_LOCKS]; flp++) { if (flp->lock_type == 0) continue; /* slot not in use */ if (flp->lock_inode == rip && flp->lock_pid == fp->fp_pid) { flp->lock_type = 0; nr_locks--; } } if (nr_locks < lock_count) lock_revive(); /* lock released */ return(OK); }
/*===========================================================================* * 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); }
/*===========================================================================* * get_work * *===========================================================================*/ PRIVATE void get_work() { /* Normally wait for new input. However, if 'reviving' is * nonzero, a suspended process must be awakened. */ int r, found_one, fd_nr; struct filp *f; register struct fproc *rp; while (reviving != 0) { found_one= FALSE; /* Revive a suspended process. */ for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) if (rp->fp_pid != PID_FREE && rp->fp_revived == REVIVING) { int blocked_on = rp->fp_blocked_on; found_one= TRUE; who_p = (int)(rp - fproc); who_e = rp->fp_endpoint; call_nr = rp->fp_block_callnr; m_in.fd = rp->fp_block_fd; m_in.buffer = rp->fp_buffer; m_in.nbytes = rp->fp_nbytes; /*no longer hanging*/ rp->fp_blocked_on = FP_BLOCKED_ON_NONE; rp->fp_revived = NOT_REVIVING; reviving--; /* This should be a pipe I/O, not a device I/O. * If it is, it'll 'leak' grants. */ assert(!GRANT_VALID(rp->fp_grant)); if (blocked_on == FP_BLOCKED_ON_PIPE) { fp= rp; fd_nr= rp->fp_block_fd; f= get_filp(fd_nr); assert(f != NULL); r= rw_pipe((call_nr == READ) ? READING : WRITING, who_e, fd_nr, f, rp->fp_buffer, rp->fp_nbytes); if (r != SUSPEND) reply(who_e, r); continue; } return; } if (!found_one) panic("get_work couldn't revive anyone"); } for(;;) { int r; /* Normal case. No one to revive. */ if ((r=sef_receive(ANY, &m_in)) != OK) panic("fs sef_receive error: %d", r); who_e = m_in.m_source; who_p = _ENDPOINT_P(who_e); /* * negative who_p is never used to access the fproc array. Negative numbers * (kernel tasks) are treated in a special way */ if(who_p >= (int)(sizeof(fproc) / sizeof(struct fproc))) panic("receive process out of range: %d", who_p); if(who_p >= 0 && fproc[who_p].fp_endpoint == NONE) { printf("FS: ignoring request from %d, endpointless slot %d (%d)\n", m_in.m_source, who_p, m_in.m_type); continue; } if(who_p >= 0 && fproc[who_p].fp_endpoint != who_e) { if(fproc[who_p].fp_endpoint == NONE) { printf("slot unknown even\n"); } printf("FS: receive endpoint inconsistent (source %d, who_p %d, stored ep %d, who_e %d).\n", m_in.m_source, who_p, fproc[who_p].fp_endpoint, who_e); #if 0 panic("FS: inconsistent endpoint "); #endif continue; } call_nr = m_in.m_type; return; } }