/* UFS server routines for io.defs interface
   Copyright (C) 1991, 1992 Free Software Foundation

This file is part of the GNU Hurd.

The GNU Hurd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

The GNU Hurd is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with the GNU Hurd; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Written by Michael I. Bushnell.  */

#include "ufs.h"
#include "inode.h"
#include "dinode.h"
#include "fs.h"
#include "iodefs.h"
#include <mach/default_pager_object.h>
#include <hurd/shared.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>

static error_t io_rdwr (struct inode *, char *, int, int, int);
static error_t fetch_inode_fields (struct protid *);
static error_t set_inode_fields (struct protid *);
static error_t internal_get_it (struct inode *);

/* Write data to disk */
error_t
io_write(struct protid *cred,
	 char *data,
	 unsigned int datalen,
	 int offset, 
	 int *amt)
{
  struct inode *ip;
  error_t err;
  volatile int off = offset;

  if (!cred)
    return EOPNOTSUPP;
  
  ip = cred->po->ip;
  if (!(cred->po->openstat & FS_LOOKUP_WRITE))
    return EBADF;
  if (*amt < 0)
    return EINVAL;

  mutex_lock (&ip->i_toplock);

  if (!(err = catch_exception ()))
    {
      if ((DI_MODE (ip->di) & IFMT) == IFDIR)
	panic ("User write of directory");

      if (err = fs_get_it (ip))
	{
	  end_catch_exception ();
	  goto out;
	}
  
      if (off == -1)
	{
	  if (cred->po->state & PO_APPEND)
	    cred->po->filepointer = ip->di->di_size;
	  off = cred->po->filepointer;
	}
  
      while (off + datalen > ip->i_allocsize)
	if (err = file_extend (ip, off, off + datalen, cred))
	  {
	    end_catch_exception ();
	    goto out;
	  }

      if (off + datalen > ip->di->di_size)
	ip->di->di_size = off + datalen;
      end_catch_exception ();
    }

  if (!err)
    {
      *amt = datalen;
      err = io_rdwr (ip, data, off, datalen, 1);
  
      if (offset == -1)
	cred->po->filepointer += *amt;
    }

 out:
  mutex_unlock (&ip->i_toplock);
  return err;
}

error_t
io_read (struct protid *cred,
	 char **data,
	 unsigned int *datalen,
	 int offset,
	 int maxread)
{
  struct inode *ip;
  int err;
  volatile int off = offset;
  char *buf;
  int ourbuf = 0;

  if (!cred)
    return EOPNOTSUPP;
  
  ip = cred->po->ip;
  if (!(cred->po->openstat & FS_LOOKUP_READ))
    return EBADF;
  if (maxread < 0)
    return EINVAL;
  
  mutex_lock (&ip->i_toplock);

  if (err = fs_get_it (ip))
    {
      mutex_unlock (&ip->i_toplock);
      return err;
    }
  
  if (off == -1)
    off = cred->po->filepointer;
  
  if (!(err = catch_exception ()))
    {
      if (off + maxread > ip->di->di_size)
	maxread = ip->di->di_size - off;
      end_catch_exception ();
    }
  
  if (!err)
    {
      if (maxread > *datalen)
	{
	  ourbuf = 1;
	  vm_allocate (mach_task_self (), (u_int *) &buf, maxread, 1);
	  *data = buf;
	}
      else
	buf = *data;

      *datalen = maxread;
      if (maxread)
	err = io_rdwr (ip, buf, off, maxread, 0);
      else
	err = 0;
      if (offset == -1 && !err)
	cred->po->filepointer += *datalen;
      if (err && ourbuf)
	vm_deallocate (mach_task_self (), (u_int) buf, maxread);
    }
  
  mutex_unlock (&ip->i_toplock);
  return err;
}


