/* vu.c - virtual unix file-system interface
 *
 ****************************************************************
 * Copyright (C) 1998, 2000 Thomas Lord
 * 
 * See the file "COPYING" for further information about
 * the copyright and warranty status of this work.
 */



#include "hackerlab/os/errno.h"
#include "hackerlab/bugs/panic.h"
#include "hackerlab/char/str.h"
#include "hackerlab/arrays/ar.h"
#include "hackerlab/vu/reserv.h"
#include "hackerlab/vu/vu-bad-arg.h"
#include "hackerlab/vu/vu-sys.h"
#include "hackerlab/vu/vu.h"


/************************************************************************
 *(h0 "A Virtual Unix File-System Interface"
 *    :include ("vu/vu.h"))
 *
 * VU provides a "virtual file-system interface" -- a way for programs
 * to redefine the meaning of the basic file-system functions for a
 * particular file descriptor or for a particular part of the
 * file-system namespace.  When a process defines a virtual
 * file-system, its definitions apply to that process only.
 */
/*(menu)
 */


/****************************************************************
 *(h1 "The VU File-System Functions")
 * 
 * For the purposes of this section, the unix file system interface is
 * defined as these functions:
 *
 *
 *	access chdir chmod chown chroot close closedir fchdir fchmod
 *	fchown fstat fsync ftruncate link lseek lstat mkdir open
 *	opendir read readdir readlink rename rmdir stat
 *	symlink truncate unlink utime write fcntl
 *	dup dup2
 *
 * For each of those functions, there is a corresponding `vu_' function:
 *
 *	vu_access vu_chdir vu_chmod ...
 *
 * The function prototypes of the `vu_' functions are slightly
 * different from the traditional unix functions.  For one thing, all
 * `vu_' functions accept an output paramter, `errn', as the first
 * argument:
 *
 *	int vu_open (int * errn, char * path, int flags, int mode)
 *
 * That parameter takes the place of the global variable `errno'.
 * When an error occurs in a `vu_' function, the error number is
 * returned in `*errn'.
 *
 * The other interface difference is the functions for reading
 * directories.  The `vu_' functions return an integer to indicate
 * success (0) or failure (-1) and return other information through
 * output parameters:
 *
 * 	int vu_opendir (int * errn, DIR ** retv,  char * path)
 *	int vu_readdir (int * errn,
 *			struct alloc_limits * limits,
 *			char ** file_ret,
 *			DIR * dir)
 * 
 * Aside from those difference, `vu_' functions can be substituted
 * freely for their unix counterparts.  If no other changes are made,
 * the modified program will run just the same as the original.
 * 
 * There are two additional `vu_' functions:
 *
 *	vu_read_retry vu_write_retry
 *
 * which are similar to `read' and `write' but which
 * automatically restart after a `EINTR' or `EAGAIN'.
 *
 */

/****************************************************************
 *(h1 "Virtual File-System Functions")
 * 
 * Programs can provide their own definitions of the `vu_' file-system
 * functions.  These definitions can be made to apply to specific file
 * descriptors or to a subset of the names in the file-system
 * namespace of the process.
 *
 * To define a set of virtual functions, a program defines functions
 * having the same return types and almost the same parameters as the
 * `vu_' virtual functions.  The virtual functions take one extra
 * (final) argument, `void * closure'.  For example:
 *
 *	int my_virtual_open (int * errn, 
 *			     char * path, 
 *			     int flags,
 * 			     int mode,
 *			     void * closure)
 * 
 * All of these functions must be named consistently by adding a
 * prefix to the root name of the file system functions:
 *
 *	my_virtual_access my_virtual_chdir my_virtual_chmod ...
 * 
 * Two additional functions must be provided:
 *
 *	void * my_virtual_make_closure (void * closure)
 *	void my_virtual_free_closure (void * closure)
 *
 * These are explained below.
 *
 * Having defined a set of virtual file system functions, a program
 * must declare a "vtable" for the functions:
 *
 *	#include "hackerlab/vu/vu.h"
 *	...
 *	
 *	struct vu_fs_discipline my_fs_vtable
 * 	 = { VU_FS_DISCIPLINE_INITIALIZERS (my_virtual_) };
 *
 * Note that the prefix used to name virtual functions is used by
 * the macro `VU_FS_DISCIPLINE_INITIALIZERS' to initialize the vtable.
 *
 * Finally, the functions may be applied to either a portion of the 
 * file-system namespace or to a particular descriptor:
 *
 *	// Applying `my_virtual_' functions to a particular 
 *	// descriptor:
 *	//
 *	vu_set_fd_handler (fd, &my_fs_vtable, closure);
 *
 *	// Applying `my_virtual_' functions to a part
 *	// of the file-system namespace.  Note that
 *	// preg is a `regex_t' -- a regular expression
 *	// compiled by `regcomp'.  `eflags' is like the
 *	// parameter of the same name to `regexec':
 *	//
 *	vu_push_name_handler (name, doc, preg, eflags, 
 *			      &my_fs_vtable, closure, 0);
 * 
 * After those calls, a call to a `vu_' file-system using descriptor
 * `fd' will do its work by calling a `my_virtual_' function (e.g.
 * `vu_read' will call `my_virtual_read' to read from `fd').
 * 
 * A call to a `vu_' function with a path that matches the regexp
 * `preg' will also do its work by calling a `my_virtual_' function
 * (e.g. `vu_open' will call `my_virtual_open' to open a matching
 * file-name).  If a new file is successfully opened this way, VU
 * automatically calls `vu_set_fd_handler' to establish the same
 * vtable as the handler for the new descriptor.
 *
 * Name-space handlers are kept on a stack and searched from the
  * top of stack down.  Each handler has a name and an array of
 * strings which are its documentation.
 */

