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/*
23 * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2013 Nexenta Systems, Inc.  All rights reserved.
25 */
26
27/*
28 * User-space door client for LanMan share management.
29 */
30
31#include <syslog.h>
32#include <door.h>
33#include <fcntl.h>
34#include <stdarg.h>
35#include <errno.h>
36#include <string.h>
37#include <strings.h>
38#include <unistd.h>
39#include <thread.h>
40#include <synch.h>
41
42#include <smbsrv/libsmb.h>
43#include <smbsrv/smb_share.h>
44#include <smbsrv/smb.h>
45
46#define	SMB_SHARE_DOOR_CALL_RETRIES		3
47
48static int smb_share_dfd = -1;
49static uint64_t smb_share_dncall = 0;
50static mutex_t smb_share_dmtx;
51static cond_t smb_share_dcv;
52
53static int smb_share_door_clnt_open(void);
54static void smb_share_door_clnt_close(void);
55
56void
57smb_share_door_clnt_init(void)
58{
59	(void) mutex_lock(&smb_share_dmtx);
60	(void) smb_share_door_clnt_open();
61	(void) mutex_unlock(&smb_share_dmtx);
62}
63
64void
65smb_share_door_clnt_fini(void)
66{
67	(void) mutex_lock(&smb_share_dmtx);
68	smb_share_door_clnt_close();
69	(void) mutex_unlock(&smb_share_dmtx);
70}
71
72/*
73 * Open smb_share_door.  This is a private call for use by
74 * smb_share_door_clnt_enter() and must be called with smb_share_dmtx held.
75 *
76 * Returns the door fd on success.  Otherwise, -1.
77 */
78static int
79smb_share_door_clnt_open(void)
80{
81	const char	*door_name;
82
83	if (smb_share_dfd == -1) {
84		door_name = getenv("SMB_SHARE_DNAME");
85		if (door_name == NULL)
86			door_name = SMB_SHARE_DNAME;
87
88		if ((smb_share_dfd = open(door_name, O_RDONLY)) < 0)
89			smb_share_dfd = -1;
90		else
91			smb_share_dncall = 0;
92	}
93
94	return (smb_share_dfd);
95}
96
97/*
98 * Close smb_share_door.
99 * Private call that must be called with smb_share_dmtx held.
100 */
101static void
102smb_share_door_clnt_close(void)
103{
104	if (smb_share_dfd != -1) {
105		while (smb_share_dncall > 0)
106			(void) cond_wait(&smb_share_dcv, &smb_share_dmtx);
107
108		if (smb_share_dfd != -1) {
109			(void) close(smb_share_dfd);
110			smb_share_dfd = -1;
111		}
112	}
113}
114
115/*
116 * Entry handler for smb_share_door calls.
117 */
118static door_arg_t *
119smb_share_door_clnt_enter(void)
120{
121	door_arg_t *arg;
122	char *buf;
123
124	(void) mutex_lock(&smb_share_dmtx);
125
126	if (smb_share_door_clnt_open() == -1) {
127		(void) mutex_unlock(&smb_share_dmtx);
128		return (NULL);
129	}
130
131	if ((arg = malloc(sizeof (door_arg_t) + SMB_SHARE_DSIZE)) != NULL) {
132		buf = ((char *)arg) + sizeof (door_arg_t);
133		bzero(arg, sizeof (door_arg_t));
134		arg->data_ptr = buf;
135		arg->rbuf = buf;
136		arg->rsize = SMB_SHARE_DSIZE;
137
138		++smb_share_dncall;
139	}
140
141	(void) mutex_unlock(&smb_share_dmtx);
142	return (arg);
143}
144
145/*
146 * Exit handler for smb_share_door calls.
147 */
148static void
149smb_share_door_clnt_exit(door_arg_t *arg, boolean_t must_close, char *errmsg)
150{
151	if (errmsg)
152		syslog(LOG_DEBUG, "smb_share_door: %s failed", errmsg);
153
154	(void) mutex_lock(&smb_share_dmtx);
155	free(arg);
156	--smb_share_dncall;
157	(void) cond_signal(&smb_share_dcv);
158
159	if (must_close)
160		smb_share_door_clnt_close();
161
162	(void) mutex_unlock(&smb_share_dmtx);
163}
164
165static int
166smb_share_door_call(int fd, door_arg_t *arg)
167{
168	int rc;
169	int i;
170
171	for (i = 0; i < SMB_SHARE_DOOR_CALL_RETRIES; ++i) {
172		errno = 0;
173
174		if ((rc = door_call(fd, arg)) == 0)
175			break;
176
177		if (errno != EAGAIN && errno != EINTR)
178			break;
179	}
180
181	return (rc);
182}
183
184static int
185smb_share_dchk(smb_dr_ctx_t *dec_ctx)
186{
187	int status = smb_dr_get_int32(dec_ctx);
188
189	if (status != SMB_SHARE_DSUCCESS) {
190		if (status == SMB_SHARE_DERROR)
191			(void) smb_dr_get_uint32(dec_ctx);
192		return (-1);
193	}
194
195	return (0);
196}
197
198uint32_t
199smb_share_list(int offset, smb_shrlist_t *list)
200{
201	door_arg_t *arg;
202	smb_dr_ctx_t *dec_ctx;
203	smb_dr_ctx_t *enc_ctx;
204	uint32_t rc;
205
206	bzero(list, sizeof (smb_shrlist_t));
207
208	if ((arg = smb_share_door_clnt_enter()) == NULL)
209		return (NERR_InternalError);
210
211	enc_ctx = smb_dr_encode_start(arg->data_ptr, SMB_SHARE_DSIZE);
212	smb_dr_put_uint32(enc_ctx, SMB_SHROP_LIST);
213	smb_dr_put_int32(enc_ctx, offset);
214
215	rc = smb_dr_encode_finish(enc_ctx, (unsigned int *)&arg->data_size);
216	if (rc != 0) {
217		smb_share_door_clnt_exit(arg, B_FALSE, "encode");
218		return (NERR_InternalError);
219	}
220
221	if (smb_share_door_call(smb_share_dfd, arg) < 0) {
222		smb_share_door_clnt_exit(arg, B_TRUE, "door call");
223		return (NERR_InternalError);
224	}
225
226	dec_ctx = smb_dr_decode_start(arg->data_ptr, arg->data_size);
227	if (smb_share_dchk(dec_ctx) != 0) {
228		(void) smb_dr_decode_finish(dec_ctx);
229		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
230		return (NERR_InternalError);
231	}
232
233	(void) smb_dr_get_buf(dec_ctx, (unsigned char *)list,
234	    sizeof (smb_shrlist_t));
235	if (smb_dr_decode_finish(dec_ctx) != 0) {
236		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
237		return (NERR_InternalError);
238	}
239
240	smb_share_door_clnt_exit(arg, B_FALSE, NULL);
241	return (NERR_Success);
242}
243
244int
245smb_share_count(void)
246{
247	door_arg_t *arg;
248	smb_dr_ctx_t *dec_ctx;
249	smb_dr_ctx_t *enc_ctx;
250	uint32_t num_shares;
251	int rc;
252
253	if ((arg = smb_share_door_clnt_enter()) == NULL)
254		return (-1);
255
256	enc_ctx = smb_dr_encode_start(arg->data_ptr, SMB_SHARE_DSIZE);
257	smb_dr_put_uint32(enc_ctx, SMB_SHROP_NUM_SHARES);
258
259	rc = smb_dr_encode_finish(enc_ctx, (unsigned int *)&arg->data_size);
260	if (rc != 0) {
261		smb_share_door_clnt_exit(arg, B_FALSE, "encode");
262		return (-1);
263	}
264
265	if (smb_share_door_call(smb_share_dfd, arg) < 0) {
266		smb_share_door_clnt_exit(arg, B_TRUE, "door call");
267		return (-1);
268	}
269
270	dec_ctx = smb_dr_decode_start(arg->data_ptr, arg->data_size);
271	if (smb_share_dchk(dec_ctx) != 0) {
272		(void) smb_dr_decode_finish(dec_ctx);
273		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
274		return (-1);
275	}
276
277	num_shares = smb_dr_get_uint32(dec_ctx);
278	if (smb_dr_decode_finish(dec_ctx) != 0) {
279		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
280		return (-1);
281	}
282
283	smb_share_door_clnt_exit(arg, B_FALSE, NULL);
284	return (num_shares);
285}
286
287uint32_t
288smb_share_delete(char *share_name)
289{
290	door_arg_t *arg;
291	smb_dr_ctx_t *dec_ctx;
292	smb_dr_ctx_t *enc_ctx;
293	uint32_t rc;
294
295	if ((arg = smb_share_door_clnt_enter()) == NULL)
296		return (NERR_InternalError);
297
298	enc_ctx = smb_dr_encode_start(arg->data_ptr, SMB_SHARE_DSIZE);
299	smb_dr_put_uint32(enc_ctx, SMB_SHROP_DELETE);
300	smb_dr_put_string(enc_ctx, share_name);
301
302	rc = smb_dr_encode_finish(enc_ctx, (unsigned int *)&arg->data_size);
303	if (rc != 0) {
304		smb_share_door_clnt_exit(arg, B_FALSE, "encode");
305		return (NERR_InternalError);
306	}
307
308	if (smb_share_door_call(smb_share_dfd, arg) < 0) {
309		smb_share_door_clnt_exit(arg, B_TRUE, "door call");
310		return (NERR_InternalError);
311	}
312
313	dec_ctx = smb_dr_decode_start(arg->data_ptr, arg->data_size);
314	if (smb_share_dchk(dec_ctx) != 0) {
315		(void) smb_dr_decode_finish(dec_ctx);
316		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
317		return (NERR_InternalError);
318	}
319
320	rc = smb_dr_get_uint32(dec_ctx);
321	if (smb_dr_decode_finish(dec_ctx) != 0) {
322		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
323		return (NERR_InternalError);
324	}
325
326	smb_share_door_clnt_exit(arg, B_FALSE, NULL);
327	return (rc);
328
329}
330
331uint32_t
332smb_share_rename(char *from, char *to)
333{
334	door_arg_t *arg;
335	smb_dr_ctx_t *dec_ctx;
336	smb_dr_ctx_t *enc_ctx;
337	uint32_t rc;
338
339	if ((arg = smb_share_door_clnt_enter()) == NULL)
340		return (NERR_InternalError);
341
342	enc_ctx = smb_dr_encode_start(arg->data_ptr, SMB_SHARE_DSIZE);
343	smb_dr_put_uint32(enc_ctx, SMB_SHROP_RENAME);
344	smb_dr_put_string(enc_ctx, from);
345	smb_dr_put_string(enc_ctx, to);
346
347	rc = smb_dr_encode_finish(enc_ctx, (unsigned int *)&arg->data_size);
348	if (rc != 0) {
349		smb_share_door_clnt_exit(arg, B_FALSE, "encode");
350		return (NERR_InternalError);
351	}
352
353	if (smb_share_door_call(smb_share_dfd, arg) < 0) {
354		smb_share_door_clnt_exit(arg, B_TRUE, "door call");
355		return (NERR_InternalError);
356	}
357
358	dec_ctx = smb_dr_decode_start(arg->data_ptr, arg->data_size);
359	if (smb_share_dchk(dec_ctx) != 0) {
360		(void) smb_dr_decode_finish(dec_ctx);
361		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
362		return (NERR_InternalError);
363	}
364
365	rc = smb_dr_get_uint32(dec_ctx);
366	if (smb_dr_decode_finish(dec_ctx) != 0) {
367		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
368		return (NERR_InternalError);
369	}
370
371	smb_share_door_clnt_exit(arg, B_FALSE, NULL);
372	return (rc);
373}
374
375uint32_t
376smb_share_create(smb_share_t *si)
377{
378	door_arg_t *arg;
379	smb_dr_ctx_t *dec_ctx;
380	smb_dr_ctx_t *enc_ctx;
381	uint32_t rc;
382
383	if ((arg = smb_share_door_clnt_enter()) == NULL)
384		return (NERR_InternalError);
385
386	enc_ctx = smb_dr_encode_start(arg->data_ptr, SMB_SHARE_DSIZE);
387	smb_dr_put_uint32(enc_ctx, SMB_SHROP_ADD);
388	smb_dr_put_share(enc_ctx, si);
389
390	rc = smb_dr_encode_finish(enc_ctx, (unsigned int *)&arg->data_size);
391	if (rc != 0) {
392		smb_share_door_clnt_exit(arg, B_FALSE, "encode");
393		return (NERR_InternalError);
394	}
395
396	if (smb_share_door_call(smb_share_dfd, arg) < 0) {
397		smb_share_door_clnt_exit(arg, B_TRUE, "door call");
398		return (NERR_InternalError);
399	}
400
401	dec_ctx = smb_dr_decode_start(arg->data_ptr, arg->data_size);
402	if (smb_share_dchk(dec_ctx) != 0) {
403		(void) smb_dr_decode_finish(dec_ctx);
404		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
405		return (NERR_InternalError);
406	}
407
408	rc = smb_dr_get_uint32(dec_ctx);
409	smb_dr_get_share(dec_ctx, si);
410	if (smb_dr_decode_finish(dec_ctx) != 0) {
411		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
412		return (NERR_InternalError);
413	}
414
415	smb_share_door_clnt_exit(arg, B_FALSE, NULL);
416	return (rc);
417}
418
419uint32_t
420smb_share_modify(smb_share_t *si)
421{
422	door_arg_t *arg;
423	smb_dr_ctx_t *dec_ctx;
424	smb_dr_ctx_t *enc_ctx;
425	uint32_t rc;
426
427	if ((arg = smb_share_door_clnt_enter()) == NULL)
428		return (NERR_InternalError);
429
430	enc_ctx = smb_dr_encode_start(arg->data_ptr, SMB_SHARE_DSIZE);
431	smb_dr_put_uint32(enc_ctx, SMB_SHROP_MODIFY);
432	smb_dr_put_share(enc_ctx, si);
433
434	rc = smb_dr_encode_finish(enc_ctx, (unsigned int *)&arg->data_size);
435	if (rc != 0) {
436		smb_share_door_clnt_exit(arg, B_FALSE, "encode");
437		return (NERR_InternalError);
438	}
439
440	if (smb_share_door_call(smb_share_dfd, arg) < 0) {
441		smb_share_door_clnt_exit(arg, B_TRUE, "door call");
442		return (NERR_InternalError);
443	}
444
445	dec_ctx = smb_dr_decode_start(arg->data_ptr, arg->data_size);
446	if (smb_share_dchk(dec_ctx) != 0) {
447		(void) smb_dr_decode_finish(dec_ctx);
448		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
449		return (NERR_InternalError);
450	}
451
452	rc = smb_dr_get_uint32(dec_ctx);
453	if (smb_dr_decode_finish(dec_ctx) != 0) {
454		smb_share_door_clnt_exit(arg, B_FALSE, "decode");
455		return (NERR_InternalError);
456	}
457
458	smb_share_door_clnt_exit(arg, B_FALSE, NULL);
459	return (rc);
460}
461