/* $FreeBSD$ */
/* $Id: nfs4_vfsops.c,v 1.14 2004/06/17 17:39:54 muzzle Exp $ */

/*
 *  nfs4client/nfs4_vfsops.c
 *
 *  Copyright (c) 2003, 2004 The Regents of the University of Michigan.
 *  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. Neither the name of the University 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 ``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 REGENTS 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.
 */

/*
 * Copyright (c) 1989, 1993, 1995
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Rick Macklem at The University of Guelph.
 *
 * 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 University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
 *
 *	@(#)nfs_vfsops.c	8.12 (Berkeley) 5/20/95
 */

#include <sys/cdefs.h>
#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#endif

#ifdef __FreeBSD__
#include "opt_bootp.h"
#include "opt_nfsroot.h"
#endif /* __FreeBSD__ */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#ifdef __FreeBSD__
#include <sys/kthread.h>
#include <sys/limits.h>
#endif /* __FreeBSD__ */
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#ifdef __FreeBSD__
#include <sys/module.h>
#endif
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/unistd.h>
#include <sys/vnode.h>

#ifdef __FreeBSD__
#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/uma.h>
#endif /* __FreeBSD__ */

#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>

#include <nfs4client/nfs4_glue.h>

#include <rpcx/rpcclnt.h>
#include <nfsx/rpcv2.h>
#include <nfsx/nfsproto.h>
#include <nfsxclient/nfs.h>
#include <nfs4client/nfs4.h>
#include <nfsxclient/nfsnode.h>
#include <nfsxclient/nfsmount.h>
#include <nfsx/xdr_subs.h>
#include <nfsxclient/nfsm_subs.h>
#include <nfsxclient/nfsdiskless.h>
#include <nfsx/nfs_socket.h>

#include <nfsxclient/nfs_vfsops.h>

#include <nfs4client/nfs4m_subs.h>
#include <nfs4client/nfs4_vfs.h>
#include <nfs4client/nfs4_idmap.h>

#ifndef __FreeBSD__		/* XXX */
#include <nfs/nqnfs.h>		/* NQ_DEADTHRESH */
#endif

#include <nfs4client/nfs4m_glue.h>
#include <nfs4client/nfs4_ops.h>

#ifdef __APPLE__
/* XXXMARIUS: name collision */
extern struct nfsstats	nfsstats_nfsx;
#define nfsstats nfsstats_nfsx
#endif

SYSCTL_NODE(_vfs, OID_AUTO, nfs4, CTLFLAG_RW, 0, "NFS4 filesystem");
SYSCTL_STRUCT(_vfs_nfs4, NFS_NFSSTATS, nfsstats, CTLFLAG_RD,
    &nfsxstats, nfsstats, "S,nfsstats");

static void	nfs4_decode_args(struct nfsmount *, struct nfs_args *);
#ifdef __FreeBSD__
static void	nfs4_daemon(void *);
#endif 
static int	mountnfs(struct nfs_args *, struct mount *,
		    struct sockaddr *, char *, char *, struct vnode **,
		    struct ucred *);
static int	nfs4_do_setclientid(struct nfsmount *, struct ucred *);

#ifdef __FreeBSD__
static vfs_mount_t nfs4_mount;
static vfs_unmount_t nfs4_unmount;
static vfs_statfs_t nfs4_statfs;
#else
static int	nfs4_mount __P(( struct mount *mp, char *path, caddr_t data,
		    struct nameidata *ndp, cthread_t *p));
static int	nfs4_unmount __P(( struct mount *mp, int mntflags,
		    cthread_t *p));
static int	nfs4_statfs __P(( struct mount *mp, struct statfs *sbp,
		    cthread_t *p));
#endif /* __FreeBSD__ */

#ifdef __APPLE__
int fake_writebp(struct buf *, int, struct thread *); /* XXX??? */

int
fake_writebp(struct buf *bp, int force, struct thread *td)
{
	return (0);
}
#endif /* __APPLE__ */
 