/****************************************************************
 *(h1 "VU Closures")
 *
 * When a set of virtual file system functions is installed by
 * `vu_set_fd_handler' or `vu_push_name_handler' the caller provides a
 * closure.  This closure is an opaque value to `vu' itself.  A copy
 * of the closure is passed as the final parameter to the caller's
 * virtual functions.  For example, to open a file that has matched a
 * regular expression passed to `vu_push_name_handler', `vu_open' uses
 * the sequence:
 *
 *     fd = handler->vtable->open (errn, path, flags, mode, 
 *				   handler->closure);
 *	
 * VU doesn't save a copy of the closure directly.  Instead, it calls
 * the `make_closure' function from the vtable to create the value
 * to save and the `free_closure' function when that copy is being
 * discarded.  `make_closure' is called once when
 * `vu_push_name_handler' is called, and once each time
 * `vu_set_fd_handler' is called.   `free_closure' is called each time
 * `vu_close' or `vu_closedir' is called.
 */

/*(c vu_handler :category type)
 * struct vu_handler;
 *
 * For each VU namespace handler and file-descriptor handler, there
 * is a `struct vu_handler' that indicates the `vtable' and `closure'
 * to use for that portion of the file-system.
 *
 *    struct vu_handler
 *    {
 *      struct vu_fs_discipline * vtable;
 *      void * closure;
 *    };
 *
 */

/****************************************************************
 *(h1 "Pseudo-Descriptors")
 *
 * The `vu_' functions can operate on file descriptors which are
 * created by the program itself without the knowledge of the
 * operating system kernel.  The advantage of such descriptors is that
 * they take up no kernel resources so it is practical to create a
 * large number of them.
 *
 * Pseudo-descriptors which are guaranteed to be distinct from all
 * kernel descriptors can be created using the function
 * `reserv_pseudo' and destroyed using the function `unreserv_pseudo'.
 * For example:
 *
 *	fd = reserv_pseudo ();
 *	vu_set_fd_handler (fd, &my_fs_vtable, 0);
 *
 * and:
 *
 *	int
 *	my_virtual_close (int * errn, int fd, void * closure)
 *	{
 * 	  unreserv_pseudo (fd);
 *	  return 0;
 *	}
 *
 * One way to use a pseudo-descriptor is to combine it with
 * buffered-I/O to create a file that corresponds to a string in the
 * program's address space.  (See xref:"Buffered File Descriptors".)
 */



/* static struct _vu_namespace_handler * _vu_fs_handlers;
 * 
 * A stack (using `ar_push' and `ar_pop') of VU namespace handlers.
 */
union _t_vu_fs_handlers _vu_fs_handlers = {0};


/* static struct _vu_namespace_handler * _vu_optional_fs_handlers;
 * 
 * A stack (using `ar_push' and `ar_pop') of optional VU namespace 
 * handlers.
 */
union _t_vu_fs_handlers _vu_optional_fs_handlers = {0};


/* static struct vu_handler * fd_handlers;
 * 
 * An array (using `ar_ref') of VU descriptor handlers, indexed by
 * descriptor numbers.
 */
static union {
    struct vu_handler * hp;
    void *void_ptr;
} fd_handlers = {0};


