/*
 * 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>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/fs_subr.h>

RCSID("$Id: xfs_vnodeops.c,v 1.32.2.1 2000/10/18 07:26:43 lha Exp $");

static int
xfs_open_valid(struct vnode *vp, struct cred *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 cred *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 cred *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 cred *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 cred *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;
    vattr2xfs_attr (&xn->attr, &msg.attr);
    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 **vpp,
	 int flag,
	 struct cred *cred)
{
  int error = 0;
  
  XFSDEB(XDEBVNOPS, ("xfs_open\n"));
  
  if (flag & FWRITE)
    error = xfs_open_valid(*vpp, cred, XFS_OPEN_NW);
  else
    error = xfs_open_valid(*vpp, cred, XFS_OPEN_NR);
  
  return error;
}

static int
xfs_close(struct vnode *vp,
	  int flag,
	  int count,
	  offset_t offset,
	  struct cred *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);
  
  XFSDEB(XDEBVNOPS, ("xfs_close: %d\n", error));

  return error;
}

static int
xfs_read(struct vnode *vp,
	 struct uio *uio,
	 int ioflag,
	 struct cred *cred)
{
    int error = 0;

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

    if (vp->v_type != VREG)
	return EISDIR;

    error = xfs_data_valid(vp, cred, XFS_DATA_R);

    if (error == 0) {
	struct vnode *t = DATA_FROM_VNODE(vp);
	ASSERT(t != NULL);
	VOP_RWLOCK(t, 0);
	error = VOP_READ(t, uio, ioflag, cred);
	VOP_RWUNLOCK(t, 0);
    }

    return error;
}

static int
xfs_write(struct vnode *vp,
	  struct uio *uio,
	  int ioflag,
	  struct cred *cred)
{
    int error = 0;

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

    if (vp->v_type != VREG)
	return EISDIR;

    error = xfs_data_valid(vp, cred, XFS_DATA_W);

    if (error == 0) {
	struct xfs_node *xn = VNODE_TO_XNODE(vp);
	struct vnode *t = DATA_FROM_VNODE(vp);
	struct vattr sub_attr;
	int error2 = 0;

	ASSERT(t != NULL);
	VOP_RWLOCK(t, 1);
	error  = VOP_WRITE(t, uio, ioflag, cred);
	error2 = VOP_GETATTR(t, &sub_attr, 0, cred);
	VOP_RWUNLOCK(t, 1);
	VNODE_TO_XNODE(vp)->flags |= XFS_DATA_DIRTY;

	if (error2 == 0) {
	    xn->attr.va_size  = sub_attr.va_size;
	    xn->attr.va_mtime = sub_attr.va_mtime;
	}
    }

    XFSDEB(XDEBVNOPS, ("xfs_write: %d\n", error));

    return error;
}

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

static int
xfs_setfl(struct vnode *vp,
	  int oflags,
	  int nflags,
	  struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_setfl\n"));
  return fs_setfl (vp, oflags, nflags, cred);
}

static int
xfs_getattr(struct vnode *vp,
	    struct vattr *vap,
	    int flags,
	    struct cred *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,
	    struct vattr *vap,
	    int flags,
	    struct cred *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;
      vattr2xfs_attr (vap, &msg.attr);
      if (XFS_TOKEN_GOT(xn, XFS_DATA_R)) {
	  if (vp->v_type == VREG) {
	      if (vap->va_mask & AT_SIZE)
		  XA_SET_SIZE(&msg.attr,  vap->va_size);
	      else
		  XA_SET_SIZE(&msg.attr,  xn->attr.va_size);
	  }
	  if (vap->va_mask & AT_MTIME)
	      XA_SET_MTIME(&msg.attr, vap->va_mtime.tv_sec);
	  else
	      XA_SET_MTIME(&msg.attr, xn->attr.va_mtime.tv_sec);
      }
      XFS_TOKEN_CLEAR(xn, XFS_ATTR_VALID, XFS_ATTR_MASK);
      error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
      if (error == 0)
	error = ((struct xfs_message_wakeup *) &msg)->error;
  }

 done:
  return error;
}

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

    if (mode & VREAD)
	if ((rights & XFS_RIGHT_R) == 0)
	    error = EACCES;
    if (mode & VWRITE)
	if ((rights & XFS_RIGHT_W) == 0)
	    error = EACCES;
    if (mode & VEXEC)
	if ((rights & XFS_RIGHT_X) == 0)
	    error = EACCES;
    return error;
}

static int
xfs_access(struct vnode *vp,
	   int mode,
	   int flags,
	   struct cred *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;

      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;
	  }
  }

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

}

static int
xfs_lookup(struct vnode *dvp,
	   char *nm,
	   struct vnode **vpp,
	   struct pathname *pnp,
	   int flags,
	   struct vnode *rdir,
	   struct cred *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 (%s)\n", nm));
  
  if (*nm == '\0') {
      VN_HOLD(dvp);
      *vpp = dvp;
      error = 0;
      goto done;
  }

  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)
	    error = ENAMETOOLONG;
	else
	    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	  error = ((struct xfs_message_wakeup *) &msg)->error;
      }
    else
      {
	*vpp = v;
	goto done;
      }
  } while (error == 0);

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

static int
xfs_create(struct vnode *dvp,
	   char *nm,
	   struct vattr *va,
	   vcexcl_t exclusive,
	   int mode,
	   struct vnode **vpp,
	   struct cred *cred
#ifdef _LARGEFILE64_SOURCE
	   ,int file_awareness  /* Solaris 2.6+ */
