/*
 * 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_locl.h"
#include <xfs_message.h>
#include <xfs_dev.h>
#include <xfs_common.h>
#include <xfs_fs.h>
#include <xfs_deb.h>
#include <xfs_syscalls.h>

RCSID("$Id: xfs_vnodeops.c,v 1.19 1998/02/22 20:43:00 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 errno = 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;
	    errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	    if (errno == 0)
		errno = ((struct xfs_message_wakeup *) & msg)->errno;
	} else {
	    goto done;
	}
    } while (errno == 0);

done:
    return errno;
}

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 errno = 0;

    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 = xfs_get_pag(cred);
	    msg.handle = xn->handle;
	    errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	    if (errno == 0)
		errno = ((struct xfs_message_wakeup *) & msg)->errno;
	} else {
	    goto done;
	}
    } while (errno == 0);

done:
    return errno;
}

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

done:
    return errno;
}

static int
do_fsync(struct xfs * xfsp,
	 struct xfs_node * xn,
	 struct ucred * cred,
	 u_int flag)
{
    int errno;
    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;
    errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (errno == 0)
	errno = ((struct xfs_message_wakeup *) & msg)->errno;

    return errno;
}

/*
 * vnode functions
 */

static int
xfs_open(struct vop_open_args * ap)
{
    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(struct vop_fsync_args * ap)
{
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_vp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);
    int errno = 0;

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

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

    if (xn == NULL)
	return 0;

    if (xn->flags & XFS_DATA_DIRTY)
	errno = do_fsync(xfsp, xn, ap->a_cred, O_FSYNC);
    return errno;
}

static int
xfs_close(struct vop_close_args * ap)
{
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_vp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);
    int errno = 0;
    u_int flag = 0;

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

    if (ap->a_fflag & FWRITE)
	flag |= XFS_WRITE;
    if (ap->a_fflag & FREAD)
	flag |= XFS_READ;

    errno = do_fsync(xfsp, xn, ap->a_cred, flag);

    return errno;
}

static int
xfs_read(struct vop_read_args * ap)
{
    int errno = 0;

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

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

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

	VREF(t);
	errno = VOP_READ(t, ap->a_uio, ap->a_ioflag, ap->a_cred);
	vrele(t);
    }
    return errno;
}

static int
xfs_write(struct vop_write_args * ap)
{
    int errno = 0;

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

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

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

	VREF(t);
	errno = VOP_WRITE(t, ap->a_uio, ap->a_ioflag, ap->a_cred);
	VNODE_TO_XNODE(ap->a_vp)->flags |= XFS_DATA_DIRTY;
	vrele(t);
    }
    return errno;
}

static int
xfs_ioctl(struct vop_ioctl_args * ap)
/* struct vnode *vp,
	  int com,
	  caddr_t data,
	  int flag,
	  struct ucred *cred) */
{
    XFSDEB(XDEBVNOPS, ("xfs_ioctl\n"));
    return EINVAL;
}

#ifdef USE_SELECT
static int
xfs_select(struct vop_select_args * ap)
/* struct vnode *vp,
	   int which,
	   struct ucred *cred ) */
{
    XFSDEB(XDEBVNOPS, ("xfs_select\n"));
    return EINVAL;
}

#endif

#ifdef USE_POLL
static int
xfs_poll(struct vop_poll_args * ap)
{
    XFSDEB(XDEBVNOPS, ("xfs_poll\n"));
    return EINVAL;
}

#endif

static int
xfs_getattr(struct vop_getattr_args * ap)
/* struct vnode *vp,
	    struct vattr *vap,
	    struct ucred *cred) */
{
    int errno = 0;

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

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

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

static int
xfs_setattr(struct vop_setattr_args * ap)
/*
struct vnode *vp,
	    struct vattr *vap,
	    struct ucred *cred)
	    */
{
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_vp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_vp);
    int errno = 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;
	errno = 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);
	errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (errno == 0)
	    errno = ((struct xfs_message_wakeup *) & msg)->errno;
	XFS_TOKEN_CLEAR(xn, XFS_ATTR_VALID, XFS_ATTR_MASK);
    }

done:
    return errno;
}

static int
xfs_access(struct vop_access_args * ap)
/*
struct vnode *vp,
	   int mode,
	   struct ucred *cred)
	   */
{
    int errno = 0;
    int mode = ap->a_mode;
    pag_t pag = xfs_get_pag(ap->a_cred);

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

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

	mode >>= 6;		       /* The kernel uses rwx------ */
	if (!((xn->id[0] == XFS_ANONYMOUSID)
	      && ((mode & ~xn->rights[0]) == 0))) {
	    int i;

	    errno = EACCES;	       /* Until otherwise proven */
	    for (i = 0; i < MAXRIGHTS; i++) {
		if ((xn->id[i] == pag)
		    && (mode & ~xn->rights[i])) {
		    errno = 0;
		    break;
		}
	    }
	}
    }
    XFSDEB(XDEBVNOPS, ("xfs_access(0%o) = %d\n", mode, errno));
    /*    return 0; *//* XXX For now! */
    return errno;
}