#ifndef __FreeBSD__
extern vop_t **nfs4_vnodeop_p;
#endif
static struct nfsx_nfsops nfs4ops = {
	.nn_readrpc = nfs4_readrpc,
	.nn_writerpc = nfs4_writerpc,
#ifdef __APPLE__
	.nn_writebp = fake_writebp, /* NULL,	/\* XXX apple *\/ */
#else
	.nn_writebp = nfsx_writebp,
#endif
	.nn_readlinkrpc = nfs4_readlinkrpc,
	.nn_invaldir = nfs4_invaldir,
	.nn_commit = nfs4_commit,
	.nn_lookuprpc = nfs4_lookuprpc,
	.nn_lookuploadattr = nfs4_lookuploadattr,
	.nn_closerpc = nfs4_closerpc,
	.nn_mknodrpc = nfs4_mknodrpc,
	.nn_removerpc = nfs4_removerpc,
	.nn_renamerpc = nfs4_renamerpc,
	.nn_setattrrpc = nfs4_setattrrpc,
	.nn_readdirrpc = nfs4_readdirrpc,
	.nn_nfsname = "nfs4",
};

/*
 * nfs vfs operations.
 */
struct vfsops nfs4_vfsops = {
	.vfs_init =		nfs4_init,
	.vfs_mount =		nfs4_mount,
	.vfs_root =		nfsx_root,
	.vfs_statfs =		nfs4_statfs,
	.vfs_sync =		nfsx_sync,
	.vfs_unmount =		nfs4_unmount,
#ifdef __FreeBSD__		/* XXXMARIUS: apple? */
	.vfs_uninit =		nfs4_uninit,
#endif
#ifdef __APPLE__
	.vfs_vget =             nfsx_vget,
	.vfs_fhtovp =           nfsx_fhtovp,
	.vfs_vptofh =           nfsx_vptofh,
	.vfs_quotactl =         nfsx_quotactl,
	.vfs_start =            nfsx_start,
	/* XXX - sysctl */
#endif /* __APPLE__ */
};
#ifndef __APPLE__
VFS_SET(nfs4_vfsops, nfsv4, VFCF_NETWORK);
#endif

#ifdef __FreeBSD__
/* So that loader and kldload(2) can find us, wherever we are.. */
MODULE_VERSION(nfsv4, 1);
#endif 

void		nfsargs_ntoh(struct nfs_args *);

#ifdef __APPLE__
extern int      nfs_ticks;
#endif

#ifdef __APPLE__
extern int vfs_opv_numops;
extern struct vnodeopv_desc nfs4_vnodeop_opv_desc;
#endif /* __APPLE__ */
/* typedef int (*PFI)(); */
int
nfs4_init(struct vfsconf *vfsp)
{
#ifdef __APPLE__
{
	int (**opv_desc_vector) ();
	struct vnodeopv_entry_desc *opve_descp;
	int j;

	/* Parts from OpenDarwin/OpenAFS */
	MALLOC(nfs4_vnodeop_p, vop_t **, vfs_opv_numops * sizeof(vop_t *), M_TEMP,
	    M_WAITOK);
	bzero(nfs4_vnodeop_p, vfs_opv_numops * sizeof(vop_t));

	opv_desc_vector = nfs4_vnodeop_p;
	for (j = 0; nfs4_vnodeop_opv_desc.opv_desc_ops[j].opve_op; j++) {
		opve_descp = &(nfs4_vnodeop_opv_desc.opv_desc_ops[j]);

		/*
		 * Sanity check:  is this operation listed
		 * in the list of operations?  We check this
		 * by seeing if its offest is zero.  Since
		 * the default routine should always be listed
		 * first, it should be the only one with a zero
		 * offset.  Any other operation with a zero
		 * offset is probably not listed in
		 * vfs_op_descs, and so is probably an error.
		 *
		 * A panic here means the layer programmer
		 * has committed the all-too common bug
		 * of adding a new operation to the layer's
		 * list of vnode operations but
		 * not adding the operation to the system-wide
		 * list of supported operations.
		 */
		if (opve_descp->opve_op->vdesc_offset == 0
		    && opve_descp->opve_op->vdesc_offset !=
		    VOFFSET(vop_default)) {
			printf("nfs4_init: operation %s not listed in %s.\n",
			    opve_descp->opve_op->vdesc_name, "vfs_op_descs");
			panic("nfs4: bad operation");
		}
		/*
		 * Fill in this entry.
		 */
		opv_desc_vector[opve_descp->opve_op->vdesc_offset] =
		    opve_descp->opve_impl;
	}

	/*
	 * Finally, go back and replace unfilled routines
	 * with their default.  (Sigh, an O(n^3) algorithm.  I
	 * could make it better, but that'd be work, and n is small.)
	 */

	/*
	 * Force every operations vector to have a default routine.
	 */
	opv_desc_vector = nfs4_vnodeop_p;
	if (opv_desc_vector[VOFFSET(vop_default)] == NULL)
		panic("nfs4_init: operation vector without default routine.");
	for (j = 0; j < vfs_opv_numops; j++)
		if (opv_desc_vector[j] == NULL) {
			printf("missing %d\n", j);
			opv_desc_vector[j] =
			    opv_desc_vector[VOFFSET(vop_default)];
		}
}
#endif
 	rpcclnt_init();
 	idmap_init(); 
	nfsm_v4init();
	nfsx_init(vfsp);
	nfs4ops.nn_vnodeop_p = nfs4_vnodeop_p;

	return (0);
}