#endif
	   )     
{
  struct xfs *xfsp = XFS_FROM_VNODE(dvp);
  struct xfs_node *xn = VNODE_TO_XNODE(dvp);
  int error = 0;
  int do_trunc = 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;
    vattr2xfs_attr (va, &msg.attr);
#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 == EEXIST) {
      do_trunc = 1;
      error = 0;
  }

  if (error == 0)
      error = xfs_lookup(dvp, nm, vpp, /*pnp*/ NULL, /*flags*/ 0,
			 /*rdir*/ NULL, cred);

  if (error == 0 && do_trunc)
      error = xfs_setattr (*vpp, va, 0, cred);

  return error;
}

static int
xfs_remove(struct vnode *dvp,
	   char *nm,
	   struct cred *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)
      dnlc_remove(dvp, nm);

  return error;
}

static int
xfs_link(struct vnode *tdvp,
	 struct vnode *vp,
	 char *tnm,
	 struct cred *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 *sdvp,
	   char *onm,
	   struct vnode *tdvp,
	   char *nnm,
	   struct cred *cred)     
{
  struct xfs_message_rename msg;
  struct vnode *vp;
  int error = 0;

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

  error = xfs_lookup(tdvp, nnm, &vp, /*pnp*/ NULL, /*flags*/ 0,
		     /*rdir*/ NULL, cred);
  if (error != ENOENT) {
      /* 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 && error != ENOENT)
	  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,
	  struct vattr *va,
	  struct vnode **vpp,
	  struct cred *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;
    vattr2xfs_attr (va, &msg.attr);
    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, nm, vpp, /*pnp*/ NULL, /*flags*/ 0,
			 /*rdir*/ NULL, cred);
  return error;
}

static int
xfs_rmdir(struct vnode *dvp,
	  char *nm,
	  struct vnode *foo,
	  struct cred *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;
    if (error == 0)
	dnlc_remove(dvp, nm);

    return error;
}

static int
xfs_readdir(struct vnode *vp,
	    struct uio *uiop,
	    struct cred *cred,
	    int *eofp)		/* XXX */
{
  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);
      VOP_RWLOCK(t, 0);
      error = VOP_READ(t, uiop, 0, cred);
      VOP_RWUNLOCK(t, 0);
    }

  return error;
}

static int
xfs_symlink(struct vnode *dvp,
	    char *lnm,
	    struct vattr *tva,
	    char *tnm,
	    struct cred *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_SYMLINK_CONTENT)
	return ENAMETOOLONG;
    vattr2xfs_attr (tva, &msg.attr);
    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 cred *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);
      VOP_RWLOCK(t, 0);
      error = VOP_READ(t, uiop, 0, cred);
      VOP_RWUNLOCK(t, 0);
    }

  return error;
}

