/*
 * copyright (c) 2004
 * the regents of the university of michigan
 * all rights reserved
 * 
 * permission is granted to use, copy, create derivative works and redistribute
 * this software and such derivative works for any purpose, so long as the name
 * of the university of michigan is not used in any advertising or publicity
 * pertaining to the use or distribution of this software without specific,
 * written prior authorization.  if the above copyright notice or any other
 * identification of the university of michigan is included in any copy of any
 * portion of this software, then the disclaimer below must also be included.
 * 
 * this software is provided as is, without representation from the university
 * of michigan as to its fitness for any purpose, and without warranty by the
 * university of michigan of any kind, either express or implied, including
 * without limitation the implied warranties of merchantability and fitness for
 * a particular purpose. the regents of the university of michigan shall not be
 * liable for any damages, including special, indirect, incidental, or
 * consequential damages, with respect to any claim arising out of or in
 * connection with the use of the software, even if it has been or is hereafter
 * advised of the possibility of such damages.
 */

/* wrappers around rpcclnt */

/* XXX add tryagain code in nfs_request_xx */

#include <sys/cdefs.h>

#ifdef __FreeBSD__
__FBSDID("$FreeBSD$");
#endif

/*
 * Socket operations for use by nfs
 */

#ifdef __FreeBSD__
#include "opt_inet6.h"
#endif

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/mount.h>
#if defined(__APPLE__)
#include <sys/namei.h>
#endif

#include <sys/proc.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/vnode.h>

#include <rpcx/rpcclnt.h>
#include <rpcx/rpcm_subs.h>

#include <nfsx/rpcv2.h>
#include <nfsx/nfsproto.h>
#include <nfs4client/nfs4_glue.h> /* XXX */
#include <nfsxclient/nfs.h>
#include <nfs4client/nfs4.h>
#include <nfsx/xdr_subs.h>
#include <nfsxclient/nfsm_subs.h>
#include <nfsxclient/nfsmount.h>
#include <nfsx/nfs_common.h>
#include <nfsx/nfs_socket.h>