/* static DIR * known_dirs;
 *
 * An array (using `ar_ref') of open directories known to VU.
 * Indexed by descriptor numbers.
 */
static union {
    DIR ** DIR_ptr_ptr;
    void * void_ptr;
} known_dirs = {0};


/* static struct vu_handler bad_arg_handler;
 * 
 * The vu_handler for non-descriptors (descriptor numbers less than 0).
 */
static struct vu_handler bad_arg_handler = { &_vu_bad_arg_functions, 0 };


/* static struct vu_handler default_handler;
 * 
 * The vu_handler for file names and descriptors with no other handler.
 */
static struct vu_handler default_handler = { &_vu_system_fs_vtable, 0 };




/************************************************************************
 *(h1 "Establishing VU Handlers")
 * 
 */

/* Dynamically setting rules for dispatching on file names and
 * descriptor numbers:
 */

/*(c vu_push_name_handler)
 * void vu_push_name_handler (const t_uchar * name,
 *			      const t_uchar ** doc,
 *			      regex_t * preg,
 *			      int eflags,
 *			      struct vu_fs_discipline * vtable,
 *			      void * closure,
 *			      int is_optional);
 * 
 * `vu_push_name_handler' establishs a vtable of virtual file system
 * functions for a portion of the file-system namespace.
 *
 * `name' is the name for the handler recognized by
 * `vu_enable_optional_name_handler'.  Conventionally,
 * this name may be used as an option argument to the 
 * command line options `-N' or `--namespace'.  For optimal 
 * help message formatting, `name' should be no longer than
 * 30 characters.  A pointer to `name' is kept by this function.
 *
 * `doc' is a documentation string for the handler, printed in the
 * outpupt of `vu_help_for_optional_handlers'.  For optimal help
 * message formatting, each line of `doc' should be no longer than 40
 * characters.  The last element of the array `doc' must be 0.
 * A pointer to `doc' is kept by this function.
 *
 * File-names matching `preg' are handled by the functions in
 * `vtable'.  Matching is performed by `regexec':
 *
 *	regexec (preg, path, 0, 0, eflags)
 *
 * If a matching file is successfully opened, `vu_open' and
 * `vu_opendir' call:
 *
 *	vu_set_fd_handler (new_fd, vtable, closure)
 *
 * If `is_optional' is not 0, the file-name handler is recorded
 * but not enabled.  It can be made active by a call to 
 * `vu_enable_optional_name_handler'.
 */
void
vu_push_name_handler (const t_uchar * name,
		      const t_uchar ** doc,
		      regex_t * preg,
		      int eflags,
		      struct vu_fs_discipline * vtable,
		      void * closure,
		      int is_optional)
{
  struct _vu_namespace_handler * name_handler;

  if (is_optional)
    name_handler = (struct _vu_namespace_handler *)ar_push ((void **)&_vu_optional_fs_handlers, lim_use_must_malloc, sizeof (*name_handler));
  else
    name_handler = (struct _vu_namespace_handler *)ar_push ((void **)&_vu_fs_handlers, lim_use_must_malloc, sizeof (*name_handler));
  name_handler->preg = preg;
  name_handler->eflags = eflags;
  name_handler->handler.vtable = vtable;
  name_handler->handler.closure = vtable->make_closure (closure);
  name_handler->name = name;
  name_handler->doc = doc;
}


/*(c vu_enable_optional_name_handler)
 * int vu_enable_optional_name_handler (const t_uchar * name);
 *
 * Push the named namespace handler on the VU namespace stack.
 *
 * The named handler must have previously been established by a 
 * call to `vu_push_optional_name_handler'.
 *
 * Return 0 on success, -1 if the named handler was not found.
 */
int
vu_enable_optional_name_handler (const t_uchar * name)
{
  int x;
  int len;

  len = ar_size (_vu_optional_fs_handlers.void_ptr, lim_use_must_malloc, sizeof (*_vu_optional_fs_handlers.nhp));
  for (x = 0; x < len; ++x)
    {
      if (!str_cmp (name, _vu_optional_fs_handlers.nhp[x].name))
	{
	  vu_push_name_handler (_vu_optional_fs_handlers.nhp[x].name,
				_vu_optional_fs_handlers.nhp[x].doc,
				_vu_optional_fs_handlers.nhp[x].preg,
				_vu_optional_fs_handlers.nhp[x].eflags,
				_vu_optional_fs_handlers.nhp[x].handler.vtable,
				_vu_optional_fs_handlers.nhp[x].handler.closure,
				0);
	  return 0;
	}
    }
  return -1;
}