static int
xfs_fsync(struct vnode *vp,
	  int syncflag,
	  struct cred *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 void
xfs_inactive(struct vnode *vp,
	     struct cred *cred)
{
  struct xfs_message_inactivenode msg;
  struct xfs *xfsp = XFS_FROM_VNODE(vp);
  struct xfs_node *xn = VNODE_TO_XNODE(vp);

  XFSDEB(XDEBVNOPS, ("xfs_inactive: 0x%x\n", (int)vp));

  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_fid(struct vnode *vp,
	struct fid *fid)
{
  XFSDEB(XDEBVNOPS, ("xfs_fid\n"));
  return ENOSYS;
}

static void
xfs_rwlock(struct vnode *vp,
	   int write_lock)
{
  XFSDEB(XDEBVNOPS, ("xfs_rwlock\n"));
}

static void
xfs_rwunlock(struct vnode *vp,
	     int write_lock)
{
  XFSDEB(XDEBVNOPS, ("xfs_rwunlock\n"));
}

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

static int
xfs_cmp(struct vnode *vp1, struct vnode *vp2)
{
  XFSDEB(XDEBVNOPS, ("xfs_cmp\n"));
  return vp1 == vp2;
}

static int
xfs_frlock(struct vnode *vp,
	   int foo,
#ifdef _LARGEFILE64_SOURCE
	   struct flock64 *fl,
#else
	   struct flock *fl,
#endif
	   int bar,
	   offset_t off,
	   struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_frlock\n"));
  return ENOSYS;
}

static int
xfs_space(struct vnode *vp,
	  int cmd,
#ifdef _LARGEFILE64_SOURCE
	  struct flock64 *fl,
#else
	  struct flock *fl,
#endif
	  int flag,
	  offset_t offset,
	  struct cred *cred)
{
  int error = 0;
  struct vattr attr;

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

  if (cmd != F_FREESP) {
      error = EINVAL;
      goto done;
  }
      
  error = convoff (vp, fl, 0, offset);
  if (error)
      goto done;

  if (fl->l_len != 0) {
      error = EINVAL;
      goto done;
  }

  attr.va_mask = AT_SIZE;
  attr.va_size = fl->l_start;
  error = xfs_setattr (vp, &attr, 0, cred);
done:
  XFSDEB(XDEBVNOPS, ("xfs_space: %d\n", error));
  return error;
}

static int
xfs_realvp(struct vnode *vp,
	   struct vnode **vpp)
{
  XFSDEB(XDEBVNOPS, ("xfs_realvp\n"));
  return ENOSYS;
}

static int
xfs_getpage(struct vnode *vp,
	    offset_t off,
	    size_t len,
	    uint_t *protp,
	    struct page *pl[],
	    size_t plsz,
	    struct seg *seg,
	    caddr_t addr,
	    enum seg_rw rw,
	    struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_getpage\n"));
  return ENOSYS;
}

static int
xfs_putpage(struct vnode *vp,
	    offset_t off,
	    size_t len,
	    int flags,
	    struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_putpage\n"));
  return ENOSYS;
}

static int
xfs_map(struct vnode *vp,
	offset_t off,
	struct as *as,
	caddr_t *addrp,
	size_t len,
	uchar_t prot,
	uchar_t maxprot,
	uint_t flags,
	struct cred *cred)
{
  int error = 0;

  XFSDEB(XDEBVNOPS,
	 ("xfs_map(0x%x, 0x%lx, 0x%x, 0x%x, 0x%lx, 0x%x, 0x%x, 0x%x, 0x%x)\n",
	  (int) vp, (unsigned long)off,
	  (int) as, (int) addrp, (unsigned long)len, prot, maxprot,
	  flags, (int) cred));

  error = xfs_attr_valid (vp, cred, XFS_ATTR_R);
  if (error)
      goto out;

  if ((prot & PROT_WRITE) && (flags & MAP_SHARED))
    error = xfs_data_valid(vp, cred, XFS_DATA_W);
  else
    error = xfs_data_valid(vp, cred, XFS_DATA_R);

  XFSDEB(XDEBVNOPS, ("xfs_map: size = %u\n",
		     (unsigned)VNODE_TO_XNODE(vp)->attr.va_size));

  if (error != 0)
    /* Can't map today */;
  else if (off + len > VNODE_TO_XNODE(vp)->attr.va_size)
    error = EINVAL;
#if 0
  else if ((prot & PROT_WRITE) && (flags & MAP_SHARED))
    error = EROFS;		/* XXX This is currently not supported */
#endif
  else
    {
      struct vnode *t = DATA_FROM_VNODE(vp);
      ASSERT(t != NULL);
      VOP_RWLOCK(t, 1);
      error = VOP_MAP(t, off, as, addrp, len, prot, maxprot, flags, cred);
      /* XXX Patch vnode so that we can intercept get/putpage and inactive. */
      VOP_RWUNLOCK(t, 1);
    }

out:
  XFSDEB(XDEBVNOPS, ("xfs_map: %d\n", error));
  return error;
}

static int
xfs_addmap(struct vnode *vp,
	   offset_t off,
	   struct as *as,
	   caddr_t addr,
	   size_t len,
	   uchar_t prot,
	   uchar_t maxprot,
	   uint_t flags,
	   struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_addmap\n"));
  return ENOSYS;

}

static int
xfs_delmap(struct vnode *vp,
	   offset_t off,
	   struct as *as,
	   caddr_t addr,
	   size_t len,
	   uint_t prot,
	   uint_t maxprot,
	   uint_t flags,
	   struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_delmap\n"));
  return ENOSYS;

}


static int
xfs_poll(struct vnode *vp,
	 short events,
	 int anyyet,
	 short *revents,
	 struct pollhead **ph)
{
  XFSDEB(XDEBVNOPS, ("xfs_poll\n"));
  return fs_poll(vp, events, anyyet, revents, ph);
}

static int
xfs_dump(struct vnode *dumpvp,
	 caddr_t addr,
	 int bn,
	 int count)
{
  XFSDEB(XDEBVNOPS, ("xfs_dump\n"));
  return ENOSYS;
}

static int
xfs_pathconf(struct vnode *vp,
	     int cmd,
	     u_long *valp,
	     struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_pathconf\n"));
  return fs_pathconf (vp, cmd, valp, cred);
}

static int
xfs_pageio(struct vnode *vp,
	   struct page *page,
#ifdef _LARGEFILE64_SOURCE
	   u_offset_t io_off,  /* Solaris 2.6+ */
#else
	   u_int io_off,  /* Solaris 2.5 */
#endif
	   size_t io_len,
	   int flags,
	   struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_pageio\n"));
  return ENOSYS;
}

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

static void
xfs_dispose(struct vnode *vp,
	    struct page *page,
	    int a,
	    int b,
	    struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_dispose\n"));
}

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

static int
xfs_getsecattr(struct vnode *vp,
	       vsecattr_t *attr,
	       int flag,
	       struct cred *cred)
{
  XFSDEB(XDEBVNOPS, ("xfs_getsecattr\n"));
  return fs_fab_acl(vp, attr, flag, cred);
}

struct vnodeops xfs_vnodeops = {
        xfs_open,
        xfs_close,
        xfs_read,
        xfs_write,
        xfs_ioctl,
	xfs_setfl,
        xfs_getattr,
        xfs_setattr,
        xfs_access,
        xfs_lookup,
        xfs_create,
        xfs_remove,
        xfs_link,
        xfs_rename,
        xfs_mkdir,
        xfs_rmdir,
        xfs_readdir,
        xfs_symlink,
        xfs_readlink,
        xfs_fsync,
        xfs_inactive,
	xfs_fid,
	xfs_rwlock,
	xfs_rwunlock,
	xfs_seek,
	xfs_cmp,
	xfs_frlock,
	xfs_space,
	xfs_realvp,
        xfs_getpage,
        xfs_putpage,
        xfs_map,
	xfs_addmap,
	xfs_delmap,
	xfs_poll,
        xfs_dump,
	xfs_pathconf,
	xfs_pageio,
	xfs_dumpctl,
	xfs_dispose,
	xfs_setsecattr,
	xfs_getsecattr
	/* xfs_shrlock */
};
