/*
 * Copyright (c) 1995, 1996, 1997, 1998 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 2000/02/20 04:24:09 assar Exp $");

extern int (**xfs_vnodeop_p) (void *);

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_rights_valid(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);

#if not_yet
    do {
	if (!xfs_has_pag(xn, pag))
#endif
	{
	    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;
	}
#if not_yet
	else {
	    goto done;
	}
    } while (error == 0);
#endif

/*done:*/
    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;
    if (cred != NOCRED) {
	msg.cred.uid = cred->cr_uid;
	msg.cred.pag = xfs_get_pag(cred);
    } else {
	msg.cred.uid = 0;
	msg.cred.pag = XFS_ANONYMOUSID;
    }
    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;
}

/*
 * vnode functions
 */

static int
xfs_open(void *vap)
{

#ifdef struct_comment
    struct vop_open_args {
	struct vnode *a_vp;
	int a_mode;
	struct ucred *a_cred;
	struct proc *a_p;
    };

#endif				       /* struct_comment */
    struct vop_open_args *ap = vap;

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

    if (ap->a_mode & FWRITE)
	return xfs_open_valid(ap->a_vp, ap->a_cred, XFS_OPEN_NW);
    else
	return xfs_open_valid(ap->a_vp, ap->a_cred, XFS_OPEN_NR);
}

static int
xfs_fsync(void *vap)
{
#ifdef struct_comment
    struct vop_fsync_args *ap {
	struct vnode *a_vp;
	struct ucred *a_cred;
	int a_waitfor;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    struct vop_fsync_args *ap = vap;

    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_vp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_fsync: 0x%x\n", (int) ap->a_vp));

    /*
     * It seems that fsync is sometimes called after reclaiming a node.
     * In that case we just look happy.
     */

    if (xn == NULL) {
	printf("XFS PANIC WARNING! xfs_fsync called after reclaiming!\n");
	return 0;
    }

    if (xn->flags & XFS_DATA_DIRTY)
	error = do_fsync(xfsp, xn, ap->a_cred, XFS_WRITE);

    return error;
}

static int
xfs_seek(void *vap)
#ifdef struct_comment
struct vop_seek_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	off_t a_oldoff;
	off_t a_newoff;
	struct ucred *a_cred;
};
#endif
{
  struct vop_seek_args *ap = vap;

  XFSDEB(XDEBVNOPS, ("xfs_seek\n"));
  return 0;
}

static int
xfs_close(void *vap)
{
#ifdef struct_comment
    struct vop_close_args {
	struct vnode *a_vp;
	int a_fflag;
	struct ucred *a_cred;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    struct vop_close_args *ap = vap;

    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_vp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_close cred = %p\n", ap->a_cred));

    if (ap->a_fflag & FWRITE && xn->flags & XFS_DATA_DIRTY)
	error = do_fsync(xfsp, xn, ap->a_cred, XFS_WRITE);

    return error;
}

static int
xfs_read(void *vap)
{
#ifdef struct_comment
    struct vop_read_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct uio *a_uio;
	int a_ioflag;
	struct ucred *a_cred;
    };
#endif				       /* struct_comment */
    struct vop_read_args *ap = vap;

    int error = 0;

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

    error = xfs_data_valid(ap->a_vp, ap->a_cred, XFS_DATA_R);

    if (error == 0) {
	struct vnode *t = DATA_FROM_VNODE(ap->a_vp);

	vn_lock(t, LK_EXCLUSIVE | LK_RETRY, ap->a_uio->uio_procp);
	error = VOP_READ(t, ap->a_uio, ap->a_ioflag, ap->a_cred);
	VOP_UNLOCK(t, 0, ap->a_uio->uio_procp);
    }
    return error;
}

static int
xfs_write(void *vap)
{
#ifdef struct_comment
    struct vop_write_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct uio *a_uio;
	int a_ioflag;
	struct ucred *a_cred;
    };