static void
close_vu_files ()
{
  int n_dirs;
  int n_handlers;
  int x;

  n_dirs = ar_size (known_dirs.void_ptr, lim_use_must_malloc, sizeof (*known_dirs.DIR_ptr_ptr));
  for (x = 0; x < n_dirs; ++x)
    {
      int errn;
      if (known_dirs.DIR_ptr_ptr[x])
	vu_closedir (&errn, known_dirs.DIR_ptr_ptr[x]);
    }
  n_handlers = ar_size (fd_handlers.void_ptr, lim_use_must_malloc, sizeof (*fd_handlers.hp));
  for (x = 0; x < n_handlers; ++x)
    {
      if (fd_handlers.hp[x].vtable)
	{
	  int errn;
	  vu_close (&errn, x);
	}
    }
}


/*(c vu_set_fd_handler)
 * void vu_set_fd_handler (int fd,
 *			   struct vu_fs_discipline * vtable,
 *			   void * closure);
 * 
 * Establish a vtable of virtual file system functions for a
 * particular descriptor or pseudo-descriptor.
 *
 * The handler is automatically removed by `vu_close'.
 */
void
vu_set_fd_handler (int fd,
		   struct vu_fs_discipline * vtable,
		   void * closure)
{
  static int initialized = 0;
  struct vu_handler * handler;

  if (!initialized)
    {
      if (0 > atexit (close_vu_files))
	panic ("error registering atexit function in vu_set_fd_handler");
      initialized = 1;
    }

  handler = (struct vu_handler *)ar_ref (&fd_handlers.void_ptr, lim_use_must_malloc, fd, sizeof (*handler));
  handler->vtable = vtable;
  handler->closure = vtable->make_closure (closure);
}


void
vu_reinit_after_unexec (void)
{
  if (0 > atexit (close_vu_files))
    panic ("error registering atexit function in vu_reinit_after_unexec");
}

/*(c vu_move_state)
 * int vu_move_state (int * errn, int fd, int newfd);
 * 
 * Move the VU handler for `fd' to `newfd'.
 *
 */
int
vu_move_state (int * errn, int fd, int newfd)
{
  struct vu_handler * handler;
  int rv;

  handler = vu_fd_dispatch (fd);
  rv = handler->vtable->move_state (errn, fd, newfd, handler->closure);
  return rv;
}





/************************************************************************
 *(h1 "Looking Up VU Handlers")
 * 
 * 
 * 
 */

/*(c vu_path_dispatch)
 * struct vu_handler * vu_path_dispatch (char * path);
 * 
 * Return the vtable and closure that handle the file-name `path'.
 * (See xref:"vu_handler".)
 */
struct vu_handler *
vu_path_dispatch (const char * path)
{
  int x;
  for (x = ar_size (_vu_fs_handlers.void_ptr, lim_use_must_malloc, sizeof (*_vu_fs_handlers.nhp)) - 1; x >= 0; --x)
    {
      if (!regexec (_vu_fs_handlers.nhp[x].preg, path, 0, 0, _vu_fs_handlers.nhp[x].eflags))
	return &_vu_fs_handlers.nhp[x].handler;
    }
  return &default_handler;
}


/*(c vu_fd_dispatch)
 * struct vu_handler * vu_fd_dispatch (int fd);
 * 
 * Return the vtable and closure that handle the descriptor `fd'.
 * (See xref:"vu_handler".)
 */
struct vu_handler *
vu_fd_dispatch (int fd)
{
  if (fd < 0)
    return &bad_arg_handler;

  if ((fd >= ar_size (fd_handlers.void_ptr, lim_use_must_malloc, sizeof (*fd_handlers.hp))) || !fd_handlers.hp[fd].vtable)
    return &default_handler;

  return &fd_handlers.hp[fd];
}


/*(c vu_dir_dispatch)
 * struct vu_handler * vu_dir_dispatch (DIR * dir);
 * 
 * Return the vtable and closure that handle the directory `dir'.
 * (See xref:"vu_handler".)
 */
struct vu_handler *
vu_dir_dispatch (DIR * dir)
{
  return vu_fd_dispatch (vu_dir_fd (dir));
}



