. libc: add vfs_mmap, a way for vfs to initiate mmap()s. This is a good special case to have as vfs is a slightly different client from regular user processes. It doesn't do it for itself, and has the dev & inode info already so the callback to VFS for the lookup isn't necessary. So it has different info to have to give to VM. . libc: also add minix_mmap64() that accepts a 64-bit offset, even though our off_t is still 32 bit now. . to support mmap, we need to create new references to FD's (such as an FD) so that the mmapped object can never be removed even if all FD's are closed. To do that we conceptually dup the filedescriptor into a new range that is only visible to the system. So there are FDS_PER_PROCESS fd's, which is more than OPEN_MAX, per process, and user processes can still use up to OPEN_MAX FD's. mmap()ped objects then get extra reference in the invisible range so user processes can't close them. The in-vfs fd_set is of size FDS_PER_PROCESS. . On exec() time, try to mmap() in the executable if available. (It is not yet available in this commit.) . To support mmap(), add do_vm_call that allows VM to lookup (to ino+dev), do i/o from and close FD's on behalf of other processes. . To make this possible, a few functions have 2 more arguments to make the target process explicit (instead of the implicit global fp) and to specify whether the request is from a user process or the system (which implies whether FD's starting at OPEN_MAX are valid). These are close_fd, common_open, actual_llseek, actual_read_write_peek, get_fd and get_filp2. Change-Id: I831551e45a6781c74313c450eb9c967a68505932
375 lines
9.7 KiB
C
375 lines
9.7 KiB
C
/* This file contains the heart of the mechanism used to read (and write)
|
|
* files. Read and write requests are split up into chunks that do not cross
|
|
* block boundaries. Each chunk is then processed in turn. Reads on special
|
|
* files are also detected and handled.
|
|
*
|
|
* The entry points into this file are
|
|
* do_read: perform the READ system call by calling read_write
|
|
* do_getdents: read entries from a directory (GETDENTS)
|
|
* read_write: actually do the work of READ and WRITE
|
|
*
|
|
*/
|
|
|
|
#include "fs.h"
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <minix/com.h>
|
|
#include <minix/u64.h>
|
|
#include "file.h"
|
|
#include "fproc.h"
|
|
#include "scratchpad.h"
|
|
#include "param.h"
|
|
#include <dirent.h>
|
|
#include <assert.h>
|
|
#include <minix/vfsif.h>
|
|
#include "vnode.h"
|
|
#include "vmnt.h"
|
|
|
|
|
|
/*===========================================================================*
|
|
* do_read *
|
|
*===========================================================================*/
|
|
int do_read(message *UNUSED(m_out))
|
|
{
|
|
return(do_read_write_peek(READING, job_m_in.fd,
|
|
job_m_in.buffer, (size_t) job_m_in.nbytes));
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* lock_bsf *
|
|
*===========================================================================*/
|
|
void lock_bsf(void)
|
|
{
|
|
struct fproc *org_fp;
|
|
struct worker_thread *org_self;
|
|
|
|
if (mutex_trylock(&bsf_lock) == 0)
|
|
return;
|
|
|
|
org_fp = fp;
|
|
org_self = self;
|
|
|
|
if (mutex_lock(&bsf_lock) != 0)
|
|
panic("unable to lock block special file lock");
|
|
|
|
fp = org_fp;
|
|
self = org_self;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* unlock_bsf *
|
|
*===========================================================================*/
|
|
void unlock_bsf(void)
|
|
{
|
|
if (mutex_unlock(&bsf_lock) != 0)
|
|
panic("failed to unlock block special file lock");
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* check_bsf *
|
|
*===========================================================================*/
|
|
void check_bsf_lock(void)
|
|
{
|
|
int r = mutex_trylock(&bsf_lock);
|
|
|
|
if (r == -EBUSY)
|
|
panic("bsf_lock locked");
|
|
else if (r != 0)
|
|
panic("bsf_lock weird state");
|
|
|
|
/* r == 0 */
|
|
unlock_bsf();
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* actual_read_write_peek *
|
|
*===========================================================================*/
|
|
int actual_read_write_peek(struct fproc *rfp, int rw_flag, int io_fd,
|
|
char *io_buf, size_t io_nbytes, int userreq)
|
|
{
|
|
/* 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, userreq)) == NULL)
|
|
return(err_code);
|
|
|
|
assert(f->filp_count > 0);
|
|
|
|
if (((f->filp_mode) & (ro ? R_BIT : W_BIT)) == 0) {
|
|
unlock_filp(f);
|
|
return(f->filp_mode == FILP_CLOSED ? EIO : 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);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_read_write_peek *
|
|
*===========================================================================*/
|
|
int do_read_write_peek(int rw_flag, int io_fd, char *io_buf, size_t io_nbytes)
|
|
{
|
|
return actual_read_write_peek(fp, rw_flag, io_fd, io_buf, io_nbytes, 1);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* read_write *
|
|
*===========================================================================*/
|
|
int read_write(struct fproc *rfp, int rw_flag, struct filp *f,
|
|
char *buf, size_t size, endpoint_t for_e)
|
|
{
|
|
register struct vnode *vp;
|
|
u64_t position, res_pos;
|
|
unsigned int cum_io, cum_io_incr, res_cum_io;
|
|
int op, r;
|
|
|
|
position = f->filp_pos;
|
|
vp = f->filp_vno;
|
|
r = OK;
|
|
cum_io = 0;
|
|
|
|
assert(rw_flag == READING || rw_flag == WRITING || rw_flag == PEEKING);
|
|
|
|
if (size > SSIZE_MAX) return(EINVAL);
|
|
|
|
op = (rw_flag == READING ? VFS_DEV_READ : VFS_DEV_WRITE);
|
|
|
|
if (S_ISFIFO(vp->v_mode)) { /* Pipes */
|
|
if (rfp->fp_cum_io_partial != 0) {
|
|
panic("VFS: read_write: fp_cum_io_partial not clear");
|
|
}
|
|
if(rw_flag == PEEKING) {
|
|
printf("read_write: peek on pipe makes no sense\n");
|
|
return EINVAL;
|
|
}
|
|
r = rw_pipe(rw_flag, for_e, f, buf, size);
|
|
} else if (S_ISCHR(vp->v_mode)) { /* Character special files. */
|
|
dev_t dev;
|
|
int suspend_reopen;
|
|
int op = (rw_flag == READING ? VFS_DEV_READ : VFS_DEV_WRITE);
|
|
|
|
if(rw_flag == PEEKING) {
|
|
printf("read_write: peek on char device makes no sense\n");
|
|
return EINVAL;
|
|
}
|
|
|
|
if (vp->v_sdev == NO_DEV)
|
|
panic("VFS: read_write tries to access char dev NO_DEV");
|
|
|
|
suspend_reopen = (f->filp_state & FS_NEEDS_REOPEN);
|
|
dev = (dev_t) vp->v_sdev;
|
|
|
|
r = dev_io(op, dev, for_e, buf, position, size, f->filp_flags,
|
|
suspend_reopen);
|
|
if (r >= 0) {
|
|
cum_io = r;
|
|
position = add64ul(position, r);
|
|
r = OK;
|
|
}
|
|
} else if (S_ISBLK(vp->v_mode)) { /* Block special files. */
|
|
if (vp->v_sdev == NO_DEV)
|
|
panic("VFS: read_write tries to access block dev NO_DEV");
|
|
|
|
lock_bsf();
|
|
|
|
if(rw_flag == PEEKING) {
|
|
r = req_bpeek(vp->v_bfs_e, vp->v_sdev, position, size);
|
|
} else {
|
|
r = req_breadwrite(vp->v_bfs_e, for_e, vp->v_sdev,
|
|
position, size, buf, rw_flag, &res_pos, &res_cum_io);
|
|
if (r == OK) {
|
|
position = res_pos;
|
|
cum_io += res_cum_io;
|
|
}
|
|
}
|
|
|
|
unlock_bsf();
|
|
} else { /* Regular files */
|
|
if (rw_flag == WRITING) {
|
|
/* Check for O_APPEND flag. */
|
|
if (f->filp_flags & O_APPEND) position = cvul64(vp->v_size);
|
|
}
|
|
|
|
/* Issue request */
|
|
if(rw_flag == PEEKING) {
|
|
r = req_peek(vp->v_fs_e, vp->v_inode_nr, position, size);
|
|
} else {
|
|
u64_t new_pos;
|
|
r = req_readwrite(vp->v_fs_e, vp->v_inode_nr, position,
|
|
rw_flag, for_e, buf, size, &new_pos, &cum_io_incr);
|
|
|
|
if (r >= 0) {
|
|
if (ex64hi(new_pos))
|
|
panic("read_write: bad new pos");
|
|
|
|
position = new_pos;
|
|
cum_io += cum_io_incr;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* On write, update file size and access time. */
|
|
if (rw_flag == WRITING) {
|
|
if (S_ISREG(vp->v_mode) || S_ISDIR(vp->v_mode)) {
|
|
if (cmp64ul(position, vp->v_size) > 0) {
|
|
if (ex64hi(position) != 0) {
|
|
panic("read_write: file size too big ");
|
|
}
|
|
vp->v_size = ex64lo(position);
|
|
}
|
|
}
|
|
}
|
|
|
|
f->filp_pos = position;
|
|
|
|
if (r == EPIPE && rw_flag == WRITING) {
|
|
/* Process is writing, but there is no reader. Tell the kernel to
|
|
* generate s SIGPIPE signal.
|
|
*/
|
|
if (!(f->filp_flags & O_NOSIGPIPE)) {
|
|
sys_kill(rfp->fp_endpoint, SIGPIPE);
|
|
}
|
|
}
|
|
|
|
if (r == OK) {
|
|
return(cum_io);
|
|
}
|
|
return(r);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* do_getdents *
|
|
*===========================================================================*/
|
|
int do_getdents(message *UNUSED(m_out))
|
|
{
|
|
/* Perform the getdents(fd, buf, size) system call. */
|
|
int r = OK;
|
|
u64_t new_pos;
|
|
register struct filp *rfilp;
|
|
|
|
scratch(fp).file.fd_nr = job_m_in.fd;
|
|
scratch(fp).io.io_buffer = job_m_in.buffer;
|
|
scratch(fp).io.io_nbytes = (size_t) job_m_in.nbytes;
|
|
|
|
/* 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) {
|
|
if (ex64hi(rfilp->filp_pos) != 0)
|
|
panic("do_getdents: can't handle large offsets");
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
/*===========================================================================*
|
|
* rw_pipe *
|
|
*===========================================================================*/
|
|
int rw_pipe(rw_flag, usr_e, f, buf, req_size)
|
|
int rw_flag; /* READING or WRITING */
|
|
endpoint_t usr_e;
|
|
struct filp *f;
|
|
char *buf;
|
|
size_t req_size;
|
|
{
|
|
int r, oflags, partial_pipe = 0;
|
|
size_t size, cum_io, cum_io_incr;
|
|
struct vnode *vp;
|
|
u64_t position, new_pos;
|
|
|
|
/* Must make sure we're operating on locked filp and vnode */
|
|
assert(tll_locked_by_me(&f->filp_vno->v_lock));
|
|
assert(mutex_trylock(&f->filp_lock) == -EDEADLK);
|
|
|
|
oflags = f->filp_flags;
|
|
vp = f->filp_vno;
|
|
position = cvu64(0); /* Not actually used */
|
|
|
|
assert(rw_flag == READING || rw_flag == WRITING);
|
|
|
|
/* fp->fp_cum_io_partial is only nonzero when doing partial writes */
|
|
cum_io = fp->fp_cum_io_partial;
|
|
|
|
r = pipe_check(f, rw_flag, oflags, req_size, 0);
|
|
if (r <= 0) {
|
|
if (r == SUSPEND) pipe_suspend(f, buf, req_size);
|
|
return(r);
|
|
}
|
|
|
|
size = r;
|
|
if (size < req_size) partial_pipe = 1;
|
|
|
|
/* Truncate read request at size. */
|
|
if (rw_flag == READING && size > vp->v_size) {
|
|
size = vp->v_size;
|
|
}
|
|
|
|
if (vp->v_mapfs_e == 0)
|
|
panic("unmapped pipe");
|
|
|
|
r = req_readwrite(vp->v_mapfs_e, vp->v_mapinode_nr, position, rw_flag, usr_e,
|
|
buf, size, &new_pos, &cum_io_incr);
|
|
|
|
if (r != OK) {
|
|
return(r);
|
|
}
|
|
|
|
if (ex64hi(new_pos))
|
|
panic("rw_pipe: bad new pos");
|
|
|
|
cum_io += cum_io_incr;
|
|
buf += cum_io_incr;
|
|
req_size -= cum_io_incr;
|
|
|
|
vp->v_size = ex64lo(new_pos);
|
|
|
|
if (partial_pipe) {
|
|
/* partial write on pipe with */
|
|
/* O_NONBLOCK, return write count */
|
|
if (!(oflags & O_NONBLOCK)) {
|
|
/* partial write on pipe with req_size > PIPE_SIZE,
|
|
* non-atomic
|
|
*/
|
|
fp->fp_cum_io_partial = cum_io;
|
|
pipe_suspend(f, buf, req_size);
|
|
return(SUSPEND);
|
|
}
|
|
}
|
|
|
|
fp->fp_cum_io_partial = 0;
|
|
|
|
return(cum_io);
|
|
}
|