/*
 * Copyright (c) 1995 - 2000 Kungliga Tekniska Högskolan
 * (Royal Institute of Technology, Stockholm, Sweden).
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Kungliga Tekniska
 *      Högskolan and its contributors.
 * 
 * 4. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * XFS operations.
 */

#include <xfs/xfs_locl.h>
#include <xfs/xfs_message.h>
#include <xfs/xfs_dev.h>
#include <xfs/xfs_common.h>
#include <xfs/xfs_fs.h>
#include <xfs/xfs_deb.h>
#include <xfs/xfs_syscalls.h>

RCSID("$Id: xfs_vnodeops.c,v 1.4.2.1 2000/10/18 07:05:54 lha Exp $");

static int
xfs_open_valid(struct vnode *vp, struct ucred *cred, u_int tok)
{
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);
  int error = 0;
  
  XFSDEB(XDEBVFOPS, ("xfs_open_valid\n"));

  do {
    if (!XFS_TOKEN_GOT(xn, tok))
      {
	struct xfs_message_open msg;

	msg.header.opcode = XFS_MSG_OPEN;
	msg.cred.uid = cred->cr_uid;
	msg.cred.pag = xfs_get_pag(cred);
	msg.handle = xn->handle;
	msg.tokens = tok;
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	  error = ((struct xfs_message_wakeup *) &msg)->error;
      }
    else
      {
	goto done;
      }
  } while (error == 0);

 done:
  return error;
}

static int
xfs_attr_valid(struct vnode *vp, struct ucred *cred, u_int tok)
{
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);
  int error = 0;
  
  xfs_pag_t pag = xfs_get_pag(cred);

  do {
    if (!XFS_TOKEN_GOT(xn, tok))
      {
	struct xfs_message_getattr msg;
	msg.header.opcode = XFS_MSG_GETATTR;
	msg.cred.uid = cred->cr_uid;
	msg.cred.pag = pag;
	msg.handle = xn->handle;
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	  error = ((struct xfs_message_wakeup *) &msg)->error;
      }
    else
      {
	goto done;
      }
  } while (error == 0);

 done:
  return error;
}

static int
xfs_fetch_rights(struct vnode *vp, struct ucred *cred)
{
    struct xfs *xfsp = XFS_FROM_VNODE(vp);
    struct xfs_node *xn = VNODE_TO_XNODE(vp);
    int error = 0;

    xfs_pag_t pag = xfs_get_pag(cred);

    struct xfs_message_getattr msg;

    msg.header.opcode = XFS_MSG_GETATTR;
    msg.cred.uid = cred->cr_uid;
    msg.cred.pag = pag;
    msg.handle = xn->handle;
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) & msg)->error;

    return (error);
}

static int
xfs_data_valid(struct vnode *vp, struct ucred *cred, u_int tok)
{
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);
  int error = 0;
  
  do {
    if (!XFS_TOKEN_GOT(xn, tok))
      {
	struct xfs_message_getdata msg;
	msg.header.opcode = XFS_MSG_GETDATA;
	msg.cred.uid = cred->cr_uid;
	msg.cred.pag = xfs_get_pag(cred);
	msg.handle = xn->handle;
	msg.tokens = tok;
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	  error = ((struct xfs_message_wakeup *) &msg)->error;
      }
    else
      {
	goto done;
      }
  } while (error == 0);

 done:
  return error;
}

static int
do_fsync(struct xfs *xfsp,
	 struct xfs_node *xn,
	 struct ucred *cred,
	 u_int flag)
{
    int error;
    struct xfs_message_putdata msg;

    msg.header.opcode = XFS_MSG_PUTDATA;
    msg.cred.uid = cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cred);
    msg.handle = xn->handle;

    msg.flag = flag;
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) & msg)->error;

    if (error == 0)
	xn->flags &= ~XFS_DATA_DIRTY;

    return error;
}

static int
xfs_open(struct vnode *vp,
	 int flag,
	 int ext,
	 caddr_t *vinfop,
	 struct ucred *cred)
{
  int error = 0;
  
  XFSDEB(XDEBVNOPS, ("xfs_open\n"));
  
  if (flag & _FWRITE)
    error = xfs_open_valid(vp, cred, XFS_OPEN_NW);
  else
    error = xfs_open_valid(vp, cred, XFS_OPEN_NR);
  