/************************************************************************
 *(h1 "Stacking Descriptor Handlers")
 *
 * The function `vu_fd_dispatch' returns a pointer to a `struct
 * vu_handler' which in turn holds a pointer to the vtable and closure
 * used to handle `vu_' functions for a particular descriptor (see
 * xref:"vu_handler").
 *
 *
 * Using `vu_fd_dispatch', descriptor handlers can be stacked.  For
 * example, the buffered-I/O functions (`vfdbuf_') work by imposing a
 * file-system vtable that maintains a buffer but that performs actual
 * I/O by calling functions from an underlying vtable.  To establish
 * the buffering vtable, the function `vfdbuf_buffer_fd' uses a
 * sequence of operations like:
 *
 *	int
 *	vfdbuf_buffer_fd (int * errn, int fd,
 *			  long bufsize, int flags, int zero_buffer)
 * 	{
 *	  struct vu_handler * sub_handler;
 * 
 * 	  ...
 *	  sub_handler = vu_fd_dispatch (fd);
 * 
 *	  ... remember that sub_handler does I/O for fd:
 *	  bufs[fd].sub_handler = sub_handler;
 *	  ...
 * 
 * 	  ... Establish the buffering functions as the new vtable for
 * 	      `fd'.
 * 
 *	  vu_set_fd_handler (fd, &vfdbuf_vtable, 0);
 *	}
 *
 * The `vfdbuf_' file-system functions follow this example:
 *
 * 	int
 * 	vfdbuf_fsync (int * errn, int fd, void * closure)
 * 	{
 * 	  ... Empty the buffer.
 *	
 * 	  if (vfdbuf_flush (errn, fd) < 0)
 * 	    return -1;
 * 	  
 * 	  ... Perform the actual `fsync' using the vtable set aside
 *	      in vfdbuf_buffer_fd:
 * 
 * 	  return bufs[fd].sub_handler.vtable->fsync 
 *			(errn, fd, bufs[fd].sub_handler.closure);
 * 	}
 *
 * Note that when closing a file, it is the responsibility of the
 * `vfdbuf_' functions to free the closure for the underlying vtable:
 *
 * 	int
 * 	vfdbuf_close (int * errn, int fd, void * closure)
 * 	{
 * 	  int errn
 * 	  int got;
 * 	  int ign;
 * 	
 *	  ...
 * 	  got = bufs[fd].sub_handler.vtable->close 
 *			(errn, fd, bufs[fd].sub_handler.closure);
 *
 * 	  bufs[fd].sub_handler.vtable->free_closure
 * 		(bufs[fd].sub_handler.closure);
 *	  ...
 * 	}
 *
 */

/************************************************************************
 *(h1 "The VU File-system Interface")
 * 
 * These functions approximately mirror the traditional unix system call
 * interface, but have these improvments:
 *
 * 	1. Error numbers are not stored in a global, but
 *	   in a return value.
 * 
 *	2. Functions dispatch on file names and descriptors,
 *	   permitting these functions to work on objects other than
 *	   ordinary files and sockets and to work on ordinary files
 *	   and sockets in unusual ways.
 */

/*(c vu_access)
 * int vu_access (int * errn, const char * path, int mode);
 * 
 * See the manual page for `access'.
 */
int
vu_access (int * errn, const char * path, int mode)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->access(errn, path, mode, handler->closure);
}


/*(c vu_chdir)
 * int vu_chdir (int * errn, const char * path);
 * 
 * See the manual page for `chdir'.
 */
int
vu_chdir (int * errn, const char * path)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->chdir(errn, path, handler->closure);
}


/*(c vu_chmod)
 * int vu_chmod (int * errn, const char * path, int mode);
 * 
 * See the manual page for `chmod'.
 */
int
vu_chmod (int * errn, const char * path, int mode)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->chmod(errn, path, mode, handler->closure);
}


/*(c vu_chown)
 * int vu_chown (int * errn, const char * path, int owner, int group);
 * 
 * See the manual page for `chown'.
 */
int
vu_chown (int * errn, const char * path, int owner, int group)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->chown(errn, path, owner, group, handler->closure);
}


/*(c vu_chroot)
 * int vu_chroot (int * errn, const char * path);
 * 
 * See the manual page for `chroot'.
 */
int
vu_chroot (int * errn, const char * path)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->chroot(errn, path, handler->closure);
}


/*(c vu_close)
 * int vu_close (int * errn, int fd);
 * 
 * See the manual page for `close'.
 */
int
vu_close (int * errn, int fd)
{
  struct vu_handler * handler;
  int status;

  handler = vu_fd_dispatch (fd);
  status = handler->vtable->close(errn, fd, handler->closure);
  if (status >= 0)
    {
      if (handler != &default_handler)
	{
	  if (handler->vtable->free_closure)
	    handler->vtable->free_closure (handler->closure);
	  handler->vtable = 0;
	  handler->closure = 0;
	}
    }
  return status;
}