error_t
io_rdwr (struct inode *ip,
	 char *data,
	 int offset, 
	 int amt, 
	 int dir)
{
  char *volatile window;
  int winoff;
  volatile int cc;
  memory_object_t memobj;
  int err;

  if (dir && readonly)
    panic ("io_rdwr: write to readonly system");

  if (!readonly)
    {
      if (!(err = catch_exception ()))
	{
	  if (dir)
	    ip->di->di_mtime = time->seconds;
	  else
	    ip->di->di_atime = time->seconds;
	  end_catch_exception ();
	}
      else
	return err;
    }
  else
    err = 0;
  
  memobj = get_filemap (ip);
  
  while (amt > 0)
    {
      winoff = trunc_page (offset);

      window = 0;
      /* We map in 8 pages at a time.  Where'd that come from?  Well, the
	 vax has a 1024 pagesize and with 8k blocks that seems like a
	 reasonable number. */
      vm_map (mach_task_self (), (u_int *)&window, 8 * __vm_page_size, 0, 1, 
	      memobj, winoff, 0, VM_PROT_READ|VM_PROT_WRITE, 
	      VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE);
      register_memory_fault_area (window, 8 * __vm_page_size);
      
      if ((offset - winoff) + amt > 8 * __vm_page_size)
	cc = 8 * __vm_page_size - (offset - winoff);
      else
	cc = amt;
      
      if (!(err = catch_exception ()))
	{
	  if (dir)
	    bcopy (data, window + (offset - winoff), cc);
	  else
	    bcopy (window + (offset - winoff), data, cc);
	  end_catch_exception ();
	}

      vm_deallocate (mach_task_self (), (u_int) window, 8 * __vm_page_size);
      unregister_memory_fault_area (window, 8 * __vm_page_size);
      if (err)
	break;
      amt -= cc;
      offset += cc;
    }
  assert (amt == 0 || err);

  mach_port_deallocate (mach_task_self (), memobj);
  return err;
}

/* Internal interface to io_rdwr -- inode must be locked.  */
error_t
fs_rdwr (struct inode *ip, 
	 char *data,
	 off_t off, 
	 int amt, 
	 int dir,
	 struct protid *cred)
{
  error_t err;
  
  if (err = fs_get_it (ip))
    return err;
  
  if (!(err = catch_exception ()))
    {
      if (dir)
	while (off + amt > ip->i_allocsize)
	  if (err = file_extend (ip, off, off + amt, cred))
	    return err;

      if (off + amt > ip->di->di_size)
	{
	  if (dir)
	    {
	      ip->di->di_size = off + amt;
	      ip->di->di_ctime = time->seconds;
	    }
	  else
	    panic ("internal read too long");
	}
      end_catch_exception ();
    }
  if (err)
    return err;
  
  err = io_rdwr (ip, data, off, amt, dir);

  return err;
}

error_t
io_readable (struct protid *cred,
	     int *amount)
{
  struct inode *ip;
  error_t err;

  if (!cred)
    return EOPNOTSUPP;
  
  if (!(cred->po->openstat & FS_LOOKUP_READ))
    return EINVAL;

  ip = cred->po->ip;
  
  mutex_lock (&ip->i_toplock);
  if (err = fs_get_it (ip))
    {
      mutex_unlock (&ip->i_toplock);
      return err;
    }
  
  if (!(err = catch_exception ()))
    {
      *amount = ip->di->di_size - cred->po->filepointer;
      end_catch_exception ();
    }
  return err;
}

error_t 
io_mod_nonblock (struct protid *cred,
		 int value)
{
  return EOPNOTSUPP;	/* XXX */
}

error_t
io_get_nonblock (struct protid *cred,
		 int *value)
{
  return EOPNOTSUPP;	/* XXX */
}

error_t
io_mod_append (struct protid *cred,
	       int value)
{
  struct inode *ip;
  error_t err;

  if (!cred)
    return EOPNOTSUPP;
  if ((cred->po->openstat & FS_LOOKUP_READ) == 0)
    return EINVAL;
  
  ip = cred->po->ip;
  
  mutex_lock (&ip->i_toplock);
  if (err = fs_get_it (ip))
    {
      mutex_unlock (&ip->i_toplock);
      return err;
    }
  if (value)
    cred->po->state |= PO_APPEND;
  else
    cred->po->state &= ~PO_APPEND;

  mutex_unlock (&ip->i_toplock);
  return err;
}

error_t
io_get_append (struct protid *cred,
	       int *value)
{
  if (!cred)
    return EOPNOTSUPP;
  if (!(cred->po->openstat & FS_LOOKUP_READ))
    return EINVAL;
  
  mutex_lock (&cred->po->ip->i_toplock);
  *value = (cred->po->state & PO_APPEND);
  mutex_unlock (&cred->po->ip->i_toplock);
  return 0;
}