  return error;
}

static int
xfs_close(struct vnode *vp,
	  int flag,
	  caddr_t vinfo,
	  struct ucred *cred)
{
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);
  int error = 0;

  XFSDEB(XDEBVNOPS, ("xfs_close\n"));
  
  if (flag & _FWRITE && xn->flags & XFS_DATA_DIRTY)
    error = do_fsync (xfsp, xn, cred, XFS_WRITE);
  
  return error;
}

static int
xfs_map (struct vnode *vp,
	 caddr_t addr,
	 uint length,
	 uint offset,
	 uint flags,
	 struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_map\n"));
  return ENOSYS;
}

static int
xfs_unmap (struct vnode *vp,
	   int flag,
	   struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_unmap\n"));
  return ENOSYS;
}

static int
xfs_rdwr(struct vnode *vp,
	 enum uio_rw op,
	 int flags,
	 struct uio *uio,
	 int ext,
	 caddr_t vinfo,
	 struct vattr *vattr,
	 struct ucred *cred)
{
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_read\n"));

    if (op == UIO_WRITE)
	error = xfs_data_valid(vp, cred, XFS_DATA_W);
    else
	error = xfs_data_valid(vp, cred, XFS_DATA_R);

    if (error == 0) {
	struct vnode *t = DATA_FROM_VNODE(vp);
	ASSERT(t != NULL);
	VNOP_HOLD(t);
	error = VNOP_RDWR(t, op, flags, uio, ext, vinfo, NULL, cred);
	if (op == UIO_WRITE)
	    VNODE_TO_XNODE(vp)->flags |= XFS_DATA_DIRTY;
	VNOP_RELE(t);
	/* XXX - vattrp */
    }

    return error;
}

static int
xfs_ioctl(struct vnode *vp,
	  int cmd,
	  caddr_t arg,
	  size_t size,
	  int flag,
	  struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_ioctl\n"));
  return ENOSYS;
}

static int
xfs_getattr(struct vnode *vp,
	    struct vattr *vap,
	    struct ucred *cred)     
{
  int error = 0;
  
  struct xfs_node *xn = VNODE_TO_XNODE(vp);

  XFSDEB(XDEBVNOPS, ("xfs_getattr\n"));
  
  error = xfs_attr_valid(vp, cred, XFS_ATTR_R);
  if (error == 0) {
      *vap = xn->attr;
  }
  
  return error;
}

static int
xfs_setattr (struct vnode *vp,
	     int cmd,
	     int arg1,
	     int arg2,
	     int arg3,
	     struct ucred *cred)
{
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);
  int error = 0;
  
  XFSDEB(XDEBVNOPS, ("xfs_setattr\n"));
  if (XFS_TOKEN_GOT(xn, XFS_ATTR_W)) {
      /* Update attributes and mark them dirty. */
      VNODE_TO_XNODE(vp)->flags |= XFS_ATTR_DIRTY;
      error = ENOSYS;		/* XXX not yet implemented */
      goto done;
  } else {
      struct xfs_message_putattr msg;
      msg.header.opcode = XFS_MSG_PUTATTR;
      msg.cred.uid = cred->cr_uid;
      msg.cred.pag = xfs_get_pag(cred);
      msg.handle = xn->handle;

      XA_CLEAR(&msg.attr);

      switch (cmd) {
      case V_OWN:
	  XA_SET_UID(&msg.attr, arg2);
	  XA_SET_GID(&msg.attr, arg3);
	  break;
      case V_UTIME: {
	  struct timeval *atime = (struct timeval *)arg2;
	  struct timeval *mtime = (struct timeval *)arg3;

	  if (arg1 & T_SETTIME) {
	      ;			/* XXX */
	  } else {
	      XA_SET_ATIME(&msg.attr, atime->tv_sec);
	      XA_SET_MTIME(&msg.attr, mtime->tv_sec);
	  }
	  break;
      }
      case V_MODE:
	  XA_SET_MODE(&msg.attr, arg1);
	  break;
      default:
	  panic ("xfs_setattr: bad cmd");
      }

      error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
      if (error == 0)
	error = ((struct xfs_message_wakeup *) &msg)->error;
      XFS_TOKEN_CLEAR(xn, XFS_ATTR_VALID, XFS_ATTR_MASK);
  }

 done:
  return error;
}