/*(c vu_closedir)
 * int vu_closedir (int * errn, DIR * dir);
 * 
 * See the manual page for `closedir'.
 */
int
vu_closedir (int * errn, DIR * dir)
{
  struct vu_handler * handler;
  int fd;
  int status;

  handler = vu_dir_dispatch (dir);
  fd = vu_dir_fd (dir);
  status = handler->vtable->closedir(errn, dir, handler->closure);
  if (status >= 0)
    {
      DIR ** dirp;

      dirp = (DIR **)ar_ref (&known_dirs.void_ptr, lim_use_must_malloc, fd, sizeof (DIR *));
      *dirp = 0;
      if (handler != &default_handler)
	{
	  if (handler->vtable->free_closure)
	    handler->vtable->free_closure (handler->closure);
	  handler->vtable = 0;
	  handler->closure = 0;
	}
      unreserv_pseudo (fd);
    }
  return status;
}


/*(c vu_fchdir)
 * int vu_fchdir (int * errn, int fd);
 * 
 * See the manual page for `fchdir'.
 */
int
vu_fchdir (int * errn, int fd)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->fchdir(errn, fd, handler->closure);
}


/*(c vu_fchmod)
 * int vu_fchmod (int * errn, int fd, int mode);
 * 
 * See the manual page for `fchmod'.
 */
int
vu_fchmod (int * errn, int fd, int mode)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->fchmod (errn, fd, mode, handler->closure);
}


/*(c vu_fchown)
 * int vu_fchown (int * errn, int fd, int owner, int group);
 * 
 * See the manual page for `fchown'.
 */
int
vu_fchown (int * errn, int fd, int owner, int group)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->fchown (errn, fd, owner, group, handler->closure);
}


/*(c vu_fstat)
 * int vu_fstat (int * errn, int fd, struct stat * buf);
 * 
 * See the manual page for `fstat'.
 */
int
vu_fstat (int * errn, int fd, struct stat * buf)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->fstat(errn, fd, buf, handler->closure);
}


/*(c vu_fsync)
 * int vu_fsync (int * errn, int fd);
 * 
 * See the manual page for `fsync'.
 */
int
vu_fsync (int * errn, int fd)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->fsync(errn, fd, handler->closure);
}


/*(c vu_ftruncate)
 * int vu_ftruncate (int * errn, int fd, off_t where);
 * 
 * See the manual page for `ftruncate'.
 */
int
vu_ftruncate (int * errn, int fd, off_t where)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->ftruncate(errn, fd, where, handler->closure);
}


/*(c vu_link)
 * int vu_link (int * errn, const char * from, const char * to);
 * 
 * See the manual page for `link'.
 */
int
vu_link (int * errn, const char * from, const char * to)
{
  struct vu_handler * handler;
  struct vu_handler * handler2;

  handler = vu_path_dispatch (from);
  handler2 = vu_path_dispatch (to);
  if (   (handler->vtable->link != handler2->vtable->link)
      || (handler->closure != handler2->closure))
    {
      if (errn) *errn = EXDEV;
      return -1;
    }
  return handler->vtable->link(errn, from, to, handler->closure);
}


/*(c vu_lseek)
 * off_t vu_lseek (int * errn, int fd, off_t offset, int whence);
 * 
 * See the manual page for `lseek'.
 */
off_t
vu_lseek (int * errn, int fd, off_t offset, int whence)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->lseek(errn, fd, offset, whence, handler->closure);
}


/*(c vu_lstat)
 * int vu_lstat (int * errn, const char * path, struct stat * buf);
 * 
 * See the manual page for `lstat'.
 */
int
vu_lstat (int * errn, const char * path, struct stat * buf)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->lstat(errn, path, buf, handler->closure);
}


/*(c vu_mkdir)
 * int vu_mkdir (int * errn, const char * path, int mode);
 * 
 * See the manual page for `mkdir'.
 */
int
vu_mkdir (int * errn, const char * path, int mode)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->mkdir(errn, path, mode, handler->closure);
}


/*(c vu_open)
 * int vu_open (int * errn, const char * path, int flags, int mode);
 * 
 * See the manual page for `open'.
 */
int
vu_open (int * errn, const char * path, int flags, int mode)
{
  struct vu_handler * handler;
  int fd;

  handler = vu_path_dispatch (path);
  fd = handler->vtable->open(errn, path, flags, mode, handler->closure);
  if (fd >= 0)
    {
      if ((ar_size (fd_handlers.void_ptr, lim_use_must_malloc, sizeof (*fd_handlers.hp)) <= fd) || !fd_handlers.hp [fd].vtable)
	vu_set_fd_handler (fd, handler->vtable, handler->closure);
    }
    
  return fd;
}


