xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs4x_srv.c (revision 9b9d39d2a32ff806d2431dbcc50968ef1e6d46b2)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
26  * Copyright 2017 RackTop Systems.
27  */
28 
29 #include <rpc/types.h>
30 #include <rpc/auth.h>
31 #include <rpc/rpcsec_gss.h>
32 #include <sys/sdt.h>
33 #include <sys/disp.h>
34 #include <nfs/nfs.h>
35 #include <nfs/nfs4.h>
36 #include <sys/systeminfo.h>
37 
38 /* Helpers */
39 
40 /* Principal handling routines */
41 /* returns 0 if no match; or 1 for a match */
42 int
43 rfs4_cmp_cred_set(cred_set_t *p, struct compound_state *cs)
44 {
45 	int			 rc = 0;
46 	rpc_gss_principal_t	 recp;		/* cached clnt princ */
47 	rpc_gss_principal_t	 ibrp;		/* inbound req princ */
48 
49 
50 	if (p->cp_cr == NULL)
51 		return (rc);	/* nothing to compare with */
52 
53 	if (p->cp_aflavor != cs->req->rq_cred.oa_flavor)
54 		return (rc);
55 
56 	if (p->cp_secmod != cs->nfsflavor)
57 		return (rc);
58 
59 	if (crcmp(p->cp_cr, cs->basecr))
60 		return (rc);
61 
62 	switch (p->cp_aflavor) {
63 	case AUTH_DES:
64 		rc = (strcmp(p->cp_princ, cs->principal) == 0);
65 		break;
66 
67 	case RPCSEC_GSS:
68 		recp = (rpc_gss_principal_t)p->cp_princ;
69 		ibrp = (rpc_gss_principal_t)cs->principal;
70 
71 		if (recp->len != ibrp->len)
72 			break;
73 		rc = (bcmp(recp->name, ibrp->name, ibrp->len) == 0);
74 		break;
75 
76 	case AUTH_SYS:
77 	case AUTH_NONE:
78 	default:
79 		rc = 1;
80 		break;
81 	}
82 	return (rc);
83 }
84 
85 static rpc_gss_principal_t
86 rfs4_dup_princ(rpc_gss_principal_t ppl)
87 {
88 	rpc_gss_principal_t	pdup;
89 	size_t			len;
90 
91 	if (ppl == NULL)
92 		return (NULL);
93 
94 	len = sizeof (int) + ppl->len;
95 	pdup = (rpc_gss_principal_t)kmem_alloc(len, KM_SLEEP);
96 	bcopy(ppl, pdup, len);
97 	return (pdup);
98 }
99 
100 void
101 rfs4_set_cred_set(cred_set_t *p, struct compound_state *cs)
102 {
103 	ASSERT(p->cp_cr == NULL);
104 
105 	p->cp_cr = crdup(cs->basecr);
106 	p->cp_aflavor = cs->req->rq_cred.oa_flavor;
107 	p->cp_secmod = cs->nfsflavor;	/* secmod != flavor for RPCSEC_GSS */
108 
109 	/*
110 	 * Set principal as per security flavor
111 	 */
112 	switch (p->cp_aflavor) {
113 	case AUTH_DES:
114 		p->cp_princ = strdup(cs->principal);
115 		break;
116 
117 	case RPCSEC_GSS:
118 		p->cp_princ =
119 		    (caddr_t)rfs4_dup_princ((rpc_gss_principal_t)cs->principal);
120 		break;
121 
122 	case AUTH_SYS:
123 	case AUTH_NONE:
124 	default:
125 		break;
126 	}
127 }
128 
129 void
130 rfs4_free_cred_set(cred_set_t *p)
131 {
132 	rpc_gss_principal_t ppl;
133 
134 	if (p->cp_cr == NULL)
135 		return;
136 
137 	switch (p->cp_aflavor) {
138 	case AUTH_DES:
139 		kmem_free(p->cp_princ, strlen(p->cp_princ) + 1);
140 		break;
141 
142 	case RPCSEC_GSS:
143 		ppl = (rpc_gss_principal_t)p->cp_princ;
144 		kmem_free(ppl, ppl->len + sizeof (int));
145 		break;
146 	}
147 
148 	crfree(p->cp_cr);
149 	p->cp_cr = NULL;
150 }
151 
152 /* principal end */
153 
154 bool_t
155 nfs_clid4_cmp(nfs_client_id4 *s1, nfs_client_id4 *s2)
156 {
157 	if (s1->verifier != s2->verifier)
158 		return (FALSE);
159 	if (s1->id_len != s2->id_len)
160 		return (FALSE);
161 	if (bcmp(s1->id_val, s2->id_val, s2->id_len))
162 		return (FALSE);
163 	return (TRUE);
164 }
165 
166 /*
167  * Rudimentary server implementation (XXX - for now)
168  */
169 void
170 rfs4x_get_server_impl_id(EXCHANGE_ID4resok *resp)
171 {
172 	char		*sol_impl = "illumos NFSv4.1 Server Implementation";
173 	char		*sol_idom = "nfsv41.ietf.org";
174 	void		*p;
175 	uint_t		 len = 0;
176 	nfs_impl_id4	*nip;
177 
178 	resp->eir_server_impl_id.eir_server_impl_id_len = 1;
179 	nip = kmem_zalloc(sizeof (nfs_impl_id4), KM_SLEEP);
180 	resp->eir_server_impl_id.eir_server_impl_id_val = nip;
181 
182 	/* Domain */
183 	nip->nii_domain.utf8string_len = len = strlen(sol_idom);
184 	p = kmem_zalloc(len * sizeof (char), KM_SLEEP);
185 	nip->nii_domain.utf8string_val = p;
186 	bcopy(sol_idom, p, len);
187 
188 	/* Implementation */
189 	nip->nii_name.utf8string_len = len = strlen(sol_impl);
190 	p = kmem_zalloc(len * sizeof (char), KM_SLEEP);
191 	nip->nii_name.utf8string_val = p;
192 	bcopy(sol_impl, p, len);
193 
194 	/* Time is zero for now */
195 }
196 
197 static void
198 rfs4x_set_trunkinfo(EXCHANGE_ID4resok *rok)
199 {
200 	const char *nodename = uts_nodename();
201 	size_t nd_len = strlen(nodename);
202 	size_t hw_len = strlen(hw_serial);
203 	size_t id_len = nd_len + 1 + hw_len;
204 	char *s = kmem_alloc(id_len, KM_SLEEP);
205 	server_owner4 *so = &rok->eir_server_owner;
206 	struct eir_server_scope *ss = &rok->eir_server_scope;
207 
208 	(void) memcpy(s, nodename, nd_len);
209 	s[nd_len] = ' ';
210 	(void) memcpy(s + nd_len + 1, hw_serial, hw_len);
211 
212 	so->so_major_id.so_major_id_len = id_len;
213 	so->so_major_id.so_major_id_val = s;
214 
215 	ss->eir_server_scope_len = id_len;
216 	ss->eir_server_scope_val = kmem_alloc(id_len, KM_SLEEP);
217 	(void) memcpy(ss->eir_server_scope_val, s, id_len);
218 
219 	rok->eir_server_owner.so_minor_id = 0;
220 }
221 
222 static bool_t
223 client_has_state_locked(rfs4_client_t *cp)
224 {
225 	if (list_head(&cp->rc_sessions) != NULL ||
226 	    list_head(&cp->rc_openownerlist) != NULL)
227 		return (TRUE);
228 	else
229 		return (FALSE);
230 }
231 
232 /* OPERATIONS */
233 
234 /*
235  * EXCHANGE_ID
236  * RFC5661 sec. 18.35
237  */
238 void
239 rfs4x_op_exchange_id(nfs_argop4 *argop, nfs_resop4 *resop,
240     struct svc_req *req, compound_state_t *cs)
241 {
242 	EXCHANGE_ID4args	*args = &argop->nfs_argop4_u.opexchange_id;
243 	EXCHANGE_ID4res		*resp = &resop->nfs_resop4_u.opexchange_id;
244 	EXCHANGE_ID4resok	*rok = &resp->EXCHANGE_ID4res_u.eir_resok4;
245 	rfs4_client_t		*cp, *conf;
246 	bool_t			 update, create;
247 	client_owner4		*cop;
248 	nfs_client_id4		 cid; /* cip */
249 	nfsstat4		status = NFS4_OK;
250 	nfs4_srv_t		*nsrv4;
251 
252 	DTRACE_NFSV4_2(op__exchange__id__start,
253 	    struct compound_state *, cs,
254 	    EXCHANGE_ID4args *, args);
255 
256 	/*
257 	 * EXCHANGE_ID's may be preceded by SEQUENCE
258 	 *
259 	 * Check that eia_flags only has "valid" spec bits
260 	 * and that no 'eir_flag' ONLY bits are specified.
261 	 */
262 	if (args->eia_flags & ~EXID4_FLAG_MASK) {
263 		status = NFS4ERR_INVAL;
264 		goto err;
265 	}
266 
267 	update = (args->eia_flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A);
268 	cop = &args->eia_clientowner;
269 	conf = NULL;
270 
271 	cid.verifier = cop->co_verifier;
272 	cid.id_len = cop->co_ownerid.co_ownerid_len;
273 	cid.id_val = cop->co_ownerid.co_ownerid_val;
274 	cid.cl_addr = (struct sockaddr *)svc_getrpccaller(req->rq_xprt)->buf;
275 
276 	/*
277 	 * Refer to Section 18.35.4
278 	 */
279 again:
280 	create = TRUE;
281 	cp = rfs4_findclient(&cid, &create, conf);
282 
283 	if (cp == NULL) {
284 		status = NFS4ERR_RESOURCE;
285 		if (conf)
286 			rfs4_client_rele(conf);
287 		goto err;
288 	}
289 
290 	if (conf) {
291 		rfs4_dbe_lock(cp->rc_dbe);
292 		if (cp->rc_cp_confirmed == NULL)
293 			cp->rc_cp_confirmed = conf;
294 		else
295 			rfs4_client_rele(conf);
296 		rfs4_dbe_unlock(cp->rc_dbe);
297 		conf = NULL;
298 	}
299 
300 	if (create) {
301 		/* Record just created */
302 		if (!update) {
303 			/* case 1 - utok */
304 			rfs4_set_cred_set(&cp->rc_cr_set, cs);
305 
306 			rok->eir_clientid = cp->rc_clientid;
307 			rok->eir_sequenceid = cp->rc_contrived.xi_sid;
308 			goto out;
309 		} else {
310 			/* no record and trying to update */
311 			status = NFS4ERR_NOENT;
312 			goto err_out;
313 		}
314 	}
315 
316 	/* Record exists */
317 
318 	/* expired clients should be ignored and released */
319 	if (rfs4_lease_expired(cp)) {
320 		rfs4_client_close(cp);
321 		update = FALSE;
322 		goto again;
323 	}
324 
325 	if (cp->rc_need_confirm) {
326 		/* UNCONFIRMED */
327 		if (!update) {
328 			/* case 4 - utok */
329 			rfs4_client_close(cp);
330 
331 			ASSERT(!update);
332 			goto again;
333 		} else {
334 			/* case 7 - utok */
335 			status = NFS4ERR_NOENT;
336 			goto err_out;
337 		}
338 	}
339 
340 	/* record exists and confirmed */
341 	if (!update) {
342 		if (!rfs4_cmp_cred_set(&cp->rc_cr_set, cs)) {
343 			/* case 3 */
344 			/* lease is checked above */
345 			rfs4_dbe_lock(cp->rc_dbe);
346 			if (!client_has_state_locked(cp)) {
347 				rfs4_dbe_unlock(cp->rc_dbe);
348 
349 				rfs4_client_close(cp);
350 				ASSERT(!update);
351 				goto again;
352 			}
353 			rfs4_dbe_unlock(cp->rc_dbe);
354 
355 			/*
356 			 * clid_in_use. old_client_ret has unexpired
357 			 * lease with state.
358 			 */
359 			status = NFS4ERR_CLID_INUSE;
360 			goto err_out;
361 		} else if (cp->rc_nfs_client.verifier != cid.verifier) {
362 			/* case 5: Client Restart */
363 			/*
364 			 * Skip confirmed client record to allow confirmed
365 			 * and unconfirmed state at the same time. The number
366 			 * of states can collapse to one once the server
367 			 * receives an applicable CREATE_SESSION or EXCHANGE_ID.
368 			 */
369 			ASSERT(conf == NULL);
370 			conf = cp;
371 			ASSERT(!update);
372 			goto again;
373 
374 		} else if (nfs_clid4_cmp(&cp->rc_nfs_client, &cid)) {
375 			/* case 2 - utok */
376 			rok->eir_clientid = cp->rc_clientid;
377 			rok->eir_sequenceid = cp->rc_contrived.xi_sid;
378 			/* trickle down to "out" */
379 
380 		} else {
381 			/* something is really wacky in srv state */
382 			status = NFS4ERR_SERVERFAULT;
383 			goto err_out;
384 		}
385 
386 	} else { /* UPDATE */
387 		if (cp->rc_nfs_client.verifier != cid.verifier) {
388 			/* 18.35.4 case 8 */
389 			status = NFS4ERR_NOT_SAME;
390 			goto err_out;
391 		}
392 		if (!rfs4_cmp_cred_set(&cp->rc_cr_set, cs)) {
393 			/* 18.35.4 case 9 */
394 			status = NFS4ERR_PERM;
395 			goto err_out;
396 		}
397 
398 		/* case 6 - utok */
399 		rok->eir_clientid = cp->rc_clientid;
400 		rok->eir_sequenceid = cp->rc_contrived.xi_sid;
401 		/* trickle down to "out" */
402 	}
403 out:
404 	rok->eir_flags = 0;
405 	if (resp->eir_status == NFS4_OK && !cp->rc_need_confirm)
406 		rok->eir_flags |= EXCHGID4_FLAG_CONFIRMED_R;
407 
408 	/*
409 	 * State Protection (See sec. 2.10.8.3)
410 	 */
411 	cp->rc_state_prot.sp_type = args->eia_state_protect.spa_how;
412 	switch (cp->rc_state_prot.sp_type) {
413 	case SP4_NONE:
414 		break;
415 
416 	case SP4_MACH_CRED:
417 		break;
418 
419 	case SP4_SSV:
420 		/*
421 		 * SSV state protection is not implemented.
422 		 */
423 		status = NFS4ERR_ENCR_ALG_UNSUPP;
424 		goto err_out;
425 	default:
426 		status = NFS4ERR_INVAL;
427 		goto err_out;
428 
429 	}
430 
431 	/*
432 	 * Referrals supports
433 	 */
434 	if (args->eia_flags & EXCHGID4_FLAG_SUPP_MOVED_REFER) {
435 		rok->eir_flags |= EXCHGID4_FLAG_SUPP_MOVED_REFER;
436 	}
437 
438 	/*
439 	 * Migration/Replication not (yet) supported
440 	 */
441 	if (args->eia_flags & EXCHGID4_FLAG_SUPP_MOVED_MIGR)
442 		rok->eir_flags &= ~EXCHGID4_FLAG_SUPP_MOVED_MIGR;
443 
444 	/*
445 	 * RFC8881 Section 13.1 Client ID and Session Considerations
446 	 * Non-metadata server, do not support pNFS (yet).
447 	 */
448 	rok->eir_flags |= EXCHGID4_FLAG_USE_NON_PNFS;
449 
450 	/* force no state protection for now */
451 	rok->eir_state_protect.spr_how = SP4_NONE;
452 
453 	/* Implementation specific mojo */
454 	if (args->eia_client_impl_id.eia_client_impl_id_len != 0) {
455 		/* EMPTY */;
456 	}
457 
458 	nsrv4 = nfs4_get_srv();
459 
460 	/* Record clientid in stable storage */
461 	rfs4_ss_clid(nsrv4, cp);
462 
463 	/* Server's implementation */
464 	rfs4x_get_server_impl_id(rok);
465 
466 	/* compute trunking capabilities */
467 	bzero(&rok->eir_server_scope, sizeof (rok->eir_server_scope));
468 	bzero(&rok->eir_server_owner, sizeof (server_owner4));
469 
470 	/* Add trunk handling */
471 	rfs4x_set_trunkinfo(rok);
472 
473 	/*
474 	 * Check to see if client can perform reclaims
475 	 */
476 	rfs4_ss_chkclid(nsrv4, cp);
477 
478 err_out:
479 	rfs4_client_rele(cp);
480 err:
481 	*cs->statusp = resp->eir_status = status;
482 
483 	DTRACE_NFSV4_2(op__exchange__id__done,
484 	    struct compound_state *, cs,
485 	    EXCHANGE_ID4res *, resp);
486 }
487 
488 void
489 rfs4x_exchange_id_free(nfs_resop4 *resop)
490 {
491 	EXCHANGE_ID4res		*resp = &resop->nfs_resop4_u.opexchange_id;
492 	EXCHANGE_ID4resok	*rok = &resp->EXCHANGE_ID4res_u.eir_resok4;
493 	struct server_owner4	*sop = &rok->eir_server_owner;
494 	nfs_impl_id4		*nip;
495 	int			 len = 0;
496 
497 	/* Server Owner: major */
498 	if ((len = sop->so_major_id.so_major_id_len) != 0)
499 		kmem_free(sop->so_major_id.so_major_id_val, len);
500 
501 	if ((nip = rok->eir_server_impl_id.eir_server_impl_id_val) != NULL) {
502 		/* Immplementation */
503 		len = nip->nii_name.utf8string_len;
504 		kmem_free(nip->nii_name.utf8string_val, len * sizeof (char));
505 
506 		/* Domain */
507 		len = nip->nii_domain.utf8string_len;
508 		kmem_free(nip->nii_domain.utf8string_val, len * sizeof (char));
509 
510 		/* Server Impl */
511 		kmem_free(nip, sizeof (nfs_impl_id4));
512 	}
513 }
514 
515 void
516 rfs4x_op_create_session(nfs_argop4 *argop, nfs_resop4 *resop,
517     struct svc_req *req, compound_state_t *cs)
518 {
519 	CREATE_SESSION4args	*args = &argop->nfs_argop4_u.opcreate_session;
520 	CREATE_SESSION4res	*resp = &resop->nfs_resop4_u.opcreate_session;
521 	CREATE_SESSION4resok	*rok = &resp->CREATE_SESSION4res_u.csr_resok4;
522 	CREATE_SESSION4resok	*crp;
523 	rfs4_client_t		*cp;
524 	rfs4_session_t		*sp;
525 	session41_create_t	 sca;
526 	sequenceid4		 stseq;
527 	sequenceid4		 agseq;
528 	nfsstat4		 status = NFS4_OK;
529 
530 	DTRACE_NFSV4_2(op__create__session__start,
531 	    struct compound_state *, cs,
532 	    CREATE_SESSION4args*, args);
533 
534 	/*
535 	 * A CREATE_SESSION request can be prefixed by OP_SEQUENCE.
536 	 * In this case, the newly created session has no relation
537 	 * to the sessid used for the OP_SEQUENCE.
538 	 */
539 
540 	/*
541 	 * Find the clientid
542 	 */
543 	cp = rfs4_findclient_by_id(args->csa_clientid, TRUE);
544 	if (cp == NULL) {
545 		status = NFS4ERR_STALE_CLIENTID;
546 		goto out;
547 	}
548 
549 	/*
550 	 * Make sure the lease is still valid.
551 	 */
552 	if (rfs4_lease_expired(cp)) {
553 		rfs4_client_close(cp);
554 		status = NFS4ERR_STALE_CLIENTID;
555 		goto out;
556 	}
557 
558 	/*
559 	 * Sequenceid processing (handling replay's, etc)
560 	 */
561 	agseq = args->csa_sequence;
562 	stseq = cp->rc_contrived.xi_sid;
563 	if (stseq == agseq + 1) {
564 		/*
565 		 * If the previous sequenceid, then must be a replay of a
566 		 * previous CREATE_SESSION; return the cached result.
567 		 */
568 		crp = (CREATE_SESSION4resok *)&cp->rc_contrived.cs_res;
569 		status = cp->rc_contrived.cs_status;
570 		rok->csr_sequence = agseq;
571 		bcopy(crp->csr_sessionid, rok->csr_sessionid,
572 		    sizeof (sessionid4));
573 		rok->csr_flags = crp->csr_flags;
574 		rok->csr_fore_chan_attrs = crp->csr_fore_chan_attrs;
575 		rok->csr_back_chan_attrs = crp->csr_back_chan_attrs;
576 
577 		rfs4_update_lease(cp);
578 		rfs4_client_rele(cp);
579 		goto out;
580 	}
581 
582 	if (stseq != agseq) {
583 		/*
584 		 * No way to differentiate MISORD_NEWREQ vs. MISORD_REPLAY,
585 		 * so anything else, we simply treat as SEQ_MISORDERED.
586 		 */
587 		status = NFS4ERR_SEQ_MISORDERED;
588 		rfs4_client_rele(cp);
589 		goto out;
590 	}
591 
592 	/*
593 	 * Clientid confirmation
594 	 */
595 	if (cp->rc_need_confirm) {
596 		if (rfs4_cmp_cred_set(&cp->rc_cr_set, cs)) {
597 			cp->rc_need_confirm = FALSE;
598 			if (cp->rc_cp_confirmed != NULL) {
599 				rfs4_client_close(cp->rc_cp_confirmed);
600 				cp->rc_cp_confirmed = NULL;
601 			}
602 		} else {
603 			status = NFS4ERR_CLID_INUSE;
604 			rfs4_client_rele(cp);
605 			goto out;
606 		}
607 	}
608 
609 	/*
610 	 * Session creation
611 	 */
612 	sca.cs_error = 0;
613 	sca.cs_req = req;
614 	sca.cs_client = cp;
615 	sca.cs_aotw = *args;
616 	sp = rfs4x_createsession(&sca);
617 
618 	if (sca.cs_error) {
619 		status = sca.cs_error;
620 		rfs4_client_rele(cp);
621 		if (sp != NULL)
622 			rfs4x_session_rele(sp);
623 		goto out;
624 	}
625 
626 	if (sp == NULL) {
627 		status = NFS4ERR_SERVERFAULT;
628 		rfs4_client_rele(cp);
629 		goto out;
630 	}
631 
632 	/*
633 	 * Need to store the result in the rfs4_client_t's contrived
634 	 * result slot and then respond from there. This way, when the
635 	 * csa_sequence == contrived.cc_sid, we can return the latest
636 	 * cached result. (see replay: above)
637 	 */
638 	crp = (CREATE_SESSION4resok *)&cp->rc_contrived.cs_res;
639 	cp->rc_contrived.cs_status = NFS4_OK;
640 	rok->csr_sequence = crp->csr_sequence = cp->rc_contrived.xi_sid;
641 	bcopy(sp->sn_sessid, rok->csr_sessionid, sizeof (sessionid4));
642 	bcopy(sp->sn_sessid, crp->csr_sessionid, sizeof (sessionid4));
643 	rok->csr_flags = crp->csr_flags = sp->sn_csflags;
644 
645 	cp->rc_contrived.xi_sid++;
646 
647 	rok->csr_fore_chan_attrs =
648 	    crp->csr_fore_chan_attrs = sp->sn_fore->cn_attrs;
649 	rok->csr_back_chan_attrs = crp->csr_back_chan_attrs =
650 	    args->csa_back_chan_attrs;
651 
652 	rfs4_update_lease(cp);
653 
654 	/*
655 	 * References from the session to the client are
656 	 * accounted for while session is being created.
657 	 */
658 	rfs4_client_rele(cp);
659 	rfs4x_session_rele(sp);
660 out:
661 	*cs->statusp = resp->csr_status = status;
662 
663 	DTRACE_NFSV4_2(op__create__session__done,
664 	    struct compound_state *, cs,
665 	    CREATE_SESSION4res *, resp);
666 }
667 
668 /* ARGSUSED */
669 void
670 rfs4x_op_destroy_session(nfs_argop4 *argop, nfs_resop4 *resop,
671     struct svc_req *req, compound_state_t *cs)
672 {
673 	DESTROY_SESSION4args	*args = &argop->nfs_argop4_u.opdestroy_session;
674 	DESTROY_SESSION4res	*resp = &resop->nfs_resop4_u.opdestroy_session;
675 	rfs4_session_t		*sp;
676 	rfs4_client_t		*cp;
677 	nfsstat4 status = NFS4_OK;
678 	int addref = 0;		/* additional reference */
679 
680 	DTRACE_NFSV4_2(op__destroy__session__start,
681 	    struct compound_state *, cs,
682 	    DESTROY_SESSION4args *, args);
683 
684 	/* section 18.37.3 rfc5661 */
685 	if (rfs4_has_session(cs)) {
686 		/* compound with a sequence */
687 		if (bcmp(args->dsa_sessionid, cs->sp->sn_sessid,
688 		    sizeof (sessionid4)) == 0) {
689 			/*
690 			 * Same session.
691 			 * must be the final operation in the COMPOUND request
692 			 */
693 			if ((cs->op_pos + 1) != cs->op_len) {
694 				status = NFS4ERR_NOT_ONLY_OP;
695 				goto out;
696 			}
697 			addref++;
698 		} else {
699 			/* Not the same session */
700 			DTRACE_PROBE(nfss41__i__destroy_encap_session);
701 
702 		}
703 	}
704 
705 	sp = rfs4x_findsession_by_id(args->dsa_sessionid);
706 	if (sp == NULL) {
707 		status = NFS4ERR_BADSESSION;
708 		goto out;
709 	}
710 
711 	/*
712 	 * State Protection (See sec. 2.10.8.3)
713 	 *
714 	 * verify cred that was used to create the session matches and is in
715 	 * concordance w/the state protection type used.
716 	 */
717 	cp = sp->sn_clnt;
718 	switch (cp->rc_state_prot.sp_type) {
719 	case SP4_MACH_CRED:
720 		if (!rfs4_cmp_cred_set(&cp->rc_cr_set, cs)) {
721 			status = NFS4ERR_PERM;
722 			goto err_out;
723 		}
724 		break;
725 
726 	case SP4_SSV:
727 		/*
728 		 * Todo -- Missing SSV validation here, if/when
729 		 * SSV state protection is implemented.
730 		 * Should not get this after check in EXCHANGE_ID
731 		 */
732 		status = NFS4ERR_PERM;
733 		goto err_out;
734 
735 	case SP4_NONE:
736 		break;
737 
738 	default:
739 		break;
740 	}
741 
742 	status = rfs4x_destroysession(sp, 2 + addref);
743 err_out:
744 	rfs4x_session_rele(sp);
745 out:
746 	*cs->statusp = resp->dsr_status = status;
747 
748 	DTRACE_NFSV4_2(op__destroy__session__done,
749 	    struct compound_state *, cs,
750 	    DESTROY_SESSION4res *, resp);
751 }
752 
753 /*
754  * Find session and validate sequence args.
755  * If this function successfully completes the compound state
756  * will contain a session pointer.
757  */
758 static nfsstat4
759 rfs4x_find_session(SEQUENCE4args *sargs, struct compound_state *cs)
760 {
761 	rfs4_session_t	*sp;
762 	slotid4		 slot;
763 
764 	ASSERT(sargs != NULL);
765 
766 	if ((sp = rfs4x_findsession_by_id(sargs->sa_sessionid)) == NULL)
767 		return (NFS4ERR_BADSESSION);
768 
769 	slot = sargs->sa_slotid;
770 	if (slot >= sp->sn_fore->cn_attrs.ca_maxrequests) {
771 		rfs4x_session_rele(sp);
772 		return (NFS4ERR_BADSLOT);
773 	}
774 	cs->sp = sp;
775 	cs->cachethis = sargs->sa_cachethis;
776 
777 	return (NFS4_OK);
778 }
779 
780 /* called under held lock */
781 static nfsstat4
782 check_slot_seqid(rfs4_slot_t *slot, sequenceid4 seqid)
783 {
784 	nfsstat4 status = NFS4ERR_SEQ_MISORDERED;
785 
786 	if (slot->se_flags & RFS4_SLOT_INUSE) {
787 		/*
788 		 * There are three cases:
789 		 * 1. Duplicated requests for currently performing
790 		 *    duplicated request.
791 		 * 2. New request for currently performing duplicated
792 		 *    request.
793 		 * 3. Request with bad seqid for non finished performing
794 		 *    request (due to a little window between 'prep'
795 		 *    stage and actual renew se_seqid).
796 		 * In all cases tell a client to retry request later.
797 		 */
798 		if (slot->se_seqid == seqid || slot->se_seqid + 1 == seqid) {
799 			status = NFS4ERR_DELAY;
800 		}
801 	} else {
802 		if (seqid == slot->se_seqid + 1)
803 			status = NFS4_OK;
804 		else if (seqid == slot->se_seqid)
805 			status = nfserr_replay_cache;
806 	}
807 	return (status);
808 }
809 
810 /*
811  * Prep stage for SEQUENCE operation.
812  *
813  * Main purpose to call this:
814  *     - check on cached replay
815  *     - Set cs.sp and cs.slot
816  */
817 int
818 rfs4x_sequence_prep(COMPOUND4args *args, COMPOUND4res *resp,
819     compound_state_t *cs)
820 {
821 	SEQUENCE4args	*sargs;
822 	nfsstat4	status;
823 	rfs4_slot_t	*slot;
824 
825 	if (args->array_len == 0 || args->array[0].argop != OP_SEQUENCE)
826 		return (NFS4_OK);
827 
828 	sargs = &args->array[0].nfs_argop4_u.opsequence;
829 
830 	status = rfs4x_find_session(sargs, cs);
831 	if (status != NFS4_OK)
832 		return (status);
833 
834 	if (args->array_len > cs->sp->sn_fore->cn_attrs.ca_maxoperations)
835 		return (NFS4ERR_TOO_MANY_OPS);
836 
837 	/*  have reference to session */
838 	slot = &cs->sp->sn_slots[sargs->sa_slotid];
839 
840 	mutex_enter(&slot->se_lock);
841 	status = check_slot_seqid(slot, sargs->sa_sequenceid);
842 	if (status == nfserr_replay_cache) {
843 		if (slot->se_flags & RFS4_SLOT_CACHED) {
844 			slot->se_flags |= RFS4_SLOT_INUSE;
845 			cs->slot = slot;
846 			*resp = slot->se_buf;
847 		} else {
848 			status =  NFS4ERR_SEQ_MISORDERED;
849 		}
850 	} else if (status == NFS4_OK) {
851 		slot->se_flags |= RFS4_SLOT_INUSE;
852 		cs->slot = slot;
853 	}
854 	mutex_exit(&slot->se_lock);
855 
856 	return (status);
857 }
858 
859 /*
860  * Do cleanup things
861  *   1. cache reply
862  *   2. release slot
863  */
864 void
865 rfs4x_sequence_done(COMPOUND4res *resp, compound_state_t *cs)
866 {
867 	rfs4_slot_t *slot = cs->slot;
868 	rfs4_session_t *sp = cs->sp;
869 	int add = 0;
870 
871 	ASSERT(slot != NULL);
872 	ASSERT(sp != NULL);
873 
874 	mutex_enter(&slot->se_lock);
875 	slot->se_flags &= ~RFS4_SLOT_INUSE;
876 
877 	if (*cs->statusp != nfserr_replay_cache) {
878 		if (slot->se_flags & RFS4_SLOT_CACHED) {
879 			rfs4_compound_free(&slot->se_buf);
880 			slot->se_flags &= ~RFS4_SLOT_CACHED;
881 			add = -1;
882 		}
883 
884 		if (*cs->statusp == NFS4_OK && cs->cachethis) {
885 			slot->se_flags |= RFS4_SLOT_CACHED;
886 			slot->se_buf = *resp;	/* cache a reply */
887 			add += 1;
888 		} else {
889 			rfs4_compound_free(resp);
890 		}
891 	}
892 	mutex_exit(&slot->se_lock);
893 
894 	if (add != 0)
895 		atomic_add_32(&sp->sn_rcached, add);
896 }
897 
898 /*
899  * Process the SEQUENCE operation. The session pointer has already been
900  * cached in the compound state, so we just dereference
901  */
902 /*ARGSUSED*/
903 void
904 rfs4x_op_sequence(nfs_argop4 *argop, nfs_resop4 *resop,
905     struct svc_req *req, compound_state_t *cs)
906 {
907 	SEQUENCE4args	*args = &argop->nfs_argop4_u.opsequence;
908 	SEQUENCE4res	*resp = &resop->nfs_resop4_u.opsequence;
909 	SEQUENCE4resok	*rok  = &resp->SEQUENCE4res_u.sr_resok4;
910 	rfs4_session_t	*sp = cs->sp;
911 	rfs4_slot_t	*slot = cs->slot;
912 	nfsstat4	 status = NFS4_OK;
913 	uint32_t	 cbstat = 0;
914 
915 	DTRACE_NFSV4_2(op__sequence__start,
916 	    struct compound_state *, cs,
917 	    SEQUENCE4args *, args);
918 
919 	ASSERT(sp != NULL && slot != NULL);
920 
921 	if (cs->op_pos != 0) {
922 		status = NFS4ERR_SEQUENCE_POS;
923 		goto out;
924 	}
925 
926 	if (rfs4_lease_expired(sp->sn_clnt)) {
927 		status = NFS4ERR_BADSESSION;
928 		goto out;
929 	}
930 
931 	rfs4_dbe_lock(sp->sn_dbe);
932 	cs->client = sp->sn_clnt;
933 
934 	DTRACE_PROBE1(compound_clid, clientid4, cs->client->rc_clientid);
935 
936 	ASSERT(args->sa_sequenceid == slot->se_seqid + 1);
937 
938 	/*
939 	 * New request.
940 	 */
941 	mutex_enter(&slot->se_lock);
942 	slot->se_seqid = args->sa_sequenceid;
943 	mutex_exit(&slot->se_lock);
944 
945 	cs->slotno = args->sa_slotid;
946 
947 	/* Update access time */
948 	sp->sn_laccess = nfs_sys_uptime();
949 
950 	/* Prepare result */
951 	bcopy(sp->sn_sessid, rok->sr_sessionid, sizeof (sessionid4));
952 	rok->sr_sequenceid = slot->se_seqid;
953 	rok->sr_slotid = args->sa_slotid;
954 	rok->sr_highest_slotid =
955 	    sp->sn_fore->cn_attrs.ca_maxrequests - 1;
956 	rok->sr_target_highest_slotid =
957 	    sp->sn_fore->cn_attrs.ca_maxrequests - 1;
958 	rok->sr_status_flags |= cbstat;
959 	rfs4_dbe_unlock(sp->sn_dbe);
960 
961 	/* Update lease (out of session lock) */
962 	rfs4_update_lease(cs->client);
963 
964 out:
965 	*cs->statusp = resp->sr_status = status;
966 	DTRACE_NFSV4_2(op__sequence__done,
967 	    struct compound_state *, cs,
968 	    SEQUENCE4res *, resp);
969 }
970 
971 /* ARGSUSED */
972 void
973 rfs4x_op_reclaim_complete(nfs_argop4 *argop, nfs_resop4 *resop,
974     struct svc_req *req, compound_state_t *cs)
975 {
976 	RECLAIM_COMPLETE4args *args = &argop->nfs_argop4_u.opreclaim_complete;
977 	RECLAIM_COMPLETE4res *resp = &resop->nfs_resop4_u.opreclaim_complete;
978 	rfs4_client_t *cp;
979 	nfsstat4 status = NFS4_OK;
980 
981 	DTRACE_NFSV4_2(op__reclaim__complete__start,
982 	    struct compound_state *, cs,
983 	    RECLAIM_COMPLETE4args *, args);
984 
985 	cp = cs->client;
986 	rfs4_dbe_lock(cp->rc_dbe);
987 	if (args->rca_one_fs) {
988 		/* do what?  we don't track this */
989 		goto out;
990 	}
991 
992 	if (cp->rc_reclaim_completed) {
993 		status = NFS4ERR_COMPLETE_ALREADY;
994 		goto out;
995 	}
996 
997 	if (cp->rc_can_reclaim) {
998 		ASSERT(rfs4_servinst(cp)->nreclaim > 0);
999 		atomic_add_32(&(rfs4_servinst(cp))->nreclaim, -1);
1000 	}
1001 
1002 	cp->rc_reclaim_completed = 1;
1003 out:
1004 	rfs4_dbe_unlock(cp->rc_dbe);
1005 
1006 	*cs->statusp = resp->rcr_status = status;
1007 	DTRACE_NFSV4_2(op__reclaim__complete__done,
1008 	    struct compound_state *, cs,
1009 	    RECLAIM_COMPLETE4res *, resp);
1010 }
1011 
1012 /* ARGSUSED */
1013 void
1014 rfs4x_op_destroy_clientid(nfs_argop4 *argop, nfs_resop4 *resop,
1015     struct svc_req *req, compound_state_t *cs)
1016 {
1017 	DESTROY_CLIENTID4args *args = &argop->nfs_argop4_u.opdestroy_clientid;
1018 	DESTROY_CLIENTID4res *resp = &resop->nfs_resop4_u.opdestroy_clientid;
1019 	rfs4_client_t *cp;
1020 	nfsstat4 status = NFS4_OK;
1021 
1022 	DTRACE_NFSV4_2(op__destroy__clientid__start,
1023 	    struct compound_state *, cs,
1024 	    DESTROY_CLIENTID4args *, args);
1025 
1026 	cp = rfs4_findclient_by_id(args->dca_clientid, TRUE);
1027 	if (cp == NULL) {
1028 		status = NFS4ERR_STALE_CLIENTID;
1029 		goto end;
1030 	}
1031 
1032 	rfs4_dbe_lock(cp->rc_dbe);
1033 	if (client_has_state_locked(cp))
1034 		status = NFS4ERR_CLIENTID_BUSY;
1035 	else
1036 		cp->rc_destroying = TRUE;
1037 	rfs4_dbe_unlock(cp->rc_dbe);
1038 
1039 	if (status == NFS4_OK)
1040 		rfs4_client_close(cp);
1041 	else
1042 		rfs4_client_rele(cp);
1043 end:
1044 	*cs->statusp = resp->dcr_status = status;
1045 
1046 	DTRACE_NFSV4_2(op__destroy__clientid__done,
1047 	    struct compound_state *, cs,
1048 	    DESTROY_CLIENTID4res *, resp);
1049 }
1050 
1051 /*ARGSUSED*/
1052 void
1053 rfs4x_op_bind_conn_to_session(nfs_argop4 *argop, nfs_resop4 *resop,
1054     struct svc_req *req, compound_state_t *cs)
1055 {
1056 	BIND_CONN_TO_SESSION4args  *args =
1057 	    &argop->nfs_argop4_u.opbind_conn_to_session;
1058 	BIND_CONN_TO_SESSION4res   *resp =
1059 	    &resop->nfs_resop4_u.opbind_conn_to_session;
1060 	BIND_CONN_TO_SESSION4resok *rok =
1061 	    &resp->BIND_CONN_TO_SESSION4res_u.bctsr_resok4;
1062 	rfs4_session_t	*sp;
1063 	nfsstat4 status = NFS4_OK;
1064 
1065 	DTRACE_NFSV4_2(op__bind__conn__to__session__start,
1066 	    struct compound_state *, cs,
1067 	    BIND_CONN_TO_SESSION4args *, args);
1068 
1069 	if (cs->op_pos != 0) {
1070 		status = NFS4ERR_NOT_ONLY_OP;
1071 		goto end;
1072 	}
1073 
1074 	sp = rfs4x_findsession_by_id(args->bctsa_sessid);
1075 	if (sp == NULL) {
1076 		status = NFS4ERR_BADSESSION;
1077 		goto end;
1078 	}
1079 
1080 	rfs4_update_lease(sp->sn_clnt); /* no need lock protection */
1081 
1082 	rfs4_dbe_lock(sp->sn_dbe);
1083 	sp->sn_laccess = nfs_sys_uptime();
1084 	rfs4_dbe_unlock(sp->sn_dbe);
1085 
1086 	rok->bctsr_use_conn_in_rdma_mode = FALSE;
1087 
1088 	switch (args->bctsa_dir) {
1089 	case CDFC4_FORE:
1090 	case CDFC4_FORE_OR_BOTH:
1091 		/* always map to Fore */
1092 		rok->bctsr_dir = CDFS4_FORE;
1093 		break;
1094 
1095 	case CDFC4_BACK:
1096 	case CDFC4_BACK_OR_BOTH:
1097 		/* TODO: always map to Back */
1098 		rok->bctsr_dir = CDFS4_FORE;
1099 		break;
1100 	default:
1101 		break;
1102 	}
1103 
1104 	bcopy(sp->sn_sessid, rok->bctsr_sessid, sizeof (sessionid4));
1105 	rfs4x_session_rele(sp);
1106 end:
1107 	*cs->statusp = resp->bctsr_status = status;
1108 
1109 	DTRACE_NFSV4_2(op__bind__conn__to__session__done,
1110 	    struct compound_state *, cs,
1111 	    BIND_CONN_TO_SESSION4res *, resp);
1112 }
1113 
1114 /* ARGSUSED */
1115 void
1116 rfs4x_op_secinfo_noname(nfs_argop4 *argop, nfs_resop4 *resop,
1117     struct svc_req *req, compound_state_t *cs)
1118 {
1119 	SECINFO_NO_NAME4res *resp = &resop->nfs_resop4_u.opsecinfo_no_name;
1120 	nfsstat4 status;
1121 	bool_t dotdot;
1122 
1123 	DTRACE_NFSV4_1(op__secinfo__no__name__start,
1124 	    struct compound_state *, cs);
1125 
1126 	if (cs->vp == NULL) {
1127 		status = NFS4ERR_NOFILEHANDLE;
1128 		goto out;
1129 	}
1130 
1131 	if (cs->vp->v_type != VDIR) {
1132 		status = NFS4ERR_NOTDIR;
1133 		goto out;
1134 	}
1135 
1136 	dotdot =
1137 	    (argop->nfs_argop4_u.opsecinfo_no_name == SECINFO_STYLE4_PARENT);
1138 
1139 	status = do_rfs4_op_secinfo(cs, dotdot ? ".." : ".", resp);
1140 
1141 	/* Cleanup FH as described at 18.45.3 and 2.6.3.1.1.8 */
1142 	if (status == NFS4_OK) {
1143 		VN_RELE(cs->vp);
1144 		cs->vp = NULL;
1145 	}
1146 out:
1147 	*cs->statusp = resp->status = status;
1148 
1149 	DTRACE_NFSV4_2(op__secinfo__no__name__done,
1150 	    struct compound_state *, cs,
1151 	    SECINFO_NO_NAME4res *, resp);
1152 }
1153