#endif				       /* struct_comment */
    struct vop_read_args *ap = vap;

    int error = 0;

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

    error = xfs_data_valid(ap->a_vp, ap->a_cred, XFS_DATA_W);

    if (error == 0) {
	struct vnode *t = DATA_FROM_VNODE(ap->a_vp);

	vn_lock(t, LK_EXCLUSIVE | LK_RETRY, ap->a_uio->uio_procp);
	error = VOP_WRITE(t, ap->a_uio, ap->a_ioflag, ap->a_cred);
	VNODE_TO_XNODE(ap->a_vp)->flags |= XFS_DATA_DIRTY;
	VOP_UNLOCK(t, 0, ap->a_uio->uio_procp);
    }

    return error;
}

static int
xfs_ioctl(void *vap)
{
#ifdef struct_comment
    struct vop_ioctl_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	u_long a_command;
	caddr_t a_data;
	int a_fflag;
	struct ucred *a_cred;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    /* struct vop_ioctl_args *ap = vap; */
    XFSDEB(XDEBVNOPS, ("xfs_ioctl\n"));

    return EOPNOTSUPP;
}

static int
xfs_select(void *vap)
{
#ifdef struct_comment
    struct vop_select_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	int a_which;
	int a_fflags;
	struct ucred *a_cred;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    /* struct vop_select_args *ap = vap; */

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

    return EOPNOTSUPP;
}

#if 0
static int
xfs_poll(struct vop_poll_args * ap)
/* vop_poll {
       IN struct vnode *vp;
       IN int events;
       IN struct proc *p;
   }; */
{
#ifdef struct_comment
    struct vop_poll_args {
	struct no clue;
    };
#endif				       /* struct_comment */    
    XFSDEB(XDEBVNOPS, ("xfs_poll\n"));
    return EOPNOTSUPP;
}
#endif /* 0 */

static int
xfs_getattr(void *vap)
{
#ifdef struct_comment
    struct vop_getattr_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct vattr *a_vap;
	struct ucred *a_cred;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    struct vop_getattr_args *ap = vap;
    int error = 0;

    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);

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

    error = xfs_attr_valid(ap->a_vp, ap->a_cred, XFS_ATTR_R);
    if (error == 0) {
	*ap->a_vap = xn->attr;
    }
    return error;
}

