66f807ed8b
MFC after: 3 months
349 lines
9.2 KiB
C
349 lines
9.2 KiB
C
/* $FreeBSD$ */
|
|
/* $Id: nfs_socket.c,v 1.12 2003/11/05 14:59:01 rees Exp $ */
|
|
|
|
/*-
|
|
* copyright (c) 2003
|
|
* 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.
|
|
*/
|
|
|
|
/*-
|
|
* Copyright (c) 1989, 1991, 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.
|
|
* 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_socket.c 8.5 (Berkeley) 3/30/95
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/*
|
|
* Socket operations for use by nfs
|
|
*/
|
|
|
|
#include "opt_inet6.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/protosw.h>
|
|
#include <sys/signalvar.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/socketvar.h>
|
|
#include <sys/syslog.h>
|
|
#include <sys/vnode.h>
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
|
|
#include <rpc/rpcclnt.h>
|
|
|
|
#include <nfs/rpcv2.h>
|
|
#include <nfs/nfsproto.h>
|
|
#include <nfsclient/nfs.h>
|
|
#include <nfs4client/nfs4.h>
|
|
#include <nfs/xdr_subs.h>
|
|
#include <nfsclient/nfsm_subs.h>
|
|
#include <nfsclient/nfsmount.h>
|
|
|
|
#ifdef NFS4_USE_RPCCLNT
|
|
#include <rpc/rpcclnt.h>
|
|
#include <rpc/rpcm_subs.h>
|
|
#endif
|
|
|
|
#ifdef NFS4_USE_RPCCLNT
|
|
static struct rpc_program nfs_program = {
|
|
NFS_PROG, NFS_VER4, "NFSv4"
|
|
};
|
|
#endif
|
|
|
|
|
|
static struct {
|
|
short nfserr;
|
|
short syserr;
|
|
} nfs_errtbl[] = {
|
|
{ NFS_OK, 0 },
|
|
{ NFSERR_PERM, EPERM },
|
|
{ NFSERR_NOENT, ENOENT },
|
|
{ NFSERR_IO, EIO },
|
|
{ NFSERR_NXIO, ENXIO },
|
|
{ NFSERR_ACCES, EACCES },
|
|
{ NFSERR_EXIST, EEXIST },
|
|
{ NFSERR_XDEV, EXDEV },
|
|
{ NFSERR_MLINK, EMLINK },
|
|
{ NFSERR_NODEV, ENODEV },
|
|
{ NFSERR_NOTDIR, ENOTDIR },
|
|
{ NFSERR_ISDIR, EISDIR },
|
|
{ NFSERR_INVAL, EINVAL },
|
|
{ NFSERR_FBIG, EFBIG },
|
|
{ NFSERR_NOSPC, ENOSPC },
|
|
{ NFSERR_ROFS, EROFS },
|
|
{ NFSERR_MLINK, EMLINK },
|
|
{ NFSERR_NAMETOL, ENAMETOOLONG },
|
|
{ NFSERR_NOTEMPTY, ENOTEMPTY },
|
|
{ NFSERR_NOTSUPP, EOPNOTSUPP },
|
|
#ifdef EDQUOT
|
|
{ NFSERR_DQUOT, EDQUOT },
|
|
#endif
|
|
{ NFSERR_STALE, ESTALE },
|
|
{ NFSERR_DENIED, EAGAIN },
|
|
{ NFSERR_SYMLINK, ELOOP },
|
|
{ NFSERR_BADXDR, EBADRPC },
|
|
{ NFSERR_WRONGSEC, EPERM },
|
|
{ -1, EIO }
|
|
};
|
|
|
|
static int
|
|
nfs4_nfserr_to_syserr(int nfserr)
|
|
{
|
|
int i, syserr;
|
|
|
|
/* XXX : not the optimal algorithm, but will do for now! */
|
|
for (i = 0; nfs_errtbl[i].nfserr != -1; i++) {
|
|
if (nfs_errtbl[i].nfserr == nfserr)
|
|
break;
|
|
}
|
|
#ifdef NFS4_MAP_UNKNOWN_ERR
|
|
syserr = nfs_errtbl[i].syserr;
|
|
#else
|
|
if (nfs_errtbl[i].nfserr != -1)
|
|
syserr = nfs_errtbl[i].syserr;
|
|
else
|
|
syserr = nfserr;
|
|
#endif
|
|
return syserr;
|
|
}
|
|
|
|
int
|
|
nfs4_connect(struct nfsmount *nmp)
|
|
{
|
|
struct rpcclnt * rpc = &nmp->nm_rpcclnt;
|
|
struct rpc_auth * auth;
|
|
int flag = 0;
|
|
int error;
|
|
|
|
/* XXX hack! */
|
|
#ifdef __OpenBSD__
|
|
struct proc * td = curproc;
|
|
#else
|
|
struct thread * td = curthread;
|
|
#endif
|
|
|
|
auth = malloc(sizeof(struct rpc_auth), M_TEMP, M_WAITOK);
|
|
auth->auth_type = RPCAUTH_UNIX;
|
|
|
|
/* translate nfs flags -> rpcclnt flags */
|
|
if (nmp->nm_flag & NFSMNT_SOFT)
|
|
flag |= RPCCLNT_SOFT;
|
|
|
|
if (nmp->nm_flag & NFSMNT_INT)
|
|
flag |= RPCCLNT_INT;
|
|
|
|
if (nmp->nm_flag & NFSMNT_NOCONN)
|
|
flag |= RPCCLNT_NOCONN;
|
|
|
|
if (nmp->nm_flag & NFSMNT_DUMBTIMR)
|
|
flag |= RPCCLNT_DUMBTIMR;
|
|
|
|
/* rpc->rc_servername = nmp->nm_mountp->mnt_stat.f_mntfromname; */
|
|
|
|
error = rpcclnt_setup(rpc, &nfs_program, nmp->nm_nam, nmp->nm_sotype,
|
|
nmp->nm_soproto, auth,
|
|
/* XXX: check nmp->nm_flag to make sure these are set */
|
|
(nmp->nm_rsize > nmp->nm_readdirsize) ? nmp->nm_rsize : nmp->nm_readdirsize,
|
|
nmp->nm_wsize, flag);
|
|
|
|
/* set deadthresh, timeo, retry */
|
|
rpc->rc_deadthresh = nmp->nm_deadthresh;
|
|
rpc->rc_timeo = nmp->nm_timeo;
|
|
rpc->rc_retry = nmp->nm_retry;
|
|
|
|
|
|
if (error)
|
|
return error;
|
|
|
|
return rpcclnt_connect(rpc, td);
|
|
}
|
|
|
|
/*
|
|
* NFS disconnect. Clean up and unlink.
|
|
*/
|
|
void
|
|
nfs4_disconnect(struct nfsmount *nmp)
|
|
{
|
|
rpcclnt_disconnect(&nmp->nm_rpcclnt);
|
|
}
|
|
|
|
void
|
|
nfs4_safedisconnect(struct nfsmount *nmp)
|
|
{
|
|
rpcclnt_safedisconnect(&nmp->nm_rpcclnt);
|
|
}
|
|
|
|
/*
|
|
* nfs_request - goes something like this
|
|
* - fill in request struct
|
|
* - links it into list
|
|
* - calls nfs_send() for first transmit
|
|
* - calls nfs_receive() to get reply
|
|
* - break down rpc header and return with nfs reply pointed to
|
|
* by mrep or error
|
|
* nb: always frees up mreq mbuf list
|
|
*/
|
|
/* XXX overloaded before */
|
|
#define NQ_TRYLATERDEL 15 /* Initial try later delay (sec) */
|
|
|
|
int
|
|
nfs4_request(struct vnode *vp, struct mbuf *mrest, int procnum,
|
|
struct thread *td, struct ucred *cred, struct mbuf **mrp,
|
|
struct mbuf **mdp, caddr_t *dposp)
|
|
{
|
|
int error;
|
|
|
|
error = nfs4_request_mnt(VFSTONFS(vp->v_mount), mrest, procnum,
|
|
td, cred, mrp, mdp, dposp);
|
|
|
|
/*
|
|
** If the File Handle was stale, invalidate the
|
|
** lookup cache, just in case.
|
|
**/
|
|
if (error == ESTALE)
|
|
cache_purge(vp);
|
|
|
|
return (error);
|
|
}
|
|
|
|
|
|
int
|
|
nfs4_request_mnt(struct nfsmount *nmp, struct mbuf *mrest, int procnum,
|
|
struct thread *td, struct ucred *cred, struct mbuf **mrp,
|
|
struct mbuf **mdp, caddr_t *dposp)
|
|
{
|
|
int error;
|
|
u_int32_t *tl;
|
|
struct rpcclnt * clnt = &nmp->nm_rpcclnt;
|
|
struct mbuf *md, *mrep;
|
|
caddr_t dpos;
|
|
struct rpc_reply reply;
|
|
|
|
if ((error = rpcclnt_request(clnt, mrest, procnum, td, cred,
|
|
&reply)) != 0) {
|
|
goto out;
|
|
}
|
|
|
|
/* XXX: don't free mrest if an error occured, to allow caller to retry*/
|
|
m_freem(mrest);
|
|
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(&fake_wchan, PSOCK, "nqnfstry", hz);
|
|
trylater_delay *= nfs_backoff[trylater_cnt];
|
|
if (trylater_cnt < NFS_NBACKOFF - 1)
|
|
trylater_cnt++;
|
|
goto tryagain;
|
|
}
|
|
#endif
|
|
goto out;
|
|
}
|
|
|
|
*mrp = mrep;
|
|
*mdp = md;
|
|
*dposp = dpos;
|
|
return (0);
|
|
nfsmout:
|
|
out:
|
|
m_freem(reply.mrep);
|
|
*mrp = NULL;
|
|
*mdp = NULL;
|
|
return (nfs4_nfserr_to_syserr(error));
|
|
}
|
|
|
|
|
|
/*
|
|
* Mark all of an nfs mount's outstanding requests with R_SOFTTERM and
|
|
* wait for all requests to complete. This is used by forced unmounts
|
|
* to terminate any outstanding RPCs.
|
|
*/
|
|
int
|
|
nfs4_nmcancelreqs(nmp)
|
|
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.
|
|
*/
|
|
int
|
|
nfs4_sigintr(struct nfsmount *nmp, struct nfsreq *rep, struct thread *td)
|
|
{
|
|
if (rep != NULL) {
|
|
printf("nfs_sigintr: attempting to use nfsreq != NULL\n");
|
|
return EINTR;
|
|
}
|
|
return rpcclnt_sigintr(&nmp->nm_rpcclnt, NULL, td);
|
|
}
|