int
nfs4_uninit(struct vfsconf *vfsp)
{
	rpcclnt_uninit();
/* 	nfs4dev_uninit(); */
/* 	idmap_uninit(); */
	nfsx_uninit(vfsp);

	return (0);
}

/*
 * nfs statfs call
 */
static int
nfs4_statfs(struct mount *mp, struct statfs *sbp, cthread_t *td)
{
	struct vnode *vp;
	struct nfs_statfs *sfp;
	caddr_t bpos, dpos;
	struct nfsmount *nmp = VFSTONFS(mp);
	int error = 0;
	struct mbuf *mreq, *mrep = NULL, *md, *mb;
	struct nfsnode *np;
	struct nfs4_compound cp;
	struct nfs4_oparg_getattr ga;
	struct nfsv4_fattr *fap = &ga.fa;

#ifndef nolint
	sfp = NULL;
#endif
	error = nfsx_nget(mp, &nmp->nm_fh, nmp->nm_fhsize, &np);
	if (error)
		return (error);
	vp = NFSTOV(np);
	nfsxstats.rpccnt[NFSPROC_FSSTAT]++;
	mreq = nfsm_reqhead(vp, NFSV4PROC_COMPOUND, NFSX_FH(1));
	mb = mreq;
	bpos = mtod(mb, caddr_t);

	ga.bm = &nfsv4_fsattrbm;
	nfs_v4initcompound(&cp);

	nfsm_v4build_compound(&cp, "statfs()");
	nfsm_v4build_putfh(&cp, vp);
	nfsm_v4build_getattr(&cp, &ga);
	nfsm_v4build_finalize(&cp);

	nfsm_request(vp, NFSV4PROC_COMPOUND, td, cthread_ucred(td));

	if (error != 0)
		goto nfsmout;

	nfsm_v4dissect_compound(&cp);
	nfsm_v4dissect_putfh(&cp);
	nfsm_v4dissect_getattr(&cp, &ga);

	nfs4_vfsop_statfs(fap, sbp, mp);

nfsmout:
	error = nfs_v4postop(&cp, error);

	vput(vp);
	if (mrep != NULL)
		m_freem(mrep);

	return (error);
}