static int
xfs_setattr(void *vap)
{
#ifdef struct_comment
    struct vop_setattr_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct vattr *a_vap;
	struct ucred *a_cred;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    struct vop_setattr_args *ap = vap;
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_vp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_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(ap->a_vp)->flags |= XFS_ATTR_DIRTY;
	error = EINVAL;		       /* XXX not yet implemented */
	goto done;
    } else {
	struct xfs_message_putattr msg;

	msg.header.opcode = XFS_MSG_PUTATTR;
	if (ap->a_cred != NOCRED) {
	    msg.cred.uid = ap->a_cred->cr_uid;
	    msg.cred.pag = xfs_get_pag(ap->a_cred);
	} else {
	    msg.cred.uid = 0;
	    msg.cred.pag = XFS_ANONYMOUSID;
	}
	msg.handle = xn->handle;
	vattr2xfs_attr(ap->a_vap, &msg.attr);
	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 & 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(void *vap)
{
#ifdef struct_comment
    struct vop_access_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	int a_mode;
	struct ucred *a_cred;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    struct vop_access_args *ap = vap;

    int error = 0;
    int mode = ap->a_mode;
    xfs_pag_t pag = xfs_get_pag(ap->a_cred);

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

    error = xfs_attr_valid(ap->a_vp, ap->a_cred, XFS_ATTR_R);
    if (error == 0) {
	struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);
	int i;

	error = check_rights (xn->anonrights, mode);

	if (error == 0)
	    goto done;

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

	xfs_rights_valid(ap->a_vp, ap->a_cred); /* ignore error */

	error = EACCES;		/* default to EACCES if pag isn't in xn->id */

	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(void *vap)
{
#ifdef struct_comment
    struct vop_lookup_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_dvp;
	struct vnode **a_vpp;
	struct componentname *a_cnp;
    };
#endif				       /* struct_comment */
    struct vop_lookup_args *ap = vap;

    struct xfs_message_getnode msg;
    struct vnode *dvp = ap->a_dvp;
    struct xfs *xfsp = XFS_FROM_VNODE(dvp);
    struct componentname *cnp = ap->a_cnp;
    int nameiop = cnp->cn_nameiop;
    int flags = cnp->cn_flags;
    int islastcn = flags & ISLASTCN;
    struct xfs_node *d = VNODE_TO_XNODE(dvp);
    struct proc *p = cnp->cn_proc;
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_lookup: (%s, %ld)\n",
		       cnp->cn_nameptr,
		       cnp->cn_namelen));

    if (dvp->v_type != VDIR)
	return ENOTDIR;

    *ap->a_vpp = NULL;

    do {
#ifdef notdef_but_correct /* probably not needed anyway */
	error = xfs_access(dvp, VEXEC, cred);
	if (error != 0)
	    goto done;
#endif
	error = xfs_dnlc_lookup(dvp, cnp, ap->a_vpp);
	if (error == 0) {
	    msg.header.opcode = XFS_MSG_GETNODE;
	    if (ap->a_cnp->cn_cred != NOCRED) {
		msg.cred.uid = cnp->cn_cred->cr_uid;
		msg.cred.pag = xfs_get_pag(cnp->cn_cred);
	    } else {
		msg.cred.uid = 0;
		msg.cred.pag = XFS_ANONYMOUSID;
	    }
	    msg.parent_handle = d->handle;

	    bcopy(cnp->cn_nameptr, msg.name, cnp->cn_namelen);
	    msg.name[cnp->cn_namelen] = '\0';

	    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));

	    if (error == 0)
		error = ((struct xfs_message_wakeup *) & msg)->error;
	    if(error == ENOENT) {
		XFSDEB(XDEBVNOPS, ("xfs_lookup: neg cache %p (%s, %ld)\n",
				   dvp,
				   cnp->cn_nameptr, cnp->cn_namelen));
		cache_enter (dvp, NULL, cnp);
	    }

	    XFSDEB(XDEBVNOPS, ("xfs_lookup error: %d\n", error));
	} else if (error == -1) {
	    vget(*ap->a_vpp, 0, p);
	    error = 0;
	    goto done;
	}
    } while (error == 0);

done:
    if (error == ENOENT
	&& (nameiop == CREATE || nameiop == RENAME)
	&& islastcn) {
	error = EJUSTRETURN;
    }

    if (nameiop != LOOKUP && flags & ISLASTCN)
	cnp->cn_flags |= SAVENAME;

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

static int
xfs_create(void *vap)
{
#ifdef struct_comment
    struct vop_create_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_dvp;
	struct vnode **a_vpp;
	struct componentname *a_cnp;
	struct vattr *a_vap;
    };
#endif				       /* struct_comment */
    struct vop_create_args *ap = vap;

    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_dvp);
    struct componentname *cnp = ap->a_cnp;
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_create: (%s, %ld)\n",
		       cnp->cn_nameptr,
		       cnp->cn_namelen));
    {
	struct xfs_message_create msg;

	msg.header.opcode = XFS_MSG_CREATE;
	msg.parent_handle = xn->handle;
	strncpy(msg.name, cnp->cn_nameptr, 256);
	vattr2xfs_attr(ap->a_vap, &msg.attr);
#if 0
	msg.exclusive = /* XXX - exclusive */ 0;
#endif
	msg.mode = 0;		       /* XXX - mode */
	if (cnp->cn_cred != NOCRED) {
	    msg.cred.uid = cnp->cn_cred->cr_uid;
	    msg.cred.pag = xfs_get_pag(cnp->cn_cred);
	} else {
	    msg.cred.uid = 0;
	    msg.cred.pag = XFS_ANONYMOUSID;
	}
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	    error = ((struct xfs_message_wakeup *) & msg)->error;
    }

    if (error == 0) {
	error = VOP_LOOKUP(ap->a_dvp, ap->a_vpp, cnp);
    }

    if (error != 0 || (cnp->cn_flags & SAVESTART) == 0)