/*(c vu_dir_fd)
 * int vu_dir_fd (DIR * dir);
 * 
 * Return the pseudo descriptor associated with DIR.
 */
int
vu_dir_fd (DIR * dir)
{
  int x;

  x = ar_size (known_dirs.void_ptr, lim_use_must_malloc, sizeof (*known_dirs.DIR_ptr_ptr)) - 1;

  while (x >= 0)
    {
      if (known_dirs.DIR_ptr_ptr[x] == dir)
	return x;
      --x;
    }

  panic ("attempt to find pseudo fd for unknown dir");
  return -1;
}


/*(c vu_opendir)
 * int vu_opendir (int * errn, DIR ** retv,  const char * path);
 * 
 * See the manual page for `opendir'.
 */
int
vu_opendir (int * errn, DIR ** retv,  const char * path)
{
  struct vu_handler * handler;
  int fd;
  int status;

  handler = vu_path_dispatch (path);
  status = handler->vtable->opendir(errn, retv, path, handler->closure);
  if (status >= 0)
    {
      fd = reserv_pseudo (errn, 0);
      if (fd < 0)
	panic ("unable to reserve pseudo fd in vu_opendir");
      {
	DIR ** dirp;
	  
	dirp = (DIR **)ar_ref (&known_dirs.void_ptr, lim_use_must_malloc, fd, sizeof (DIR *));
	*dirp = *retv;
	vu_set_fd_handler (fd, handler->vtable, handler->closure);
      }
    }
  return status;
}


/*(c vu_read)
 * ssize_t vu_read (int * errn, int fd, char * buf, size_t count);
 * 
 * See the manual page for `read'.
 */
ssize_t
vu_read (int * errn, int fd, char * buf, size_t count)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->read (errn, fd, buf, count, handler->closure);
}


/*(c vu_read_retry)
 * ssize_t vu_read_retry (int * errn, int fd, char * buf, size_t count);
 * 
 * Use `vu_read' to read from `fd'.  Read repeatedly (even if a read
 * returns early from EINTR or EAGAIN) until `count' characters are
 * read, or the end-of-file is reached.
 *
 * Return the number of characters read or -1 on error.
 */
ssize_t
vu_read_retry (int * errn, int fd, char * buf, size_t count)
{
  ssize_t amt_read;

  amt_read = 0;
  while (1)
    {
      ssize_t amt;
      amt = vu_read (errn, fd, buf, count);
      if (amt < 0)
        {
          if ((*errn == EAGAIN) || (*errn == EINTR))
            continue;
          else
            return amt;
        }
      
      amt_read += amt;
      if (!amt || (amt == count))
	return amt_read;
      count -= amt;
      buf += amt;
    }
}


/*(c vu_readdir)
 * int vu_readdir (int * errn,
 *		   struct alloc_limits * limits,
 *		   char ** file_ret,
 *		   DIR * dir);
 * 
 * See the manual page for `readdir'.
 * 
 * Note that in `vu', the file name is returned in `*file_ret'
 * and is dynamically allocated using `limits'.  It is up to the
 * caller to free the file name.
 */
int
vu_readdir (int * errn,
	    struct alloc_limits * limits,
	    char ** file_ret,
	    DIR * dir)
{
  struct vu_handler * handler;

  handler = vu_dir_dispatch (dir);
  return handler->vtable->readdir(errn, limits, file_ret, dir, handler->closure);
}


/*(c vu_readlink)
 * int vu_readlink (int * errn, const char * path, char * buf, int bufsize);
 * 
 * See the manual page for `readlink'.
 */
int
vu_readlink (int * errn, const char * path, char * buf, int bufsize)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->readlink(errn, path, buf, bufsize, handler->closure);
}


/*(c vu_rename)
 * int vu_rename (int * errn, const char * from, const char * to);
 * 
 * See the manual page for `rename'.
 */
int
vu_rename (int * errn, const char * from, const char * to)
{
  struct vu_handler * handler;
  struct vu_handler * handler2;

  handler = vu_path_dispatch (from);
  handler2 = vu_path_dispatch (to);
  if (   (handler->vtable->rename != handler2->vtable->rename)
      || (handler->closure != handler2->closure))
    {
      if (errn) *errn = EXDEV;
      return -1;
    }
  return handler->vtable->rename(errn, from, to, handler->closure);
}