static void
nfs4_decode_args(struct nfsmount *nmp, struct nfs_args *argp)
{
	int s;
	int adjsock;
	int maxio;

	s = splnet();
	/*
	 * Silently clear NFSMNT_NOCONN if it's a TCP mount, it makes
	 * no sense in that context.
	 */
	if (argp->sotype == SOCK_STREAM)
		nmp->nm_flag &= ~NFSMNT_NOCONN;

	nmp->nm_flag &= ~NFSMNT_RDIRPLUS;

	/* Re-bind if rsrvd port requested and wasn't on one */
	adjsock = !(nmp->nm_flag & NFSMNT_RESVPORT)
		  && (argp->flags & NFSMNT_RESVPORT);
	/* Also re-bind if we're switching to/from a connected UDP socket */
	adjsock |= ((nmp->nm_flag & NFSMNT_NOCONN) !=
		    (argp->flags & NFSMNT_NOCONN));

	/* Update flags atomically.  Don't change the lock bits. */
	nmp->nm_flag = argp->flags | nmp->nm_flag | NFSMNT_NFSV3 | NFSMNT_NFSV4; /* XXXMARIUS DEBUG */
	splx(s);

	if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
		nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
		if (nmp->nm_timeo < NFS_MINTIMEO)
			nmp->nm_timeo = NFS_MINTIMEO;
		else if (nmp->nm_timeo > NFS_MAXTIMEO)
			nmp->nm_timeo = NFS_MAXTIMEO;
	}

	if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
		nmp->nm_retry = argp->retrans;
		if (nmp->nm_retry > NFS_MAXREXMIT)
			nmp->nm_retry = NFS_MAXREXMIT;
	}

	if (argp->flags & NFSMNT_NFSV3) {
		if (argp->sotype == SOCK_DGRAM)
			maxio = NFS_MAXDGRAMDATA;
		else
			maxio = NFS_MAXDATA;
	} else
		maxio = NFS_V2MAXDATA;

	if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
		nmp->nm_wsize = argp->wsize;
		/* Round down to multiple of blocksize */
		nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
		if (nmp->nm_wsize <= 0)
			nmp->nm_wsize = NFS_FABLKSIZE;
	}
	if (nmp->nm_wsize > maxio)
		nmp->nm_wsize = maxio;
	if (nmp->nm_wsize > MAXBSIZE)
		nmp->nm_wsize = MAXBSIZE;

	if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
		nmp->nm_rsize = argp->rsize;
		/* Round down to multiple of blocksize */
		nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
		if (nmp->nm_rsize <= 0)
			nmp->nm_rsize = NFS_FABLKSIZE;
	}
	if (nmp->nm_rsize > maxio)
		nmp->nm_rsize = maxio;
	if (nmp->nm_rsize > MAXBSIZE)
		nmp->nm_rsize = MAXBSIZE;

	if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
		nmp->nm_readdirsize = argp->readdirsize;
	}
	if (nmp->nm_readdirsize > maxio)
		nmp->nm_readdirsize = maxio;
	if (nmp->nm_readdirsize > nmp->nm_rsize)
		nmp->nm_readdirsize = nmp->nm_rsize;

#ifdef __FreeBSD__
	if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
		nmp->nm_acregmin = argp->acregmin;
	else
		nmp->nm_acregmin = NFS_MINATTRTIMO;
	if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
		nmp->nm_acregmax = argp->acregmax;
	else
		nmp->nm_acregmax = NFS_MAXATTRTIMO;
	if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
		nmp->nm_acdirmin = argp->acdirmin;
	else
		nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
	if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
		nmp->nm_acdirmax = argp->acdirmax;
	else
		nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
	if (nmp->nm_acdirmin > nmp->nm_acdirmax)
		nmp->nm_acdirmin = nmp->nm_acdirmax;
	if (nmp->nm_acregmin > nmp->nm_acregmax)
		nmp->nm_acregmin = nmp->nm_acregmax;
#endif /* __FreeBSD__ */
	if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0) {
		if (argp->maxgrouplist <= NFS_MAXGRPS)
			nmp->nm_numgrps = argp->maxgrouplist;
		else
			nmp->nm_numgrps = NFS_MAXGRPS;
	}
	if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0) {
		if (argp->readahead <= NFS_MAXRAHEAD)
			nmp->nm_readahead = argp->readahead;
		else
			nmp->nm_readahead = NFS_MAXRAHEAD;
	}
	if ((argp->flags & NFSMNT_DEADTHRESH) && argp->deadthresh >= 0) {
#ifdef __FreeBSD__
		if (argp->deadthresh <= NFS_MAXDEADTHRESH)
#else
		if (argp->deadthresh <= NQ_DEADTHRESH)
#endif /* __FreeBSD__ */
			nmp->nm_deadthresh = argp->deadthresh;
		else
#ifdef __FreeBSD__
			nmp->nm_deadthresh = NFS_MAXDEADTHRESH;
#else
			nmp->nm_deadthresh = NQ_DEADTHRESH;
#endif /* __FreeBSD__ */
	}

	adjsock |= ((nmp->nm_sotype != argp->sotype) ||
		    (nmp->nm_soproto != argp->proto));
	nmp->nm_sotype = argp->sotype;
	nmp->nm_soproto = argp->proto;

	if (nmp->nm_rpcclnt.rc_so && adjsock) {
		nfsx_safedisconnect(nmp);
		if (nmp->nm_sotype == SOCK_DGRAM) {
			while (nfsx_connect(nmp)) {
				printf("nfs_args: retrying connect\n");
				(void) tsleep((caddr_t)&lbolt,
					      PSOCK, "nfscon", 0);
			}
		}
	}
}