#if defined(__APPLE__)
	_FREE (cnp->cn_pnbuf, M_NAMEI);
#else
	free (cnp->cn_pnbuf, M_NAMEI);
#endif

    return error;
}

static int
xfs_remove(void *vap)
{
#ifdef struct_comment
    struct vop_remove_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_dvp;
	struct vnode *a_vp;
	struct componentname *a_cnp;
    };
#endif				       /* struct_comment */
    struct vop_remove_args *ap = vap;
    struct vnode *dvp = ap->a_dvp;
    struct vnode *vp  = ap->a_vp;
    struct xfs *xfsp  = XFS_FROM_VNODE(dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(dvp);
    struct componentname *cnp = ap->a_cnp;
    struct xfs_message_remove msg;
    int error;

    XFSDEB(XDEBVNOPS, ("xfs_remove: (%s, %ld\n",
		       cnp->cn_nameptr,
		       cnp->cn_namelen));

    msg.header.opcode = XFS_MSG_REMOVE;
    msg.parent_handle = xn->handle;
    strncpy(msg.name, cnp->cn_nameptr, 256);
    msg.cred.uid = cnp->cn_cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cnp->cn_cred);
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) &msg)->error;

    if (error == 0)
	cache_purge (vp);

    if (dvp == vp)
	vrele(vp);
    else
	vput(vp);
    vput(dvp);

    if (error != 0 || (cnp->cn_flags & SAVESTART) == 0)
#if defined(__APPLE__)
	_FREE (cnp->cn_pnbuf, M_NAMEI);
#else
	free (cnp->cn_pnbuf, M_NAMEI);
#endif

    return error;
}

static int
xfs_rename(void *vap)
{
#ifdef struct_comment
    struct vop_rename_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_fdvp;
	struct vnode *a_fvp;
	struct componentname *a_fcnp;
	struct vnode *a_tdvp;
	struct vnode *a_tvp;
	struct componentname *a_tcnp;
    };
#endif				       /* struct_comment */
    struct vop_rename_args *ap = vap;

    struct vnode *fdvp = ap->a_fdvp;
    struct vnode *fvp  = ap->a_fvp;
    struct componentname *fcnp = ap->a_fcnp;
    struct vnode *tdvp = ap->a_tdvp;
    struct vnode *tvp  = ap->a_tvp;
    struct componentname *tcnp = ap->a_tcnp;
    struct xfs *xfsp = XFS_FROM_VNODE(fdvp);
    int error;

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

    if ((fvp->v_mount != tdvp->v_mount)
	|| (tvp && (fvp->v_mount != tvp->v_mount))) {
	error = EXDEV;
	goto abort;
    }

    if (tvp) {
	struct xfs_message_remove msg;

	msg.header.opcode = XFS_MSG_REMOVE;
	msg.parent_handle = VNODE_TO_XNODE(tdvp)->handle;
	strncpy(msg.name, tcnp->cn_nameptr, 256);
	msg.cred.uid = tcnp->cn_cred->cr_uid;
	msg.cred.pag = xfs_get_pag(tcnp->cn_cred);
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	    error = ((struct xfs_message_wakeup *) &msg)->error;

	if (error)
	    goto abort;

	vrele(tvp);
	tvp = NULL;
    }

    {
	struct xfs_message_rename msg;

	msg.header.opcode = XFS_MSG_RENAME;
	msg.old_parent_handle = VNODE_TO_XNODE(fdvp)->handle;
	strncpy(msg.old_name, fcnp->cn_nameptr, 256);
	msg.new_parent_handle = VNODE_TO_XNODE(tdvp)->handle;
	strncpy(msg.new_name, tcnp->cn_nameptr, 256);
	msg.cred.uid = tcnp->cn_cred->cr_uid;
	msg.cred.pag = xfs_get_pag(tcnp->cn_cred);
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	    error = ((struct xfs_message_wakeup *) &msg)->error;

    }