/*(c vu_rmdir)
 * int vu_rmdir (int * errn, const char * path);
 * 
 * See the manual page for `rmdir'.
 */
int
vu_rmdir (int * errn, const char * path)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->rmdir(errn, path, handler->closure);
}


/*(c vu_stat)
 * int vu_stat (int * errn, const char * path, struct stat * buf);
 * 
 * See the manual page for `stat'.
 */
int
vu_stat (int * errn, const char * path, struct stat * buf)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->stat(errn, path, buf, handler->closure);
}


/*(c vu_symlink)
 * int vu_symlink (int * errn, const char * from, const char * to);
 * 
 * See the manual page for `symlink'.
 */
int
vu_symlink (int * errn, const char * from, const char * to)
{
  struct vu_handler * handler;
  struct vu_handler * handler2;

  handler = vu_path_dispatch (from);
  handler2 = vu_path_dispatch (to);
  if (   (handler->vtable->symlink != handler2->vtable->symlink)
      || (handler->closure != handler2->closure))
    {
      if (errn) *errn = EXDEV;
      return -1;
    }
  return handler->vtable->symlink(errn, from, to, handler->closure);
}



/*(c vu_truncate)
 * int vu_truncate (int * errn, const char * path, off_t where);
 * 
 * See the manual page for `truncate'.
 */
int
vu_truncate (int * errn, const char * path, off_t where)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->truncate(errn, path, where, handler->closure);
}


/*(c vu_unlink)
 * int vu_unlink (int * errn, const char * path);
 * 
 * See the manual page for `unlink'.
 */
int
vu_unlink (int * errn, const char * path)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->unlink(errn, path, handler->closure);
}


/*(c vu_utime)
 * int vu_utime (int * errn, const char * path, struct utimbuf *times);
 * 
 * See the manual page for `utime'.
 */
int
vu_utime (int * errn, const char * path, struct utimbuf * times)
{
  struct vu_handler * handler;

  handler = vu_path_dispatch (path);
  return handler->vtable->utime (errn, path, times, handler->closure);
}


/*(c vu_write)
 * ssize_t vu_write (int * errn, int fd, const char * buf, size_t count);
 * 
 * See the manual page for `write'.
 */
ssize_t
vu_write (int * errn, int fd, const char * buf, size_t count)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->write(errn, fd, buf, count, handler->closure);
}


/*(c vu_write_retry)
 * ssize_t vu_write_retry (int * errn, int fd, const char * buf, size_t count);
 * 
 * Use `vu_write' to write to `fd'.  Write repeatedly (even if a write
 * returns early from EINTR or EAGAIN) until `count' characters are
 * written.
 * 
 * Return the number of characters written or -1 on error.
 */
ssize_t
vu_write_retry (int * errn, int fd, const char * buf, size_t count)
{
  size_t orig_count;

  orig_count = count;
  while (1)
    {
      ssize_t amt;
      
      amt = vu_write (errn, fd, buf, count);
      if (amt < 0)
        {
          if ((*errn == EAGAIN) || (*errn == EINTR))
            continue;
          else
            return amt;
        }
      else if (amt == count)
	return orig_count;
      else
        {
          count -= amt;
          buf += amt;
        }
    }
}


/*(c vu_fcntl)
 * int vu_fcntl (int * errn, int fd, int cmd, long arg);
 * 
 * See the manual page for `fcntl'.
 */
int
vu_fcntl (int * errn, int fd, int cmd, long arg)
{
  struct vu_handler * handler;

  handler = vu_fd_dispatch (fd);
  return handler->vtable->fcntl(errn, fd, cmd, arg, handler->closure);
}


/*(c vu_dup)
 * int vu_dup (int * errn, int fd);
 * 
 * See the manual page for `dup'.
 */
int
vu_dup (int * errn, int fd)
{
  struct vu_handler * handler;
  int new_fd;

  handler = vu_fd_dispatch (fd);
  new_fd = handler->vtable->dup(errn, fd, handler->closure);
  
  return new_fd;
}


/*(c vu_dup2)
 * int vu_dup2 (int * errn, int fd, int newfd);
 * 
 * See the manual page for `dup2'.
 */
int
vu_dup2 (int * errn, int fd, int newfd)
{
  struct vu_handler * handler;
  int new_fd;

  handler = vu_fd_dispatch (fd);
  new_fd = handler->vtable->dup2(errn, fd, newfd, handler->closure);
  return new_fd;
}