/*
 * VFS Operations.
 *
 * mount system call
 * It seems a bit dumb to copyinstr() the host and path here and then
 * bcopy() them in mountnfs(), but I wanted to detect errors before
 * doing the sockargs() call because sockargs() allocates an mbuf and
 * an error after that means that I have to release the mbuf.
 */
/* ARGSUSED */
static int
nfs4_mount(struct mount *mp, char *path, caddr_t data, struct nameidata *ndp,
    cthread_t *td)
{
	int error;
	struct nfs_args args;
	struct sockaddr *nam;
	struct vnode *vp;
	char hst[MNAMELEN];
#ifdef __APPLE__
	char pth[MNAMELEN];
#endif
	size_t len;

	if (path == NULL) {
		printf("NFSv4: nfs_mountroot not supported\n");
		return EINVAL;
	}

	error = copyin(data, (caddr_t)&args, sizeof (struct nfs_args));
	if (error)
		return (error);
	if (args.version != NFS_ARGSVERSION)
		return (EPROGMISMATCH);
	if (mp->mnt_flag & MNT_UPDATE) {
		struct nfsmount *nmp = VFSTONFS(mp);

		if (nmp == NULL)
			return (EIO);
		/*
		 * When doing an update, we can't change from or to
		 * v3, switch lockd strategies or change cookie translation
		 */
#ifdef __FreeBSD__
		/* XXX - OpenBSD */
#define __MASK (NFSMNT_NFSV3 | NFSMNT_NFSV4 | NFSMNT_NOLOCKD)
#else		
#define __MASK (NFSMNT_NFSV3 | NFSMNT_NFSV4)
#endif /* __FreeBSD__ */
		args.flags = (args.flags & ~__MASK) | (nmp->nm_flag & __MASK);
#undef __MASK
		nfs4_decode_args(nmp, &args);
		return (0);
	}

	if (error)
		return (error);
	error = copyinstr(args.hostname, hst, MNAMELEN - 1, &len);
	if (error)
		return (error);
	bzero(&hst[len], MNAMELEN - len);

#ifdef __APPLE__
	error = copyinstr(path, pth, MNAMELEN-1, &len);
	if (error)
		return (error);
	bzero(&pth[len], MNAMELEN - len);
#endif /* __APPLE__ */
	/* sockargs() call must be after above copyin() calls */
	error = getsockaddr(&nam, (caddr_t)args.addr, args.addrlen);
	if (error) {
		printf("this is the error\n");
		return (error);
	} else {
		struct sockaddr_in *sin = (void *)nam;
		printf("in nfs4_mount, we have port = %d\n",
		    ntohs(sin->sin_port));
	}
	

#ifdef __APPLE__
	error = mountnfs(&args, mp, nam, pth, hst, &vp, cthread_ucred(td));
#else
	error = mountnfs(&args, mp, nam, path, hst, &vp, cthread_ucred(td));
#endif
 
	return (error);
}

/*
 * renew should be done async
 * should re-scan mount queue each time
 */
#ifdef __FreeBSD__
struct proc *nfs4_daemonproc;
#endif

#ifndef __APPLE__ 			/* XXX -- darwin */
static int
nfs4_do_renew(struct nfsmount *nmp, struct ucred *cred)
{
	struct nfs4_compound cp;
	struct mbuf *mreq, *mrep = NULL, *md, *mb;
	caddr_t bpos, dpos;	
	int error;

	mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, sizeof(uint64_t));
	mb = mreq;
	bpos = mtod(mb, caddr_t);

	nfs_v4initcompound(&cp);

	nfsm_v4build_compound(&cp, "nfs4_do_renew()");
	nfsm_v4build_renew(&cp, nmp->nm_clientid);
	nfsm_v4build_finalize(&cp);

	nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curcthread, cred);

	if (error != 0)
		goto nfsmout;

	nfsm_v4dissect_compound(&cp);
	nfsm_v4dissect_renew(&cp);
	nmp->nm_last_renewal = time_second;
	return (0);

 nfsmout:
	error = nfs_v4postop(&cp, error);

	/* XXX */
	if (mrep != NULL)
		m_freem(mrep);
	return (error);
}
#endif