abort:
    VOP_ABORTOP(tdvp, tcnp);
    if(tdvp == tvp)
	vrele(tdvp);
    else
	vput(tdvp);
    if(tvp)
	vput(tvp);
    VOP_ABORTOP(fdvp, fcnp);
    vrele(fdvp);
    vrele(fvp);
    return error;
}

static int
xfs_mkdir(void *vap)
{
#ifdef struct_comment
    struct vop_mkdir_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_dvp;
	struct vnode **a_vpp;
	struct componentname *a_cnp;
	struct vattr *a_vap;
    };
#endif				       /* struct_comment */
    struct vop_mkdir_args *ap = vap;

    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_dvp);
    struct componentname *cnp = ap->a_cnp;
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_mkdir\n"));
    {
	struct xfs_message_mkdir msg;

	msg.header.opcode = XFS_MSG_MKDIR;
	msg.parent_handle = xn->handle;
	strncpy(msg.name, cnp->cn_nameptr, 256);
	vattr2xfs_attr(ap->a_vap, &msg.attr);
	if (cnp->cn_cred != NOCRED) {
	    msg.cred.uid = cnp->cn_cred->cr_uid;
	    msg.cred.pag = xfs_get_pag(cnp->cn_cred);
	} else {
	    msg.cred.uid = 0;
	    msg.cred.pag = XFS_ANONYMOUSID;
	}
	error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (error == 0)
	    error = ((struct xfs_message_wakeup *) & msg)->error;
    }

    if (error == 0) {
	error = VOP_LOOKUP(ap->a_dvp, ap->a_vpp, cnp);
    }

    if (error != 0 || (cnp->cn_flags & SAVESTART) == 0)
#if defined(__APPLE__)
	_FREE (cnp->cn_pnbuf, M_NAMEI);
#else
	free (cnp->cn_pnbuf, M_NAMEI);
#endif

    return error;
}

static int
xfs_rmdir(void *vap)
{
#ifdef struct_comment
    struct vop_rmdir_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_dvp;
	struct vnode *a_vp;
	struct componentname *a_cnp;
    };
#endif				       /* struct_comment */
    struct vop_rmdir_args *ap = vap;

    struct vnode *dvp = ap->a_dvp;
    struct vnode *vp  = ap->a_vp;
    struct xfs *xfsp  = XFS_FROM_VNODE(dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(dvp);
    struct componentname *cnp = ap->a_cnp;
    struct xfs_message_rmdir msg;
    int error;

    XFSDEB(XDEBVNOPS, ("xfs_rmdir: (%s, %ld\n",
		       cnp->cn_nameptr,
		       cnp->cn_namelen));

    msg.header.opcode = XFS_MSG_RMDIR;
    msg.parent_handle = xn->handle;
    strncpy(msg.name, cnp->cn_nameptr, 256);
    msg.cred.uid = cnp->cn_cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cnp->cn_cred);
    error = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (error == 0)
	error = ((struct xfs_message_wakeup *) &msg)->error;

    if (error == 0)
	cache_purge(vp);

    if (dvp == vp)
	vrele(vp);
    else
	vput(vp);
    vput(dvp);

    if (error != 0 || (cnp->cn_flags & SAVESTART) == 0)
#if defined(__APPLE__)
	_FREE (cnp->cn_pnbuf, M_NAMEI);
#else
	free (cnp->cn_pnbuf, M_NAMEI);