/* Flag translations */
#define nfsmnt_to_rpcclnt(nf, rf, name) do {	\
  if (ISSET(nf, NFSMNT_##name)) {		\
    SET(rf, RPCCLNT_##name);			\
  }						\
} while(0)

#if 0
static struct rpc_program nfs2_program = {
	NFS_PROG, NFS_VER2, "NFSv2"
};

static struct rpc_program nfs3_program = {
	NFS_PROG, NFS_VER3, "NFSv3"
};
#endif /* 0 */

static struct rpc_program nfs4_program = {
	NFS_PROG, NFS_VER4, "NFSv4"
};

/* XXXMARIUS: name collision */
int
nfsx_connect(struct nfsmount *nmp)
{
	struct rpcclnt * rpc; 
	int error = 0;
	cthread_t *ctd = curcthread;

	if (nmp == NULL)
		return EFAULT; 

	rpc = &nmp->nm_rpcclnt;
	
	rpc->rc_prog = &nfs4_program;	

	printf("nfsxconnect!\n");

/* 	if (ISSET(nmp->nm_flag, NFSMNT_NFSV3)) */
/* 		rpc->rc_prog = &nfs3_program; */
/* 	else if (ISSET(nmp->nm_flag, NFSMNT_NFSV4)) */
/* 		rpc->rc_prog = &nfs4_program; */
/* 	else */
/* 		rpc->rc_prog = &nfs2_program; */

	/* translate nfsmnt flags -> rpcclnt flags */
	rpc->rc_flag = 0;
	nfsmnt_to_rpcclnt(nmp->nm_flag, rpc->rc_flag, SOFT);
	nfsmnt_to_rpcclnt(nmp->nm_flag, rpc->rc_flag, INT);
	nfsmnt_to_rpcclnt(nmp->nm_flag, rpc->rc_flag, NOCONN);
	nfsmnt_to_rpcclnt(nmp->nm_flag, rpc->rc_flag, DUMBTIMR);

	rpc->rc_flag |= RPCCLNT_REDIRECT; /* Make this a mount option. */

	rpc->rc_authtype = RPCAUTH_UNIX; /* for now */
	rpc->rc_servername = nmp->nm_mountp->mnt_stat.f_mntfromname;
	rpc->rc_name = (struct sockaddr *)nmp->nm_nam;

	rpc->rc_sotype = nmp->nm_sotype;
	rpc->rc_soproto = nmp->nm_soproto;
	rpc->rc_rsize = (nmp->nm_rsize > nmp->nm_readdirsize) ? 
	    nmp->nm_rsize : nmp->nm_readdirsize;
	rpc->rc_wsize = nmp->nm_wsize;
	rpc->rc_deadthresh = nmp->nm_deadthresh;
	rpc->rc_timeo = nmp->nm_timeo;
	rpc->rc_retry = nmp->nm_retry;

	/* XXX v2,3 need to use this */
	rpc->rc_proctlen = 0;
	rpc->rc_proct = NULL;

	if (error)
		return error;

	return rpcclnt_connect(rpc, ctd);
}

/*
 * NFS disconnect. Clean up and unlink.
 */
/* XXXMARIUS: name collision */
void
nfsx_disconnect(struct nfsmount *nmp)
{
	rpcclnt_disconnect(&nmp->nm_rpcclnt);
}

void
nfsx_safedisconnect(struct nfsmount *nmp)
{
	rpcclnt_safedisconnect(&nmp->nm_rpcclnt);
}

int
nfsx_request_xx(struct nfsmount * nm, struct vnode *vp, struct mbuf *mrest, 
    int procnum, cthread_t *td, struct ucred *cred, struct mbuf **mrp,
    struct mbuf **mdp, caddr_t *dposp)
{
	int error;
	u_int32_t *tl;
	struct nfsmount * nmp;
	struct rpcclnt * clnt;
	struct mbuf *md, *mrep;
	caddr_t dpos;
	struct rpc_reply reply;
#if 0
	int t1;
	caddr_t cp2;
#endif /* 0 */
 
	if (vp != NULL)
       		nmp = VFSTONFS(vp->v_mount);
	else
	   	nmp = nm;

	clnt = &nmp->nm_rpcclnt;

#if 0
tryagain:
#endif

	memset(&reply, 0, sizeof(reply));

	if ((error = rpcclnt_request(clnt, mrest,
		 procnum, td, cred, &reply)) != 0)
		goto out;

	mrep = reply.mrep;
	md = reply.result_md;
	dpos = reply.result_dpos;

	tl = nfsm_dissect(u_int32_t *, NFSX_UNSIGNED);
	if (*tl != 0) {
		error = fxdr_unsigned(int, *tl);
#if 0
		if ((nmp->nm_flag & NFSMNT_NFSV3) &&
		    error == NFSERR_TRYLATER) {
			m_freem(mrep);
			error = 0;
			waituntil = time_second + trylater_delay;
			while (time_second < waituntil)
				(void) tsleep(&lbolt, PSOCK, "nqnfstry", 0);
			trylater_delay *= nfs_backoff[trylater_cnt];
			if (trylater_cnt < NFS_NBACKOFF - 1)
				trylater_cnt++;
			goto tryagain;
		}
#endif

		/*
 		** If the File Handle was stale, invalidate the
 		** lookup cache, just in case.
 		**/
		if (error == ESTALE)
			if (vp != NULL)
				cache_purge(vp);
			else
		  		printf("%s: ESTALE on mount from server  %s\n",
				    nmp->nm_rpcclnt.rc_prog->prog_name,
				    nmp->nm_rpcclnt.rc_servername);
		else
		  	printf("%s: unknown error %d from server %s\n",
			    nmp->nm_rpcclnt.rc_prog->prog_name, error,
			    nmp->nm_rpcclnt.rc_servername);
		goto out;
	}

	m_freem(mrest);

	*mrp = mrep;
	*mdp = md;
	*dposp = dpos;
	return (0);
nfsmout:
out:
	/* XXX: don't free mrest if an error occured, to allow caller to retry*/
	m_freem(mrest);
	m_freem(reply.mrep);
	*mrp = NULL;
	*mdp = NULL;

	return (error);
}


/*
 * terminate any outstanding RPCs.
 */
int
nfsx_nmcancelreqs(struct nfsmount *nmp)
{
	return rpcclnt_cancelreqs(&nmp->nm_rpcclnt);
}

/*
 * Test for a termination condition pending on the process.
 * This is used for NFSMNT_INT mounts.
 */
/* XXXMARIUS: name collision */
int
nfsx_sigintr(struct nfsmount *nmp, struct nfsreq *rep, cthread_t *td)
{
	if (nmp == NULL)
	  	return EFAULT;

	if (rep != NULL)
		printf("nfs_sigintr: attempting to use nfsreq != NULL\n");

	return rpcclnt_sigintr_extern(&nmp->nm_rpcclnt, td);
}