#ifdef __FreeBSD__
static void
nfs4_daemon(void *arg)
{
	struct mount *mp;
	struct nfsmount *nmp;
	int nmounts;

	while (1) {
		nmounts = 0;
		cmtx_lock(&mountlist_mtx);
#ifdef __FreeBSD__
		TAILQ_FOREACH(mp, &mountlist, mnt_list) {
#else
		CIRCLEQ_FOREACH(mp, &mountlist, mnt_list) {
#endif /* __FreeBSD__ */
			if (strcmp(mp->mnt_vfc->vfc_name, "nfs4") != 0)
				continue;
			nmounts++;
			nmp = VFSTONFS(mp);
			if (time_second < nmp->nm_last_renewal +
			    nmp->nm_lease_time - 4)
				continue;
			cmtx_unlock(&mountlist_mtx);
			cmtx_lock(&Giant);
			nfs4_do_renew(nmp, (struct ucred *) arg);
			cmtx_unlock(&Giant);
			cmtx_lock(&mountlist_mtx);
		}
		cmtx_unlock(&mountlist_mtx);

		/* Must kill the daemon here, or module unload will cause a panic */
		if (nmounts == 0) {
			cmtx_lock(&Giant);
			nfs4_daemonproc = NULL;
			printf("nfsv4(nfsv4) renewd exiting\n");
			kthread_exit(0);
		}
		tsleep(&nfs4_daemonproc, PVFS, "nfs4", 2 * hz);
	}
}
#endif /* __FreeBSD__ */

/*
 * Common code for mount and mountroot
 */
static int
mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
    char *pth, char *hst, struct vnode **vpp, struct ucred *cred)
{
	struct nfsmount *nmp;
	char *rpth, *cp1, *cp2;
	int nlkup = 0, error;
	struct nfs4_compound cp;
	struct mbuf *mreq, *mrep = NULL, *md, *mb;
	caddr_t bpos, dpos;	
	struct nfs4_oparg_lookup lkup;
	struct nfs4_oparg_getfh gfh;
	struct nfs4_oparg_getattr ga;
	cthread_t *td = curcthread; /* XXX */

	if (mp->mnt_flag & MNT_UPDATE) {
		nmp = VFSTONFS(mp);
		/* update paths, file handles, etc, here	XXX */
		FREE(nam, M_SONAME);
		return (0);
	} else {
		czone_alloc(nmp, struct nfsmount *,
		    sizeof(*nmp), czone_nfsmnt, M_WAITOK);
		bzero((caddr_t)nmp, sizeof (struct nfsmount));
		TAILQ_INIT(&nmp->nm_bufq);
		mp->mnt_data = (qaddr_t)nmp;
	}

	nmp->nm_nfsops = &nfs4ops;

	vfs_getnewfsid(mp);
	nmp->nm_mountp = mp;
#ifdef __FreeBSD__
	nmp->nm_maxfilesize = 0xffffffffLL;
#endif
	nmp->nm_timeo = NFS_TIMEO;
	nmp->nm_retry = NFS_RETRANS;
	nmp->nm_wsize = NFS_WSIZE;
	nmp->nm_rsize = NFS_RSIZE;
	nmp->nm_readdirsize = NFS_READDIRSIZE;
	nmp->nm_numgrps = NFS_MAXGRPS;
	nmp->nm_readahead = NFS_DEFRAHEAD;

#ifdef __APPLE__		/* XXX - OpenBSD */
        nmp->nm_leaseterm = NQ_DEFLEASE;
        nmp->nm_deadthresh = NQ_DEADTHRESH;
#else
	nmp->nm_deadthresh = NFS_MAXDEADTHRESH;
#endif /* __APPLE__ */

	if (hst == NULL || pth == NULL)
		return (EINVAL);

	bcopy(hst, mp->mnt_stat.f_mntfromname, MNAMELEN);
	bcopy(pth, mp->mnt_stat.f_mntonname, MNAMELEN);

	nmp->nm_nam = nam;
	/* Set up the sockets and per-host congestion */
	nmp->nm_sotype = argp->sotype;
	nmp->nm_soproto = argp->proto;
	nmp->nm_dvp = NULL;
	/* XXX */
        mp->mnt_stat.f_iosize = PAGE_SIZE;

	argp->flags |= NFSMNT_NFSV3|NFSMNT_NFSV4;

	nfs4_decode_args(nmp, argp);

	if ((error = nfsx_connect(nmp)))
		goto bad;

	mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1));
	mb = mreq;
	bpos = mtod(mb, caddr_t);

	ga.bm = &nfsv4_fsinfobm;
	nfs_v4initcompound(&cp);

	/* Get remote path */
	rpth = hst;
	strsep(&rpth, ":");

	nfsm_v4build_compound(&cp, "mountnfs()");
	nfsm_v4build_putrootfh(&cp);
	for (cp1 = rpth; cp1 && *cp1; cp1 = cp2)  {
		while (*cp1 == '/')
			cp1++;
		if (!*cp1)
			break;
		for (cp2 = cp1; *cp2 && *cp2 != '/'; cp2++)
			;
		lkup.name = cp1;
		lkup.namelen = cp2 - cp1;
		nfsm_v4build_lookup(&cp, &lkup);
		nlkup++;
	}
	nfsm_v4build_getfh(&cp, &gfh);
	nfsm_v4build_getattr(&cp, &ga);
	nfsm_v4build_finalize(&cp);

	nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, td, cred);
	if (error != 0)
		goto nfsmout;

	nfsm_v4dissect_compound(&cp);
	nfsm_v4dissect_putrootfh(&cp);
	while (nlkup--)
		nfsm_v4dissect_lookup(&cp);
	nfsm_v4dissect_getfh(&cp, &gfh);
	nfsm_v4dissect_getattr(&cp, &ga);

	nfs4_vfsop_fsinfo(&ga.fa, nmp);
	nmp->nm_state |= NFSSTA_GOTFSINFO;

	/* Copy root fh into nfsmount. */
	nmp->nm_fhsize = gfh.fh_len;
	bcopy(&gfh.fh_val, &nmp->nm_fh, nmp->nm_fhsize);
	nmp->nm_last_renewal = time_second;