static int
xfs_lookup(struct vop_lookup_args * ap)
/* struct vop_lookup_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_dvp;
	struct vnode **a_vpp;
	struct componentname *a_cnp;
}; */
{
    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 vnode *v;
    int errno = 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
	errno = xfs_access(dvp, VEXEC, cred);
	if (errno != 0)
	    goto done;
#endif
	v = xfs_dnlc_lookup(dvp, cnp);
	if (v == NULL) {
	    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';

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

	    if (errno == 0)
		errno = ((struct xfs_message_wakeup *) & msg)->errno;
	    XFSDEB(XDEBVNOPS, ("xfs_lookup errno: %d\n", errno));
	} else {
	    *ap->a_vpp = v;
	    vget(v, 0);
#if 0
	    if (!v->v_usecount)
		vget(v, 0);
	    else
		vref(v);
#endif

	    goto done;
	}
    } while (errno == 0);

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

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

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

static int
xfs_create(struct vop_create_args * ap)
/* struct vnode *dvp,
	   char *nm,
	   struct vattr *va,
	   enum vcexcl exclusive,
	   int mode,
	   struct vnode **vpp,
	   struct ucred *cred)      */
{
    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 errno = 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;
	}
	errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (errno == 0)
	    errno = ((struct xfs_message_wakeup *) & msg)->errno;
    }

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

    if (errno != 0 || (cnp->cn_flags & SAVESTART) == 0)
	free (cnp->cn_pnbuf, M_NAMEI);

    return errno;
}

static int
xfs_remove(struct vop_remove_args * ap)
/* struct vnode *dvp,
   struct vnode *vp,
   struct componentname *cnp */
{
    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 errno;

    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);
    errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (errno == 0)
	errno = ((struct xfs_message_wakeup *) &msg)->errno;

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

    if (errno != 0 || (cnp->cn_flags & SAVESTART) == 0)
	free (cnp->cn_pnbuf, M_NAMEI);

    return errno;
}

static int
xfs_rename(struct vop_rename_args * ap)
/*
	IN WILLRELE struct vnode *fdvp;
	IN WILLRELE struct vnode *fvp;
	IN struct componentname *fcnp;
	IN WILLRELE struct vnode *tdvp;
	IN WILLRELE struct vnode *tvp;
	IN struct componentname *tcnp;
	*/
{
    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)->errno;

	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)->errno;

    }


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(struct vop_mkdir_args * ap)
/* struct vnode *dvp,
	  char *nm,
	  struct vattr *va,
	  struct vnode **vpp,
	  struct ucred *cred)      */
{
    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 errno = 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, ap->a_cnp->cn_nameptr, 256);
	vattr2xfs_attr(ap->a_vap, &msg.attr);
	if (ap->a_cnp->cn_cred != NOCRED) {
	    msg.cred.uid = ap->a_cnp->cn_cred->cr_uid;
	    msg.cred.pag = xfs_get_pag(ap->a_cnp->cn_cred);
	} else {
	    msg.cred.uid = 0;
	    msg.cred.pag = XFS_ANONYMOUSID;
	}
	errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
	if (errno == 0)
	    errno = ((struct xfs_message_wakeup *) & msg)->errno;
    }

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

    if (errno != 0 || (cnp->cn_flags & SAVESTART) == 0)
	free (cnp->cn_pnbuf, M_NAMEI);

    return errno;
}

static int
xfs_rmdir(struct vop_rmdir_args * ap)
/* struct vnode *dvp,
   struct vnode *vp,
   struct componentname *cnp */
{
    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 errno;

    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);
    errno = xfs_message_rpc(xfsp->fd, &msg.header, sizeof(msg));
    if (errno == 0)
	errno = ((struct xfs_message_wakeup *) &msg)->errno;

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

    if (errno != 0 || (cnp->cn_flags & SAVESTART) == 0)
	free (cnp->cn_pnbuf, M_NAMEI);

    return errno;
}

static int
xfs_readdir(struct vop_readdir_args * ap)
/* struct vnode *vp,
	    struct uio *uiop,
	    struct ucred *cred) */
{
    int errno = 0;

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

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

