xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_kshare.c (revision 9cb0a1d4446fe891a01d01e07b2e27f0177c84cd)
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 2017 Nexenta Systems, Inc.  All rights reserved.
25  * Copyright 2017 Joyent, Inc.
26  */
27 
28 #include <smbsrv/smb_door.h>
29 #include <smbsrv/smb_ktypes.h>
30 #include <smbsrv/smb2_kproto.h>
31 #include <smbsrv/smb_kstat.h>
32 
33 typedef struct smb_unshare {
34 	list_node_t	us_lnd;
35 	char		us_sharename[MAXNAMELEN];
36 } smb_unshare_t;
37 
38 static kmem_cache_t	*smb_kshare_cache_share;
39 static kmem_cache_t	*smb_kshare_cache_unexport;
40 
41 static int smb_kshare_cmp(const void *, const void *);
42 static void smb_kshare_hold(const void *);
43 static boolean_t smb_kshare_rele(const void *);
44 static void smb_kshare_destroy(void *);
45 static char *smb_kshare_oemname(const char *);
46 static int smb_kshare_is_special(const char *);
47 static boolean_t smb_kshare_is_admin(const char *);
48 static smb_kshare_t *smb_kshare_decode(nvlist_t *);
49 static uint32_t smb_kshare_decode_bool(nvlist_t *, const char *, uint32_t);
50 static void smb_kshare_unexport_thread(smb_thread_t *, void *);
51 static int smb_kshare_export(smb_server_t *, smb_kshare_t *);
52 static int smb_kshare_unexport(smb_server_t *, const char *);
53 static int smb_kshare_export_trans(smb_server_t *, char *, char *, char *);
54 static void smb_kshare_csc_flags(smb_kshare_t *, const char *);
55 
56 static boolean_t smb_export_isready(smb_server_t *);
57 
58 #ifdef	_KERNEL
59 static int smb_kshare_chk_dsrv_status(int, smb_dr_ctx_t *);
60 #endif	/* _KERNEL */
61 
62 static const smb_avl_nops_t smb_kshare_avlops = {
63 	smb_kshare_cmp,
64 	smb_kshare_hold,
65 	smb_kshare_rele,
66 	smb_kshare_destroy
67 };
68 
69 #ifdef	_KERNEL
70 /*
71  * This function is not MultiThread safe. The caller has to make sure only one
72  * thread calls this function.
73  */
74 door_handle_t
75 smb_kshare_door_init(int door_id)
76 {
77 	return (door_ki_lookup(door_id));
78 }
79 
80 /*
81  * This function is not MultiThread safe. The caller has to make sure only one
82  * thread calls this function.
83  */
84 void
85 smb_kshare_door_fini(door_handle_t dhdl)
86 {
87 	if (dhdl)
88 		door_ki_rele(dhdl);
89 }
90 
91 /*
92  * This is a special interface that will be utilized by ZFS to cause
93  * a share to be added/removed
94  *
95  * arg is either a smb_share_t or share_name from userspace.
96  * It will need to be copied into the kernel.   It is smb_share_t
97  * for add operations and share_name for delete operations.
98  */
99 int
100 smb_kshare_upcall(door_handle_t dhdl, void *arg, boolean_t add_share)
101 {
102 	door_arg_t	doorarg = { 0 };
103 	char		*buf = NULL;
104 	char		*str = NULL;
105 	int		error;
106 	int		rc;
107 	unsigned int	used;
108 	smb_dr_ctx_t	*dec_ctx;
109 	smb_dr_ctx_t	*enc_ctx;
110 	smb_share_t	*lmshare = NULL;
111 	int		opcode;
112 
113 	opcode = (add_share) ? SMB_SHROP_ADD : SMB_SHROP_DELETE;
114 
115 	buf = kmem_alloc(SMB_SHARE_DSIZE, KM_SLEEP);
116 	enc_ctx = smb_dr_encode_start(buf, SMB_SHARE_DSIZE);
117 	smb_dr_put_uint32(enc_ctx, opcode);
118 
119 	switch (opcode) {
120 	case SMB_SHROP_ADD:
121 		lmshare = kmem_alloc(sizeof (smb_share_t), KM_SLEEP);
122 		error = xcopyin(arg, lmshare, sizeof (smb_share_t));
123 		if (error != 0) {
124 			kmem_free(lmshare, sizeof (smb_share_t));
125 			kmem_free(buf, SMB_SHARE_DSIZE);
126 			return (error);
127 		}
128 		smb_dr_put_share(enc_ctx, lmshare);
129 		break;
130 
131 	case SMB_SHROP_DELETE:
132 		str = kmem_alloc(MAXPATHLEN, KM_SLEEP);
133 		error = copyinstr(arg, str, MAXPATHLEN, NULL);
134 		if (error != 0) {
135 			kmem_free(str, MAXPATHLEN);
136 			kmem_free(buf, SMB_SHARE_DSIZE);
137 			return (error);
138 		}
139 		smb_dr_put_string(enc_ctx, str);
140 		kmem_free(str, MAXPATHLEN);
141 		break;
142 	}
143 
144 	if ((error = smb_dr_encode_finish(enc_ctx, &used)) != 0) {
145 		kmem_free(buf, SMB_SHARE_DSIZE);
146 		if (lmshare)
147 			kmem_free(lmshare, sizeof (smb_share_t));
148 		return (NERR_InternalError);
149 	}
150 
151 	doorarg.data_ptr = buf;
152 	doorarg.data_size = used;
153 	doorarg.rbuf = buf;
154 	doorarg.rsize = SMB_SHARE_DSIZE;
155 
156 	error = door_ki_upcall_limited(dhdl, &doorarg, NULL, SIZE_MAX, 0);
157 
158 	if (error) {
159 		kmem_free(buf, SMB_SHARE_DSIZE);
160 		if (lmshare)
161 			kmem_free(lmshare, sizeof (smb_share_t));
162 		return (error);
163 	}
164 
165 	dec_ctx = smb_dr_decode_start(doorarg.data_ptr, doorarg.data_size);
166 	if (smb_kshare_chk_dsrv_status(opcode, dec_ctx) != 0) {
167 		kmem_free(buf, SMB_SHARE_DSIZE);
168 		if (lmshare)
169 			kmem_free(lmshare, sizeof (smb_share_t));
170 		return (NERR_InternalError);
171 	}
172 
173 	rc = smb_dr_get_uint32(dec_ctx);
174 	if (opcode == SMB_SHROP_ADD)
175 		smb_dr_get_share(dec_ctx, lmshare);
176 
177 	if (smb_dr_decode_finish(dec_ctx))
178 		rc = NERR_InternalError;
179 
180 	kmem_free(buf, SMB_SHARE_DSIZE);
181 	if (lmshare)
182 		kmem_free(lmshare, sizeof (smb_share_t));
183 
184 	return ((rc == NERR_DuplicateShare && add_share) ? 0 : rc);
185 }
186 #endif	/* _KERNEL */
187 
188 /*
189  * Executes map and unmap command for shares.
190  */
191 int
192 smb_kshare_exec(smb_server_t *sv, smb_shr_execinfo_t *execinfo)
193 {
194 	int exec_rc = 0;
195 
196 	(void) smb_kdoor_upcall(sv, SMB_DR_SHR_EXEC,
197 	    execinfo, smb_shr_execinfo_xdr, &exec_rc, xdr_int);
198 
199 	return (exec_rc);
200 }
201 
202 /*
203  * Obtains any host access restriction on the specified
204  * share for the given host (ipaddr) by calling smbd
205  */
206 uint32_t
207 smb_kshare_hostaccess(smb_kshare_t *shr, smb_session_t *session)
208 {
209 	smb_shr_hostaccess_query_t req;
210 	smb_inaddr_t *ipaddr = &session->ipaddr;
211 	uint32_t host_access = SMB_SHRF_ACC_OPEN;
212 	uint32_t flag = SMB_SHRF_ACC_OPEN;
213 	uint32_t access;
214 
215 	if (smb_inet_iszero(ipaddr))
216 		return (ACE_ALL_PERMS);
217 
218 	if ((shr->shr_access_none == NULL || *shr->shr_access_none == '\0') &&
219 	    (shr->shr_access_ro == NULL || *shr->shr_access_ro == '\0') &&
220 	    (shr->shr_access_rw == NULL || *shr->shr_access_rw == '\0'))
221 		return (ACE_ALL_PERMS);
222 
223 	if (shr->shr_access_none != NULL)
224 		flag |= SMB_SHRF_ACC_NONE;
225 	if (shr->shr_access_ro != NULL)
226 		flag |= SMB_SHRF_ACC_RO;
227 	if (shr->shr_access_rw != NULL)
228 		flag |= SMB_SHRF_ACC_RW;
229 
230 	req.shq_none = shr->shr_access_none;
231 	req.shq_ro = shr->shr_access_ro;
232 	req.shq_rw = shr->shr_access_rw;
233 	req.shq_flag = flag;
234 	req.shq_ipaddr = *ipaddr;
235 
236 	(void) smb_kdoor_upcall(session->s_server, SMB_DR_SHR_HOSTACCESS,
237 	    &req, smb_shr_hostaccess_query_xdr, &host_access, xdr_uint32_t);
238 
239 	switch (host_access) {
240 	case SMB_SHRF_ACC_RO:
241 		access = ACE_ALL_PERMS & ~ACE_ALL_WRITE_PERMS;
242 		break;
243 	case SMB_SHRF_ACC_OPEN:
244 	case SMB_SHRF_ACC_RW:
245 		access = ACE_ALL_PERMS;
246 		break;
247 	case SMB_SHRF_ACC_NONE:
248 	default:
249 		access = 0;
250 	}
251 
252 	return (access);
253 }
254 
255 /*
256  * This function is called when smb_server_t is
257  * created which means smb/service is ready for
258  * exporting SMB shares
259  */
260 void
261 smb_export_start(smb_server_t *sv)
262 {
263 	mutex_enter(&sv->sv_export.e_mutex);
264 	if (sv->sv_export.e_ready) {
265 		mutex_exit(&sv->sv_export.e_mutex);
266 		return;
267 	}
268 
269 	sv->sv_export.e_ready = B_TRUE;
270 	mutex_exit(&sv->sv_export.e_mutex);
271 
272 	smb_avl_create(&sv->sv_export.e_share_avl, sizeof (smb_kshare_t),
273 	    offsetof(smb_kshare_t, shr_link), &smb_kshare_avlops);
274 
275 	(void) smb_kshare_export_trans(sv, "IPC$", "IPC$", "Remote IPC");
276 	(void) smb_kshare_export_trans(sv, "c$", SMB_CVOL, "Default Share");
277 	(void) smb_kshare_export_trans(sv, "vss$", SMB_VSS, "VSS");
278 }
279 
280 /*
281  * This function is called when smb_server_t goes
282  * away which means SMB shares should not be made
283  * available to clients
284  */
285 void
286 smb_export_stop(smb_server_t *sv)
287 {
288 	mutex_enter(&sv->sv_export.e_mutex);
289 	if (!sv->sv_export.e_ready) {
290 		mutex_exit(&sv->sv_export.e_mutex);
291 		return;
292 	}
293 	sv->sv_export.e_ready = B_FALSE;
294 	mutex_exit(&sv->sv_export.e_mutex);
295 
296 	smb_avl_destroy(&sv->sv_export.e_share_avl);
297 }
298 
299 void
300 smb_kshare_g_init(void)
301 {
302 	smb_kshare_cache_share = kmem_cache_create("smb_share_cache",
303 	    sizeof (smb_kshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
304 
305 	smb_kshare_cache_unexport = kmem_cache_create("smb_unexport_cache",
306 	    sizeof (smb_unshare_t), 8, NULL, NULL, NULL, NULL, NULL, 0);
307 }
308 
309 void
310 smb_kshare_init(smb_server_t *sv)
311 {
312 
313 	smb_slist_constructor(&sv->sv_export.e_unexport_list,
314 	    sizeof (smb_unshare_t), offsetof(smb_unshare_t, us_lnd));
315 }
316 
317 int
318 smb_kshare_start(smb_server_t *sv)
319 {
320 	smb_thread_init(&sv->sv_export.e_unexport_thread, "smb_kshare_unexport",
321 	    smb_kshare_unexport_thread, sv, smbsrv_base_pri);
322 
323 	return (smb_thread_start(&sv->sv_export.e_unexport_thread));
324 }
325 
326 void
327 smb_kshare_stop(smb_server_t *sv)
328 {
329 	smb_thread_stop(&sv->sv_export.e_unexport_thread);
330 	smb_thread_destroy(&sv->sv_export.e_unexport_thread);
331 }
332 
333 void
334 smb_kshare_fini(smb_server_t *sv)
335 {
336 	smb_unshare_t *ux;
337 
338 	while ((ux = list_head(&sv->sv_export.e_unexport_list.sl_list))
339 	    != NULL) {
340 		smb_slist_remove(&sv->sv_export.e_unexport_list, ux);
341 		kmem_cache_free(smb_kshare_cache_unexport, ux);
342 	}
343 	smb_slist_destructor(&sv->sv_export.e_unexport_list);
344 }
345 
346 void
347 smb_kshare_g_fini(void)
348 {
349 	kmem_cache_destroy(smb_kshare_cache_unexport);
350 	kmem_cache_destroy(smb_kshare_cache_share);
351 }
352 
353 /*
354  * A list of shares in nvlist format can be sent down
355  * from userspace thourgh the IOCTL interface. The nvlist
356  * is unpacked here and all the shares in the list will
357  * be exported.
358  */
359 int
360 smb_kshare_export_list(smb_ioc_share_t *ioc)
361 {
362 	smb_server_t	*sv = NULL;
363 	nvlist_t	*shrlist = NULL;
364 	nvlist_t	 *share;
365 	nvpair_t	 *nvp;
366 	smb_kshare_t	 *shr;
367 	char		*shrname;
368 	int		rc;
369 
370 	if ((rc = smb_server_lookup(&sv)) != 0)
371 		return (rc);
372 
373 	if (!smb_export_isready(sv)) {
374 		rc = ENOTACTIVE;
375 		goto out;
376 	}
377 
378 	/*
379 	 * Reality check that the nvlist's reported length doesn't exceed the
380 	 * ioctl's total length.  We then assume the nvlist_unpack() will
381 	 * sanity check the nvlist itself.
382 	 */
383 	if ((ioc->shrlen + offsetof(smb_ioc_share_t, shr)) > ioc->hdr.len) {
384 		rc = EINVAL;
385 		goto out;
386 	}
387 	rc = nvlist_unpack(ioc->shr, ioc->shrlen, &shrlist, KM_SLEEP);
388 	if (rc != 0)
389 		goto out;
390 
391 	for (nvp = nvlist_next_nvpair(shrlist, NULL); nvp != NULL;
392 	    nvp = nvlist_next_nvpair(shrlist, nvp)) {
393 
394 		/*
395 		 * Since this loop can run for a while we want to exit
396 		 * as soon as the server state is anything but RUNNING
397 		 * to allow shutdown to proceed.
398 		 */
399 		if (sv->sv_state != SMB_SERVER_STATE_RUNNING)
400 			goto out;
401 
402 		if (nvpair_type(nvp) != DATA_TYPE_NVLIST)
403 			continue;
404 
405 		shrname = nvpair_name(nvp);
406 		ASSERT(shrname);
407 
408 		if ((rc = nvpair_value_nvlist(nvp, &share)) != 0) {
409 			cmn_err(CE_WARN, "export[%s]: failed accessing",
410 			    shrname);
411 			continue;
412 		}
413 
414 		if ((shr = smb_kshare_decode(share)) == NULL) {
415 			cmn_err(CE_WARN, "export[%s]: failed decoding",
416 			    shrname);
417 			continue;
418 		}
419 
420 		/* smb_kshare_export consumes shr so it's not leaked */
421 		if ((rc = smb_kshare_export(sv, shr)) != 0) {
422 			smb_kshare_destroy(shr);
423 			continue;
424 		}
425 	}
426 	rc = 0;
427 
428 out:
429 	nvlist_free(shrlist);
430 	smb_server_release(sv);
431 	return (rc);
432 }
433 
434 /*
435  * This function is invoked when a share is disabled to disconnect trees
436  * and close files.  Cleaning up may involve VOP and/or VFS calls, which
437  * may conflict/deadlock with stuck threads if something is amiss with the
438  * file system.  Queueing the request for asynchronous processing allows the
439  * call to return immediately so that, if the unshare is being done in the
440  * context of a forced unmount, the forced unmount will always be able to
441  * proceed (unblocking stuck I/O and eventually allowing all blocked unshare
442  * processes to complete).
443  *
444  * The path lookup to find the root vnode of the VFS in question and the
445  * release of this vnode are done synchronously prior to any associated
446  * unmount.  Doing these asynchronous to an associated unmount could run
447  * the risk of a spurious EBUSY for a standard unmount or an EIO during
448  * the path lookup due to a forced unmount finishing first.
449  */
450 int
451 smb_kshare_unexport_list(smb_ioc_share_t *ioc)
452 {
453 	smb_server_t	*sv = NULL;
454 	smb_unshare_t	*ux;
455 	nvlist_t	*shrlist = NULL;
456 	nvpair_t	*nvp;
457 	boolean_t	unexport = B_FALSE;
458 	char		*shrname;
459 	int		rc;
460 
461 	if ((rc = smb_server_lookup(&sv)) != 0)
462 		return (rc);
463 
464 	/*
465 	 * Reality check that the nvlist's reported length doesn't exceed the
466 	 * ioctl's total length.  We then assume the nvlist_unpack() will
467 	 * sanity check the nvlist itself.
468 	 */
469 	if ((ioc->shrlen + offsetof(smb_ioc_share_t, shr)) > ioc->hdr.len) {
470 		rc = EINVAL;
471 		goto out;
472 	}
473 	if ((rc = nvlist_unpack(ioc->shr, ioc->shrlen, &shrlist, 0)) != 0)
474 		goto out;
475 
476 	for (nvp = nvlist_next_nvpair(shrlist, NULL); nvp != NULL;
477 	    nvp = nvlist_next_nvpair(shrlist, nvp)) {
478 		if (nvpair_type(nvp) != DATA_TYPE_NVLIST)
479 			continue;
480 
481 		shrname = nvpair_name(nvp);
482 		ASSERT(shrname);
483 
484 		if ((rc = smb_kshare_unexport(sv, shrname)) != 0)
485 			continue;
486 
487 		ux = kmem_cache_alloc(smb_kshare_cache_unexport, KM_SLEEP);
488 		(void) strlcpy(ux->us_sharename, shrname, MAXNAMELEN);
489 
490 		smb_slist_insert_tail(&sv->sv_export.e_unexport_list, ux);
491 		unexport = B_TRUE;
492 	}
493 
494 	if (unexport)
495 		smb_thread_signal(&sv->sv_export.e_unexport_thread);
496 	rc = 0;
497 
498 out:
499 	nvlist_free(shrlist);
500 	smb_server_release(sv);
501 	return (rc);
502 }
503 
504 /*
505  * Get properties (currently only shortname enablement)
506  * of specified share.
507  */
508 int
509 smb_kshare_info(smb_ioc_shareinfo_t *ioc)
510 {
511 	ioc->shortnames = smb_shortnames;
512 	return (0);
513 }
514 
515 /*
516  * This function builds a response for a NetShareEnum RAP request.
517  * List of shares is scanned twice. In the first round the total number
518  * of shares which their OEM name is shorter than 13 chars (esi->es_ntotal)
519  * and also the number of shares that fit in the given buffer are calculated.
520  * In the second round the shares data are encoded in the buffer.
521  *
522  * The data associated with each share has two parts, a fixed size part and
523  * a variable size part which is share's comment. The outline of the response
524  * buffer is so that fixed part for all the shares will appear first and follows
525  * with the comments for all those shares and that's why the data cannot be
526  * encoded in one round without unnecessarily complicating the code.
527  */
528 void
529 smb_kshare_enum(smb_server_t *sv, smb_enumshare_info_t *esi)
530 {
531 	smb_avl_t *share_avl;
532 	smb_avl_cursor_t cursor;
533 	smb_kshare_t *shr;
534 	int remained;
535 	uint16_t infolen = 0;
536 	uint16_t cmntlen = 0;
537 	uint16_t sharelen;
538 	uint16_t clen;
539 	uint32_t cmnt_offs;
540 	smb_msgbuf_t info_mb;
541 	smb_msgbuf_t cmnt_mb;
542 	boolean_t autohome_added = B_FALSE;
543 
544 	if (!smb_export_isready(sv)) {
545 		esi->es_ntotal = esi->es_nsent = 0;
546 		esi->es_datasize = 0;
547 		return;
548 	}
549 
550 	esi->es_ntotal = esi->es_nsent = 0;
551 	remained = esi->es_bufsize;
552 	share_avl = &sv->sv_export.e_share_avl;
553 
554 	/* Do the necessary calculations in the first round */
555 	smb_avl_iterinit(share_avl, &cursor);
556 
557 	while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
558 		if (shr->shr_oemname == NULL) {
559 			smb_avl_release(share_avl, shr);
560 			continue;
561 		}
562 
563 		if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
564 			if (esi->es_posix_uid == shr->shr_uid) {
565 				autohome_added = B_TRUE;
566 			} else {
567 				smb_avl_release(share_avl, shr);
568 				continue;
569 			}
570 		}
571 
572 		esi->es_ntotal++;
573 
574 		if (remained <= 0) {
575 			smb_avl_release(share_avl, shr);
576 			continue;
577 		}
578 
579 		clen = strlen(shr->shr_cmnt) + 1;
580 		sharelen = SHARE_INFO_1_SIZE + clen;
581 
582 		if (sharelen <= remained) {
583 			infolen += SHARE_INFO_1_SIZE;
584 			cmntlen += clen;
585 		}
586 
587 		remained -= sharelen;
588 		smb_avl_release(share_avl, shr);
589 	}
590 
591 	esi->es_datasize = infolen + cmntlen;
592 
593 	smb_msgbuf_init(&info_mb, (uint8_t *)esi->es_buf, infolen, 0);
594 	smb_msgbuf_init(&cmnt_mb, (uint8_t *)esi->es_buf + infolen, cmntlen, 0);
595 	cmnt_offs = infolen;
596 
597 	/* Encode the data in the second round */
598 	smb_avl_iterinit(share_avl, &cursor);
599 	autohome_added = B_FALSE;
600 
601 	while ((shr = smb_avl_iterate(share_avl, &cursor)) != NULL) {
602 		if (shr->shr_oemname == NULL) {
603 			smb_avl_release(share_avl, shr);
604 			continue;
605 		}
606 
607 		if ((shr->shr_flags & SMB_SHRF_AUTOHOME) && !autohome_added) {
608 			if (esi->es_posix_uid == shr->shr_uid) {
609 				autohome_added = B_TRUE;
610 			} else {
611 				smb_avl_release(share_avl, shr);
612 				continue;
613 			}
614 		}
615 
616 		if (smb_msgbuf_encode(&info_mb, "13c.wl",
617 		    shr->shr_oemname, shr->shr_type, cmnt_offs) < 0) {
618 			smb_avl_release(share_avl, shr);
619 			break;
620 		}
621 
622 		if (smb_msgbuf_encode(&cmnt_mb, "s", shr->shr_cmnt) < 0) {
623 			smb_avl_release(share_avl, shr);
624 			break;
625 		}
626 
627 		cmnt_offs += strlen(shr->shr_cmnt) + 1;
628 		esi->es_nsent++;
629 
630 		smb_avl_release(share_avl, shr);
631 	}
632 
633 	smb_msgbuf_term(&info_mb);
634 	smb_msgbuf_term(&cmnt_mb);
635 }
636 
637 /*
638  * Looks up the given share and returns a pointer
639  * to its definition if it's found. A hold on the
640  * object is taken before the pointer is returned
641  * in which case the caller MUST always call
642  * smb_kshare_release().
643  */
644 smb_kshare_t *
645 smb_kshare_lookup(smb_server_t *sv, const char *shrname)
646 {
647 	smb_kshare_t key;
648 	smb_kshare_t *shr;
649 
650 	ASSERT(shrname);
651 
652 	if (!smb_export_isready(sv))
653 		return (NULL);
654 
655 	key.shr_name = (char *)shrname;
656 	shr = smb_avl_lookup(&sv->sv_export.e_share_avl, &key);
657 	return (shr);
658 }
659 
660 /*
661  * Releases the hold taken on the specified share object
662  */
663 void
664 smb_kshare_release(smb_server_t *sv, smb_kshare_t *shr)
665 {
666 	ASSERT(shr);
667 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
668 
669 	smb_avl_release(&sv->sv_export.e_share_avl, shr);
670 }
671 
672 /*
673  * Add the given share in the specified server.
674  * If the share is a disk share, lookup the share path
675  * and hold the smb_node_t for the share root.
676  *
677  * If the share is an Autohome share and it is
678  * already in the AVL only a reference count for
679  * that share is incremented.
680  */
681 static int
682 smb_kshare_export(smb_server_t *sv, smb_kshare_t *shr)
683 {
684 	smb_avl_t	*share_avl;
685 	smb_kshare_t	*auto_shr;
686 	smb_node_t	*snode = NULL;
687 	int		rc = 0;
688 
689 	share_avl = &sv->sv_export.e_share_avl;
690 
691 	if (!STYPE_ISDSK(shr->shr_type)) {
692 		if ((rc = smb_avl_add(share_avl, shr)) != 0) {
693 			cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
694 			    shr->shr_name, rc);
695 		}
696 
697 		return (rc);
698 	}
699 
700 	if ((auto_shr = smb_avl_lookup(share_avl, shr)) != NULL) {
701 		rc = EEXIST;
702 		if ((auto_shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
703 			mutex_enter(&auto_shr->shr_mutex);
704 			auto_shr->shr_autocnt++;
705 			mutex_exit(&auto_shr->shr_mutex);
706 			rc = 0;
707 		}
708 		smb_avl_release(share_avl, auto_shr);
709 		return (rc);
710 	}
711 
712 	/*
713 	 * Get the root smb_node_t for this share, held.
714 	 * This hold is normally released during AVL destroy,
715 	 * via the element destructor:  smb_kshare_destroy
716 	 */
717 	rc = smb_server_share_lookup(sv, shr->shr_path, &snode);
718 	if (rc != 0) {
719 		cmn_err(CE_WARN, "export[%s(%s)]: lookup failed (%d)",
720 		    shr->shr_name, shr->shr_path, rc);
721 		return (rc);
722 	}
723 
724 	shr->shr_root_node = snode;
725 	if ((rc = smb_avl_add(share_avl, shr)) != 0) {
726 		cmn_err(CE_WARN, "export[%s]: failed caching (%d)",
727 		    shr->shr_name, rc);
728 		shr->shr_root_node = NULL;
729 		smb_node_release(snode);
730 		return (rc);
731 	}
732 
733 	/*
734 	 * For CA shares, find or create the CA handle dir,
735 	 * and (if restarted) import persistent handles.
736 	 */
737 	if ((shr->shr_flags & SMB_SHRF_CA) != 0) {
738 		rc = smb2_dh_new_ca_share(sv, shr);
739 		if (rc != 0) {
740 			/* Just make it a non-CA share. */
741 			mutex_enter(&shr->shr_mutex);
742 			shr->shr_flags &= ~SMB_SHRF_CA;
743 			mutex_exit(&shr->shr_mutex);
744 			rc = 0;
745 		}
746 	}
747 
748 	return (rc);
749 }
750 
751 /*
752  * Removes the share specified by 'shrname' from the AVL
753  * tree of the given server if it's there.
754  *
755  * If the share is an Autohome share, the autohome count
756  * is decremented and the share is only removed if the
757  * count goes to zero.
758  *
759  * If the share is a disk share, the hold on the corresponding
760  * file system is released before removing the share from
761  * the AVL tree.
762  */
763 static int
764 smb_kshare_unexport(smb_server_t *sv, const char *shrname)
765 {
766 	smb_avl_t	*share_avl;
767 	smb_kshare_t	key;
768 	smb_kshare_t	*shr;
769 	boolean_t	auto_unexport;
770 
771 	share_avl = &sv->sv_export.e_share_avl;
772 
773 	key.shr_name = (char *)shrname;
774 	if ((shr = smb_avl_lookup(share_avl, &key)) == NULL)
775 		return (ENOENT);
776 
777 	if ((shr->shr_flags & SMB_SHRF_AUTOHOME) != 0) {
778 		mutex_enter(&shr->shr_mutex);
779 		shr->shr_autocnt--;
780 		auto_unexport = (shr->shr_autocnt == 0);
781 		mutex_exit(&shr->shr_mutex);
782 		if (!auto_unexport) {
783 			smb_avl_release(share_avl, shr);
784 			return (0);
785 		}
786 	}
787 
788 	smb_avl_remove(share_avl, shr);
789 
790 	mutex_enter(&shr->shr_mutex);
791 	shr->shr_flags |= SMB_SHRF_REMOVED;
792 	mutex_exit(&shr->shr_mutex);
793 
794 	smb_avl_release(share_avl, shr);
795 
796 	return (0);
797 }
798 
799 /*
800  * Exports IPC$ or Admin shares
801  */
802 static int
803 smb_kshare_export_trans(smb_server_t *sv, char *name, char *path, char *cmnt)
804 {
805 	smb_kshare_t *shr;
806 
807 	ASSERT(name);
808 	ASSERT(path);
809 
810 	shr = kmem_cache_alloc(smb_kshare_cache_share, KM_SLEEP);
811 	bzero(shr, sizeof (smb_kshare_t));
812 
813 	shr->shr_magic = SMB_SHARE_MAGIC;
814 	shr->shr_refcnt = 1;
815 	shr->shr_flags = SMB_SHRF_TRANS | smb_kshare_is_admin(shr->shr_name);
816 	if (strcasecmp(name, "IPC$") == 0)
817 		shr->shr_type = STYPE_IPC;
818 	else
819 		shr->shr_type = STYPE_DISKTREE;
820 
821 	shr->shr_type |= smb_kshare_is_special(shr->shr_name);
822 
823 	shr->shr_name = smb_mem_strdup(name);
824 	if (path)
825 		shr->shr_path = smb_mem_strdup(path);
826 	if (cmnt)
827 		shr->shr_cmnt = smb_mem_strdup(cmnt);
828 	shr->shr_oemname = smb_kshare_oemname(name);
829 
830 	return (smb_kshare_export(sv, shr));
831 }
832 
833 /*
834  * Decodes share information in an nvlist format into a smb_kshare_t
835  * structure.
836  *
837  * This is a temporary function and will be replaced by functions
838  * provided by libsharev2 code after it's available.
839  */
840 static smb_kshare_t *
841 smb_kshare_decode(nvlist_t *share)
842 {
843 	smb_kshare_t tmp;
844 	smb_kshare_t *shr;
845 	nvlist_t *smb;
846 	char *csc_name = NULL, *strbuf = NULL;
847 	int rc;
848 
849 	ASSERT(share);
850 
851 	bzero(&tmp, sizeof (smb_kshare_t));
852 
853 	rc = nvlist_lookup_string(share, "name", &tmp.shr_name);
854 	rc |= nvlist_lookup_string(share, "path", &tmp.shr_path);
855 	(void) nvlist_lookup_string(share, "desc", &tmp.shr_cmnt);
856 
857 	ASSERT(tmp.shr_name && tmp.shr_path);
858 
859 	rc |= nvlist_lookup_nvlist(share, "smb", &smb);
860 	if (rc != 0) {
861 		cmn_err(CE_WARN, "kshare: failed looking up SMB properties"
862 		    " (%d)", rc);
863 		return (NULL);
864 	}
865 
866 	rc = nvlist_lookup_uint32(smb, "type", &tmp.shr_type);
867 	if (rc != 0) {
868 		cmn_err(CE_WARN, "kshare[%s]: failed getting the share type"
869 		    " (%d)", tmp.shr_name, rc);
870 		return (NULL);
871 	}
872 
873 	(void) nvlist_lookup_string(smb, SHOPT_AD_CONTAINER,
874 	    &tmp.shr_container);
875 	(void) nvlist_lookup_string(smb, SHOPT_NONE, &tmp.shr_access_none);
876 	(void) nvlist_lookup_string(smb, SHOPT_RO, &tmp.shr_access_ro);
877 	(void) nvlist_lookup_string(smb, SHOPT_RW, &tmp.shr_access_rw);
878 
879 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_ABE, SMB_SHRF_ABE);
880 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CATIA,
881 	    SMB_SHRF_CATIA);
882 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_GUEST,
883 	    SMB_SHRF_GUEST_OK);
884 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_DFSROOT,
885 	    SMB_SHRF_DFSROOT);
886 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_QUOTAS,
887 	    SMB_SHRF_QUOTAS);
888 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_CA, SMB_SHRF_CA);
889 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_FSO, SMB_SHRF_FSO);
890 	tmp.shr_flags |= smb_kshare_decode_bool(smb, SHOPT_AUTOHOME,
891 	    SMB_SHRF_AUTOHOME);
892 
893 	if ((tmp.shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME) {
894 		rc = nvlist_lookup_uint32(smb, "uid", &tmp.shr_uid);
895 		rc |= nvlist_lookup_uint32(smb, "gid", &tmp.shr_gid);
896 		if (rc != 0) {
897 			cmn_err(CE_WARN, "kshare: failed looking up uid/gid"
898 			    " (%d)", rc);
899 			return (NULL);
900 		}
901 	}
902 
903 	(void) nvlist_lookup_string(smb, SHOPT_ENCRYPT, &strbuf);
904 	smb_cfg_set_require(strbuf, &tmp.shr_encrypt);
905 
906 	(void) nvlist_lookup_string(smb, SHOPT_CSC, &csc_name);
907 	smb_kshare_csc_flags(&tmp, csc_name);
908 
909 	shr = kmem_cache_alloc(smb_kshare_cache_share, KM_SLEEP);
910 	bzero(shr, sizeof (smb_kshare_t));
911 
912 	shr->shr_magic = SMB_SHARE_MAGIC;
913 	shr->shr_refcnt = 1;
914 
915 	shr->shr_name = smb_mem_strdup(tmp.shr_name);
916 	shr->shr_path = smb_mem_strdup(tmp.shr_path);
917 	if (tmp.shr_cmnt)
918 		shr->shr_cmnt = smb_mem_strdup(tmp.shr_cmnt);
919 	if (tmp.shr_container)
920 		shr->shr_container = smb_mem_strdup(tmp.shr_container);
921 	if (tmp.shr_access_none)
922 		shr->shr_access_none = smb_mem_strdup(tmp.shr_access_none);
923 	if (tmp.shr_access_ro)
924 		shr->shr_access_ro = smb_mem_strdup(tmp.shr_access_ro);
925 	if (tmp.shr_access_rw)
926 		shr->shr_access_rw = smb_mem_strdup(tmp.shr_access_rw);
927 
928 	shr->shr_oemname = smb_kshare_oemname(shr->shr_name);
929 	shr->shr_flags = tmp.shr_flags | smb_kshare_is_admin(shr->shr_name);
930 	shr->shr_type = tmp.shr_type | smb_kshare_is_special(shr->shr_name);
931 	shr->shr_encrypt = tmp.shr_encrypt;
932 
933 	shr->shr_uid = tmp.shr_uid;
934 	shr->shr_gid = tmp.shr_gid;
935 
936 	if ((shr->shr_flags & SMB_SHRF_AUTOHOME) == SMB_SHRF_AUTOHOME)
937 		shr->shr_autocnt = 1;
938 
939 	return (shr);
940 }
941 
942 #if 0
943 static void
944 smb_kshare_log(smb_kshare_t *shr)
945 {
946 	cmn_err(CE_NOTE, "Share info:");
947 	cmn_err(CE_NOTE, "\tname: %s", (shr->shr_name) ? shr->shr_name : "");
948 	cmn_err(CE_NOTE, "\tpath: %s", (shr->shr_path) ? shr->shr_path : "");
949 	cmn_err(CE_NOTE, "\tcmnt: (%s)",
950 	    (shr->shr_cmnt) ? shr->shr_cmnt : "NULL");
951 	cmn_err(CE_NOTE, "\toemname: (%s)",
952 	    (shr->shr_oemname) ? shr->shr_oemname : "NULL");
953 	cmn_err(CE_NOTE, "\tflags: %X", shr->shr_flags);
954 	cmn_err(CE_NOTE, "\ttype: %d", shr->shr_type);
955 }
956 #endif
957 
958 /*
959  * Compare function used by shares AVL
960  */
961 static int
962 smb_kshare_cmp(const void *p1, const void *p2)
963 {
964 	smb_kshare_t *shr1 = (smb_kshare_t *)p1;
965 	smb_kshare_t *shr2 = (smb_kshare_t *)p2;
966 	int rc;
967 
968 	ASSERT(shr1);
969 	ASSERT(shr1->shr_name);
970 
971 	ASSERT(shr2);
972 	ASSERT(shr2->shr_name);
973 
974 	rc = smb_strcasecmp(shr1->shr_name, shr2->shr_name, 0);
975 
976 	if (rc < 0)
977 		return (-1);
978 
979 	if (rc > 0)
980 		return (1);
981 
982 	return (0);
983 }
984 
985 /*
986  * This function is called by smb_avl routines whenever
987  * there is a need to take a hold on a share structure
988  * inside AVL
989  */
990 static void
991 smb_kshare_hold(const void *p)
992 {
993 	smb_kshare_t *shr = (smb_kshare_t *)p;
994 
995 	ASSERT(shr);
996 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
997 
998 	mutex_enter(&shr->shr_mutex);
999 	shr->shr_refcnt++;
1000 	mutex_exit(&shr->shr_mutex);
1001 }
1002 
1003 /*
1004  * This function must be called by smb_avl routines whenever
1005  * smb_kshare_hold is called and the hold needs to be released.
1006  */
1007 static boolean_t
1008 smb_kshare_rele(const void *p)
1009 {
1010 	smb_kshare_t *shr = (smb_kshare_t *)p;
1011 	boolean_t destroy;
1012 
1013 	ASSERT(shr);
1014 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
1015 
1016 	mutex_enter(&shr->shr_mutex);
1017 	ASSERT(shr->shr_refcnt > 0);
1018 	shr->shr_refcnt--;
1019 	destroy = (shr->shr_refcnt == 0);
1020 	mutex_exit(&shr->shr_mutex);
1021 
1022 	return (destroy);
1023 }
1024 
1025 /*
1026  * Frees all the memory allocated for the given
1027  * share structure. It also removes the structure
1028  * from the share cache.
1029  */
1030 static void
1031 smb_kshare_destroy(void *p)
1032 {
1033 	smb_kshare_t *shr = (smb_kshare_t *)p;
1034 
1035 	ASSERT(shr);
1036 	ASSERT(shr->shr_magic == SMB_SHARE_MAGIC);
1037 
1038 	if (shr->shr_ca_dir != NULL)
1039 		smb_node_release(shr->shr_ca_dir);
1040 	if (shr->shr_root_node)
1041 		smb_node_release(shr->shr_root_node);
1042 
1043 	smb_mem_free(shr->shr_name);
1044 	smb_mem_free(shr->shr_path);
1045 	smb_mem_free(shr->shr_cmnt);
1046 	smb_mem_free(shr->shr_container);
1047 	smb_mem_free(shr->shr_oemname);
1048 	smb_mem_free(shr->shr_access_none);
1049 	smb_mem_free(shr->shr_access_ro);
1050 	smb_mem_free(shr->shr_access_rw);
1051 
1052 	kmem_cache_free(smb_kshare_cache_share, shr);
1053 }
1054 
1055 
1056 /*
1057  * Generate an OEM name for the given share name.  If the name is
1058  * shorter than 13 bytes the oemname will be returned; otherwise NULL
1059  * is returned.
1060  */
1061 static char *
1062 smb_kshare_oemname(const char *shrname)
1063 {
1064 	smb_wchar_t *unibuf;
1065 	char *oem_name;
1066 	int length;
1067 
1068 	length = strlen(shrname) + 1;
1069 
1070 	oem_name = smb_mem_alloc(length);
1071 	unibuf = smb_mem_alloc(length * sizeof (smb_wchar_t));
1072 
1073 	(void) smb_mbstowcs(unibuf, shrname, length);
1074 
1075 	if (ucstooem(oem_name, unibuf, length, OEM_CPG_850) == 0)
1076 		(void) strcpy(oem_name, shrname);
1077 
1078 	smb_mem_free(unibuf);
1079 
1080 	if (strlen(oem_name) + 1 > SMB_SHARE_OEMNAME_MAX) {
1081 		smb_mem_free(oem_name);
1082 		return (NULL);
1083 	}
1084 
1085 	return (oem_name);
1086 }
1087 
1088 /*
1089  * Special share reserved for interprocess communication (IPC$) or
1090  * remote administration of the server (ADMIN$). Can also refer to
1091  * administrative shares such as C$, D$, E$, and so forth.
1092  */
1093 static int
1094 smb_kshare_is_special(const char *sharename)
1095 {
1096 	int len;
1097 
1098 	if (sharename == NULL)
1099 		return (0);
1100 
1101 	if ((len = strlen(sharename)) == 0)
1102 		return (0);
1103 
1104 	if (sharename[len - 1] == '$')
1105 		return (STYPE_SPECIAL);
1106 
1107 	return (0);
1108 }
1109 
1110 /*
1111  * Check whether or not this is a default admin share: C$, D$ etc.
1112  */
1113 static boolean_t
1114 smb_kshare_is_admin(const char *sharename)
1115 {
1116 	if (sharename == NULL)
1117 		return (B_FALSE);
1118 
1119 	if (strlen(sharename) == 2 &&
1120 	    smb_isalpha(sharename[0]) && sharename[1] == '$') {
1121 		return (B_TRUE);
1122 	}
1123 
1124 	return (B_FALSE);
1125 }
1126 
1127 /*
1128  * Decodes the given boolean share option.
1129  * If the option is present in the nvlist and it's value is true
1130  * returns the corresponding flag value, otherwise returns 0.
1131  */
1132 static uint32_t
1133 smb_kshare_decode_bool(nvlist_t *nvl, const char *propname, uint32_t flag)
1134 {
1135 	char *boolp;
1136 
1137 	if (nvlist_lookup_string(nvl, propname, &boolp) == 0)
1138 		if (strcasecmp(boolp, "true") == 0)
1139 			return (flag);
1140 
1141 	return (0);
1142 }
1143 
1144 /*
1145  * Map a client-side caching (CSC) option to the appropriate share
1146  * flag.  Only one option is allowed; an error will be logged if
1147  * multiple options have been specified.  We don't need to do anything
1148  * about multiple values here because the SRVSVC will not recognize
1149  * a value containing multiple flags and will return the default value.
1150  *
1151  * If the option value is not recognized, it will be ignored: invalid
1152  * values will typically be caught and rejected by sharemgr.
1153  */
1154 static void
1155 smb_kshare_csc_flags(smb_kshare_t *shr, const char *value)
1156 {
1157 	int i;
1158 	static struct {
1159 		char *value;
1160 		uint32_t flag;
1161 	} cscopt[] = {
1162 		{ "disabled",	SMB_SHRF_CSC_DISABLED },
1163 		{ "manual",	SMB_SHRF_CSC_MANUAL },
1164 		{ "auto",	SMB_SHRF_CSC_AUTO },
1165 		{ "vdo",	SMB_SHRF_CSC_VDO }
1166 	};
1167 
1168 	if (value == NULL)
1169 		return;
1170 
1171 	for (i = 0; i < (sizeof (cscopt) / sizeof (cscopt[0])); ++i) {
1172 		if (strcasecmp(value, cscopt[i].value) == 0) {
1173 			shr->shr_flags |= cscopt[i].flag;
1174 			break;
1175 		}
1176 	}
1177 
1178 	switch (shr->shr_flags & SMB_SHRF_CSC_MASK) {
1179 	case 0:
1180 	case SMB_SHRF_CSC_DISABLED:
1181 	case SMB_SHRF_CSC_MANUAL:
1182 	case SMB_SHRF_CSC_AUTO:
1183 	case SMB_SHRF_CSC_VDO:
1184 		break;
1185 
1186 	default:
1187 		cmn_err(CE_NOTE, "csc option conflict: 0x%08x",
1188 		    shr->shr_flags & SMB_SHRF_CSC_MASK);
1189 		break;
1190 	}
1191 }
1192 
1193 /*
1194  * This function processes the unexport event list and disconnects shares
1195  * asynchronously.  The function executes as a zone-specific thread.
1196  *
1197  * The server arg passed in is safe to use without a reference count, because
1198  * the server cannot be deleted until smb_thread_stop()/destroy() return,
1199  * which is also when the thread exits.
1200  */
1201 /*ARGSUSED*/
1202 static void
1203 smb_kshare_unexport_thread(smb_thread_t *thread, void *arg)
1204 {
1205 	smb_server_t	*sv = arg;
1206 	smb_unshare_t	*ux;
1207 
1208 	while (smb_thread_continue(thread)) {
1209 		while ((ux = list_head(&sv->sv_export.e_unexport_list.sl_list))
1210 		    != NULL) {
1211 			smb_slist_remove(&sv->sv_export.e_unexport_list, ux);
1212 			(void) smb_server_unshare(ux->us_sharename);
1213 			kmem_cache_free(smb_kshare_cache_unexport, ux);
1214 		}
1215 	}
1216 }
1217 
1218 static boolean_t
1219 smb_export_isready(smb_server_t *sv)
1220 {
1221 	boolean_t ready;
1222 
1223 	mutex_enter(&sv->sv_export.e_mutex);
1224 	ready = sv->sv_export.e_ready;
1225 	mutex_exit(&sv->sv_export.e_mutex);
1226 
1227 	return (ready);
1228 }
1229 
1230 #ifdef	_KERNEL
1231 /*
1232  * Return 0 upon success. Otherwise > 0
1233  */
1234 static int
1235 smb_kshare_chk_dsrv_status(int opcode, smb_dr_ctx_t *dec_ctx)
1236 {
1237 	int status = smb_dr_get_int32(dec_ctx);
1238 	int err;
1239 
1240 	switch (status) {
1241 	case SMB_SHARE_DSUCCESS:
1242 		return (0);
1243 
1244 	case SMB_SHARE_DERROR:
1245 		err = smb_dr_get_uint32(dec_ctx);
1246 		cmn_err(CE_WARN, "%d: Encountered door server error %d",
1247 		    opcode, err);
1248 		(void) smb_dr_decode_finish(dec_ctx);
1249 		return (err);
1250 	}
1251 
1252 	ASSERT(0);
1253 	return (EINVAL);
1254 }
1255 #endif	/* _KERNEL */
1256