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