	VREF(t);
	errno = VOP_READ(t, ap->a_uio, 0, ap->a_cred);
	vrele(t);
    }
    return errno;
}

static int
xfs_link(struct vop_link_args * ap)
/*
	IN WILLRELE struct vnode *tdvp;
	IN struct vnode *vp;
	IN struct componentname *cnp;
	*/
{
#if defined (__OpenBSD__) || defined(__NetBSD__)
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_dvp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_dvp);
#elif defined(__FreeBSD__)
    struct xfs *xfsp = XFS_FROM_VNODE(ap->a_tdvp);
    struct xfs_node *xn = VNODE_TO_XNODE(ap->a_tdvp);
#endif
    struct xfs_node *xn2 = VNODE_TO_XNODE(ap->a_vp);
    struct componentname *cnp = ap->a_cnp;
    struct xfs_message_link msg;
    int errno = 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);

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

    if (errno != 0 || (cnp->cn_flags & SAVESTART) == 0)
	free (cnp->cn_pnbuf, M_NAMEI);

    return errno;
}

static int
xfs_symlink(struct vop_symlink_args * ap)
/*
  IN WILLRELE struct vnode *dvp;
  OUT WILLRELE struct vnode **vpp;
  IN struct componentname *cnp;
  IN struct vattr *vap;
  IN char *target;
  */
{
    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 errno = 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));

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

    if (errno != 0 || (cnp->cn_flags & SAVESTART) == 0)
	free (cnp->cn_pnbuf, M_NAMEI);

    return errno;
}

static int
xfs_readlink(struct vop_readlink_args * ap)
/* struct vnode *vp,
	     struct uio *uiop,
	     struct ucred *cred) */
{
    int errno = 0;

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

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

	VREF(t);
	errno = VOP_READ(t, ap->a_uio, 0, ap->a_cred);
	vrele(t);
    }
    return errno;
}

static int
xfs_inactive(struct vop_inactive_args * ap)
/*struct vnode *vp,
	     struct ucred *cred)*/
{
    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));

#if 0
    msg.header.opcode = XFS_MSG_INACTIVENODE;
    msg.handle = xn->handle;
    xfs_message_send(xfsp->fd, &msg.header, sizeof(msg));
#endif
    return 0;
}

static int
xfs_reclaim(struct vop_reclaim_args * ap)
/*struct vop_reclaim_args {
	struct vnodeop_desc *a_desc;
	struct vnode *a_vp;
};*/
{
    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;
    xfs_message_send(xfsp->fd, &msg.header, sizeof(msg));

    cache_purge(ap->a_vp);
    free_xfs_node(xn);
    return 0;
}

static int
xfs_lock(struct vop_lock_args * ap)
{
    XFSDEB(XDEBVNOPS, ("xfs_lock\n"));
    return EINVAL;
}

static int
xfs_unlock(struct vop_unlock_args * ap)
{
    XFSDEB(XDEBVNOPS, ("xfs_unlock\n"));
    return EINVAL;
}

static int
xfs_abortop (struct vop_abortop_args *ap)
/* struct vnode *dvp;
   struct componentname *cnp; */
{
    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;
}

#if 0
static int
xfs_mmap(struct vop_mmap_args * ap)
/*struct vnode *vp,
	u_int off,
	struct as *as,
	addr_t *addrp,
	u_int len,
	u_int prot,
	u_int maxprot,
	u_int flags,
	struct ucred *cred) */
{
    int errno = 0;

    XFSDEB(XDEBVNOPS, ("xfs_mmap\n"));
#if 0
    XFSDEB(XDEBVNOPS,
	 ("xfs_map(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
	  (int) ap->a_vp, off, (int) as, (int) addrp, len, prot, maxprot, flags, (int) cred));
#endif

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

    if (errno != 0)
	 /* Can't map today */ ;
    else if (off + len > VNODE_TO_XNODE(vp)->attr.va_size)
	errno = EINVAL;
    else if ((prot & PROT_WRITE) && (flags & MAP_SHARED))
	errno = EROFS;		       /* XXX This is currently not supported */
    else {
	struct vnode *t = DATA_FROM_VNODE(vp);

	VREF(t);
	errno = VOP_MAP(t, off, as, addrp, len, prot, maxprot, flags, cred);
	/* XXX Patch vnode so that we can intercept get/putpage and inactive. */
	vrele(t);
    }

    return errno;
}

#endif

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

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

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

#endif

#if defined(__NetBSD__) || defined(__OpenBSD__)

int (**xfs_vnodeop_p) (void *);

typedef int (*xfs_vop_t) (void *);