static int
check_rights (u_char rights, int mode)
{
    int error = 0;

    if (mode & S_IREAD)
	if ((rights & XFS_RIGHT_R) == 0)
	    error = EACCES;
    if (mode & S_IWRITE)
	if ((rights & XFS_RIGHT_W) == 0)
	    error = EACCES;
    if (mode & S_IEXEC)
	if ((rights & XFS_RIGHT_X) == 0)
	    error = EACCES;
    return error;
}

static int
xfs_access(struct vnode *vp,
	   int mode,
	   int who,
	   struct ucred *cred)     
{
  int error = 0;
  xfs_pag_t pag = xfs_get_pag(cred);

  XFSDEB(XDEBVNOPS, ("xfs_access mode = 0%o\n", mode));

  error = xfs_attr_valid(vp, cred, XFS_ATTR_R);
  if (error == 0) {
      struct xfs_node *xn = VNODE_TO_XNODE(vp);
      int i;

      switch (who) {
      case ACC_SELF : {
	  error = check_rights (xn->anonrights, mode);
	
	  if (error == 0)
	      goto done;

	  XFSDEB(XDEBVNOPS, ("xfs_access anonaccess failed\n"));

	  if (error != 0)
	      xfs_fetch_rights(vp, cred); /* ignore error */
	
	  error = EACCES;
	
	  for (i = 0; i < MAXRIGHTS; i++)
	      if (xn->id[i] == pag) {
		  error = check_rights (xn->rights[i], mode);
		  break;
	      }
	  break;
      }
      case ACC_OTHERS :
	  error = 0;		/* XXX */
	  break;
      case ACC_ANY :
      case ACC_ALL :
	  error = check_rights (xn->anonrights, mode);
	  break;
      default :
	  panic ("xfs_access: bad who");
      }
  }

done:
    XFSDEB(XDEBVNOPS, ("xfs_access(0%o) = %d\n", mode, error));
    return error;

}

static int
xfs_lookup(struct vnode *dvp,
	   struct vnode **vpp,
	   char *nm,
	   int flag,
	   struct vattr *vattr,
	   struct ucred *cred)
{
  struct xfs_message_getnode msg;
  struct xfs *xfsp = XFS_FROM_VNODE(dvp);
  int error = 0;

  struct xfs_node *d = VNODE_TO_XNODE(dvp);
  struct vnode *v;
  
  XFSDEB(XDEBVNOPS, ("xfs_lookup\n"));
  
  do {
#ifdef notdef_but_correct
    error = xfs_access(dvp, VEXEC, cred);
    if (error != 0)
      goto done;
#endif
    v = xfs_dnlc_lookup(dvp, nm);
    if (!v)
      {
	msg.header.opcode = XFS_MSG_GETNODE;
	msg.cred.uid = cred->cr_uid;
	msg.cred.pag = xfs_get_pag(cred);
	msg.parent_handle = d->handle;
	if (strlcpy(msg.name, nm, sizeof(msg.name)) >= XFS_MAX_NAME)
	    return ENAMETOOLONG;
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	  error = ((struct xfs_message_wakeup *) &msg)->error;
      }
    else
      {
	*vpp = v;
	VNOP_HOLD(v);
	goto done;
      }
  } while (error == 0);

 done:
  XFSDEB(XDEBVNOPS, ("xfs_lookup() = %d\n", error));
  return error;
}

static int
xfs_create(struct vnode *dvp,
	   struct vnode **vpp,
	   int flag,
	   /*char **/ caddr_t nm,
	   int mode,
	   caddr_t *vinfop,
	   struct ucred *cred)
{
  struct xfs *xfsp = XFS_FROM_VNODE(dvp);
  struct xfs_node *xn = VNODE_TO_XNODE(dvp);
  int error = 0;
  
  XFSDEB(XDEBVNOPS, ("xfs_create\n"));
  {
    struct xfs_message_create msg;
    msg.header.opcode = XFS_MSG_CREATE;
    msg.parent_handle = xn->handle;
    if (strlcpy(msg.name, nm, sizeof(msg.name)) >= XFS_MAX_NAME)
	return ENAMETOOLONG;
#if 0
    vattr2xfs_attr (va, &msg.attr);
#endif
#if 0
    msg.exclusive = exclusive;
#endif
    msg.mode = mode;
    msg.cred.uid = cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cred);
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
      error = ((struct xfs_message_wakeup *) &msg)->error;
  }

  if (error == 0)
    error = xfs_lookup(dvp, vpp, nm, /*flag*/0, /*vattr*/ NULL, cred);
  return error;
}

