fksmb_kdoor.c revision b819cea2f73f98c5662230cc9affc8cc84f77fcf
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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
24 */
25
26/*
27 * "Upcall" glue for the fake (user-mode) smbsrv module.
28 */
29
30#include <sys/types.h>
31#include <sys/kmem.h>
32#include <sys/ddi.h>
33#include <sys/sunddi.h>
34#include <sys/cmn_err.h>
35#include <sys/door.h>
36#include <smbsrv/smb_kproto.h>
37#include <smbsrv/smb_door.h>
38
39static int smb_kdoor_encode(smb_doorarg_t *);
40static int smb_kdoor_decode(smb_doorarg_t *);
41static void smb_kdoor_sethdr(smb_doorarg_t *, uint32_t);
42static boolean_t smb_kdoor_chkhdr(smb_doorarg_t *, smb_doorhdr_t *);
43static void smb_kdoor_free(door_arg_t *);
44
45void
46smb_kdoor_init(smb_server_t *sv)
47{
48	sv->sv_kdoor_id = -1;
49	mutex_init(&sv->sv_kdoor_mutex, NULL, MUTEX_DEFAULT, NULL);
50	cv_init(&sv->sv_kdoor_cv, NULL, CV_DEFAULT, NULL);
51}
52
53void
54smb_kdoor_fini(smb_server_t *sv)
55{
56	smb_kdoor_close(sv);
57	cv_destroy(&sv->sv_kdoor_cv);
58	mutex_destroy(&sv->sv_kdoor_mutex);
59}
60
61/*
62 * In the "fake kernen", our "upcalls" don't use the
63 * real door, but just call via a function pointer.
64 * This is where we setup that pointer, which is
65 * fksmbd_door_dispatch()
66 */
67void
68fksmb_kdoor_open(smb_server_t *sv, void *varg)
69{
70	sv->sv_kdoor_hd = varg;
71}
72
73void
74smb_kdoor_close(smb_server_t *sv)
75{
76	sv->sv_kdoor_hd = NULL;
77	sv->sv_kdoor_id = -1;
78}
79
80/* ARGSUSED */
81int
82smb_kdoor_upcall(smb_server_t *sv, uint32_t cmd,
83    void *req_data, xdrproc_t req_xdr,
84    void *rsp_data, xdrproc_t rsp_xdr)
85{
86	smb_doorarg_t	da;
87	fksmb_kdoor_disp_func_t *func;
88	int rc;
89
90	bzero(&da, sizeof (smb_doorarg_t));
91	da.da_opcode = cmd;
92	da.da_opname = smb_doorhdr_opname(cmd);
93	da.da_req_xdr = req_xdr;
94	da.da_rsp_xdr = rsp_xdr;
95	da.da_req_data = req_data;
96	da.da_rsp_data = rsp_data;
97
98	if ((req_data == NULL && req_xdr != NULL) ||
99	    (rsp_data == NULL && rsp_xdr != NULL)) {
100		cmn_err(CE_WARN, "smb_kdoor_upcall[%s]: invalid param",
101		    da.da_opname);
102		return (-1);
103	}
104
105	/* NB: no ASYNC, nor event stuff */
106
107	func = (fksmb_kdoor_disp_func_t *)(sv->sv_kdoor_hd);
108	if (func == NULL)
109		return (EFAULT);
110
111	if ((rc = smb_kdoor_encode(&da)) != 0)
112		goto out;
113
114	/*
115	 * The "upcall" (just call via function pointer)
116	 * i.e. see: fksmbd_door_dispatch()
117	 */
118	if ((rc = (*func)(&da)) != 0)
119		goto out;
120
121	rc = smb_kdoor_decode(&da);
122out:
123	smb_kdoor_free(&da.da_arg);
124
125	return (rc);
126}
127
128/* no smb_kdoor_send, smb_kdoor_receive */
129/* no smb_kdoor_upcall_private */
130
131static int
132smb_kdoor_encode(smb_doorarg_t *da)
133{
134	XDR		xdrs;
135	char		*buf;
136	uint32_t	len;
137
138	len = xdr_sizeof(smb_doorhdr_xdr, &da->da_hdr);
139	if (da->da_req_xdr != NULL)
140		len += xdr_sizeof(da->da_req_xdr, da->da_req_data);
141
142	smb_kdoor_sethdr(da, len);
143
144	buf = kmem_zalloc(len, KM_SLEEP);
145	xdrmem_create(&xdrs, buf, len, XDR_ENCODE);
146
147	if (!smb_doorhdr_xdr(&xdrs, &da->da_hdr)) {
148		cmn_err(CE_WARN, "smb_kdoor_encode[%s]: header encode failed",
149		    da->da_opname);
150		kmem_free(buf, len);
151		xdr_destroy(&xdrs);
152		return (-1);
153	}
154
155	if (da->da_req_xdr != NULL) {
156		if (!da->da_req_xdr(&xdrs, da->da_req_data)) {
157			cmn_err(CE_WARN, "smb_kdoor_encode[%s]: encode failed",
158			    da->da_opname);
159			kmem_free(buf, len);
160			xdr_destroy(&xdrs);
161			return (-1);
162		}
163	}
164
165	da->da_arg.data_ptr = buf;
166	da->da_arg.data_size = len;
167	da->da_arg.desc_ptr = NULL;
168	da->da_arg.desc_num = 0;
169	da->da_arg.rbuf = buf;
170	da->da_arg.rsize = len;
171
172	xdr_destroy(&xdrs);
173	return (0);
174}
175
176/*
177 * Decode the response in rbuf and rsize.
178 */
179static int
180smb_kdoor_decode(smb_doorarg_t *da)
181{
182	XDR		xdrs;
183	smb_doorhdr_t	hdr;
184	char		*rbuf = da->da_arg.rbuf;
185	uint32_t	rsize = da->da_arg.rsize;
186
187	if (rbuf == NULL || rsize == 0) {
188		cmn_err(CE_WARN, "smb_kdoor_decode[%s]: invalid param",
189		    da->da_opname);
190		return (-1);
191	}
192
193	xdrmem_create(&xdrs, rbuf, rsize, XDR_DECODE);
194
195	if (!smb_doorhdr_xdr(&xdrs, &hdr)) {
196		cmn_err(CE_WARN, "smb_kdoor_decode[%s]: header decode failed",
197		    da->da_opname);
198		xdr_destroy(&xdrs);
199		return (-1);
200	}
201
202	if (!smb_kdoor_chkhdr(da, &hdr)) {
203		xdr_destroy(&xdrs);
204		return (-1);
205	}
206
207	if (hdr.dh_datalen != 0 && da->da_rsp_xdr != NULL) {
208		if (!da->da_rsp_xdr(&xdrs, da->da_rsp_data)) {
209			cmn_err(CE_WARN, "smb_kdoor_decode[%s]: decode failed",
210			    da->da_opname);
211			xdr_destroy(&xdrs);
212			return (-1);
213		}
214	}
215
216	xdr_destroy(&xdrs);
217	return (0);
218}
219
220static void
221smb_kdoor_sethdr(smb_doorarg_t *da, uint32_t datalen)
222{
223	smb_doorhdr_t	*hdr = &da->da_hdr;
224
225	bzero(hdr, sizeof (smb_doorhdr_t));
226	hdr->dh_magic = SMB_DOOR_HDR_MAGIC;
227	hdr->dh_flags = da->da_flags | SMB_DF_FAKE_KERNEL;
228	hdr->dh_op = da->da_opcode;
229	/* hdr->dh_txid = 0 (not used) */
230	hdr->dh_datalen = datalen;
231	hdr->dh_door_rc = SMB_DOP_NOT_CALLED;
232}
233
234static boolean_t
235smb_kdoor_chkhdr(smb_doorarg_t *da, smb_doorhdr_t *hdr)
236{
237	if ((hdr->dh_magic != SMB_DOOR_HDR_MAGIC) ||
238	    (hdr->dh_op != da->da_hdr.dh_op) ||
239	    (hdr->dh_txid != da->da_hdr.dh_txid)) {
240		cmn_err(CE_WARN, "smb_kdoor_chkhdr[%s]: invalid header",
241		    da->da_opname);
242		return (B_FALSE);
243	}
244
245	switch (hdr->dh_door_rc) {
246	case SMB_DOP_SUCCESS:
247		break;
248
249	/* SMB_DOP_EMPTYBUF is a "normal" error (silent). */
250	case SMB_DOP_EMPTYBUF:
251		return (B_FALSE);
252
253	default:
254		cmn_err(CE_WARN, "smb_kdoor_chkhdr[%s]: call failed: %u",
255		    da->da_opname, hdr->dh_door_rc);
256		return (B_FALSE);
257	}
258
259	return (B_TRUE);
260}
261
262/*
263 * Free both the argument and result door buffers regardless of the status
264 * of the up-call.  The doorfs allocates a new buffer if the result buffer
265 * passed by the client is too small.
266 */
267static void
268smb_kdoor_free(door_arg_t *arg)
269{
270	if (arg->rbuf != NULL && arg->rbuf != arg->data_ptr)
271		kmem_free(arg->rbuf, arg->rsize);
272
273	if (arg->data_ptr != NULL)
274		kmem_free(arg->data_ptr, arg->data_size);
275}
276