#endif

    return error;
}

static int
xfs_readdir(void *vap)
{
#ifdef struct_comment
    struct vop_readdir_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct uio *a_uio;
	struct ucred *a_cred;
	int *a_eofflag;
	int *a_ncookies;
	u_long **a_cookies;
    };
#endif				       /* struct_comment */
    struct vop_readdir_args *ap = vap;

    int error = 0;

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

    error = xfs_data_valid(ap->a_vp, ap->a_cred, XFS_DATA_R);
    if (error == 0) {
	struct vnode *t = DATA_FROM_VNODE(ap->a_vp);

	vn_lock(t, LK_EXCLUSIVE | LK_RETRY, ap->a_uio->uio_procp);
	error = VOP_READ(t, ap->a_uio, 0, ap->a_cred);
	VOP_UNLOCK(t, 0, ap->a_uio->uio_procp);
    }
    return error;
}

static int
xfs_link(void *vap)
{
#ifdef struct_comment
    struct vop_link_args {
        struct vnodeop_desc *a_desc;
        struct vnode *a_dvp;
        struct vnode *a_vp;
        struct componentname *a_cnp;
    };
#endif
    struct vop_link_args *ap = vap;

#if defined(__APPLE__)
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_tdvp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_tdvp);
#else
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_dvp);
#endif
    struct xfs_node *xn2 = VNODE_TO_XNODE(ap->a_vp);
    struct componentname *cnp = ap->a_cnp;
    struct xfs_message_link msg;
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_link: (%s, %ld\n",
		       cnp->cn_nameptr,
		       cnp->cn_namelen));

    msg.header.opcode = XFS_MSG_LINK;
    msg.parent_handle = xn->handle;
    msg.from_handle   = xn2->handle;
    strncpy(msg.name, cnp->cn_nameptr, 256);
    msg.cred.uid = cnp->cn_cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cnp->cn_cred);

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

    if (error != 0 || (cnp->cn_flags & SAVESTART) == 0)
#if defined(__APPLE__)
	_FREE (cnp->cn_pnbuf, M_NAMEI);
#else
	free (cnp->cn_pnbuf, M_NAMEI);
#endif

    return error;
}

static int
xfs_symlink(void *vap)
{
#ifdef struct_comment
    struct vop_symlink_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_dvp;
	struct vnode **a_vpp;
	struct componentname *a_cnp;
	struct vattr *a_vap;
	char *a_target;
    };
#endif				       /* struct_comment */
    struct vop_symlink_args *ap = vap;

    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_dvp);
    struct componentname *cnp = ap->a_cnp;
    struct xfs_message_symlink msg;
    int error = 0;

    XFSDEB(XDEBVNOPS, ("xfs_symlink: (%s, %ld)\n",
		       cnp->cn_nameptr,
		       cnp->cn_namelen));

    msg.header.opcode = XFS_MSG_SYMLINK;
    msg.parent_handle = xn->handle;
    strncpy(msg.name, cnp->cn_nameptr, 256);
    vattr2xfs_attr(ap->a_vap, &msg.attr);
    msg.cred.uid = cnp->cn_cred->cr_uid;
    msg.cred.pag = xfs_get_pag(cnp->cn_cred);
    strncpy (msg.contents, ap->a_target, sizeof(msg.contents));

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

    if (error != 0 || (cnp->cn_flags & SAVESTART) == 0)
#if defined(__APPLE__)
	_FREE (cnp->cn_pnbuf, M_NAMEI);
#else
	free (cnp->cn_pnbuf, M_NAMEI);
#endif

    return error;
}

static int
xfs_readlink(void *vap)
{
#ifdef struct_comment
    struct vop_readlink_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct uio *a_uio;
	struct ucred *a_cred;
    };