static int
xfs_hold (struct vnode *vp)
{
    XFSDEB(XDEBVFOPS, ("xfs_hold\n"));
    ++vp->v_count;
    return 0;
}

static void
xfs_inactive(struct vnode *vp)
{
  struct xfs_message_inactivenode msg;
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);

  XFSDEB(XDEBVNOPS, ("xfs_inactive\n"));

  msg.header.opcode = XFS_MSG_INACTIVENODE;
  msg.handle = xn->handle;
  msg.flag   = XFS_NOREFS | XFS_DELETE;
  free_xfs_node(VNODE_TO_XNODE(vp));
  xfs_message_send(xfsp->fd, &msg.header, sizeof(msg));
}

static int
xfs_rele (struct vnode *vp)
{
    XFSDEB(XDEBVFOPS, ("xfs_rele\n"));
    if (--vp->v_count == 0)
	xfs_inactive (vp);
    return 0;
}

static int
xfs_remove(struct vnode *vp,
	   struct vnode *dvp,
	   char *nm,
	   struct ucred *cred)
{
  struct xfs *xfsp = XFS_FROM_VNODE(dvp);
  struct xfs_node *xn = VNODE_TO_XNODE(dvp);
  struct xfs_message_remove msg;
  int error;

  XFSDEB(XDEBVNOPS, ("xfs_remove: %s\n", nm));

  msg.header.opcode = XFS_MSG_REMOVE;
  msg.parent_handle = xn->handle;
  if (strlcpy(msg.name, nm, sizeof(msg.name)) >= XFS_MAX_NAME)
      return ENAMETOOLONG;
  msg.cred.uid = cred->cr_uid;
  msg.cred.pag = xfs_get_pag(cred);
  error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
  if (error == 0)
      error = ((struct xfs_message_wakeup *) &msg)->error;

  if (error == 0)
      xfs_dnlc_remove (dvp, nm);
  VNOP_RELE(vp);

  return error;
}

static int
xfs_link(struct vnode *vp,
	 struct vnode *tdvp,
	 char *tnm,
	 struct ucred *cred)     
{
    struct xfs *xfsp = XFS_FROM_VNODE(vp);
    struct xfs_node *xn = VNODE_TO_XNODE(tdvp);
    struct xfs_node *xn2 = VNODE_TO_XNODE(vp);
    struct xfs_message_link msg;
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_link: (%s)\n", tnm));

    msg.header.opcode = XFS_MSG_LINK;
    msg.parent_handle = xn->handle;
    msg.from_handle   = xn2->handle;
    if (strlcpy(msg.name, tnm, sizeof(msg.name)) >= XFS_MAX_NAME)
	return ENAMETOOLONG;
    msg.cred.uid = cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cred);

    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) & msg)->error;

    return error;
}

static int
xfs_rename(struct vnode *svp,
	   struct vnode *sdvp,
	   char *onm,
	   struct vnode *tvp,
	   struct vnode *tdvp,
	   char *nnm,
	   struct ucred *cred)     
{
  struct xfs_message_rename msg;
  struct vnode **vpp;
  int error = 0;

  XFSDEB(XDEBVNOPS, ("xfs_rename\n"));

  /* If the old name is too long, don't remove then name */
  if (strlen(onm) >= XFS_MAX_NAME)
      return ENAMETOOLONG;

  if (tvp) {
      /* the filename being moved to already exists */
      struct xfs_message_remove remmsg;
      
      remmsg.header.opcode = XFS_MSG_REMOVE;
      remmsg.parent_handle = VNODE_TO_XNODE(tdvp)->handle;
      if (strlcpy(remmsg.name, nnm, sizeof(remmsg.name)) >= XFS_MAX_NAME)
	  return ENAMETOOLONG;
      remmsg.cred.uid = cred->cr_uid;
      remmsg.cred.pag = xfs_get_pag(cred);
      error = xfs_message_rpc(XFS_FROM_VNODE(tdvp)->fd, &remmsg.header,
			      sizeof(remmsg));
      if (error == 0)
	  error = ((struct xfs_message_wakeup *) & remmsg)->error;
      if (error != 0)
	  return error;
  }