#ifdef __APPLE__
{
	struct nfsnode *np;
	struct vattr va;
	 /* Just get the root vnode now. */

	error = nfsx_nget(mp, &nmp->nm_fh, nmp->nm_fhsize, &np);
	if (error)
		goto bad;

	/*
	 * save this vnode pointer. That way nfs_unmount()
	 * does not need to call nfs_net() just get it to drop
	 * this vnode reference.
	 */
	nmp->nm_dvp = NFSTOV(np);

/* 	vput(nmp->nm_dvp); */

	error = VOP_GETATTR(nmp->nm_dvp, &va,
	    cthread_ucred(curcthread), curcthread);
	if (error) {
		/*
		 * we got problems... we couldn't get the attributes
		 * from the NFS server... so the mount fails.
		 */
/* 		vput(nmp->nm_dvp); */
		goto bad;
	}

}
#endif /* __APPLE__ */

	if ((error = nfs4_do_setclientid(nmp, cred)) != 0)
		goto nfsmout;

#ifdef __APPLE__
	nmp->nm_mntcred = crdup(cred); /* XXX leak. */
#endif

#ifdef __FreeBSD__			/* XXX - others get system call "threads" */
	/* Start renewd if it isn't already running */
	if (nfs4_daemonproc == NULL)
		kthread_create(nfs4_daemon, crdup(cred), &nfs4_daemonproc,
			       (RFPROC|RFMEM), 0, "nfsv4rd");
#endif /* __FreeBSD__ */

	printf("finished kthread_create.  in nfsmount\n");

#ifdef __APPLE__
/* 	VOP_UNLOCK(nmp->nm_dvp, 0, curcthread); */
#endif

 nfsmout:
#ifdef __APPLE__
	if (nmp->nm_dvp != NULL)
		VOP_UNLOCK(nmp->nm_dvp, 0, curcthread);
#endif
	error = nfs_v4postop(&cp, error); /* XXX don't return before this...*/

	/* XXX */
	if (mrep != NULL)
		m_freem(mrep);

	return (0);
bad:
	/* XXX */
	if (mrep != NULL)
		m_freem(mrep);

	nfsx_disconnect(nmp);
	czone_free(nmp, sizeof(*nmp), czone_nfsmnt);
	FREE(nam, M_SONAME);

	return (error);
}

/*
 * unmount system call
 */