error_t
io_async (struct protid *cred,
	  mach_port_t notify,
	  mach_port_t *idport)
{
  mach_port_deallocate (mach_task_self (), notify);
  *idport = MACH_PORT_NULL;
  return EOPNOTSUPP;
}

error_t
io_mod_owner (struct protid *cred,
	      pid_t owner)
{
  struct inode *ip;

  if (!cred)
    return EOPNOTSUPP;
  ip = cred->po->ip;
  
  mutex_lock (&ip->i_toplock);
  ip->i_owner = owner;
  mutex_unlock (&ip->i_toplock);
  return 0;
}

error_t
io_get_owner (struct protid *cred,
	      pid_t *owner)
{
  struct inode *ip;

  if (!cred)
    return EOPNOTSUPP;
  ip = cred->po->ip;
  
  mutex_lock (&ip->i_toplock);
  *owner = ip->i_owner;
  mutex_unlock (&ip->i_toplock);
  return 0;
}

error_t
io_mod_async_icky (struct protid *cred,
		   int value,
		   mach_port_t *idport)
{
  *idport = MACH_PORT_NULL;
  return EOPNOTSUPP;
}

error_t
io_get_icky_async_id (struct protid *cred,
		      mach_port_t *idport)
{
  *idport = MACH_PORT_NULL;
  return 0;
}

error_t
io_get_async_icky (struct protid *cred,
		   int *value)
{
  return EOPNOTSUPP;
}

error_t
io_get_openstat (struct protid *cred,
		 int *state)
{
  if (!cred)
    return EOPNOTSUPP;
  *state = cred->po->openstat;
  return 0;
}

error_t
io_select (struct protid *cred,
	   int type, 
	   mach_port_t port,
	   int tag,
	   int *possible)
{
  if (!cred)
    return EOPNOTSUPP;
  
  /* Select is always possible */
  mach_port_deallocate (mach_task_self (), port);
  *possible = type;
  return 0;
}


error_t
io_stat (struct protid *cred,
	 io_statbuf_t *statbuf)
{
  struct inode *ip;
  error_t error;

  if (!cred)
    return EOPNOTSUPP;
  
  ip = cred->po->ip;
  mutex_lock (&ip->i_toplock);
  if (error = fs_get_it (ip))
    goto out;
  if (error = do_stat (ip, statbuf))
    goto out;

 out:
  mutex_unlock (&ip->i_toplock);
  return error;
}

error_t
io_reauthenticate (struct protid *cred)
{
  printf ("reauthenticate\n");
  return 0;
}

error_t
io_interrupt (struct protid *cred)
{
  if (!cred)
    return EOPNOTSUPP;
  
  return 0;
}

error_t
io_server_version (struct protid *cred, 
		   char *server_name,
		   int *major_vers,
		   int *minor_vers,
		   int *edit_level)
{
  strcpy (server_name, "GNU UFS");
  *major_vers = *minor_vers = *edit_level = 0; /* XXX */
  return 0;
}
  
/* Return a memory object mapping the file */
error_t
io_map (struct protid *cred,
	memory_object_t *xxobj,
	memory_object_t *rdobj,
	memory_object_t *wrobj)
{
  if (!cred)
    return EOPNOTSUPP;
  
  /* XXX should get a RO pager if appropriate... */
  *xxobj = get_filemap (cred->po->ip);
  *rdobj = MACH_PORT_NULL;
  *wrobj = MACH_PORT_NULL;
  return 0;
}

/* Map a control page for the file */
error_t
io_map_cntl (struct protid *cred,
	     memory_object_t *ctlobj)
{
  if (!cred)
    return EOPNOTSUPP;
  
  assert (__vm_page_size >= sizeof (struct shared_io));
  mutex_lock (&cred->po->ip->i_toplock);
  if (!cred->mapped)
    {
      default_pager_object_create (default_pager, &cred->shared_object,
				   __vm_page_size);
      vm_map (mach_task_self (), (u_int *)&cred->mapped, __vm_page_size, 0, 1, 
	      cred->shared_object, 0, 0, VM_PROT_READ|VM_PROT_WRITE,
	      VM_PROT_READ|VM_PROT_WRITE, 0);
      cred->mapped->it_status = USER_NOT_IT;
      spin_lock_init (&cred->mapped->lock);
      *ctlobj = cred->shared_object;
      mutex_unlock (&cred->po->ip->i_toplock);
      return 0;
    }
  else
    {
      mutex_unlock (&cred->po->ip->i_toplock);
      return EBUSY;
    }
}