  msg.header.opcode = XFS_MSG_RENAME;
  msg.old_parent_handle = VNODE_TO_XNODE(sdvp)->handle;
  if (strlcpy(msg.old_name, onm, sizeof(msg.old_name)) >= XFS_MAX_NAME)
      return ENAMETOOLONG;
  msg.new_parent_handle = VNODE_TO_XNODE(tdvp)->handle;
  if (strlcpy(msg.new_name, nnm, sizeof(msg.new_name)) >= XFS_MAX_NAME)
      return ENAMETOOLONG;
  msg.cred.uid = cred->cr_uid;
  msg.cred.pag = xfs_get_pag(cred);
  error = xfs_message_rpc(XFS_FROM_VNODE(sdvp)->fd, &msg.header,
			  sizeof(msg));
  if (error == 0)
      error = ((struct xfs_message_wakeup *) & msg)->error;
  
  return error;
}

static int
xfs_mkdir(struct vnode *dvp,
	  char *nm,
	  int foo,		/* XXX */
	  struct ucred *cred)     
{
  struct xfs *xfsp = XFS_FROM_VNODE(dvp);
  struct xfs_node *xn = VNODE_TO_XNODE(dvp);
  int error = 0;
  
  XFSDEB(XDEBVNOPS, ("xfs_mkdir\n"));
  {
    struct xfs_message_mkdir msg;
    msg.header.opcode = XFS_MSG_MKDIR;
    msg.parent_handle = xn->handle;
    if (strlcpy(msg.name, nm, sizeof(msg.name)) >= XFS_MAX_NAME)
	return ENAMETOOLONG;
#if 0
    vattr2xfs_attr (va, &msg.attr);
#endif
    msg.cred.uid = cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cred);
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
      error = ((struct xfs_message_wakeup *) &msg)->error;
  }
#if 0
  if (error == 0)
      error = xfs_lookup(dvp, vpp, nm, /*flag*/ 0, /*vattr*/NULL, cred);
#endif
  return error;
}

static int
xfs_mknod(struct vnode *vp,
	  caddr_t ext,
	  int foo,
	  dev_t dev,
	  struct ucred *cred)
{
    XFSDEB(XDEBVNOPS, ("xfs_mknod\n"));
    return ENOSYS;
}

static int
xfs_rmdir(struct vnode *dvp,
	  struct vnode *vp,
	  char *nm,
	  struct ucred *cred)     
{
    struct xfs_message_rmdir msg;
    struct xfs *xfsp  = XFS_FROM_VNODE(dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(dvp);
    int error = 0;


    XFSDEB(XDEBVNOPS, ("xfs_rmdir\n"));

    msg.header.opcode = XFS_MSG_RMDIR;
    msg.parent_handle = xn->handle;
    if (strlcpy(msg.name, nm, sizeof(msg.name)) < XFS_MAX_NAME)
	return ENAMETOOLONG;
    msg.cred.uid = cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cred);
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) &msg)->error;

    return error;
}

static int
xfs_readdir(struct vnode *vp,
	    struct uio *uiop,
	    struct ucred *cred)
{
  int error = 0;

  XFSDEB(XDEBVNOPS, ("xfs_readdir\n"));

  error = xfs_data_valid(vp, cred, XFS_DATA_R);
  if (error == 0)
    {
      struct vnode *t = DATA_FROM_VNODE(vp);
      ASSERT(t != NULL);
      VNOP_HOLD(t);
      error = VNOP_RDWR(t, UIO_READ, 0, uiop, 0, NULL, NULL, cred);
      VNOP_RELE(t);
    }

  return error;
}

static int
xfs_symlink(struct vnode *dvp,
	    char *lnm,
	    char *tnm,
	    struct ucred *cred)
{
    struct xfs *xfsp  = XFS_FROM_VNODE(dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(dvp);
    struct xfs_message_symlink msg;
    int error = 0;
 
    XFSDEB(XDEBVNOPS, ("xfs_symlink\n"));
   
    msg.header.opcode = XFS_MSG_SYMLINK;
    msg.parent_handle = xn->handle;
    if (strlcpy(msg.name, lnm, sizeof(msg.name)) >= XFS_MAX_NAME)
	return ENAMETOOLONG;
    if (strlcpy(msg.contents, tnm, sizeof(msg.contents)) >= XFS_MAX_NAME)
	return ENAMETOOLONG;
#if 0
    vattr2xfs_attr (tva, &msg.attr);
#endif
    msg.cred.uid = cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cred);

    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) & msg)->error;

    return error;
}

