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 39 static int smb_kdoor_encode(smb_doorarg_t *); 40 static int smb_kdoor_decode(smb_doorarg_t *); 41 static void smb_kdoor_sethdr(smb_doorarg_t *, uint32_t); 42 static boolean_t smb_kdoor_chkhdr(smb_doorarg_t *, smb_doorhdr_t *); 43 static void smb_kdoor_free(door_arg_t *); 44 45 void 46 smb_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 53 void 54 smb_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 */ 67 void 68 fksmb_kdoor_open(smb_server_t *sv, void *varg) 69 { 70 sv->sv_kdoor_hd = varg; 71 } 72 73 void 74 smb_kdoor_close(smb_server_t *sv) 75 { 76 sv->sv_kdoor_hd = NULL; 77 sv->sv_kdoor_id = -1; 78 } 79 80 /* ARGSUSED */ 81 int 82 smb_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); 122 out: 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 131 static int 132 smb_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 */ 179 static int 180 smb_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 220 static void 221 smb_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 234 static boolean_t 235 smb_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 */ 267 static void 268 smb_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