382 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			382 lines
		
	
	
		
			9.4 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
 | 
						|
 *
 | 
						|
 * Changes for VFS:
 | 
						|
 *   Jul 2006 (Balazs Gerofi)
 | 
						|
 */
 | 
						|
 | 
						|
#include "fs.h"
 | 
						|
#include <fcntl.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <minix/com.h>
 | 
						|
#include <minix/u64.h>
 | 
						|
#include "file.h"
 | 
						|
#include "fproc.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(read_write(READING));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				read_write				     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int read_write(rw_flag)
 | 
						|
int rw_flag;			/* READING or WRITING */
 | 
						|
{
 | 
						|
/* Perform read(fd, buffer, nbytes) or write(fd, buffer, nbytes) call. */
 | 
						|
  register struct filp *f;
 | 
						|
  register struct vnode *vp;
 | 
						|
  off_t bytes_left;
 | 
						|
  u64_t position, res_pos, new_pos;
 | 
						|
  unsigned int off, cum_io, cum_io_incr, res_cum_io, num_of_bytes;
 | 
						|
  int op, oflags, r, chunk, usr, block_spec, char_spec;
 | 
						|
  int regular;
 | 
						|
  mode_t mode_word;
 | 
						|
  phys_bytes p;
 | 
						|
  struct dmap *dp;
 | 
						|
 | 
						|
  /* PM loads segments by putting funny things in other bits of the
 | 
						|
   * message, indicated by a high bit in fd. */
 | 
						|
  if (who_e == PM_PROC_NR && (m_in.fd & _PM_SEG_FLAG)) {
 | 
						|
	panic(__FILE__,
 | 
						|
	"read_write: special read/write calls by PM no longer supported",
 | 
						|
		NO_NUM);
 | 
						|
  } 
 | 
						|
  else {
 | 
						|
      usr = who_e;		/* normal case */
 | 
						|
  }
 | 
						|
 | 
						|
  /* If the file descriptor is valid, get the vnode, size and mode. */
 | 
						|
  if (m_in.nbytes < 0)
 | 
						|
  	return(EINVAL);
 | 
						|
 | 
						|
  if ((f = get_filp(m_in.fd)) == NIL_FILP) {
 | 
						|
  	return(err_code);
 | 
						|
  }
 | 
						|
  if (((f->filp_mode) & (rw_flag == READING ? R_BIT : W_BIT)) == 0) {
 | 
						|
#if 0
 | 
						|
	printf("vfs:read_write: returning error\n");
 | 
						|
#endif
 | 
						|
      return(f->filp_mode == FILP_CLOSED ? EIO : EBADF);
 | 
						|
  }
 | 
						|
 | 
						|
  if (m_in.nbytes == 0)
 | 
						|
      return(0);	/* so char special files need not check for 0*/
 | 
						|
 | 
						|
  position = f->filp_pos;
 | 
						|
  oflags = f->filp_flags;
 | 
						|
 | 
						|
  vp = f->filp_vno;
 | 
						|
 | 
						|
  if (vp->v_pipe == I_PIPE)
 | 
						|
  {
 | 
						|
	if (fp->fp_cum_io_partial != 0)
 | 
						|
	{
 | 
						|
		panic(__FILE__, "read_write: fp_cum_io_partial not clear",
 | 
						|
			NO_NUM);
 | 
						|
	}
 | 
						|
	return rw_pipe(rw_flag, usr, m_in.fd, f, m_in.buffer, m_in.nbytes);
 | 
						|
  }
 | 
						|
 | 
						|
  r = OK;
 | 
						|
  cum_io = 0;
 | 
						|
 | 
						|
  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(__FILE__,"read_write tries to read from "
 | 
						|
                  "character device NO_DEV", NO_NUM);
 | 
						|
  }
 | 
						|
 | 
						|
  if ((block_spec = (mode_word == I_BLOCK_SPECIAL ? 1 : 0))) {
 | 
						|
      if (vp->v_sdev == NO_DEV)
 | 
						|
          panic(__FILE__,"read_write tries to read from "
 | 
						|
                  " block device NO_DEV", NO_NUM);
 | 
						|
  }
 | 
						|
 | 
						|
  /* Character special files. */
 | 
						|
  if (char_spec) {
 | 
						|
	dev_t dev;
 | 
						|
	int suspend_reopen;
 | 
						|
 | 
						|
	suspend_reopen= (f->filp_state != FS_NORMAL);
 | 
						|
 | 
						|
	dev = (dev_t) vp->v_sdev;
 | 
						|
	r = dev_io(op, dev, usr, m_in.buffer, position, m_in.nbytes, oflags,
 | 
						|
		suspend_reopen);
 | 
						|
	if (r >= 0) {
 | 
						|
		cum_io = r;
 | 
						|
		position = add64ul(position, r);
 | 
						|
		r = OK;
 | 
						|
	}
 | 
						|
  } 
 | 
						|
  /* Block special files. */
 | 
						|
  else if (block_spec) {
 | 
						|
 | 
						|
      /* Issue request */
 | 
						|
      r = req_breadwrite(vp->v_bfs_e, usr, vp->v_sdev, position,
 | 
						|
	m_in.nbytes, m_in.buffer, rw_flag, &res_pos, &res_cum_io);
 | 
						|
 | 
						|
      position = res_pos;
 | 
						|
      cum_io += res_cum_io;
 | 
						|
  }
 | 
						|
  /* Regular files */
 | 
						|
  else {
 | 
						|
      if (rw_flag == WRITING && block_spec == 0) {
 | 
						|
          /* Check for O_APPEND flag. */
 | 
						|
          if (oflags & O_APPEND) position = cvul64(vp->v_size);
 | 
						|
      }
 | 
						|
 | 
						|
 | 
						|
      /* Fill in request structure */
 | 
						|
      num_of_bytes = m_in.nbytes;
 | 
						|
 | 
						|
#if 0  /* Don't truncate read request at size. The filesystem process will
 | 
						|
	* do this itself.
 | 
						|
	*/
 | 
						|
      if((rw_flag == READING) &&
 | 
						|
	cmp64ul(add64ul(position, num_of_bytes), 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);
 | 
						|
		num_of_bytes = vp->v_size - pos32;
 | 
						|
		assert(num_of_bytes >= 0);
 | 
						|
      }
 | 
						|
#endif
 | 
						|
 | 
						|
      /* Issue request */
 | 
						|
      r = req_readwrite(vp->v_fs_e, vp->v_inode_nr, vp->v_index, position,
 | 
						|
	rw_flag, usr, m_in.buffer, num_of_bytes, &new_pos, &cum_io_incr);
 | 
						|
 | 
						|
      if (r >= 0)
 | 
						|
      {
 | 
						|
	if (ex64hi(new_pos))
 | 
						|
		panic(__FILE__, "read_write: bad new pos", NO_NUM);
 | 
						|
 | 
						|
	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(__FILE__,
 | 
						|
				"read_write: file size too big for v_size",
 | 
						|
				NO_NUM);
 | 
						|
		}
 | 
						|
		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;
 | 
						|
  off_t pos_change;
 | 
						|
  cp_grant_id_t gid;
 | 
						|
  register struct filp *rfilp;
 | 
						|
 | 
						|
  /* Is the file descriptor valid? */
 | 
						|
  if ( (rfilp = get_filp(m_in.fd)) == NIL_FILP) {
 | 
						|
	  return(err_code);
 | 
						|
  }
 | 
						|
  
 | 
						|
  if (!(rfilp->filp_mode & R_BIT))
 | 
						|
	return EBADF;
 | 
						|
 | 
						|
  if ((rfilp->filp_vno->v_mode & I_TYPE) != I_DIRECTORY)
 | 
						|
	return EBADF;
 | 
						|
 | 
						|
  gid=cpf_grant_magic(rfilp->filp_vno->v_fs_e, who_e, (vir_bytes) m_in.buffer,
 | 
						|
	m_in.nbytes, CPF_WRITE);
 | 
						|
  if (gid < 0) panic(__FILE__, "cpf_grant_magic failed", gid);
 | 
						|
 | 
						|
  /* Issue request */
 | 
						|
  if (ex64hi(rfilp->filp_pos) != 0)
 | 
						|
	panic(__FILE__, "do_getdents: should handle large offsets", NO_NUM);
 | 
						|
	
 | 
						|
  r= req_getdents(rfilp->filp_vno->v_fs_e, rfilp->filp_vno->v_inode_nr, 
 | 
						|
	ex64lo(rfilp->filp_pos), gid, m_in.nbytes, &pos_change);
 | 
						|
 | 
						|
  cpf_revoke(gid);
 | 
						|
 | 
						|
  if (r > 0)
 | 
						|
	rfilp->filp_pos= add64ul(rfilp->filp_pos, pos_change);
 | 
						|
  return r;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				rw_pipe					     *
 | 
						|
 *===========================================================================*/
 | 
						|
PUBLIC int rw_pipe(rw_flag, usr, fd_nr, f, buf, req_size)
 | 
						|
int rw_flag;			/* READING or WRITING */
 | 
						|
endpoint_t usr;
 | 
						|
int fd_nr;
 | 
						|
struct filp *f;
 | 
						|
char *buf;
 | 
						|
size_t req_size;
 | 
						|
{
 | 
						|
  int r, oflags, op, partial_pipe;
 | 
						|
  size_t size, cum_io, cum_io_incr;
 | 
						|
  struct vnode *vp;
 | 
						|
  u64_t position, new_pos;
 | 
						|
 | 
						|
  oflags = f->filp_flags;
 | 
						|
 | 
						|
  vp = f->filp_vno;
 | 
						|
  position = cvu64((rw_flag == READING) ? vp->v_pipe_rd_pos :
 | 
						|
	vp->v_pipe_wr_pos);
 | 
						|
 | 
						|
#if 0
 | 
						|
  printf("vfs:rw_pipe: filp 0x%x pipe %s, buf 0x%x, size %d\n",
 | 
						|
	f, rw_flag == READING ? "read" : "write", buf, req_size);
 | 
						|
  printf("vfs:rw_pipe: pipe vp 0x%x, dev/num 0x%x/%d size %d, pos 0x%x:%08x\n",
 | 
						|
  	vp, vp->v_dev, vp->v_inode_nr, 
 | 
						|
  	vp->v_size, ex64hi(position), ex64lo(position));
 | 
						|
#endif
 | 
						|
 | 
						|
  /* fp->fp_cum_io_partial is only nonzero when doing partial writes */
 | 
						|
  cum_io = fp->fp_cum_io_partial; 
 | 
						|
 | 
						|
  op = (rw_flag == READING ? VFS_DEV_READ : VFS_DEV_WRITE);
 | 
						|
 | 
						|
  r = Xpipe_check(vp, rw_flag, oflags, req_size, position, 0);
 | 
						|
  if (r <= 0)
 | 
						|
  {
 | 
						|
  	if (r == SUSPEND)
 | 
						|
		pipe_suspend(rw_flag, fd_nr, buf, req_size);
 | 
						|
  	return(r);
 | 
						|
  }
 | 
						|
 | 
						|
  size = r;
 | 
						|
  if (r < req_size)
 | 
						|
	partial_pipe = 1;
 | 
						|
  else 
 | 
						|
  	partial_pipe = 0;
 | 
						|
 | 
						|
  /* 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;
 | 
						|
  }
 | 
						|
  /* Issue request */
 | 
						|
  r = req_readwrite(vp->v_fs_e, vp->v_inode_nr, vp->v_index, position,
 | 
						|
	rw_flag, usr, buf, size, &new_pos, &cum_io_incr);
 | 
						|
 | 
						|
  if (r >= 0)
 | 
						|
  {
 | 
						|
	if (ex64hi(new_pos))
 | 
						|
		panic(__FILE__, "read_write: bad new pos", NO_NUM);
 | 
						|
 | 
						|
	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(__FILE__,
 | 
						|
				"read_write: file size too big for v_size",
 | 
						|
				NO_NUM);
 | 
						|
		}
 | 
						|
		vp->v_size = ex64lo(position);
 | 
						|
	}
 | 
						|
  }
 | 
						|
  else {
 | 
						|
	if (cmp64ul(position, vp->v_size) >= 0) {
 | 
						|
		/* Reset pipe pointers */
 | 
						|
#if 0
 | 
						|
		printf("vfs:rw_pipe: resetting pipe size/positions\n");
 | 
						|
#endif
 | 
						|
		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(rw_flag, fd_nr, buf, req_size);
 | 
						|
			return(SUSPEND);
 | 
						|
		}
 | 
						|
	}
 | 
						|
	fp->fp_cum_io_partial = 0;
 | 
						|
	return cum_io;
 | 
						|
  }
 | 
						|
 | 
						|
  return r;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 |