#endif				       /* struct_comment */
    struct vop_readlink_args *ap = vap;

    int error = 0;

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

    error = xfs_data_valid(ap->a_vp, ap->a_cred, XFS_DATA_R);
    if (error == 0) {
	struct vnode *t = DATA_FROM_VNODE(ap->a_vp);

	vn_lock(t, LK_EXCLUSIVE | LK_RETRY, ap->a_uio->uio_procp);
	error = VOP_READ(t, ap->a_uio, 0, ap->a_cred);
	VOP_UNLOCK(t, 0, ap->a_uio->uio_procp);
    }
    return error;
}

static int
xfs_inactive(void *vap)
{
#ifdef struct_comment
    struct vop_inactive_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    struct vop_inactive_args *ap = vap;
    struct xfs_message_inactivenode msg;
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_vp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);

    XFSDEB(XDEBVNOPS, ("xfs_inactive, 0x%x\n", (int) ap->a_vp));

    xn->tokens = 0;
    msg.header.opcode = XFS_MSG_INACTIVENODE;
    msg.handle = xn->handle;
    msg.flag   = XFS_NOREFS;
    xfs_message_send(xfsp->fd, &msg.header, sizeof(msg));

    return 0;
}

static int
xfs_reclaim(void *vap)
{
#ifdef struct_comment
    struct vop_reclaim_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct proc *a_p;
    };
#endif /* struct_comment */

    struct vop_reclaim_args *ap = vap;

    struct xfs_message_inactivenode msg;
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_vp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);

    XFSDEB(XDEBVNOPS, ("xfs_reclaim, 0x%x\n", (int) ap->a_vp));

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

    cache_purge(ap->a_vp);
    free_xfs_node(xn);

    return 0;
}

static int
xfs_lock(void *vap)
{
#ifdef struct_comment
    struct vop_lock_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	int a_flags;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    /* struct vop_lock_args *ap = vap; */
    XFSDEB(XDEBVNOPS, ("xfs_lock\n"));
    return vop_nolock(vap);
}

static int
xfs_unlock(void *vap)
{
#ifdef struct_comment
    struct vop_unlock_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	int a_flags;
	struct proc *a_p;
    };
#endif				       /* struct_comment */
    XFSDEB(XDEBVNOPS, ("xfs_unlock\n"));
    return vop_nounlock(vap);
}

/*
 * XXX - For some odd reason this implementation of islocked is a bad idea.
 */

static int
xfs_islocked(void *vap)
{
#ifdef struct_comment
  struct vop_islocked_args {
    struct vnodeop_desc *a_desc;
    struct vnode *a_vp;
  };
#endif
  XFSDEB(XDEBVNOPS, ("xfs_islocked\n"));
  return vop_noislocked(vap);
}

static int
xfs_blkatoff(void *vap)
{
  XFSDEB(XDEBVNOPS, ("xfs_blkatoff\n"));
  return EOPNOTSUPP;
}

static int
xfs_valloc(void *vap)
{
  XFSDEB(XDEBVNOPS, ("xfs_valloc\n"));
  return EOPNOTSUPP;
}

static int
xfs_reallocblks(void *vap)
{
  XFSDEB(XDEBVNOPS, ("xfs_reallocblks\n"));
  return EOPNOTSUPP;
}

static int
xfs_bwrite(void *vap)
{
  XFSDEB(XDEBVNOPS, ("xfs_bwrite\n"));
  return EOPNOTSUPP;
}

#if 0

/*
 * XXX - These need to be implemented when mmap is implemented.
 */

static int
xfs_pagein(void *vap)
#ifdef struct comment_
struct vop_pagein_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct uio *a_uio;
	int a_ioflag;
	struct ucred *a_cred;
};
#endif
{
  return VOP_READ(vp, ap->a_uio, ap->a_ioflag, cred);
}

static int
xfs_pageout(void *vap)
#ifdef struct comment_
struct vop_pageout_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
	struct uio *a_uio;
	int a_ioflag;
	struct ucred *a_cred;
};
#endif
{
  return VOP_WRITE(vp, ap->a_uio, ap->a_ioflag, cred);
}

