minix/servers/vfs/filedes.c
David van Moolenbroek c51cd5fe91 Server/driver protocols: no longer allow third-party copies.
Before safecopies, the IO_ENDPT and DL_ENDPT message fields were needed
to know which actual process to copy data from/to, as that process may
not always be the caller. Now that we have full safecopy support, these
fields have become useless for that purpose: the owner of the grant is
*always* the caller. Allowing the caller to supply another endpoint is
in fact dangerous, because the callee may then end up using a grant
from a third party. One could call this a variant of the confused
deputy problem.

From now on, safecopy calls should always use the caller's endpoint as
grant owner. This fully obsoletes the DL_ENDPT field in the
inet/ethernet protocol. IO_ENDPT has other uses besides identifying the
grant owner though. This patch renames IO_ENDPT to USER_ENDPT, not only
because that is a more fitting name (it should never be used for I/O
after all), but also in order to intentionally break any old system
source code outside the base system. If this patch breaks your code,
fixing it is fairly simple:

- DL_ENDPT should be replaced with m_source;
- IO_ENDPT should be replaced with m_source when used for safecopies;
- IO_ENDPT should be replaced with USER_ENDPT for any other use, e.g.
  when setting REP_ENDPT, matching requests in CANCEL calls, getting
  DEV_SELECT flags, and retrieving of the real user process's endpoint
  in DEV_OPEN.

The changes in this patch are binary backward compatible.
2011-04-11 17:35:05 +00:00

351 lines
11 KiB
C

