nfs_commonkrpc.c revision 220752
1191783Srmacklem/*-
2191783Srmacklem * Copyright (c) 1989, 1991, 1993, 1995
3191783Srmacklem *	The Regents of the University of California.  All rights reserved.
4191783Srmacklem *
5191783Srmacklem * This code is derived from software contributed to Berkeley by
6191783Srmacklem * Rick Macklem at The University of Guelph.
7191783Srmacklem *
8191783Srmacklem * Redistribution and use in source and binary forms, with or without
9191783Srmacklem * modification, are permitted provided that the following conditions
10191783Srmacklem * are met:
11191783Srmacklem * 1. Redistributions of source code must retain the above copyright
12191783Srmacklem *    notice, this list of conditions and the following disclaimer.
13191783Srmacklem * 2. Redistributions in binary form must reproduce the above copyright
14191783Srmacklem *    notice, this list of conditions and the following disclaimer in the
15191783Srmacklem *    documentation and/or other materials provided with the distribution.
16191783Srmacklem * 4. Neither the name of the University nor the names of its contributors
17191783Srmacklem *    may be used to endorse or promote products derived from this software
18191783Srmacklem *    without specific prior written permission.
19191783Srmacklem *
20191783Srmacklem * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21191783Srmacklem * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22191783Srmacklem * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23191783Srmacklem * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24191783Srmacklem * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25191783Srmacklem * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26191783Srmacklem * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27191783Srmacklem * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28191783Srmacklem * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29191783Srmacklem * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30191783Srmacklem * SUCH DAMAGE.
31191783Srmacklem *
32191783Srmacklem */
33191783Srmacklem
34191783Srmacklem#include <sys/cdefs.h>
35191783Srmacklem__FBSDID("$FreeBSD: head/sys/fs/nfs/nfs_commonkrpc.c 220752 2011-04-17 20:01:32Z rmacklem $");
36191783Srmacklem
37191783Srmacklem/*
38191783Srmacklem * Socket operations for use by nfs
39191783Srmacklem */
40191783Srmacklem
41191783Srmacklem#include "opt_inet6.h"
42191783Srmacklem#include "opt_kgssapi.h"
43191783Srmacklem#include "opt_nfs.h"
44191783Srmacklem
45191783Srmacklem#include <sys/param.h>
46191783Srmacklem#include <sys/systm.h>
47191783Srmacklem#include <sys/kernel.h>
48191783Srmacklem#include <sys/limits.h>
49191783Srmacklem#include <sys/lock.h>
50191783Srmacklem#include <sys/malloc.h>
51191783Srmacklem#include <sys/mbuf.h>
52191783Srmacklem#include <sys/mount.h>
53191783Srmacklem#include <sys/mutex.h>
54191783Srmacklem#include <sys/proc.h>
55191783Srmacklem#include <sys/signalvar.h>
56191783Srmacklem#include <sys/syscallsubr.h>
57191783Srmacklem#include <sys/sysctl.h>
58191783Srmacklem#include <sys/syslog.h>
59191783Srmacklem#include <sys/vnode.h>
60191783Srmacklem
61191783Srmacklem#include <rpc/rpc.h>
62191783Srmacklem
63191783Srmacklem#include <kgssapi/krb5/kcrypto.h>
64191783Srmacklem
65191783Srmacklem#include <fs/nfs/nfsport.h>
66191783Srmacklem
67191783SrmacklemNFSSTATESPINLOCK;
68191783SrmacklemNFSREQSPINLOCK;
69191783Srmacklemextern struct nfsstats newnfsstats;
70191783Srmacklemextern struct nfsreqhead nfsd_reqq;
71191783Srmacklemextern int nfscl_ticks;
72191783Srmacklemextern void (*ncl_call_invalcaches)(struct vnode *);
73191783Srmacklem
74191783Srmacklemstatic int	nfsrv_gsscallbackson = 0;
75191783Srmacklemstatic int	nfs_bufpackets = 4;
76191783Srmacklemstatic int	nfs_reconnects;
77191783Srmacklemstatic int	nfs3_jukebox_delay = 10;
78191783Srmacklemstatic int	nfs_skip_wcc_data_onerr = 1;
79191783Srmacklemstatic int	nfs_keytab_enctype = ETYPE_DES_CBC_CRC;
80191783Srmacklem
81191783SrmacklemSYSCTL_DECL(_vfs_newnfs);
82191783Srmacklem
83191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, bufpackets, CTLFLAG_RW, &nfs_bufpackets, 0,
84191783Srmacklem    "Buffer reservation size 2 < x < 64");
85191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, reconnects, CTLFLAG_RD, &nfs_reconnects, 0,
86191783Srmacklem    "Number of times the nfs client has had to reconnect");
87191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, nfs3_jukebox_delay, CTLFLAG_RW, &nfs3_jukebox_delay, 0,
88191783Srmacklem    "Number of seconds to delay a retry after receiving EJUKEBOX");
89191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, skip_wcc_data_onerr, CTLFLAG_RW, &nfs_skip_wcc_data_onerr, 0,
90191783Srmacklem    "Disable weak cache consistency checking when server returns an error");
91191783SrmacklemSYSCTL_INT(_vfs_newnfs, OID_AUTO, keytab_enctype, CTLFLAG_RW, &nfs_keytab_enctype, 0,
92191783Srmacklem    "Encryption type for the keytab entry used by nfs");
93191783Srmacklem
94191783Srmacklemstatic void	nfs_down(struct nfsmount *, struct thread *, const char *,
95191783Srmacklem    int, int);
96191783Srmacklemstatic void	nfs_up(struct nfsmount *, struct thread *, const char *,
97191783Srmacklem    int, int);
98191783Srmacklemstatic int	nfs_msg(struct thread *, const char *, const char *, int);
99191783Srmacklem
100191783Srmacklemstruct nfs_cached_auth {
101191783Srmacklem	int		ca_refs; /* refcount, including 1 from the cache */
102191783Srmacklem	uid_t		ca_uid;	 /* uid that corresponds to this auth */
103191783Srmacklem	AUTH		*ca_auth; /* RPC auth handle */
104191783Srmacklem};
105191783Srmacklem
106207764Srmacklemstatic int nfsv2_procid[NFS_V3NPROCS] = {
107207764Srmacklem	NFSV2PROC_NULL,
108207764Srmacklem	NFSV2PROC_GETATTR,
109207764Srmacklem	NFSV2PROC_SETATTR,
110207764Srmacklem	NFSV2PROC_LOOKUP,
111207764Srmacklem	NFSV2PROC_NOOP,
112207764Srmacklem	NFSV2PROC_READLINK,
113207764Srmacklem	NFSV2PROC_READ,
114207764Srmacklem	NFSV2PROC_WRITE,
115207764Srmacklem	NFSV2PROC_CREATE,
116207764Srmacklem	NFSV2PROC_MKDIR,
117207764Srmacklem	NFSV2PROC_SYMLINK,
118207764Srmacklem	NFSV2PROC_CREATE,
119207764Srmacklem	NFSV2PROC_REMOVE,
120207764Srmacklem	NFSV2PROC_RMDIR,
121207764Srmacklem	NFSV2PROC_RENAME,
122207764Srmacklem	NFSV2PROC_LINK,
123207764Srmacklem	NFSV2PROC_READDIR,
124207764Srmacklem	NFSV2PROC_NOOP,
125207764Srmacklem	NFSV2PROC_STATFS,
126207764Srmacklem	NFSV2PROC_NOOP,
127207764Srmacklem	NFSV2PROC_NOOP,
128207764Srmacklem	NFSV2PROC_NOOP,
129207764Srmacklem};
130207764Srmacklem
131191783Srmacklem/*
132191783Srmacklem * Initialize sockets and congestion for a new NFS connection.
133191783Srmacklem * We do not free the sockaddr if error.
134191783Srmacklem */
135191783Srmacklemint
136191783Srmacklemnewnfs_connect(struct nfsmount *nmp, struct nfssockreq *nrp,
137191783Srmacklem    struct ucred *cred, NFSPROC_T *p, int callback_retry_mult)
138191783Srmacklem{
139191783Srmacklem	int rcvreserve, sndreserve;
140191783Srmacklem	int pktscale;
141191783Srmacklem	struct sockaddr *saddr;
142191783Srmacklem	struct ucred *origcred;
143191783Srmacklem	CLIENT *client;
144191783Srmacklem	struct netconfig *nconf;
145191783Srmacklem	struct socket *so;
146220752Srmacklem	int one = 1, retries, error;
147191783Srmacklem	struct thread *td = curthread;
148191783Srmacklem
149191783Srmacklem	/*
150191783Srmacklem	 * We need to establish the socket using the credentials of
151191783Srmacklem	 * the mountpoint.  Some parts of this process (such as
152191783Srmacklem	 * sobind() and soconnect()) will use the curent thread's
153191783Srmacklem	 * credential instead of the socket credential.  To work
154191783Srmacklem	 * around this, temporarily change the current thread's
155191783Srmacklem	 * credential to that of the mountpoint.
156191783Srmacklem	 *
157191783Srmacklem	 * XXX: It would be better to explicitly pass the correct
158191783Srmacklem	 * credential to sobind() and soconnect().
159191783Srmacklem	 */
160191783Srmacklem	origcred = td->td_ucred;
161191783Srmacklem
162191783Srmacklem	/*
163191783Srmacklem	 * Use the credential in nr_cred, if not NULL.
164191783Srmacklem	 */
165191783Srmacklem	if (nrp->nr_cred != NULL)
166191783Srmacklem		td->td_ucred = nrp->nr_cred;
167191783Srmacklem	else
168191783Srmacklem		td->td_ucred = cred;
169191783Srmacklem	saddr = nrp->nr_nam;
170191783Srmacklem
171191783Srmacklem	if (saddr->sa_family == AF_INET)
172191783Srmacklem		if (nrp->nr_sotype == SOCK_DGRAM)
173191783Srmacklem			nconf = getnetconfigent("udp");
174191783Srmacklem		else
175191783Srmacklem			nconf = getnetconfigent("tcp");
176191783Srmacklem	else
177191783Srmacklem		if (nrp->nr_sotype == SOCK_DGRAM)
178191783Srmacklem			nconf = getnetconfigent("udp6");
179191783Srmacklem		else
180191783Srmacklem			nconf = getnetconfigent("tcp6");
181191783Srmacklem
182191783Srmacklem	pktscale = nfs_bufpackets;
183191783Srmacklem	if (pktscale < 2)
184191783Srmacklem		pktscale = 2;
185191783Srmacklem	if (pktscale > 64)
186191783Srmacklem		pktscale = 64;
187191783Srmacklem	/*
188191783Srmacklem	 * soreserve() can fail if sb_max is too small, so shrink pktscale
189191783Srmacklem	 * and try again if there is an error.
190191783Srmacklem	 * Print a log message suggesting increasing sb_max.
191191783Srmacklem	 * Creating a socket and doing this is necessary since, if the
192191783Srmacklem	 * reservation sizes are too large and will make soreserve() fail,
193191783Srmacklem	 * the connection will work until a large send is attempted and
194191783Srmacklem	 * then it will loop in the krpc code.
195191783Srmacklem	 */
196191783Srmacklem	so = NULL;
197191783Srmacklem	saddr = NFSSOCKADDR(nrp->nr_nam, struct sockaddr *);
198191783Srmacklem	error = socreate(saddr->sa_family, &so, nrp->nr_sotype,
199191783Srmacklem	    nrp->nr_soproto, td->td_ucred, td);
200191783Srmacklem	if (error) {
201191783Srmacklem		td->td_ucred = origcred;
202191783Srmacklem		return (error);
203191783Srmacklem	}
204191783Srmacklem	do {
205220752Srmacklem	    if (error != 0 && pktscale > 2)
206191783Srmacklem		pktscale--;
207191783Srmacklem	    if (nrp->nr_sotype == SOCK_DGRAM) {
208191783Srmacklem		if (nmp != NULL) {
209191783Srmacklem			sndreserve = (NFS_MAXDGRAMDATA + NFS_MAXPKTHDR) *
210191783Srmacklem			    pktscale;
211191783Srmacklem			rcvreserve = (NFS_MAXDGRAMDATA + NFS_MAXPKTHDR) *
212191783Srmacklem			    pktscale;
213191783Srmacklem		} else {
214191783Srmacklem			sndreserve = rcvreserve = 1024 * pktscale;
215191783Srmacklem		}
216191783Srmacklem	    } else {
217191783Srmacklem		if (nrp->nr_sotype != SOCK_STREAM)
218191783Srmacklem			panic("nfscon sotype");
219191783Srmacklem		if (nmp != NULL) {
220191783Srmacklem			sndreserve = (NFS_MAXBSIZE + NFS_MAXPKTHDR +
221191783Srmacklem			    sizeof (u_int32_t)) * pktscale;
222191783Srmacklem			rcvreserve = (NFS_MAXBSIZE + NFS_MAXPKTHDR +
223191783Srmacklem			    sizeof (u_int32_t)) * pktscale;
224191783Srmacklem		} else {
225191783Srmacklem			sndreserve = rcvreserve = 1024 * pktscale;
226191783Srmacklem		}
227191783Srmacklem	    }
228191783Srmacklem	    error = soreserve(so, sndreserve, rcvreserve);
229191783Srmacklem	} while (error != 0 && pktscale > 2);
230191783Srmacklem	soclose(so);
231191783Srmacklem	if (error) {
232191783Srmacklem		td->td_ucred = origcred;
233191783Srmacklem		return (error);
234191783Srmacklem	}
235191783Srmacklem
236191783Srmacklem	client = clnt_reconnect_create(nconf, saddr, nrp->nr_prog,
237191783Srmacklem	    nrp->nr_vers, sndreserve, rcvreserve);
238191783Srmacklem	CLNT_CONTROL(client, CLSET_WAITCHAN, "newnfsreq");
239191783Srmacklem	if (nmp != NULL) {
240191783Srmacklem		if ((nmp->nm_flag & NFSMNT_INT))
241191783Srmacklem			CLNT_CONTROL(client, CLSET_INTERRUPTIBLE, &one);
242191783Srmacklem		if ((nmp->nm_flag & NFSMNT_RESVPORT))
243191783Srmacklem			CLNT_CONTROL(client, CLSET_PRIVPORT, &one);
244191783Srmacklem		if (NFSHASSOFT(nmp))
245191783Srmacklem			retries = nmp->nm_retry;
246191783Srmacklem		else
247191783Srmacklem			retries = INT_MAX;
248191783Srmacklem	} else {
249191783Srmacklem		/*
250191783Srmacklem		 * Three cases:
251191783Srmacklem		 * - Null RPC callback to client
252191783Srmacklem		 * - Non-Null RPC callback to client, wait a little longer
253191783Srmacklem		 * - upcalls to nfsuserd and gssd (clp == NULL)
254191783Srmacklem		 */
255191783Srmacklem		if (callback_retry_mult == 0) {
256191783Srmacklem			retries = NFSV4_UPCALLRETRY;
257191783Srmacklem			CLNT_CONTROL(client, CLSET_PRIVPORT, &one);
258191783Srmacklem		} else {
259191783Srmacklem			retries = NFSV4_CALLBACKRETRY * callback_retry_mult;
260191783Srmacklem		}
261191783Srmacklem	}
262191783Srmacklem	CLNT_CONTROL(client, CLSET_RETRIES, &retries);
263191783Srmacklem
264191783Srmacklem	mtx_lock(&nrp->nr_mtx);
265191783Srmacklem	if (nrp->nr_client != NULL) {
266191783Srmacklem		/*
267191783Srmacklem		 * Someone else already connected.
268191783Srmacklem		 */
269191783Srmacklem		CLNT_RELEASE(client);
270191783Srmacklem	} else {
271191783Srmacklem		nrp->nr_client = client;
272191783Srmacklem	}
273191783Srmacklem
274191783Srmacklem	/*
275191783Srmacklem	 * Protocols that do not require connections may be optionally left
276191783Srmacklem	 * unconnected for servers that reply from a port other than NFS_PORT.
277191783Srmacklem	 */
278191783Srmacklem	if (nmp == NULL || (nmp->nm_flag & NFSMNT_NOCONN) == 0) {
279191783Srmacklem		mtx_unlock(&nrp->nr_mtx);
280191783Srmacklem		CLNT_CONTROL(client, CLSET_CONNECT, &one);
281191783Srmacklem	} else {
282191783Srmacklem		mtx_unlock(&nrp->nr_mtx);
283191783Srmacklem	}
284191783Srmacklem
285191783Srmacklem	/* Restore current thread's credentials. */
286191783Srmacklem	td->td_ucred = origcred;
287191783Srmacklem	return (0);
288191783Srmacklem}
289191783Srmacklem
290191783Srmacklem/*
291191783Srmacklem * NFS disconnect. Clean up and unlink.
292191783Srmacklem */
293191783Srmacklemvoid
294191783Srmacklemnewnfs_disconnect(struct nfssockreq *nrp)
295191783Srmacklem{
296191783Srmacklem	CLIENT *client;
297191783Srmacklem
298191783Srmacklem	mtx_lock(&nrp->nr_mtx);
299191783Srmacklem	if (nrp->nr_client != NULL) {
300191783Srmacklem		client = nrp->nr_client;
301191783Srmacklem		nrp->nr_client = NULL;
302191783Srmacklem		mtx_unlock(&nrp->nr_mtx);
303191783Srmacklem#ifdef KGSSAPI
304191783Srmacklem		rpc_gss_secpurge(client);
305191783Srmacklem#endif
306191783Srmacklem		CLNT_CLOSE(client);
307191783Srmacklem		CLNT_RELEASE(client);
308191783Srmacklem	} else {
309191783Srmacklem		mtx_unlock(&nrp->nr_mtx);
310191783Srmacklem	}
311191783Srmacklem}
312191783Srmacklem
313191783Srmacklemstatic AUTH *
314191783Srmacklemnfs_getauth(struct nfssockreq *nrp, int secflavour, char *clnt_principal,
315191783Srmacklem    char *srv_principal, gss_OID mech_oid, struct ucred *cred)
316191783Srmacklem{
317191783Srmacklem#ifdef KGSSAPI
318191783Srmacklem	rpc_gss_service_t svc;
319191783Srmacklem	AUTH *auth;
320192616Srmacklem#ifdef notyet
321191783Srmacklem	rpc_gss_options_req_t req_options;
322191783Srmacklem#endif
323192616Srmacklem#endif
324191783Srmacklem
325191783Srmacklem	switch (secflavour) {
326191783Srmacklem#ifdef KGSSAPI
327191783Srmacklem	case RPCSEC_GSS_KRB5:
328191783Srmacklem	case RPCSEC_GSS_KRB5I:
329191783Srmacklem	case RPCSEC_GSS_KRB5P:
330191783Srmacklem		if (!mech_oid) {
331191783Srmacklem			if (!rpc_gss_mech_to_oid("kerberosv5", &mech_oid))
332191783Srmacklem				return (NULL);
333191783Srmacklem		}
334191783Srmacklem		if (secflavour == RPCSEC_GSS_KRB5)
335191783Srmacklem			svc = rpc_gss_svc_none;
336191783Srmacklem		else if (secflavour == RPCSEC_GSS_KRB5I)
337191783Srmacklem			svc = rpc_gss_svc_integrity;
338191783Srmacklem		else
339191783Srmacklem			svc = rpc_gss_svc_privacy;
340192616Srmacklem#ifdef notyet
341191783Srmacklem		req_options.req_flags = GSS_C_MUTUAL_FLAG;
342191783Srmacklem		req_options.time_req = 0;
343191783Srmacklem		req_options.my_cred = GSS_C_NO_CREDENTIAL;
344191783Srmacklem		req_options.input_channel_bindings = NULL;
345191783Srmacklem		req_options.enc_type = nfs_keytab_enctype;
346191783Srmacklem
347191783Srmacklem		auth = rpc_gss_secfind(nrp->nr_client, cred,
348191783Srmacklem		    clnt_principal, srv_principal, mech_oid, svc,
349191783Srmacklem		    &req_options);
350192616Srmacklem#else
351192616Srmacklem		/*
352192616Srmacklem		 * Until changes to the rpcsec_gss code are committed,
353192616Srmacklem		 * there is no support for host based initiator
354192616Srmacklem		 * principals. As such, that case cannot yet be handled.
355192616Srmacklem		 */
356192616Srmacklem		if (clnt_principal == NULL)
357192616Srmacklem			auth = rpc_gss_secfind(nrp->nr_client, cred,
358192616Srmacklem			    srv_principal, mech_oid, svc);
359192616Srmacklem		else
360192616Srmacklem			auth = NULL;
361192616Srmacklem#endif
362192675Srmacklem		if (auth != NULL)
363192675Srmacklem			return (auth);
364192675Srmacklem		/* fallthrough */
365192616Srmacklem#endif	/* KGSSAPI */
366191783Srmacklem	case AUTH_SYS:
367191783Srmacklem	default:
368191783Srmacklem		return (authunix_create(cred));
369191783Srmacklem
370191783Srmacklem	}
371191783Srmacklem}
372191783Srmacklem
373191783Srmacklem/*
374191783Srmacklem * Callback from the RPC code to generate up/down notifications.
375191783Srmacklem */
376191783Srmacklem
377191783Srmacklemstruct nfs_feedback_arg {
378191783Srmacklem	struct nfsmount *nf_mount;
379191783Srmacklem	int		nf_lastmsg;	/* last tprintf */
380191783Srmacklem	int		nf_tprintfmsg;
381191783Srmacklem	struct thread	*nf_td;
382191783Srmacklem};
383191783Srmacklem
384191783Srmacklemstatic void
385191783Srmacklemnfs_feedback(int type, int proc, void *arg)
386191783Srmacklem{
387191783Srmacklem	struct nfs_feedback_arg *nf = (struct nfs_feedback_arg *) arg;
388191783Srmacklem	struct nfsmount *nmp = nf->nf_mount;
389191783Srmacklem	struct timeval now;
390191783Srmacklem
391191783Srmacklem	getmicrouptime(&now);
392191783Srmacklem
393191783Srmacklem	switch (type) {
394191783Srmacklem	case FEEDBACK_REXMIT2:
395191783Srmacklem	case FEEDBACK_RECONNECT:
396191783Srmacklem		if (nf->nf_lastmsg + nmp->nm_tprintf_delay < now.tv_sec) {
397191783Srmacklem			nfs_down(nmp, nf->nf_td,
398191783Srmacklem			    "not responding", 0, NFSSTA_TIMEO);
399191783Srmacklem			nf->nf_tprintfmsg = TRUE;
400191783Srmacklem			nf->nf_lastmsg = now.tv_sec;
401191783Srmacklem		}
402191783Srmacklem		break;
403191783Srmacklem
404191783Srmacklem	case FEEDBACK_OK:
405191783Srmacklem		nfs_up(nf->nf_mount, nf->nf_td,
406191783Srmacklem		    "is alive again", NFSSTA_TIMEO, nf->nf_tprintfmsg);
407191783Srmacklem		break;
408191783Srmacklem	}
409191783Srmacklem}
410191783Srmacklem
411191783Srmacklem/*
412191783Srmacklem * newnfs_request - goes something like this
413191783Srmacklem *	- does the rpc by calling the krpc layer
414191783Srmacklem *	- break down rpc header and return with nfs reply
415191783Srmacklem * nb: always frees up nd_mreq mbuf list
416191783Srmacklem */
417191783Srmacklemint
418191783Srmacklemnewnfs_request(struct nfsrv_descript *nd, struct nfsmount *nmp,
419191783Srmacklem    struct nfsclient *clp, struct nfssockreq *nrp, vnode_t vp,
420191783Srmacklem    struct thread *td, struct ucred *cred, u_int32_t prog, u_int32_t vers,
421191783Srmacklem    u_char *retsum, int toplevel, u_int64_t *xidp)
422191783Srmacklem{
423191783Srmacklem	u_int32_t *tl;
424191783Srmacklem	time_t waituntil;
425195642Srmacklem	int i, j, set_uid = 0, set_sigset = 0;
426191783Srmacklem	int trycnt, error = 0, usegssname = 0, secflavour = AUTH_SYS;
427191783Srmacklem	u_int16_t procnum;
428191783Srmacklem	u_int trylater_delay = 1;
429191783Srmacklem	struct nfs_feedback_arg nf;
430191783Srmacklem	struct timeval timo, now;
431191783Srmacklem	AUTH *auth;
432191783Srmacklem	struct rpc_callextra ext;
433191783Srmacklem	enum clnt_stat stat;
434191783Srmacklem	struct nfsreq *rep = NULL;
435191783Srmacklem	char *srv_principal = NULL;
436192675Srmacklem	uid_t saved_uid = (uid_t)-1;
437195642Srmacklem	sigset_t oldset;
438191783Srmacklem
439191783Srmacklem	if (xidp != NULL)
440191783Srmacklem		*xidp = 0;
441191783Srmacklem	/* Reject requests while attempting a forced unmount. */
442191783Srmacklem	if (nmp != NULL && (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)) {
443191783Srmacklem		m_freem(nd->nd_mreq);
444191783Srmacklem		return (ESTALE);
445191783Srmacklem	}
446191783Srmacklem
447195642Srmacklem	/* For client side interruptible mounts, mask off the signals. */
448195642Srmacklem	if (nmp != NULL && td != NULL && NFSHASINT(nmp)) {
449195642Srmacklem		newnfs_set_sigmask(td, &oldset);
450195642Srmacklem		set_sigset = 1;
451195642Srmacklem	}
452195642Srmacklem
453191783Srmacklem	/*
454192675Srmacklem	 * XXX if not already connected call nfs_connect now. Longer
455192675Srmacklem	 * term, change nfs_mount to call nfs_connect unconditionally
456192675Srmacklem	 * and let clnt_reconnect_create handle reconnects.
457192675Srmacklem	 */
458192675Srmacklem	if (nrp->nr_client == NULL)
459192675Srmacklem		newnfs_connect(nmp, nrp, cred, td, 0);
460192675Srmacklem
461192675Srmacklem	/*
462191783Srmacklem	 * For a client side mount, nmp is != NULL and clp == NULL. For
463191783Srmacklem	 * server calls (callbacks or upcalls), nmp == NULL.
464191783Srmacklem	 */
465191783Srmacklem	if (clp != NULL) {
466191783Srmacklem		NFSLOCKSTATE();
467191783Srmacklem		if ((clp->lc_flags & LCL_GSS) && nfsrv_gsscallbackson) {
468191783Srmacklem			secflavour = RPCSEC_GSS_KRB5;
469191783Srmacklem			if (nd->nd_procnum != NFSPROC_NULL) {
470191783Srmacklem				if (clp->lc_flags & LCL_GSSINTEGRITY)
471191783Srmacklem					secflavour = RPCSEC_GSS_KRB5I;
472191783Srmacklem				else if (clp->lc_flags & LCL_GSSPRIVACY)
473191783Srmacklem					secflavour = RPCSEC_GSS_KRB5P;
474191783Srmacklem			}
475191783Srmacklem		}
476191783Srmacklem		NFSUNLOCKSTATE();
477191783Srmacklem	} else if (nmp != NULL && NFSHASKERB(nmp) &&
478191783Srmacklem	     nd->nd_procnum != NFSPROC_NULL) {
479191783Srmacklem		if (NFSHASALLGSSNAME(nmp) && nmp->nm_krbnamelen > 0)
480191783Srmacklem			nd->nd_flag |= ND_USEGSSNAME;
481192675Srmacklem		if ((nd->nd_flag & ND_USEGSSNAME) != 0) {
482192675Srmacklem			/*
483192675Srmacklem			 * If there is a client side host based credential,
484192675Srmacklem			 * use that, otherwise use the system uid, if set.
485192675Srmacklem			 */
486192675Srmacklem			if (nmp->nm_krbnamelen > 0) {
487192675Srmacklem				usegssname = 1;
488192675Srmacklem			} else if (nmp->nm_uid != (uid_t)-1) {
489192675Srmacklem				saved_uid = cred->cr_uid;
490192675Srmacklem				cred->cr_uid = nmp->nm_uid;
491192675Srmacklem				set_uid = 1;
492192675Srmacklem			}
493192675Srmacklem		} else if (nmp->nm_krbnamelen == 0 &&
494192675Srmacklem		    nmp->nm_uid != (uid_t)-1 && cred->cr_uid == (uid_t)0) {
495192675Srmacklem			/*
496192675Srmacklem			 * If there is no host based principal name and
497192675Srmacklem			 * the system uid is set and this is root, use the
498192675Srmacklem			 * system uid, since root won't have user
499192675Srmacklem			 * credentials in a credentials cache file.
500192675Srmacklem			 */
501192675Srmacklem			saved_uid = cred->cr_uid;
502192675Srmacklem			cred->cr_uid = nmp->nm_uid;
503192675Srmacklem			set_uid = 1;
504192675Srmacklem		}
505191783Srmacklem		if (NFSHASINTEGRITY(nmp))
506191783Srmacklem			secflavour = RPCSEC_GSS_KRB5I;
507191783Srmacklem		else if (NFSHASPRIVACY(nmp))
508191783Srmacklem			secflavour = RPCSEC_GSS_KRB5P;
509191783Srmacklem		else
510191783Srmacklem			secflavour = RPCSEC_GSS_KRB5;
511191783Srmacklem		srv_principal = NFSMNT_SRVKRBNAME(nmp);
512191783Srmacklem	}
513191783Srmacklem
514191783Srmacklem	if (nmp != NULL) {
515191783Srmacklem		bzero(&nf, sizeof(struct nfs_feedback_arg));
516191783Srmacklem		nf.nf_mount = nmp;
517191783Srmacklem		nf.nf_td = td;
518191783Srmacklem		getmicrouptime(&now);
519191783Srmacklem		nf.nf_lastmsg = now.tv_sec -
520191783Srmacklem		    ((nmp->nm_tprintf_delay)-(nmp->nm_tprintf_initial_delay));
521191783Srmacklem	}
522191783Srmacklem
523192181Srmacklem	if (nd->nd_procnum == NFSPROC_NULL)
524192181Srmacklem		auth = authnone_create();
525192181Srmacklem	else if (usegssname)
526191783Srmacklem		auth = nfs_getauth(nrp, secflavour, nmp->nm_krbname,
527191783Srmacklem		    srv_principal, NULL, cred);
528191783Srmacklem	else
529191783Srmacklem		auth = nfs_getauth(nrp, secflavour, NULL,
530191783Srmacklem		    srv_principal, NULL, cred);
531192675Srmacklem	if (set_uid)
532192675Srmacklem		cred->cr_uid = saved_uid;
533191783Srmacklem	if (auth == NULL) {
534191783Srmacklem		m_freem(nd->nd_mreq);
535195642Srmacklem		if (set_sigset)
536195642Srmacklem			newnfs_restore_sigmask(td, &oldset);
537191783Srmacklem		return (EACCES);
538191783Srmacklem	}
539191783Srmacklem	bzero(&ext, sizeof(ext));
540191783Srmacklem	ext.rc_auth = auth;
541191783Srmacklem	if (nmp != NULL) {
542191783Srmacklem		ext.rc_feedback = nfs_feedback;
543191783Srmacklem		ext.rc_feedback_arg = &nf;
544191783Srmacklem	}
545191783Srmacklem
546191783Srmacklem	procnum = nd->nd_procnum;
547191783Srmacklem	if ((nd->nd_flag & ND_NFSV4) &&
548192181Srmacklem	    nd->nd_procnum != NFSPROC_NULL &&
549191783Srmacklem	    nd->nd_procnum != NFSV4PROC_CBCOMPOUND)
550191783Srmacklem		procnum = NFSV4PROC_COMPOUND;
551191783Srmacklem
552191783Srmacklem	if (nmp != NULL) {
553191783Srmacklem		NFSINCRGLOBAL(newnfsstats.rpcrequests);
554207764Srmacklem
555207764Srmacklem		/* Map the procnum to the old NFSv2 one, as required. */
556207764Srmacklem		if ((nd->nd_flag & ND_NFSV2) != 0) {
557207764Srmacklem			if (nd->nd_procnum < NFS_V3NPROCS)
558207764Srmacklem				procnum = nfsv2_procid[nd->nd_procnum];
559207764Srmacklem			else
560207764Srmacklem				procnum = NFSV2PROC_NOOP;
561207764Srmacklem		}
562207764Srmacklem
563191783Srmacklem		/*
564191783Srmacklem		 * Now only used for the R_DONTRECOVER case, but until that is
565191783Srmacklem		 * supported within the krpc code, I need to keep a queue of
566191783Srmacklem		 * outstanding RPCs for nfsv4 client requests.
567191783Srmacklem		 */
568191783Srmacklem		if ((nd->nd_flag & ND_NFSV4) && procnum == NFSV4PROC_COMPOUND)
569191783Srmacklem			MALLOC(rep, struct nfsreq *, sizeof(struct nfsreq),
570191783Srmacklem			    M_NFSDREQ, M_WAITOK);
571191783Srmacklem	}
572191783Srmacklem	trycnt = 0;
573191783Srmacklemtryagain:
574191783Srmacklem	if (nmp == NULL) {
575191783Srmacklem		timo.tv_usec = 0;
576191783Srmacklem		if (clp == NULL)
577191783Srmacklem			timo.tv_sec = NFSV4_UPCALLTIMEO;
578191783Srmacklem		else
579191783Srmacklem			timo.tv_sec = NFSV4_CALLBACKTIMEO;
580191783Srmacklem	} else {
581191783Srmacklem		if (nrp->nr_sotype != SOCK_DGRAM) {
582191783Srmacklem			timo.tv_usec = 0;
583191783Srmacklem			if ((nmp->nm_flag & NFSMNT_NFSV4))
584191783Srmacklem				timo.tv_sec = INT_MAX;
585191783Srmacklem			else
586191783Srmacklem				timo.tv_sec = NFS_TCPTIMEO;
587191783Srmacklem		} else {
588191783Srmacklem			timo.tv_sec = nmp->nm_timeo / NFS_HZ;
589191783Srmacklem			timo.tv_usec = (nmp->nm_timeo * 1000000) / NFS_HZ;
590191783Srmacklem		}
591191783Srmacklem
592191783Srmacklem		if (rep != NULL) {
593191783Srmacklem			rep->r_flags = 0;
594191783Srmacklem			rep->r_nmp = nmp;
595191783Srmacklem			/*
596191783Srmacklem			 * Chain request into list of outstanding requests.
597191783Srmacklem			 */
598191783Srmacklem			NFSLOCKREQ();
599191783Srmacklem			TAILQ_INSERT_TAIL(&nfsd_reqq, rep, r_chain);
600191783Srmacklem			NFSUNLOCKREQ();
601191783Srmacklem		}
602191783Srmacklem	}
603191783Srmacklem
604191783Srmacklem	nd->nd_mrep = NULL;
605191783Srmacklem	stat = CLNT_CALL_MBUF(nrp->nr_client, &ext, procnum, nd->nd_mreq,
606191783Srmacklem	    &nd->nd_mrep, timo);
607191783Srmacklem
608191783Srmacklem	if (rep != NULL) {
609191783Srmacklem		/*
610191783Srmacklem		 * RPC done, unlink the request.
611191783Srmacklem		 */
612191783Srmacklem		NFSLOCKREQ();
613191783Srmacklem		TAILQ_REMOVE(&nfsd_reqq, rep, r_chain);
614191783Srmacklem		NFSUNLOCKREQ();
615191783Srmacklem	}
616191783Srmacklem
617191783Srmacklem	/*
618191783Srmacklem	 * If there was a successful reply and a tprintf msg.
619191783Srmacklem	 * tprintf a response.
620191783Srmacklem	 */
621191783Srmacklem	if (stat == RPC_SUCCESS) {
622191783Srmacklem		error = 0;
623191783Srmacklem	} else if (stat == RPC_TIMEDOUT) {
624191783Srmacklem		error = ETIMEDOUT;
625191783Srmacklem	} else if (stat == RPC_VERSMISMATCH) {
626191783Srmacklem		error = EOPNOTSUPP;
627191783Srmacklem	} else if (stat == RPC_PROGVERSMISMATCH) {
628191783Srmacklem		error = EPROTONOSUPPORT;
629191783Srmacklem	} else {
630191783Srmacklem		error = EACCES;
631191783Srmacklem	}
632191783Srmacklem	if (error) {
633191783Srmacklem		m_freem(nd->nd_mreq);
634191783Srmacklem		AUTH_DESTROY(auth);
635191783Srmacklem		if (rep != NULL)
636191783Srmacklem			FREE((caddr_t)rep, M_NFSDREQ);
637195642Srmacklem		if (set_sigset)
638195642Srmacklem			newnfs_restore_sigmask(td, &oldset);
639191783Srmacklem		return (error);
640191783Srmacklem	}
641191783Srmacklem
642191783Srmacklem	KASSERT(nd->nd_mrep != NULL, ("mrep shouldn't be NULL if no error\n"));
643191783Srmacklem
644192695Srmacklem	/*
645192695Srmacklem	 * Search for any mbufs that are not a multiple of 4 bytes long
646192695Srmacklem	 * or with m_data not longword aligned.
647192695Srmacklem	 * These could cause pointer alignment problems, so copy them to
648192695Srmacklem	 * well aligned mbufs.
649192695Srmacklem	 */
650192695Srmacklem	newnfs_realign(&nd->nd_mrep);
651191783Srmacklem	nd->nd_md = nd->nd_mrep;
652191783Srmacklem	nd->nd_dpos = NFSMTOD(nd->nd_md, caddr_t);
653191783Srmacklem	nd->nd_repstat = 0;
654191783Srmacklem	if (nd->nd_procnum != NFSPROC_NULL) {
655191783Srmacklem		/*
656191783Srmacklem		 * and now the actual NFS xdr.
657191783Srmacklem		 */
658191783Srmacklem		NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
659191783Srmacklem		nd->nd_repstat = fxdr_unsigned(u_int32_t, *tl);
660191783Srmacklem		if (nd->nd_repstat != 0) {
661191783Srmacklem			if ((nd->nd_repstat == NFSERR_DELAY &&
662191783Srmacklem			     (nd->nd_flag & ND_NFSV4) &&
663191783Srmacklem			     nd->nd_procnum != NFSPROC_SETATTR &&
664191783Srmacklem			     nd->nd_procnum != NFSPROC_READ &&
665191783Srmacklem			     nd->nd_procnum != NFSPROC_WRITE &&
666191783Srmacklem			     nd->nd_procnum != NFSPROC_OPEN &&
667191783Srmacklem			     nd->nd_procnum != NFSPROC_CREATE &&
668191783Srmacklem			     nd->nd_procnum != NFSPROC_OPENCONFIRM &&
669191783Srmacklem			     nd->nd_procnum != NFSPROC_OPENDOWNGRADE &&
670191783Srmacklem			     nd->nd_procnum != NFSPROC_CLOSE &&
671191783Srmacklem			     nd->nd_procnum != NFSPROC_LOCK &&
672191783Srmacklem			     nd->nd_procnum != NFSPROC_LOCKU) ||
673191783Srmacklem			    (nd->nd_repstat == NFSERR_DELAY &&
674191783Srmacklem			     (nd->nd_flag & ND_NFSV4) == 0) ||
675191783Srmacklem			    nd->nd_repstat == NFSERR_RESOURCE) {
676191783Srmacklem				if (trylater_delay > NFS_TRYLATERDEL)
677191783Srmacklem					trylater_delay = NFS_TRYLATERDEL;
678191783Srmacklem				waituntil = NFSD_MONOSEC + trylater_delay;
679191783Srmacklem				while (NFSD_MONOSEC < waituntil)
680207170Srmacklem					(void) nfs_catnap(PZERO, 0, "nfstry");
681191783Srmacklem				trylater_delay *= 2;
682191783Srmacklem				goto tryagain;
683191783Srmacklem			}
684191783Srmacklem
685191783Srmacklem			/*
686191783Srmacklem			 * If the File Handle was stale, invalidate the
687191783Srmacklem			 * lookup cache, just in case.
688191783Srmacklem			 * (vp != NULL implies a client side call)
689191783Srmacklem			 */
690191783Srmacklem			if (nd->nd_repstat == ESTALE && vp != NULL) {
691191783Srmacklem				cache_purge(vp);
692191783Srmacklem				if (ncl_call_invalcaches != NULL)
693191783Srmacklem					(*ncl_call_invalcaches)(vp);
694191783Srmacklem			}
695191783Srmacklem		}
696191783Srmacklem
697191783Srmacklem		/*
698191783Srmacklem		 * Get rid of the tag, return count, and PUTFH result for V4.
699191783Srmacklem		 */
700191783Srmacklem		if (nd->nd_flag & ND_NFSV4) {
701191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
702191783Srmacklem			i = fxdr_unsigned(int, *tl);
703191783Srmacklem			error = nfsm_advance(nd, NFSM_RNDUP(i), -1);
704191783Srmacklem			if (error)
705191783Srmacklem				goto nfsmout;
706191783Srmacklem			NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
707191783Srmacklem			i = fxdr_unsigned(int, *++tl);
708191783Srmacklem
709191783Srmacklem			/*
710191783Srmacklem			 * If the first op's status is non-zero, mark that
711191783Srmacklem			 * there is no more data to process.
712191783Srmacklem			 */
713191783Srmacklem			if (*++tl)
714191783Srmacklem				nd->nd_flag |= ND_NOMOREDATA;
715191783Srmacklem
716191783Srmacklem			/*
717191783Srmacklem			 * If the first op is Putfh, throw its results away
718191783Srmacklem			 * and toss the op# and status for the first op.
719191783Srmacklem			 */
720191783Srmacklem			if (nmp != NULL && i == NFSV4OP_PUTFH && *tl == 0) {
721191783Srmacklem				NFSM_DISSECT(tl,u_int32_t *,2 * NFSX_UNSIGNED);
722191783Srmacklem				i = fxdr_unsigned(int, *tl++);
723191783Srmacklem				j = fxdr_unsigned(int, *tl);
724191783Srmacklem				/*
725191783Srmacklem				 * All Compounds that do an Op that must
726191783Srmacklem				 * be in sequence consist of NFSV4OP_PUTFH
727191783Srmacklem				 * followed by one of these. As such, we
728191783Srmacklem				 * can determine if the seqid# should be
729191783Srmacklem				 * incremented, here.
730191783Srmacklem				 */
731191783Srmacklem				if ((i == NFSV4OP_OPEN ||
732191783Srmacklem				     i == NFSV4OP_OPENCONFIRM ||
733191783Srmacklem				     i == NFSV4OP_OPENDOWNGRADE ||
734191783Srmacklem				     i == NFSV4OP_CLOSE ||
735191783Srmacklem				     i == NFSV4OP_LOCK ||
736191783Srmacklem				     i == NFSV4OP_LOCKU) &&
737191783Srmacklem				    (j == 0 ||
738191783Srmacklem				     (j != NFSERR_STALECLIENTID &&
739191783Srmacklem				      j != NFSERR_STALESTATEID &&
740191783Srmacklem				      j != NFSERR_BADSTATEID &&
741191783Srmacklem				      j != NFSERR_BADSEQID &&
742191783Srmacklem				      j != NFSERR_BADXDR &&
743191783Srmacklem				      j != NFSERR_RESOURCE &&
744191783Srmacklem				      j != NFSERR_NOFILEHANDLE)))
745191783Srmacklem					nd->nd_flag |= ND_INCRSEQID;
746191783Srmacklem				/*
747191783Srmacklem				 * If the first op's status is non-zero, mark
748191783Srmacklem				 * that there is no more data to process.
749191783Srmacklem				 */
750191783Srmacklem				if (j)
751191783Srmacklem					nd->nd_flag |= ND_NOMOREDATA;
752191783Srmacklem			}
753191783Srmacklem
754191783Srmacklem			/*
755191783Srmacklem			 * If R_DONTRECOVER is set, replace the stale error
756191783Srmacklem			 * reply, so that recovery isn't initiated.
757191783Srmacklem			 */
758191783Srmacklem			if ((nd->nd_repstat == NFSERR_STALECLIENTID ||
759191783Srmacklem			     nd->nd_repstat == NFSERR_STALESTATEID) &&
760191783Srmacklem			    rep != NULL && (rep->r_flags & R_DONTRECOVER))
761191783Srmacklem				nd->nd_repstat = NFSERR_STALEDONTRECOVER;
762191783Srmacklem		}
763192181Srmacklem	}
764191783Srmacklem
765192181Srmacklem	m_freem(nd->nd_mreq);
766192181Srmacklem	AUTH_DESTROY(auth);
767192181Srmacklem	if (rep != NULL)
768192181Srmacklem		FREE((caddr_t)rep, M_NFSDREQ);
769195642Srmacklem	if (set_sigset)
770195642Srmacklem		newnfs_restore_sigmask(td, &oldset);
771192181Srmacklem	return (0);
772191783Srmacklemnfsmout:
773191783Srmacklem	mbuf_freem(nd->nd_mrep);
774191783Srmacklem	mbuf_freem(nd->nd_mreq);
775191783Srmacklem	AUTH_DESTROY(auth);
776191783Srmacklem	if (rep != NULL)
777191783Srmacklem		FREE((caddr_t)rep, M_NFSDREQ);
778195642Srmacklem	if (set_sigset)
779195642Srmacklem		newnfs_restore_sigmask(td, &oldset);
780191783Srmacklem	return (error);
781191783Srmacklem}
782191783Srmacklem
783191783Srmacklem/*
784191783Srmacklem * Mark all of an nfs mount's outstanding requests with R_SOFTTERM and
785191783Srmacklem * wait for all requests to complete. This is used by forced unmounts
786191783Srmacklem * to terminate any outstanding RPCs.
787191783Srmacklem */
788191783Srmacklemint
789191783Srmacklemnewnfs_nmcancelreqs(struct nfsmount *nmp)
790191783Srmacklem{
791191783Srmacklem
792191783Srmacklem	if (nmp->nm_sockreq.nr_client != NULL)
793191783Srmacklem		CLNT_CLOSE(nmp->nm_sockreq.nr_client);
794191783Srmacklem	return (0);
795191783Srmacklem}
796191783Srmacklem
797191783Srmacklem/*
798191783Srmacklem * Any signal that can interrupt an NFS operation in an intr mount
799191783Srmacklem * should be added to this set. SIGSTOP and SIGKILL cannot be masked.
800191783Srmacklem */
801191783Srmacklemint newnfs_sig_set[] = {
802191783Srmacklem	SIGINT,
803191783Srmacklem	SIGTERM,
804191783Srmacklem	SIGHUP,
805191783Srmacklem	SIGKILL,
806191783Srmacklem	SIGSTOP,
807191783Srmacklem	SIGQUIT
808191783Srmacklem};
809191783Srmacklem
810191783Srmacklem/*
811191783Srmacklem * Check to see if one of the signals in our subset is pending on
812191783Srmacklem * the process (in an intr mount).
813191783Srmacklem */
814191783Srmacklemstatic int
815191783Srmacklemnfs_sig_pending(sigset_t set)
816191783Srmacklem{
817191783Srmacklem	int i;
818191783Srmacklem
819191783Srmacklem	for (i = 0 ; i < sizeof(newnfs_sig_set)/sizeof(int) ; i++)
820191783Srmacklem		if (SIGISMEMBER(set, newnfs_sig_set[i]))
821191783Srmacklem			return (1);
822191783Srmacklem	return (0);
823191783Srmacklem}
824191783Srmacklem
825191783Srmacklem/*
826191783Srmacklem * The set/restore sigmask functions are used to (temporarily) overwrite
827191783Srmacklem * the process p_sigmask during an RPC call (for example). These are also
828191783Srmacklem * used in other places in the NFS client that might tsleep().
829191783Srmacklem */
830191783Srmacklemvoid
831191783Srmacklemnewnfs_set_sigmask(struct thread *td, sigset_t *oldset)
832191783Srmacklem{
833191783Srmacklem	sigset_t newset;
834191783Srmacklem	int i;
835191783Srmacklem	struct proc *p;
836191783Srmacklem
837191783Srmacklem	SIGFILLSET(newset);
838191783Srmacklem	if (td == NULL)
839191783Srmacklem		td = curthread; /* XXX */
840191783Srmacklem	p = td->td_proc;
841191783Srmacklem	/* Remove the NFS set of signals from newset */
842191783Srmacklem	PROC_LOCK(p);
843191783Srmacklem	mtx_lock(&p->p_sigacts->ps_mtx);
844191783Srmacklem	for (i = 0 ; i < sizeof(newnfs_sig_set)/sizeof(int) ; i++) {
845191783Srmacklem		/*
846191783Srmacklem		 * But make sure we leave the ones already masked
847191783Srmacklem		 * by the process, ie. remove the signal from the
848191783Srmacklem		 * temporary signalmask only if it wasn't already
849191783Srmacklem		 * in p_sigmask.
850191783Srmacklem		 */
851191783Srmacklem		if (!SIGISMEMBER(td->td_sigmask, newnfs_sig_set[i]) &&
852191783Srmacklem		    !SIGISMEMBER(p->p_sigacts->ps_sigignore, newnfs_sig_set[i]))
853191783Srmacklem			SIGDELSET(newset, newnfs_sig_set[i]);
854191783Srmacklem	}
855191783Srmacklem	mtx_unlock(&p->p_sigacts->ps_mtx);
856191783Srmacklem	PROC_UNLOCK(p);
857191783Srmacklem	kern_sigprocmask(td, SIG_SETMASK, &newset, oldset, 0);
858191783Srmacklem}
859191783Srmacklem
860191783Srmacklemvoid
861191783Srmacklemnewnfs_restore_sigmask(struct thread *td, sigset_t *set)
862191783Srmacklem{
863191783Srmacklem	if (td == NULL)
864191783Srmacklem		td = curthread; /* XXX */
865191783Srmacklem	kern_sigprocmask(td, SIG_SETMASK, set, NULL, 0);
866191783Srmacklem}
867191783Srmacklem
868191783Srmacklem/*
869191783Srmacklem * NFS wrapper to msleep(), that shoves a new p_sigmask and restores the
870191783Srmacklem * old one after msleep() returns.
871191783Srmacklem */
872191783Srmacklemint
873191783Srmacklemnewnfs_msleep(struct thread *td, void *ident, struct mtx *mtx, int priority, char *wmesg, int timo)
874191783Srmacklem{
875191783Srmacklem	sigset_t oldset;
876191783Srmacklem	int error;
877191783Srmacklem	struct proc *p;
878191783Srmacklem
879191783Srmacklem	if ((priority & PCATCH) == 0)
880191783Srmacklem		return msleep(ident, mtx, priority, wmesg, timo);
881191783Srmacklem	if (td == NULL)
882191783Srmacklem		td = curthread; /* XXX */
883191783Srmacklem	newnfs_set_sigmask(td, &oldset);
884191783Srmacklem	error = msleep(ident, mtx, priority, wmesg, timo);
885191783Srmacklem	newnfs_restore_sigmask(td, &oldset);
886191783Srmacklem	p = td->td_proc;
887191783Srmacklem	return (error);
888191783Srmacklem}
889191783Srmacklem
890191783Srmacklem/*
891191783Srmacklem * Test for a termination condition pending on the process.
892191783Srmacklem * This is used for NFSMNT_INT mounts.
893191783Srmacklem */
894191783Srmacklemint
895191783Srmacklemnewnfs_sigintr(struct nfsmount *nmp, struct thread *td)
896191783Srmacklem{
897191783Srmacklem	struct proc *p;
898191783Srmacklem	sigset_t tmpset;
899191783Srmacklem
900191783Srmacklem	/* Terminate all requests while attempting a forced unmount. */
901191783Srmacklem	if (nmp->nm_mountp->mnt_kern_flag & MNTK_UNMOUNTF)
902191783Srmacklem		return (EIO);
903191783Srmacklem	if (!(nmp->nm_flag & NFSMNT_INT))
904191783Srmacklem		return (0);
905191783Srmacklem	if (td == NULL)
906191783Srmacklem		return (0);
907191783Srmacklem	p = td->td_proc;
908191783Srmacklem	PROC_LOCK(p);
909191783Srmacklem	tmpset = p->p_siglist;
910191783Srmacklem	SIGSETOR(tmpset, td->td_siglist);
911191783Srmacklem	SIGSETNAND(tmpset, td->td_sigmask);
912191783Srmacklem	mtx_lock(&p->p_sigacts->ps_mtx);
913191783Srmacklem	SIGSETNAND(tmpset, p->p_sigacts->ps_sigignore);
914191783Srmacklem	mtx_unlock(&p->p_sigacts->ps_mtx);
915191783Srmacklem	if ((SIGNOTEMPTY(p->p_siglist) || SIGNOTEMPTY(td->td_siglist))
916191783Srmacklem	    && nfs_sig_pending(tmpset)) {
917191783Srmacklem		PROC_UNLOCK(p);
918191783Srmacklem		return (EINTR);
919191783Srmacklem	}
920191783Srmacklem	PROC_UNLOCK(p);
921191783Srmacklem	return (0);
922191783Srmacklem}
923191783Srmacklem
924191783Srmacklemstatic int
925191783Srmacklemnfs_msg(struct thread *td, const char *server, const char *msg, int error)
926191783Srmacklem{
927191783Srmacklem	struct proc *p;
928191783Srmacklem
929191783Srmacklem	p = td ? td->td_proc : NULL;
930191783Srmacklem	if (error) {
931191783Srmacklem		tprintf(p, LOG_INFO, "newnfs server %s: %s, error %d\n",
932191783Srmacklem		    server, msg, error);
933191783Srmacklem	} else {
934191783Srmacklem		tprintf(p, LOG_INFO, "newnfs server %s: %s\n", server, msg);
935191783Srmacklem	}
936191783Srmacklem	return (0);
937191783Srmacklem}
938191783Srmacklem
939191783Srmacklemstatic void
940191783Srmacklemnfs_down(struct nfsmount *nmp, struct thread *td, const char *msg,
941191783Srmacklem    int error, int flags)
942191783Srmacklem{
943191783Srmacklem	if (nmp == NULL)
944191783Srmacklem		return;
945191783Srmacklem	mtx_lock(&nmp->nm_mtx);
946191783Srmacklem	if ((flags & NFSSTA_TIMEO) && !(nmp->nm_state & NFSSTA_TIMEO)) {
947191783Srmacklem		nmp->nm_state |= NFSSTA_TIMEO;
948191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
949191783Srmacklem		vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
950191783Srmacklem		    VQ_NOTRESP, 0);
951191783Srmacklem	} else
952191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
953191783Srmacklem	mtx_lock(&nmp->nm_mtx);
954191783Srmacklem	if ((flags & NFSSTA_LOCKTIMEO) && !(nmp->nm_state & NFSSTA_LOCKTIMEO)) {
955191783Srmacklem		nmp->nm_state |= NFSSTA_LOCKTIMEO;
956191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
957191783Srmacklem		vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
958191783Srmacklem		    VQ_NOTRESPLOCK, 0);
959191783Srmacklem	} else
960191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
961191783Srmacklem	nfs_msg(td, nmp->nm_mountp->mnt_stat.f_mntfromname, msg, error);
962191783Srmacklem}
963191783Srmacklem
964191783Srmacklemstatic void
965191783Srmacklemnfs_up(struct nfsmount *nmp, struct thread *td, const char *msg,
966191783Srmacklem    int flags, int tprintfmsg)
967191783Srmacklem{
968191783Srmacklem	if (nmp == NULL)
969191783Srmacklem		return;
970191783Srmacklem	if (tprintfmsg) {
971191783Srmacklem		nfs_msg(td, nmp->nm_mountp->mnt_stat.f_mntfromname, msg, 0);
972191783Srmacklem	}
973191783Srmacklem
974191783Srmacklem	mtx_lock(&nmp->nm_mtx);
975191783Srmacklem	if ((flags & NFSSTA_TIMEO) && (nmp->nm_state & NFSSTA_TIMEO)) {
976191783Srmacklem		nmp->nm_state &= ~NFSSTA_TIMEO;
977191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
978191783Srmacklem		vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
979191783Srmacklem		    VQ_NOTRESP, 1);
980191783Srmacklem	} else
981191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
982191783Srmacklem
983191783Srmacklem	mtx_lock(&nmp->nm_mtx);
984191783Srmacklem	if ((flags & NFSSTA_LOCKTIMEO) && (nmp->nm_state & NFSSTA_LOCKTIMEO)) {
985191783Srmacklem		nmp->nm_state &= ~NFSSTA_LOCKTIMEO;
986191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
987191783Srmacklem		vfs_event_signal(&nmp->nm_mountp->mnt_stat.f_fsid,
988191783Srmacklem		    VQ_NOTRESPLOCK, 1);
989191783Srmacklem	} else
990191783Srmacklem		mtx_unlock(&nmp->nm_mtx);
991191783Srmacklem}
992191783Srmacklem
993