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