minix/servers/avfs/read.c
Thomas Veerman de5a9a3e8b AVFS: Use scratchpad instead of m_in to pass around file descriptors
Some code relies on having the file descriptor in m_in.fd. Consequently,
m_in is not only used to provide syscall parameters from user space to
VFS, but also as a global variable to store temporary data within VFS.
This has the ugly side effect that m_in gets overwritten during core
dumping.*

To work around this problem VFS now uses a so called "scratchpad" to
store temporary data that has to be globally accessible. This is a simple
table indexed by process number, just like fproc. The scratchpad allows
us to store the buffer pointer and buffer size for suspended system calls
(i.e., read, write, open, lock) instead of using fproc. This makes fproc
a bit smaller and fproc iterators a bit faster. Moreover, suspension of
processes becomes simpler altogether and suspended operations on pipes
are now less of a special case.

* This patch fixes a bug where due to unexpected m_in overwriting a
coredump would fail, and consequently resources are leaked. The coredump
was triggered with:
$ a() { a; }
$ a
2011-12-21 10:52:51 +00:00

348 lines
8.9 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 *
*===========================================================================*/
PUBLIC int do_read()
{
return(do_read_write(READING));
}
/*===========================================================================*
* lock_bsf *
*===========================================================================*/
PUBLIC void lock_bsf(void)
{
message org_m_in;
struct fproc *org_fp;
struct worker_thread *org_self;
if (mutex_trylock(&bsf_lock) == 0)
return;
org_m_in = m_in;
org_fp = fp;
org_self = self;
if (mutex_lock(&bsf_lock) != 0)
panic("unable to lock block special file lock");
m_in = org_m_in;
fp = org_fp;
self = org_self;
}
/*===========================================================================*
* unlock_bsf *
*===========================================================================*/
PUBLIC void unlock_bsf(void)
{
if (mutex_unlock(&bsf_lock) != 0)
panic("failed to unlock block special file lock");
}
/*===========================================================================*
* do_read_write *
*===========================================================================*/
PUBLIC int do_read_write(rw_flag)
int rw_flag; /* READING or WRITING */
{
/* Perform read(fd, buffer, nbytes) or write(fd, buffer, nbytes) call. */
struct filp *f;
tll_access_t locktype;
int r;
scratch(fp).file.fd_nr = m_in.fd;
scratch(fp).io.io_buffer = m_in.buffer;
scratch(fp).io.io_nbytes = (size_t) m_in.nbytes;
locktype = (rw_flag == READING) ? VNODE_READ : VNODE_WRITE;
if ((f = get_filp(scratch(fp).file.fd_nr, locktype)) == NULL)
return(err_code);
if (((f->filp_mode) & (rw_flag == READING ? R_BIT : W_BIT)) == 0) {
unlock_filp(f);
return(f->filp_mode == FILP_CLOSED ? EIO : EBADF);
}
if (scratch(fp).io.io_nbytes == 0) {
unlock_filp(f);
return(0); /* so char special files need not check for 0*/
}
r = read_write(rw_flag, f, scratch(fp).io.io_buffer, scratch(fp).io.io_nbytes,
who_e);
unlock_filp(f);
return(r);
}
/*===========================================================================*
* read_write *
*===========================================================================*/
PUBLIC int read_write(int rw_flag, struct filp *f, char *buf, size_t size,
endpoint_t for_e)
{
register struct vnode *vp;
u64_t position, res_pos, new_pos;
unsigned int cum_io, cum_io_incr, res_cum_io;
int op, oflags, r, block_spec, char_spec, regular;
mode_t mode_word;
position = f->filp_pos;
oflags = f->filp_flags;
vp = f->filp_vno;
r = OK;
cum_io = 0;
if (size > SSIZE_MAX) return(EINVAL);
if (vp->v_pipe == I_PIPE) {
if (fp->fp_cum_io_partial != 0) {
panic("VFS: read_write: fp_cum_io_partial not clear");
}
r = rw_pipe(rw_flag, for_e, f, buf, size);
return(r);
}
op = (rw_flag == READING ? VFS_DEV_READ : VFS_DEV_WRITE);
mode_word = vp->v_mode & I_TYPE;
regular = mode_word == I_REGULAR;
if ((char_spec = (mode_word == I_CHAR_SPECIAL ? 1 : 0))) {
if (vp->v_sdev == NO_DEV)
panic("VFS: read_write tries to access char dev NO_DEV");
}
if ((block_spec = (mode_word == I_BLOCK_SPECIAL ? 1 : 0))) {
if (vp->v_sdev == NO_DEV)
panic("VFS: read_write tries to access block dev NO_DEV");
}
if (char_spec) { /* Character special files. */
dev_t dev;
int suspend_reopen;
suspend_reopen = (f->filp_state != FS_NORMAL);
dev = (dev_t) vp->v_sdev;
r = dev_io(op, dev, for_e, buf, position, size, oflags,
suspend_reopen);
if (r >= 0) {
cum_io = r;
position = add64ul(position, r);
r = OK;
}
} else if (block_spec) { /* Block special files. */
lock_bsf();
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 && block_spec == 0) {
/* Check for O_APPEND flag. */
if (oflags & O_APPEND) position = cvul64(vp->v_size);
}
/* Issue request */
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 (regular || mode_word == I_DIRECTORY) {
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 == OK) return(cum_io);
return(r);
}
/*===========================================================================*
* do_getdents *
*===========================================================================*/
PUBLIC int do_getdents()
{
/* Perform the getdents(fd, buf, size) system call. */
int r = OK;
u64_t new_pos;
register struct filp *rfilp;
/* Is the file descriptor valid? */
if ( (rfilp = get_filp(m_in.fd, VNODE_READ)) == NULL) return(err_code);
if (!(rfilp->filp_mode & R_BIT))
r = EBADF;
else if ((rfilp->filp_vno->v_mode & I_TYPE) != I_DIRECTORY)
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, m_in.buffer, m_in.nbytes,&new_pos,0);
if (r > 0) rfilp->filp_pos = new_pos;
}
unlock_filp(rfilp);
return(r);
}
/*===========================================================================*
* rw_pipe *
*===========================================================================*/
PUBLIC 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_islocked(&f->filp_vno->v_lock));
assert(mutex_trylock(&f->filp_lock) == -EDEADLK);
oflags = f->filp_flags;
vp = f->filp_vno;
position = cvu64((rw_flag == READING) ? vp->v_pipe_rd_pos :
vp->v_pipe_wr_pos);
/* fp->fp_cum_io_partial is only nonzero when doing partial writes */
cum_io = fp->fp_cum_io_partial;
r = pipe_check(vp, rw_flag, oflags, req_size, position, 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) &&
cmp64ul(add64ul(position, size), vp->v_size) > 0) {
/* Position always should fit in an off_t (LONG_MAX). */
off_t pos32;
assert(cmp64ul(position, LONG_MAX) <= 0);
pos32 = cv64ul(position);
assert(pos32 >= 0);
assert(pos32 <= LONG_MAX);
size = vp->v_size - pos32;
}
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 >= 0) {
if (ex64hi(new_pos))
panic("rw_pipe: bad new pos");
position = new_pos;
cum_io += cum_io_incr;
buf += cum_io_incr;
req_size -= cum_io_incr;
}
/* On write, update file size and access time. */
if (rw_flag == WRITING) {
if (cmp64ul(position, vp->v_size) > 0) {
if (ex64hi(position) != 0) {
panic("read_write: file size too big for v_size");
}
vp->v_size = ex64lo(position);
}
} else {
if (cmp64ul(position, vp->v_size) >= 0) {
/* Reset pipe pointers */
vp->v_size = 0;
vp->v_pipe_rd_pos= 0;
vp->v_pipe_wr_pos= 0;
position = cvu64(0);
}
}
if (rw_flag == READING)
vp->v_pipe_rd_pos= cv64ul(position);
else
vp->v_pipe_wr_pos= cv64ul(position);
if (r == OK) {
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);
}
return(r);
}