static int
nfs4_unmount(struct mount *mp, int mntflags, cthread_t *td)
{
	struct nfsmount *nmp;
	int error, flags = 0;

	if (mntflags & MNT_FORCE)
		flags |= FORCECLOSE;
	nmp = VFSTONFS(mp);
	/*
	 * Goes something like this..
	 * - Call vflush() to clear out vnodes for this filesystem
	 * - Close the socket
	 * - Free up the data structures
	 */
	/* In the forced case, cancel any outstanding requests. */
	if (flags & FORCECLOSE) {
		error = nfsx_nmcancelreqs(nmp);
		if (error)
			return (error);
		/* XXX */
/* 		nfs4dev_purge(); */
	}

	error = vflush(mp, 0, flags);
	if (error)
		return (error);

	/* XXXMARIUS: apple.  we are releasing this here, because we
	 * kept a reference in mountnfs(). */
#ifdef __APPLE__
	vrele(nmp->nm_dvp);
#endif

	/*
	 * We are now committed to the unmount.
	 */
	nfsx_disconnect(nmp);
	FREE(nmp->nm_nam, M_SONAME);

#ifdef __FreeBSD__
	/* XXX there's a race condition here for SMP */
	wakeup(&nfs4_daemonproc);
#endif

	czone_free(nmp, sizeof(*nmp), czone_nfsmnt);
	return (0);
}

static int
nfs4_do_setclientid(struct nfsmount *nmp, struct ucred *cred)
{
	struct nfs4_oparg_setclientid scid;
	struct nfs4_compound cp;
	struct mbuf *mreq, *mrep = NULL, *md, *mb;
	caddr_t bpos, dpos;	
	struct route ro;
	char *ipsrc = NULL, uaddr[24], name[24];
	int try = 0;
	static unsigned long seq;
	int error;

	if (nmp->nm_clientid) {
		printf("nfs4_do_setclientid: already have clientid!\n");
		error = 0;
		goto nfsmout;
	}

	/* Try not to re-use clientids */
	if (seq == 0)
		seq = time_second;

	scid.cb_netid = (nmp->nm_rpcclnt.rc_sotype == SOCK_STREAM) ? "tcp" : "udp";
	scid.cb_netid = "tcp";
	scid.cb_netidlen = 3;
	scid.cb_prog = 0x1234; /* XXX */

	/*
	 * Do a route lookup to find our source address for talking to
	 * this server.
	 */
	bzero(&ro, sizeof ro);

	ro.ro_dst = *nmp->nm_rpcclnt.rc_name;
	rtalloc(&ro);
	if (ro.ro_rt == NULL) {
		error = EHOSTUNREACH;
		goto nfsmout;
	}
	ipsrc = inet_ntoa(IA_SIN(ifatoia(ro.ro_rt->rt_ifa))->sin_addr);
	sprintf(uaddr, "%s.12.48", ipsrc);
	scid.cb_univaddr = uaddr;
	scid.cb_univaddrlen = strlen(uaddr);
	RTFREE(ro.ro_rt);

 try_again:
	sprintf(name, "%s-%d", ipsrc, (int) ((seq + try) % 1000000L));
	scid.namelen = strlen(name);
	scid.name = name;
	nfs_v4initcompound(&cp);

	mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1));
	mb = mreq;
	bpos = mtod(mb, caddr_t);

	nfsm_v4build_compound(&cp, "nfs4_do_setclientid()");
	nfsm_v4build_setclientid(&cp, &scid);
	nfsm_v4build_finalize(&cp);

	nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curcthread, cred);
	if (error != 0)
		goto nfsmout;

	nfsm_v4dissect_compound(&cp);
	nfsm_v4dissect_setclientid(&cp, &scid);
	nmp->nm_clientid = scid.clientid;

	error = nfs_v4postop(&cp, error);

	/* Confirm */
	m_freem(mrep);
	mreq = nfsm_reqhead(NULL, NFSV4PROC_COMPOUND, NFSX_FH(1));
	mb = mreq;
	bpos = mtod(mb, caddr_t);

	nfs_v4initcompound(&cp);

	nfsm_v4build_compound(&cp, "nfs4_do_setclientid() (confirm)");
	nfsm_v4build_setclientid_confirm(&cp, &scid);
	nfsm_v4build_finalize(&cp);

	nfsm_request_mnt(nmp, NFSV4PROC_COMPOUND, curcthread, cred);
	if (error != 0)
		goto nfsmout;

	nfsm_v4dissect_compound(&cp);
	nfsm_v4dissect_setclientid_confirm(&cp);

 nfsmout:
	error = nfs_v4postop(&cp, error);

	if (mrep)
		m_freem(mrep);
	if (error == NFSERR_CLID_INUSE && (++try < NFS4_SETCLIENTID_MAXTRIES))
		goto try_again;

	return (error);
}