/* Called by clients to become IT */
error_t
io_get_it (struct protid *cred,
	   memory_object_t cntl)
{
  struct protid *currently_it;
  struct inode *ip;
  int error = 0;
  
  if (!cred)
    return EOPNOTSUPP;
  
  ip = cred->po->ip;
  
  /* Try and catch simple problems */
  mutex_lock (&ip->i_toplock);
  if (!cred->mapped || cntl != cred->shared_object)
    {
      mutex_unlock (&ip->i_toplock);
      return EINVAL;
    }

  mutex_lock (&ip->i_itlock);
  currently_it = ip->i_it;

  if (currently_it == cred)
    {
      if (cred->mapped->it_status != USER_NOT_IT)
	{
	  if (error = fetch_inode_fields (cred))
	    goto out;
	}
      else
	cred->mapped->accessed = cred->mapped->written = 0;

      if (error = set_inode_fields (cred))
	goto out;
      cred->mapped->it_status = USER_IT;
      goto out;
    }
  
  if (error = internal_get_it(ip))
    goto out;

  ip->i_it = cred;
  if (cred->mapped->it_status == USER_NOT_IT)
    cred->mapped->accessed = cred->mapped->written = 0;
  cred->mapped->it_status = USER_IT;
  set_inode_fields (cred);

 out:
  mutex_unlock (&ip->i_itlock);
  mutex_unlock (&ip->i_toplock);
  return error;
}

/* Called by the filesystem to become it -- ip->i_toplock must be locked */
error_t
fs_get_it (struct inode *ip)
{
  error_t error;
  mutex_lock (&ip->i_itlock);
  error = internal_get_it (ip);
  mutex_unlock (&ip->i_itlock);
  return error;
}

static error_t
internal_get_it (struct inode *ip)
{
  struct protid *currently_it;
  int error = 0;

 again:
  currently_it = ip->i_it;

  if (currently_it)
    {
      assert (currently_it->mapped);
      spin_lock (&currently_it->mapped->lock);
      
      switch (currently_it->mapped->it_status)
	{
	case USER_IT:
	  currently_it->mapped->it_status = USER_RELEASE_IT;
	  /* fall through... */
	case USER_RELEASE_IT:
	  spin_unlock (&currently_it->mapped->lock);
	  mutex_unlock (&ip->i_itlock);
	  condition_wait (&ip->i_itwait, &ip->i_toplock);
	  /* anything can have happened */
	  goto again;
	  
	case USER_POTENTIALLY_IT:
	  currently_it->mapped->it_status = USER_NOT_IT;
	  spin_unlock (&currently_it->mapped->lock);
	  error = fetch_inode_fields (currently_it);
	  break;
	case USER_NOT_IT:
	  spin_unlock (&currently_it->mapped->lock);
	  break;
	}
    }
  return error;
}  
  


/* Called by clients to release it, update inode fields, and such */
error_t
io_release_it (struct protid *cred,
	       memory_object_t cntl)
{
  struct inode *ip;
  int error = 0;
  
  if (!cred)
    return EOPNOTSUPP;
  
  ip = cred->po->ip;
  mutex_lock (&ip->i_toplock);
  if (!cred->mapped || cntl != cred->shared_object)
    {
      mutex_unlock (&ip->i_toplock);
      return EINVAL;
    }
  
  ip = cred->po->ip;
  
  if (cred->mapped->it_status != USER_NOT_IT)
    {
      cred->mapped->it_status = USER_NOT_IT;
      error = fetch_inode_fields (cred);
    }

  mutex_lock (&ip->i_itlock);
  if (ip->i_it == cred)
    ip->i_it = 0;
  mutex_unlock (&ip->i_itlock);
  
  condition_broadcast (&ip->i_itwait);
  mutex_unlock (&ip->i_toplock);
  return error;
}

/* Update our copy of the relevant fields from a shared page.  Callers
   must have the share lock on the shared page as well as the inode
   toplock. */
