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