static int
xfs_readlink(struct vnode *vp,
	     struct uio *uiop,
	     struct ucred *cred)
{
  int error = 0;

  XFSDEB(XDEBVNOPS, ("xfs_readlink\n"));

  error = xfs_data_valid(vp, cred, XFS_DATA_R);
  if (error == 0)
    {
      struct vnode *t = DATA_FROM_VNODE(vp);
      ASSERT(t != NULL);
      VNOP_HOLD(t);
      error = VNOP_RDWR(t, UIO_READ, 0, uiop, 0, NULL, NULL, cred);
      VNOP_RELE(t);
    }

  return error;
}

static int
xfs_fsync(struct vnode *vp,
	  int syncflag,
	  int foo,
	  struct ucred *cred)
{
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);
  int error = 0;
  
  XFSDEB(XDEBVNOPS, ("xfs_fsync\n"));

  if (xn->flags & XFS_DATA_DIRTY)
      error = do_fsync (xfsp, xn, cred, XFS_WRITE | XFS_FSYNC);
  return error;
}

static int
xfs_fid(struct vnode *vp,
	struct fileid *fileid,
	struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_fid\n"));
  return ENOSYS;
}

static int
xfs_fclear (struct vnode *vp,
	    int flags,
	    offset_t offset,
	    offset_t len,
	    caddr_t vinfo,
	    struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_fclear\n"));
  return ENOSYS;
}

static int
xfs_ftrunc (struct vnode *vp,
	    int flags,
	    offset_t length,
	    caddr_t vinfo,
	    struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_ftrunc\n"));
  return ENOSYS;
}

static int
xfs_lockctl (struct vnode *vp,
	     offset_t offset,
	     struct eflock *lckdat,
	     int cmd,
	     int (*retry_fn)(),
	     ulong *retry_id,
	     struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_lockctl\n"));
  return ENOSYS;
}

static int
xfs_select (struct vnode *vp,
	    int correl,
	    ushort e,
	    ushort *re,
	    void (*notify)(),
	    caddr_t vinfo,
	    struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_select\n"));
  return ENOSYS;
}

static int
xfs_strategy (struct vnode *vp,
	      struct buf *buf,
	      struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_strategy\n"));
  return ENOSYS;
}

static int
xfs_revoke (struct vnode *vp,
	    int cmd,
	    int flag,
	    struct vattr *attr,
	    struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_revoke\n"));
  return ENOSYS;
}

static int
xfs_getacl (struct vnode *vp,
	    struct uio *uip,
	    struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_getacl\n"));
  return ENOSYS;
}

static int
xfs_setacl (struct vnode *vp,
	    struct uio *uip,
	    struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_getacl\n"));
  return ENOSYS;
}

static int
xfs_getpcl (struct vnode *vp,
	    struct uio *uip,
	    struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_getpcl\n"));
  return ENOSYS;
}

static int
xfs_setpcl (struct vnode *vp,
	    struct uio *uip,
	    struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_setpcl\n"));
  return ENOSYS;
}

static int
xfs_seek (struct vnode *vp,
	  offset_t *offset,
	  struct ucred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_seek\n"));
  return ENOSYS;
}

struct vnodeops xfs_vnodeops = {
    xfs_link,
    xfs_mkdir,
    xfs_mknod,
    xfs_remove,
    xfs_rename,
    xfs_rmdir,
    xfs_lookup,
    xfs_fid,
    xfs_open,
    xfs_create,
    xfs_hold,
    xfs_rele,
    xfs_close,
    xfs_map,
    xfs_unmap,
    xfs_access,
    xfs_getattr,
    xfs_setattr,
    xfs_fclear,
    xfs_fsync,
    xfs_ftrunc,
    xfs_rdwr,
    xfs_lockctl,
    xfs_ioctl,
    xfs_readlink,
    xfs_select,
    xfs_symlink,
    xfs_readdir,
    xfs_strategy,
    xfs_revoke,
    xfs_getacl,
    xfs_setacl,
    xfs_getpcl,
    xfs_setpcl,
    xfs_seek
};