static error_t 
fetch_inode_fields (struct protid *cred)
{
  error_t error;
  
  if (!(error = catch_exception ()))
    {
      if (cred->mapped->file_size > cred->po->ip->i_allocsize)
	cred->mapped->file_size = cred->po->ip->i_allocsize;
      
      if (cred->mapped->file_size < cred->po->ip->di->di_size)
	cred->mapped->file_size = cred->po->ip->di->di_size;
      else if (cred->po->ip->di->di_size != cred->mapped->file_size
	       && !readonly)
	{
	  cred->po->ip->di->di_size = cred->mapped->file_size;
	  cred->po->ip->di->di_ctime = time->seconds;
	}
      
      cred->po->filepointer = cred->mapped->xx_file_pointer;
      
      if (!readonly)
	{
	  if (cred->mapped->written)
	    cred->po->ip->di->di_mtime = time->seconds;
	  if (cred->mapped->accessed)
	    cred->po->ip->di->di_atime = time->seconds;
	}
      cred->mapped->written = 0;
      cred->mapped->accessed = 0;

      end_catch_exception ();
    }
  return error;
}

/* Write current values into the shared page.  Callers must have the
   share lock on the shared page, as well as the inode toplock. */
static error_t
set_inode_fields (struct protid *cred)
{
  error_t error;
  
  if (!(error = catch_exception ()))
    {
      cred->mapped->append_mode = (cred->po->state & PO_APPEND);
      cred->mapped->eof_notify = 0;
      cred->mapped->do_sigio = 0;
      cred->mapped->use_file_size = 1;
      cred->mapped->use_read_size = 0;
      cred->mapped->seekable = 1;
      cred->mapped->use_prenotify_size = 1;
      cred->mapped->use_postnotify_size = 1;
      cred->mapped->prenotify_size = cred->po->ip->i_allocsize;
      
      cred->mapped->xx_file_pointer = cred->po->filepointer;
      cred->mapped->rd_file_pointer = -1;
      cred->mapped->wr_file_pointer = -1;
      cred->mapped->file_size = cred->po->ip->di->di_size;
      cred->mapped->written = 0;
      cred->mapped->accessed = 0;
      
      end_catch_exception ();
    }
  return error;
}

/* Called by the file pager to determine the current virtual size of
   the file. */
off_t
get_inode_vsize (struct inode *ip)
{
  struct protid *it;
  off_t size;
  
  mutex_lock (&ip->i_itlock);
  if (catch_exception ())
    panic ("get_inode_vsize error on inode read");
  if (it = ip->i_it)
    {
      size = it->mapped->file_size;
      assert (ip->i_allocsize >= ip->di->di_size);
      if (size > ip->i_allocsize)
	size = ip->i_allocsize;
      if (it->mapped->file_size < ip->di->di_size)
	size = ip->di->di_size;
    }
  else
    size = ip->di->di_size;
  end_catch_exception ();
  mutex_unlock (&ip->i_itlock);
  return size;
}

/* Called by clients when they are exceeding the prenotify_size.  We
   have to allocate more space on disk for the last block of the file. */
error_t
io_prenotify (struct protid *cred,
	      memory_object_t cntl,
	      int start,
	      int end)
{
  struct inode *ip;
  int err = 0;
  
  if (!cred)
    return EOPNOTSUPP;
  
  ip = cred->po->ip;

  /* Clamp it down */
  mutex_lock (&ip->i_toplock);

  if (!cred->mapped)
    {
      err = EINVAL;
      goto out;
    }

  if (ip->i_it != cred || cntl != cred->shared_object)
    {
      err = EPERM;
      goto out;
    }
  
  spin_lock (&cred->mapped->lock);
  if (cred->mapped->it_status != USER_IT
      && cred->mapped->it_status != USER_RELEASE_IT)
    {
      err = EPERM;
      spin_unlock (&cred->mapped->lock);
      goto out;
    }
  spin_unlock (&cred->mapped->lock);
  err = fetch_inode_fields (cred);
  if (err)
    goto out;
  
  if (end < ip->i_allocsize)
    {
      /* The user is either foolin' with us, or has the wrong
	 prenotify size, hence the diagnostic. */
      printf ("io_prenotify: unnecessary call\n");
      spin_lock (&cred->mapped->lock);
      err = set_inode_fields (cred);
      spin_unlock (&cred->mapped->lock);
      goto out;
    }
  
  err =  file_extend (ip, start, end, cred);
 out:
  mutex_unlock (&ip->i_toplock);
  return err;
}