#endif

static int
xfs_abortop (void *vap)
{
    struct vop_abortop_args *ap = vap;
    struct componentname *cnp = ap->a_cnp;

    XFSDEB(XDEBVNOPS, ("xfs_abortop\n"));
    
    if ((cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF)
	FREE(cnp->cn_pnbuf, M_NAMEI);
    return 0;
}

static int
xfs_mmap(void *vap)
{
    XFSDEB(XDEBVNOPS, ("xfs_mmap\n"));
    return EOPNOTSUPP;
}

static int
xfs_bmap(void *vap)
{
    XFSDEB(XDEBVNOPS, ("xfs_bmap\n"));
    return EOPNOTSUPP;
}

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

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

static int
xfs_cntl(struct vnode * vp,
	 int cmd,
	 caddr_t idata,
	 caddr_t odata,
	 int iflag,
	 int oflag)
{
    XFSDEB(XDEBVNOPS, ("xfs_cntl\n"));
    return EOPNOTSUPP;
}

#endif


int (**xfs_vnodeop_p) __P((void *));

static struct vnodeopv_entry_desc xfs_vnodeop_entries[] = {
    {&vop_default_desc, vn_default_error},
    {&vop_lookup_desc, xfs_lookup},
    {&vop_create_desc, xfs_create},
    /* whiteout */
    /* mknod */
    /* mkcomplex */
    {&vop_open_desc, xfs_open},
    {&vop_close_desc, xfs_close},
    {&vop_access_desc, xfs_access},
    /* checkuseraccess */
    {&vop_getattr_desc, xfs_getattr},
    {&vop_setattr_desc, xfs_setattr},
    /* getattrlist */
    /* setattrlist */
    {&vop_read_desc, xfs_read},
    {&vop_write_desc, xfs_write},
    /* lease */
    {&vop_ioctl_desc, xfs_ioctl},
    {&vop_select_desc, xfs_select},
    /* exchange */
    /* revoke */
    {&vop_mmap_desc, xfs_mmap},
    {&vop_fsync_desc, xfs_fsync},
    {&vop_seek_desc, xfs_seek},
    {&vop_remove_desc, xfs_remove},
    {&vop_link_desc, xfs_link},
    {&vop_rename_desc, xfs_rename},
    {&vop_mkdir_desc, xfs_mkdir},
    {&vop_rmdir_desc, xfs_rmdir},
    {&vop_symlink_desc, xfs_symlink},
    {&vop_readdir_desc, xfs_readdir},
    /* readdirattr */
    {&vop_readlink_desc, xfs_readlink},
    {&vop_abortop_desc, xfs_abortop},
    {&vop_inactive_desc, xfs_inactive},
    {&vop_reclaim_desc, xfs_reclaim},
    {&vop_lock_desc, xfs_lock},
    {&vop_unlock_desc, xfs_unlock},
    {&vop_bmap_desc, xfs_bmap},
    /* print */
    /*    {&vop_islocked_desc, xfs_islocked}, */
    /* pathconf */
    /* advlock */
    {&vop_blkatoff_desc, xfs_blkatoff},
    {&vop_valloc_desc, xfs_valloc},
    {&vop_reallocblks_desc, xfs_reallocblks},
    /* vfree */
    /* truncate */
    /* update */
    /* pgrd */
    /* pgwr */
    /* strategy */
    {&vop_bwrite_desc, xfs_bwrite},
    {&vop_pagein_desc, /* xfs_pagein */ xfs_read},
    {&vop_pageout_desc, /* xfs_pageout */ xfs_write},
    {(struct vnodeop_desc *) NULL, (int (*) __P((void *))) NULL}
};


struct vnodeopv_desc xfs_vnodeop_opv_desc =
{&xfs_vnodeop_p, xfs_vnodeop_entries};