/* This file contains the procedures that manipulate file descriptors.
*
* The entry points into this file are
* get_fd: look for free file descriptor and free filp slots
* get_filp: look up the filp entry for a given file descriptor
* find_filp: find a filp slot that points to a given vnode
* inval_filp: invalidate a filp and associated fd's, only let close()
* happen on it
* do_verify_fd: verify whether the given file descriptor is valid for
* the given endpoint.
* do_set_filp: marks a filp as in-flight.
* do_copy_filp: copies a filp to another endpoint.
* do_put_filp: marks a filp as not in-flight anymore.
* do_cancel_fd: cancel the transaction when something goes wrong for
* the receiver.
*/
#include <sys/select.h>
#include <minix/callnr.h>
#include <minix/u64.h>
#include <assert.h>
#include "fs.h"
#include "file.h"
#include "fproc.h"
#include "vnode.h"
/*===========================================================================*
* get_fd *
*===========================================================================*/
PUBLIC int get_fd(int start, mode_t bits, int *k, struct filp **fpt)
{
/* Look for a free file descriptor and a free filp slot. Fill in the mode word
* in the latter, but don't claim either one yet, since the open() or creat()
* may yet fail.
*/
register struct filp *f;
register int i;
/* Search the fproc fp_filp table for a free file descriptor. */
for (i = start; i < OPEN_MAX; i++) {
if (fp->fp_filp[i] == NULL && !FD_ISSET(i, &fp->fp_filp_inuse)) {
/* A file descriptor has been located. */
*k = i;
break;
}
}
/* Check to see if a file descriptor has been found. */
if (i >= OPEN_MAX) return(EMFILE);
/* Now that a file descriptor has been found, look for a free filp slot. */
for (f = &filp[0]; f < &filp[NR_FILPS]; f++) {
assert(f->filp_count >= 0);
if (f->filp_count == 0) {
f->filp_mode = bits;
f->filp_pos = cvu64(0);
f->filp_selectors = 0;
f->filp_select_ops = 0;
f->filp_pipe_select_ops = 0;
f->filp_flags = 0;
f->filp_state = FS_NORMAL;
f->filp_select_flags = 0;
*fpt = f;
return(OK);
}
}
/* If control passes here, the filp table must be full. Report that back. */
return(ENFILE);
}
/*===========================================================================*
* get_filp *
*===========================================================================*/
PUBLIC struct filp *get_filp(fild)
int fild; /* file descriptor */
{
/* See if 'fild' refers to a valid file descr. If so, return its filp ptr. */
return get_filp2(fp, fild);
}
/*===========================================================================*
* get_filp2 *
*===========================================================================*/
PUBLIC struct filp *get_filp2(rfp, fild)
register struct fproc *rfp;
int fild; /* file descriptor */
{
/* See if 'fild' refers to a valid file descr. If so, return its filp ptr. */
err_code = EBADF;
if (fild < 0 || fild >= OPEN_MAX ) return(NULL);
if (rfp->fp_filp[fild] == NULL && FD_ISSET(fild, &rfp->fp_filp_inuse))
err_code = EIO; /* The filedes is not there, but is not closed either.
*/
return(rfp->fp_filp[fild]); /* may also be NULL */
}
/*===========================================================================*
* find_filp *
*===========================================================================*/
PUBLIC struct filp *find_filp(register struct vnode *vp, mode_t bits)
{
/* Find a filp slot that refers to the vnode 'vp' in a way as described
* by the mode bit 'bits'. Used for determining whether somebody is still
* interested in either end of a pipe. Also used when opening a FIFO to
* find partners to share a filp field with (to shared the file position).
* Like 'get_fd' it performs its job by linear search through the filp table.
*/
register struct filp *f;
for (f = &filp[0]; f < &filp[NR_FILPS]; f++) {
if (f->filp_count != 0 && f->filp_vno == vp && (f->filp_mode & bits)){
return(f);
}
}
/* If control passes here, the filp wasn't there. Report that back. */
return(NULL);
}
/*===========================================================================*
* invalidate *
*===========================================================================*/
PUBLIC int invalidate(struct filp *fp)
{
/* Invalidate filp. fp_filp_inuse is not cleared, so filp can't be reused
until it is closed first. */
int f, fd, n = 0;
for(f = 0; f < NR_PROCS; f++) {
if(fproc[f].fp_pid == PID_FREE) continue;
for(fd = 0; fd < OPEN_MAX; fd++) {
if(fproc[f].fp_filp[fd] && fproc[f].fp_filp[fd] == fp) {
fproc[f].fp_filp[fd] = NULL;
n++;
}
}
}
return(n); /* Report back how often this filp has been invalidated. */
}
/*===========================================================================*
* verify_fd *
*===========================================================================*/
PUBLIC filp_id_t verify_fd(ep, fd)
endpoint_t ep;
int fd;
{
/*
* verify whether the given file descriptor 'fd' is valid for the
* endpoint 'ep'. When the file descriptor is valid verify_fd returns a
* pointer to that filp, else it returns NULL.
*/
int proc;
if (isokendpt(ep, &proc) != OK) {
return NULL;
}
return get_filp2(&fproc[proc], fd);
}
/*===========================================================================*
* do_verify_fd *
*===========================================================================*/
PUBLIC int do_verify_fd(void)
{
m_out.ADDRESS = (void *) verify_fd(m_in.USER_ENDPT, m_in.COUNT);
return (m_out.ADDRESS != NULL) ? OK : EINVAL;
}
/*===========================================================================*
* set_filp *
*===========================================================================*/
PUBLIC int set_filp(sfilp)
filp_id_t sfilp;
{
if (sfilp == NULL) {
return EINVAL;
} else {
sfilp->filp_count++;
return OK;
}
}
/*===========================================================================*
* do_set_filp *
*===========================================================================*/
PUBLIC int do_set_filp(void)
{
return set_filp((filp_id_t) m_in.ADDRESS);;
}
/*===========================================================================*
* copy_filp *
*===========================================================================*/
PUBLIC int copy_filp(to_ep, cfilp)
endpoint_t to_ep;
filp_id_t cfilp;
{
int j;
int proc;
if (isokendpt(to_ep, &proc) != OK) {
return EINVAL;
}
/* Find an open slot in fp_filp */
for (j = 0; j < OPEN_MAX; j++) {
if (fproc[proc].fp_filp[j] == NULL &&
!FD_ISSET(j, &fproc[proc].fp_filp_inuse)) {
/* Found a free slot, add descriptor */
FD_SET(j, &fproc[proc].fp_filp_inuse);
fproc[proc].fp_filp[j] = cfilp;
fproc[proc].fp_filp[j]->filp_count++;
return j;
}
}
/* File Descriptor Table is Full */
return EMFILE;
}
/*===========================================================================*
* do_copy_filp *
*===========================================================================*/
PUBLIC int do_copy_filp(void)
{
return copy_filp(m_in.USER_ENDPT, (filp_id_t) m_in.ADDRESS);
}
/*===========================================================================*
* put_filp *
*===========================================================================*/
PUBLIC int put_filp(pfilp)
filp_id_t pfilp;
{
if (pfilp == NULL) {
return EINVAL;
} else {
close_filp(pfilp);
return OK;
}
}
/*===========================================================================*
* do_put_filp *
*===========================================================================*/
PUBLIC int do_put_filp(void)
{
return put_filp((filp_id_t) m_in.ADDRESS);
}
/*===========================================================================*
* cancel_fd *
*===========================================================================*/
PUBLIC int cancel_fd(ep, fd)
endpoint_t ep;
int fd;
{
int proc;
if (isokendpt(ep, &proc) != OK) {
return EINVAL;
}
/* Check that the input 'fd' is valid */
if (verify_fd(ep, fd) != NULL) {
/* Found a valid descriptor, remove it */
FD_CLR(fd, &fproc[proc].fp_filp_inuse);
fproc[proc].fp_filp[fd]->filp_count--;
fproc[proc].fp_filp[fd] = NULL;
return fd;
}
/* File descriptor is not valid for the endpoint. */
return EINVAL;
}
/*===========================================================================*
* do_cancel_fd *
*===========================================================================*/
PUBLIC int do_cancel_fd(void)
{
return cancel_fd(m_in.USER_ENDPT, m_in.COUNT);
}
/*===========================================================================*
* close_filp *
*===========================================================================*/
PUBLIC void close_filp(fp)
struct filp *fp;
{
int mode_word, rw;
dev_t dev;
struct vnode *vp;
vp = fp->filp_vno;
if (fp->filp_count - 1 == 0 && fp->filp_mode != FILP_CLOSED) {
/* Check to see if the file is special. */
mode_word = vp->v_mode & I_TYPE;
if (mode_word == I_CHAR_SPECIAL || mode_word == I_BLOCK_SPECIAL) {
dev = (dev_t) vp->v_sdev;
if (mode_word == I_BLOCK_SPECIAL) {
if (vp->v_bfs_e == ROOT_FS_E) {
/* Invalidate the cache unless the special is
* mounted. Assume that the root filesystem's
* is open only for fsck.
*/
req_flush(vp->v_bfs_e, dev);
}
}
/* Do any special processing on device close. */
(void) dev_close(dev, fp-filp);
/* Ignore any errors, even SUSPEND. */
fp->filp_mode = FILP_CLOSED;
}
}
/* If the inode being closed is a pipe, release everyone hanging on it. */
if (vp->v_pipe == I_PIPE) {
rw = (fp->filp_mode & R_BIT ? WRITE : READ);
release(vp, rw, NR_PROCS);
}
/* If a write has been done, the inode is already marked as DIRTY. */
if (--fp->filp_count == 0) {
if (vp->v_pipe == I_PIPE) {
/* Last reader or writer is going. Tell PFS about latest
* pipe size.
*/
truncate_vnode(vp, vp->v_size);
}
put_vnode(fp->filp_vno);
}
}