error_t
file_extend (struct inode *ip,
	     int start, 
	     int end,
	     struct protid *cred)
{
  daddr_t lbn, pbn, nb;
  int osize, nsize, size;
  int err;
  volatile daddr_t dealloc_on_error = 0;
  volatile int dealloc_size;
  
  size = end - ip->i_allocsize;
  mutex_lock (&ip->i_datalock);

  /* this deallocation works for the calls to alloc, but not for
     realloccg.  i'm not sure how to prune the fragment down, especially if
     we grew a fragment and then couldn't allocate the piece later.
     freeing it all up is a royal pain, largely punted right now... -mib.
     */
  if (err = catch_exception())
    {
      if (dealloc_on_error)
	blkfree (dealloc_on_error, dealloc_size);
      goto out;
    }

  /* if we are writing a new block, then an old one may need to be
     reallocated into a full block. */
  lbn = lblkno (end);
  nb = lblkno (ip->i_allocsize);
  if (nb < NDADDR && nb < lbn)
    {
      osize = blksize (ip, nb);
      if (osize < sblock->fs_bsize && osize > 0)
	{
	  err = realloccg (ip, nb,
			   blkpref (ip, nb, (int)nb, ip->di->di_db),
			   osize, sblock->fs_bsize, &pbn, cred);
	  if (err)
	    goto out;
	  ip->i_allocsize = (nb + 1) * sblock->fs_bsize;
	  ip->di->di_db[nb] = pbn;
	  /* xxx if this is a new location, we need to poke the data
	     to ensure that it gets written out. */
	}
    }
  
  /* allocate this block */
  if (lbn < NDADDR)
    {
      nb = ip->di->di_db[lbn];

      if (nb != 0)
	{
	  /* consider need to reallocate a fragment. */
	  osize = fragroundup (blkoff (ip->i_allocsize));
	  nsize = fragroundup (size);
	  if (nsize > osize)
	    {
	      err = realloccg (ip, lbn, 
			       blkpref (ip, lbn, lbn, ip->di->di_db),
			       osize, nsize, &pbn, cred);
	      if (err)
		goto out;
	      ip->di->di_db[lbn] = pbn;
	      /* xxx if this is a new location, we need to poke the data
		 to ensure that it gets written out. */
	    }
	  else
	    printf ("io_prenotify: unnecessary call\n");
	}
      else
	{
	  nsize = fragroundup (size);
	  err = alloc (ip, lbn,
		       blkpref (ip, lbn, lbn, ip->di->di_db), nsize, 
		       &pbn, cred);
	  if (err)
	    goto out;
	  dealloc_on_error = pbn;
	  dealloc_size = nsize;
	  ip->di->di_db[lbn] = pbn;
	}
      ip->i_allocsize = fragroundup (end);
    }
  else
    {
      sin_remap (ip, end);	/* grow the indirect block map */
      
      lbn -= NDADDR;
      if (!ip->i_sinloc[lbn])
	{
	  err = alloc (ip, lbn, blkpref (ip, lbn + NDADDR, lbn, ip->di->di_ib),
		       sblock->fs_bsize, &pbn, cred);
	  if (err)
	    goto out;
	  dealloc_on_error = pbn;
	  dealloc_size = sblock->fs_bsize;
	  ip->i_sinloc[lbn] = pbn;
	}
      else
	printf ("io_prenotify: unnecessary call\n");
      ip->i_allocsize = blkroundup (end);
    }

  spin_lock (&cred->mapped->lock);
  err = set_inode_fields (cred);
  spin_unlock (&cred->mapped->lock);
  
 out:
  end_catch_exception ();
  mutex_unlock (&ip->i_datalock);
  return err;
}  

error_t
io_postnotify (struct protid *cred,
	       memory_object_t cntl,
	       int start, 
	       int end)
{
  if (!cred)
    return EOPNOTSUPP;
  
  printf ("io_postnotify: unnecessary call\n");
  return 0;
}

error_t
io_readsleep (struct protid *cred,
	      memory_object_t cntl)
{
  if (!cred)
    return EOPNOTSUPP;
  
  return 0;
}

error_t
io_eofnotify (struct protid *cred,
	      memory_object_t cntl)
{
  if (!cred)
    return EOPNOTSUPP;
  
  return 0;
}

error_t
io_sigio (struct protid *cred,
	  memory_object_t cntl)
{
  if (!cred)
    return EOPNOTSUPP;
  
  return 0;
}

