1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2017 RackTop Systems.
14  */
15 
16 #include <sys/systm.h>
17 #include <sys/sdt.h>
18 #include <rpc/types.h>
19 #include <rpc/auth.h>
20 #include <rpc/auth_unix.h>
21 #include <rpc/auth_des.h>
22 #include <rpc/svc.h>
23 #include <rpc/xdr.h>
24 #include <nfs/nfs4.h>
25 #include <nfs/nfs_dispatch.h>
26 #include <sys/cmn_err.h>
27 #include <sys/modctl.h>
28 
29 static void
rfs4_err_resp(COMPOUND4args * args,COMPOUND4res * resp,nfsstat4 err)30 rfs4_err_resp(COMPOUND4args *args, COMPOUND4res *resp, nfsstat4 err)
31 {
32 	size_t	sz;
33 
34 	resp->array_len = 1;
35 	sz = resp->array_len * sizeof (nfs_resop4);
36 	resp->array = kmem_zalloc(sz, KM_SLEEP);
37 
38 	resp->array[0].resop = args->array[0].argop;
39 	resp->status = resp->array[0].nfs_resop4_u.opillegal.status = err;
40 }
41 
42 /*
43  * The function checks if given compound operation is allowed
44  * to be the very fist operation in compound array.
45  */
46 static bool_t
valid_first_compound_op(nfs_opnum4 op)47 valid_first_compound_op(nfs_opnum4 op)
48 {
49 	if (op == OP_BIND_CONN_TO_SESSION	||
50 	    op == OP_SEQUENCE			||
51 	    op == OP_EXCHANGE_ID		||
52 	    op == OP_CREATE_SESSION		||
53 	    op == OP_DESTROY_SESSION		||
54 	    op == OP_DESTROY_CLIENTID		||
55 	    op == OP_ILLEGAL)
56 		return (TRUE);
57 
58 	return (FALSE);
59 }
60 
61 /*
62  * The function verifies arguments passed to mds_op_compound.
63  * If agrguments are valid, NFS4_OK is returned, otherwise
64  * function returns correspoinding NFS4 error code.
65  */
66 static nfsstat4
verify_compound_args(COMPOUND4args * args)67 verify_compound_args(COMPOUND4args *args)
68 {
69 	if (args->array_len == 0)
70 		return (NFS4_OK);
71 
72 	if (!valid_first_compound_op(args->array[0].argop))
73 		return (NFS4ERR_OP_NOT_IN_SESSION);
74 
75 	if (args->array_len > 1 && args->array[0].argop != OP_SEQUENCE) {
76 		/*
77 		 * Compound is outside the session. There must be
78 		 * only one operation in request.
79 		 */
80 		return (NFS4ERR_NOT_ONLY_OP);
81 	}
82 
83 	return (NFS4_OK);
84 }
85 
86 static void
rfs4x_dispatch_done(compound_state_t * cs)87 rfs4x_dispatch_done(compound_state_t *cs)
88 {
89 	if (cs->slot)
90 		rfs4x_sequence_done(cs->cmpresp, cs);
91 	else {
92 		rfs4_compound_free(cs->cmpresp);
93 	}
94 	cs->cs_flags |= RFS4_DISPATCH_DONE;
95 }
96 
97 static bool_t
xdr_compound_wrapper(XDR * xdrs,compound_state_t * cs)98 xdr_compound_wrapper(XDR *xdrs, compound_state_t *cs)
99 {
100 	COMPOUND4res *resp = cs->cmpresp;
101 	bool_t res = FALSE;
102 	bool_t isreal = (xdrs->x_handy != 0);    /* real data encoding ? */
103 
104 	if (!(cs->cs_flags & RFS4_DISPATCH_DONE)) {
105 		res = xdr_COMPOUND4res_srv(xdrs, resp);
106 		if (isreal)
107 			rfs4x_dispatch_done(cs);
108 	}
109 
110 	return (res);
111 }
112 
113 int
rfs4x_dispatch(struct svc_req * req,SVCXPRT * xprt,char * ap)114 rfs4x_dispatch(struct svc_req *req, SVCXPRT *xprt, char *ap)
115 {
116 	struct compound_state cs;
117 	COMPOUND4res res_buf;
118 	COMPOUND4res *rbp;
119 	COMPOUND4args	*cap;
120 	int rpcerr = 0;
121 	nfsstat4 error;
122 
123 	bzero(&res_buf, sizeof (COMPOUND4res));
124 	rbp = &res_buf;
125 	cap = (COMPOUND4args *)ap;
126 	rfs4_init_compound_state(&cs);
127 
128 	cs.statusp = &error;
129 	cs.cmpresp = rbp;
130 
131 	error = verify_compound_args(cap);
132 	if (error != NFS4_OK) {
133 		rfs4_err_resp(cap, rbp, error);
134 		goto out_send;
135 	}
136 
137 	error = rfs4x_sequence_prep(cap, rbp, &cs);
138 	if (error != NFS4_OK) {
139 		if (error != nfserr_replay_cache)
140 			rfs4_err_resp(cap, rbp, error);
141 		goto out_send;
142 	}
143 
144 	/* Regular processing */
145 	curthread->t_flag |= T_DONTPEND;
146 	rfs4_compound(cap, rbp, &cs, req, &rpcerr);
147 	curthread->t_flag &= ~T_DONTPEND;
148 
149 	/*
150 	 * On RPC error, short sendreply
151 	 */
152 	if (rpcerr) {
153 		goto out_free;
154 	}
155 
156 	if (curthread->t_flag & T_WOULDBLOCK) {
157 		curthread->t_flag &= ~T_WOULDBLOCK;
158 		error = 1;
159 		goto out_free;
160 	}
161 
162 out_send:
163 	if (!svc_sendreply(xprt, xdr_compound_wrapper, (char *)&cs)) {
164 		DTRACE_PROBE2(sendfail, SVCXPRT *, xprt,
165 		    compound_state_t *, &cs);
166 		svcerr_systemerr(xprt);
167 		rpcerr = 1;
168 	}
169 
170 out_free:
171 	if (!(cs.cs_flags & RFS4_DISPATCH_DONE)) {
172 		rfs4x_dispatch_done(&cs);
173 	}
174 
175 	rfs4_fini_compound_state(&cs);
176 	return ((error != NFS4_OK || rpcerr) ? 1 : 0);
177 }
178