xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_kshare.c (revision 74e7dc986c89efca1f2e4451c7a572e05e4a6e4f)
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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"@(#)smb_kshare.c	1.7	08/08/07 SMI"
27 
28 /*
29  * Kernel door client for LanMan share management.
30  */
31 
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <sys/cmn_err.h>
35 #include <sys/door.h>
36 #include <smbsrv/lmerr.h>
37 #include <smbsrv/smb_share.h>
38 #include <smbsrv/smb_common_door.h>
39 #include <smbsrv/smbinfo.h>
40 
41 static int smb_kshare_chk_dsrv_status(int, smb_dr_ctx_t *);
42 
43 /*
44  * smb_kshare_init
45  *
46  * This function is not MultiThread safe. The caller has to make sure only one
47  * thread calls this function.
48  */
49 door_handle_t
50 smb_kshare_init(int door_id)
51 {
52 	return (door_ki_lookup(door_id));
53 }
54 
55 /*
56  * smb_kshare_fini
57  *
58  * This function is not MultiThread safe. The caller has to make sure only one
59  * thread calls this function.
60  */
61 void
62 smb_kshare_fini(door_handle_t dhdl)
63 {
64 	if (dhdl)
65 		door_ki_rele(dhdl);
66 }
67 
68 uint32_t
69 smb_kshare_getinfo(door_handle_t dhdl, const char *share_name, smb_share_t *si)
70 {
71 	door_arg_t arg;
72 	char *buf;
73 	unsigned int used;
74 	smb_dr_ctx_t *dec_ctx;
75 	smb_dr_ctx_t *enc_ctx;
76 	int status;
77 	uint32_t rc;
78 	int opcode = SMB_SHROP_GETINFO;
79 
80 	buf = kmem_alloc(SMB_SHARE_DSIZE, KM_SLEEP);
81 
82 	enc_ctx = smb_dr_encode_start(buf, SMB_SHARE_DSIZE);
83 	smb_dr_put_uint32(enc_ctx, opcode);
84 	smb_dr_put_string(enc_ctx, share_name);
85 
86 	if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
87 		cmn_err(CE_WARN, "smb_kshare_getinfo: Encode error %d",
88 		    status);
89 		kmem_free(buf, SMB_SHARE_DSIZE);
90 		return (NERR_InternalError);
91 	}
92 
93 	arg.data_ptr = buf;
94 	arg.data_size = used;
95 	arg.desc_ptr = NULL;
96 	arg.desc_num = 0;
97 	arg.rbuf = buf;
98 	arg.rsize = SMB_SHARE_DSIZE;
99 
100 	if (door_ki_upcall_limited(dhdl, &arg, NULL, SIZE_MAX, 0) != 0) {
101 		cmn_err(CE_WARN, "smb_kshare_getinfo: Door call failed");
102 		kmem_free(buf, SMB_SHARE_DSIZE);
103 		return (NERR_InternalError);
104 	}
105 
106 	dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
107 	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
108 		kmem_free(buf, SMB_SHARE_DSIZE);
109 		return (NERR_InternalError);
110 	}
111 
112 	rc = smb_dr_get_uint32(dec_ctx);
113 	smb_dr_get_share(dec_ctx, si);
114 	if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
115 		cmn_err(CE_WARN, "smb_kshare_getinfo: Decode error %d",
116 		    status);
117 		rc = NERR_InternalError;
118 	}
119 
120 	kmem_free(buf, SMB_SHARE_DSIZE);
121 	return (rc);
122 }
123 
124 uint32_t
125 smb_kshare_enum(door_handle_t dhdl, smb_enumshare_info_t *enuminfo)
126 {
127 	door_arg_t arg;
128 	char *door_buf;
129 	int door_bufsz;
130 	unsigned int used;
131 	smb_dr_ctx_t *dec_ctx;
132 	smb_dr_ctx_t *enc_ctx;
133 	int status;
134 	uint32_t rc;
135 	int opcode = SMB_SHROP_ENUM;
136 
137 	enuminfo->es_ntotal = enuminfo->es_nsent = 0;
138 
139 	door_bufsz = enuminfo->es_bufsize + strlen(enuminfo->es_username)
140 	    + sizeof (smb_enumshare_info_t);
141 	door_buf = kmem_alloc(door_bufsz, KM_SLEEP);
142 
143 	enc_ctx = smb_dr_encode_start(door_buf, door_bufsz);
144 	smb_dr_put_uint32(enc_ctx, opcode);
145 	smb_dr_put_ushort(enc_ctx, enuminfo->es_bufsize);
146 	smb_dr_put_string(enc_ctx, enuminfo->es_username);
147 
148 	if ((status = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
149 		cmn_err(CE_WARN, "smb_kshare_enum: Encode error %d", status);
150 		kmem_free(door_buf, door_bufsz);
151 		return (NERR_InternalError);
152 	}
153 
154 	arg.data_ptr = door_buf;
155 	arg.data_size = used;
156 	arg.desc_ptr = NULL;
157 	arg.desc_num = 0;
158 	arg.rbuf = door_buf;
159 	arg.rsize = door_bufsz;
160 
161 	if (door_ki_upcall_limited(dhdl, &arg, NULL, SIZE_MAX, 0) != 0) {
162 		cmn_err(CE_WARN, "smb_kshare_enum: Door call failed");
163 		kmem_free(door_buf, door_bufsz);
164 		return (NERR_InternalError);
165 	}
166 
167 	dec_ctx = smb_dr_decode_start(arg.data_ptr, arg.data_size);
168 	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
169 		kmem_free(door_buf, door_bufsz);
170 		return (NERR_InternalError);
171 	}
172 
173 	rc = smb_dr_get_uint32(dec_ctx);
174 	if (rc == NERR_Success) {
175 		enuminfo->es_ntotal = smb_dr_get_ushort(dec_ctx);
176 		enuminfo->es_nsent = smb_dr_get_ushort(dec_ctx);
177 		enuminfo->es_datasize = smb_dr_get_ushort(dec_ctx);
178 		(void) smb_dr_get_buf(dec_ctx,
179 		    (unsigned char *)enuminfo->es_buf,
180 		    enuminfo->es_bufsize);
181 	}
182 
183 	if ((status = smb_dr_decode_finish(dec_ctx)) != 0) {
184 		cmn_err(CE_WARN, "smb_kshare_enum: Decode error %d", status);
185 		rc = NERR_InternalError;
186 	}
187 
188 	kmem_free(door_buf, door_bufsz);
189 	return (rc);
190 }
191 
192 /*
193  * This is a special interface that will be utilized by ZFS to cause
194  * a share to be added/removed
195  *
196  * arg is either a smb_share_t or share_name from userspace.
197  * It will need to be copied into the kernel.   It is smb_share_t
198  * for add operations and share_name for delete operations.
199  */
200 int
201 smb_kshare_upcall(door_handle_t dhdl, void *arg, boolean_t add_share)
202 {
203 	door_arg_t	doorarg = { 0 };
204 	char		*buf = NULL;
205 	char		*str = NULL;
206 	int		error;
207 	int		rc;
208 	unsigned int	used;
209 	smb_dr_ctx_t	*dec_ctx;
210 	smb_dr_ctx_t	*enc_ctx;
211 	smb_share_t	*lmshare = NULL;
212 	int		opcode;
213 
214 	opcode = (add_share) ? SMB_SHROP_ADD : SMB_SHROP_DELETE;
215 
216 	buf = kmem_alloc(SMB_SHARE_DSIZE, KM_SLEEP);
217 	enc_ctx = smb_dr_encode_start(buf, SMB_SHARE_DSIZE);
218 	smb_dr_put_uint32(enc_ctx, opcode);
219 
220 	switch (opcode) {
221 	case SMB_SHROP_ADD:
222 		lmshare = kmem_alloc(sizeof (smb_share_t), KM_SLEEP);
223 		if (error = xcopyin(arg, lmshare, sizeof (smb_share_t))) {
224 			kmem_free(lmshare, sizeof (smb_share_t));
225 			kmem_free(buf, SMB_SHARE_DSIZE);
226 			return (error);
227 		}
228 		smb_dr_put_share(enc_ctx, lmshare);
229 		break;
230 
231 	case SMB_SHROP_DELETE:
232 		str = kmem_alloc(MAXPATHLEN, KM_SLEEP);
233 		if (error = copyinstr(arg, str, MAXPATHLEN, NULL)) {
234 			kmem_free(str, MAXPATHLEN);
235 			kmem_free(buf, SMB_SHARE_DSIZE);
236 			return (error);
237 		}
238 		smb_dr_put_string(enc_ctx, str);
239 		kmem_free(str, MAXPATHLEN);
240 		break;
241 	}
242 
243 	if ((error = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
244 		kmem_free(buf, SMB_SHARE_DSIZE);
245 		if (lmshare)
246 			kmem_free(lmshare, sizeof (smb_share_t));
247 		return (NERR_InternalError);
248 	}
249 
250 	doorarg.data_ptr = buf;
251 	doorarg.data_size = used;
252 	doorarg.rbuf = buf;
253 	doorarg.rsize = SMB_SHARE_DSIZE;
254 
255 	error = door_ki_upcall_limited(dhdl, &doorarg, NULL, SIZE_MAX, 0);
256 
257 	if (error) {
258 		kmem_free(buf, SMB_SHARE_DSIZE);
259 		if (lmshare)
260 			kmem_free(lmshare, sizeof (smb_share_t));
261 		return (error);
262 	}
263 
264 	dec_ctx = smb_dr_decode_start(doorarg.data_ptr, doorarg.data_size);
265 	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
266 		kmem_free(buf, SMB_SHARE_DSIZE);
267 		if (lmshare)
268 			kmem_free(lmshare, sizeof (smb_share_t));
269 		return (NERR_InternalError);
270 	}
271 
272 	rc = smb_dr_get_uint32(dec_ctx);
273 	if (opcode == SMB_SHROP_ADD)
274 		smb_dr_get_share(dec_ctx, lmshare);
275 
276 	if (smb_dr_decode_finish(dec_ctx))
277 		rc = NERR_InternalError;
278 
279 	kmem_free(buf, SMB_SHARE_DSIZE);
280 	if (lmshare)
281 		kmem_free(lmshare, sizeof (smb_share_t));
282 
283 	return ((rc == NERR_DuplicateShare && add_share) ? 0 : rc);
284 }
285 
286 /*
287  * Return 0 upon success. Otherwise > 0
288  */
289 static int
290 smb_kshare_chk_dsrv_status(int opcode, smb_dr_ctx_t *dec_ctx)
291 {
292 	int status = smb_dr_get_int32(dec_ctx);
293 	int err;
294 
295 	switch (status) {
296 	case SMB_SHARE_DSUCCESS:
297 		return (0);
298 
299 	case SMB_SHARE_DERROR:
300 		err = smb_dr_get_uint32(dec_ctx);
301 		cmn_err(CE_WARN, "%d: Encountered door server error %d",
302 		    opcode, err);
303 		(void) smb_dr_decode_finish(dec_ctx);
304 		return (err);
305 	}
306 
307 	ASSERT(0);
308 	return (EINVAL);
309 }
310