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