static struct vnodeopv_entry_desc xfs_vnodeop_entries[] = {
    {&vop_default_desc, (xfs_vop_t) vn_default_error},
    {&vop_lookup_desc, (xfs_vop_t) xfs_lookup},
    {&vop_open_desc, (xfs_vop_t) xfs_open},
    {&vop_fsync_desc, (xfs_vop_t) xfs_fsync},
    {&vop_close_desc, (xfs_vop_t) xfs_close},
    {&vop_read_desc, (xfs_vop_t) xfs_read},
    {&vop_write_desc, (xfs_vop_t) xfs_write},
    {&vop_ioctl_desc, (xfs_vop_t) xfs_ioctl},
#ifdef USE_SELECT
    {&vop_select_desc, (xfs_vop_t) xfs_select},
#endif
#ifdef USE_POLL
    {&vop_poll_desc, (xfs_vop_t) xfs_poll},
#endif
    {&vop_getattr_desc, (xfs_vop_t) xfs_getattr},
    {&vop_setattr_desc, (xfs_vop_t) xfs_setattr},
    {&vop_access_desc, (xfs_vop_t) xfs_access},
    {&vop_create_desc, (xfs_vop_t) xfs_create},
    {&vop_remove_desc, (xfs_vop_t) xfs_remove},
    {&vop_link_desc, (xfs_vop_t) xfs_link},
    {&vop_rename_desc,(xfs_vop_t)  xfs_rename},
    {&vop_mkdir_desc, (xfs_vop_t) xfs_mkdir},
    {&vop_rmdir_desc, (xfs_vop_t) xfs_rmdir},
    {&vop_readdir_desc, (xfs_vop_t) xfs_readdir},
    {&vop_symlink_desc, (xfs_vop_t) xfs_symlink},
    {&vop_readlink_desc, (xfs_vop_t) xfs_readlink},
    {&vop_inactive_desc,  (xfs_vop_t) xfs_inactive},
    {&vop_reclaim_desc,  (xfs_vop_t) xfs_reclaim},
    {&vop_lock_desc,  (xfs_vop_t) xfs_lock},
    {&vop_unlock_desc,  (xfs_vop_t) xfs_unlock},
    {&vop_abortop_desc, (xfs_vop_t) xfs_abortop},
    {(struct vnodeop_desc *) NULL, (int (*) (void *)) NULL}
};

struct vnodeopv_desc xfs_vnodeop_opv_desc =
{&xfs_vnodeop_p, xfs_vnodeop_entries};


#elif defined(__FreeBSD__)

vop_t **xfs_vnodeop_p;

static struct vnodeopv_entry_desc xfs_vnodeop_entries[] = {
    {&vop_default_desc, (vop_t *) vn_default_error},
    {&vop_lookup_desc, (vop_t *) xfs_lookup},
    {&vop_open_desc, (vop_t *) xfs_open},
    {&vop_fsync_desc, (vop_t *) xfs_fsync},
    {&vop_close_desc, (vop_t *) xfs_close},
    {&vop_read_desc, (vop_t *) xfs_read},
    {&vop_write_desc, (vop_t *) xfs_write},
    {&vop_ioctl_desc, (vop_t *) xfs_ioctl},
    {&vop_select_desc, (vop_t *) xfs_select},
    {&vop_getattr_desc, (vop_t *) xfs_getattr},
    {&vop_setattr_desc, (vop_t *) xfs_setattr},
    {&vop_access_desc, (vop_t *) xfs_access},
    {&vop_create_desc, (vop_t *) xfs_create},
    {&vop_remove_desc, (vop_t *) xfs_remove},
    {&vop_link_desc, (vop_t *) xfs_link},
    {&vop_rename_desc, (vop_t *) xfs_rename},
    {&vop_mkdir_desc, (vop_t *) xfs_mkdir},
    {&vop_rmdir_desc, (vop_t *) xfs_rmdir},
    {&vop_readdir_desc, (vop_t *) xfs_readdir},
    {&vop_symlink_desc, (vop_t *) xfs_symlink},
    {&vop_readlink_desc, (vop_t *) xfs_readlink},
    {&vop_inactive_desc, (vop_t *) xfs_inactive},
    {&vop_reclaim_desc, (vop_t *) xfs_reclaim},
    {&vop_lock_desc, (vop_t *) xfs_lock},
    {&vop_unlock_desc, (vop_t *) xfs_unlock},
    {&vop_abortop_desc, (vop_t *) xfs_abortop},
{NULL, NULL}};

struct vnodeopv_desc xfs_vnodeop_opv_desc =
{&xfs_vnodeop_p, xfs_vnodeop_entries};

VNODEOP_SET(xfs_vnodeop_opv_desc);

#endif
