xref: /titanic_41/usr/src/lib/smbsrv/libmlsvc/common/smb_share.c (revision 93c20f2609342fd05f6625f16dfcb9348e7977f2)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * SMB/CIFS share cache implementation.
28  */
29 
30 #include <errno.h>
31 #include <synch.h>
32 #include <stdlib.h>
33 #include <strings.h>
34 #include <syslog.h>
35 #include <thread.h>
36 #include <pthread.h>
37 #include <assert.h>
38 #include <libshare.h>
39 
40 #include <smbsrv/libsmb.h>
41 #include <smbsrv/libsmbns.h>
42 #include <smbsrv/libmlsvc.h>
43 
44 #include <smbsrv/lm.h>
45 #include <smbsrv/smb_share.h>
46 #include <smbsrv/cifs.h>
47 #include <smbsrv/nterror.h>
48 
49 #define	SMB_SHR_ERROR_THRESHOLD		3
50 
51 /*
52  * Cache functions and vars
53  */
54 #define	SMB_SHR_HTAB_SZ			1024
55 
56 /*
57  * Cache handle
58  *
59  * Shares cache is a hash table.
60  *
61  * sc_cache		pointer to hash table handle
62  * sc_cache_lck		synchronize cache read/write accesses
63  * sc_state		cache state machine values
64  * sc_nops		number of inflight/pending cache operations
65  * sc_mtx		protects handle fields
66  */
67 typedef struct smb_shr_cache {
68 	HT_HANDLE	*sc_cache;
69 	rwlock_t	sc_cache_lck;
70 	mutex_t		sc_mtx;
71 	cond_t		sc_cv;
72 	uint32_t	sc_state;
73 	uint32_t	sc_nops;
74 } smb_shr_cache_t;
75 
76 /*
77  * Cache states
78  */
79 #define	SMB_SHR_CACHE_STATE_NONE	0
80 #define	SMB_SHR_CACHE_STATE_CREATED	1
81 #define	SMB_SHR_CACHE_STATE_DESTROYING	2
82 
83 /*
84  * Cache lock modes
85  */
86 #define	SMB_SHR_CACHE_RDLOCK	0
87 #define	SMB_SHR_CACHE_WRLOCK	1
88 
89 static smb_shr_cache_t smb_shr_cache;
90 
91 static uint32_t smb_shr_cache_create(void);
92 static void smb_shr_cache_destroy(void);
93 static uint32_t smb_shr_cache_lock(int);
94 static void smb_shr_cache_unlock(void);
95 static int smb_shr_cache_count(void);
96 static smb_share_t *smb_shr_cache_iterate(smb_shriter_t *);
97 
98 static smb_share_t *smb_shr_cache_findent(char *);
99 static uint32_t smb_shr_cache_addent(smb_share_t *);
100 static void smb_shr_cache_delent(char *);
101 static void smb_shr_cache_freent(HT_ITEM *);
102 
103 /*
104  * sharemgr functions
105  */
106 static void *smb_shr_sa_loadall(void *);
107 static void smb_shr_sa_loadgrp(sa_group_t);
108 static uint32_t smb_shr_sa_load(sa_share_t, sa_resource_t);
109 static uint32_t smb_shr_sa_get(sa_share_t, sa_resource_t, smb_share_t *);
110 
111 /*
112  * share publishing
113  */
114 #define	SMB_SHR_PUBLISH		0
115 #define	SMB_SHR_UNPUBLISH	1
116 
117 typedef struct smb_shr_pitem {
118 	list_node_t	spi_lnd;
119 	char		spi_name[MAXNAMELEN];
120 	char		spi_container[MAXPATHLEN];
121 	char		spi_op;
122 } smb_shr_pitem_t;
123 
124 /*
125  * publish queue states
126  */
127 #define	SMB_SHR_PQS_NOQUEUE	0
128 #define	SMB_SHR_PQS_READY	1	/* the queue is ready */
129 #define	SMB_SHR_PQS_PUBLISHING	2	/* publisher thread is running */
130 #define	SMB_SHR_PQS_STOPPING	3
131 
132 /*
133  * share publishing queue
134  */
135 typedef struct smb_shr_pqueue {
136 	list_t		spq_list;
137 	mutex_t		spq_mtx;
138 	cond_t		spq_cv;
139 	uint32_t	spq_state;
140 } smb_shr_pqueue_t;
141 
142 static smb_shr_pqueue_t ad_queue;
143 
144 static int smb_shr_publisher_start(void);
145 static void smb_shr_publisher_stop(void);
146 static void smb_shr_publisher_send(smb_ads_handle_t *, list_t *, const char *);
147 static void smb_shr_publisher_queue(const char *, const char *, char);
148 static void *smb_shr_publisher(void *);
149 static void smb_shr_publisher_flush(list_t *);
150 static void smb_shr_publish(const char *, const char *);
151 static void smb_shr_unpublish(const char *, const char *);
152 
153 /*
154  * Utility/helper functions
155  */
156 static uint32_t smb_shr_addipc(void);
157 static void smb_shr_set_oemname(smb_share_t *);
158 
159 /*
160  * Starts the publisher thread and another thread which
161  * populates the share cache by share information stored
162  * by sharemgr
163  */
164 int
165 smb_shr_start(void)
166 {
167 	pthread_t load_thr;
168 	pthread_attr_t tattr;
169 	int rc;
170 
171 	if ((rc = smb_shr_publisher_start()) != 0)
172 		return (rc);
173 
174 	if (smb_shr_cache_create() != NERR_Success)
175 		return (ENOMEM);
176 
177 	if (smb_shr_addipc() != NERR_Success)
178 		return (ENOMEM);
179 
180 	(void) pthread_attr_init(&tattr);
181 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
182 	rc = pthread_create(&load_thr, &tattr, smb_shr_sa_loadall, 0);
183 	(void) pthread_attr_destroy(&tattr);
184 
185 	return (rc);
186 }
187 
188 void
189 smb_shr_stop(void)
190 {
191 	smb_shr_cache_destroy();
192 	smb_shr_publisher_stop();
193 }
194 
195 /*
196  * Return the total number of shares
197  */
198 int
199 smb_shr_count(void)
200 {
201 	int n_shares = 0;
202 
203 	if (smb_shr_cache_lock(SMB_SHR_CACHE_RDLOCK) == NERR_Success) {
204 		n_shares = smb_shr_cache_count();
205 		smb_shr_cache_unlock();
206 	}
207 
208 	return (n_shares);
209 }
210 
211 /*
212  * smb_shr_iterinit
213  *
214  * Initialize given iterator for traversing hash table.
215  */
216 void
217 smb_shr_iterinit(smb_shriter_t *shi)
218 {
219 	bzero(shi, sizeof (smb_shriter_t));
220 	shi->si_first = B_TRUE;
221 }
222 
223 /*
224  * smb_shr_iterate
225  *
226  * Iterate on the shares in the hash table. The iterator must be initialized
227  * before the first iteration. On subsequent calls, the iterator must be
228  * passed unchanged.
229  *
230  * Returns NULL on failure or when all shares are visited, otherwise
231  * returns information of visited share.
232  */
233 smb_share_t *
234 smb_shr_iterate(smb_shriter_t *shi)
235 {
236 	smb_share_t *share = NULL;
237 	smb_share_t *cached_si;
238 
239 	if (shi == NULL)
240 		return (NULL);
241 
242 	if (smb_shr_cache_lock(SMB_SHR_CACHE_RDLOCK) == NERR_Success) {
243 		if ((cached_si = smb_shr_cache_iterate(shi)) != NULL) {
244 			share = &shi->si_share;
245 			bcopy(cached_si, share, sizeof (smb_share_t));
246 		}
247 		smb_shr_cache_unlock();
248 	}
249 
250 	return (share);
251 }
252 
253 /*
254  * Adds the given share to cache, publishes the share in ADS
255  * if it has an AD container, calls kernel to take a hold on
256  * the shared file system. If it can't take a hold on the
257  * shared file system, it's either because shared directory
258  * does not exist or some other error has occurred, in any
259  * case the share is removed from the cache.
260  *
261  * If the specified share is an autohome share which already
262  * exists in the cache, just increments the reference count.
263  */
264 uint32_t
265 smb_shr_add(smb_share_t *si)
266 {
267 	smb_share_t *cached_si;
268 	uint32_t status;
269 	int rc;
270 
271 	assert(si != NULL);
272 
273 	if (!smb_shr_chkname(si->shr_name))
274 		return (ERROR_INVALID_NAME);
275 
276 	if (smb_shr_cache_lock(SMB_SHR_CACHE_WRLOCK) != NERR_Success)
277 		return (NERR_InternalError);
278 
279 	cached_si = smb_shr_cache_findent(si->shr_name);
280 	if (cached_si) {
281 		if (si->shr_flags & SMB_SHRF_AUTOHOME) {
282 			cached_si->shr_refcnt++;
283 			status = NERR_Success;
284 		} else {
285 			status = NERR_DuplicateShare;
286 		}
287 		smb_shr_cache_unlock();
288 		return (status);
289 	}
290 
291 	if ((status = smb_shr_cache_addent(si)) != NERR_Success) {
292 		smb_shr_cache_unlock();
293 		return (status);
294 	}
295 
296 	/* don't hold the lock across door call */
297 	smb_shr_cache_unlock();
298 
299 	/* call kernel to take a hold on the shared file system */
300 	rc = mlsvc_set_share(SMB_SHROP_ADD, si->shr_path, si->shr_name);
301 
302 	if (rc == 0) {
303 		smb_shr_publish(si->shr_name, si->shr_container);
304 		return (NERR_Success);
305 	}
306 
307 	if (smb_shr_cache_lock(SMB_SHR_CACHE_WRLOCK) == NERR_Success) {
308 		smb_shr_cache_delent(si->shr_name);
309 		smb_shr_cache_unlock();
310 	}
311 
312 	/*
313 	 * rc == ENOENT means the shared directory doesn't exist
314 	 */
315 	return ((rc == ENOENT) ? NERR_UnknownDevDir : NERR_InternalError);
316 }
317 
318 /*
319  * Removes the specified share from cache, removes it from AD
320  * if it has an AD container, and calls the kernel to release
321  * the hold on the shared file system.
322  *
323  * If this is an autohome share then decrement the reference
324  * count. If it reaches 0 then it proceeds with removing steps.
325  */
326 uint32_t
327 smb_shr_remove(char *sharename)
328 {
329 	smb_share_t *si;
330 	char path[MAXPATHLEN];
331 	char container[MAXPATHLEN];
332 
333 	assert(sharename != NULL);
334 
335 	if (!smb_shr_chkname(sharename))
336 		return (ERROR_INVALID_NAME);
337 
338 	if (smb_shr_cache_lock(SMB_SHR_CACHE_WRLOCK) != NERR_Success)
339 		return (NERR_InternalError);
340 
341 	if ((si = smb_shr_cache_findent(sharename)) == NULL) {
342 		smb_shr_cache_unlock();
343 		return (NERR_NetNameNotFound);
344 	}
345 
346 	if (si->shr_type & STYPE_IPC) {
347 		/* IPC$ share cannot be removed */
348 		smb_shr_cache_unlock();
349 		return (ERROR_ACCESS_DENIED);
350 	}
351 
352 	if (si->shr_flags & SMB_SHRF_AUTOHOME) {
353 		if ((--si->shr_refcnt) > 0) {
354 			smb_shr_cache_unlock();
355 			return (NERR_Success);
356 		}
357 	}
358 
359 	(void) strlcpy(path, si->shr_path, sizeof (path));
360 	(void) strlcpy(container, si->shr_container, sizeof (container));
361 	smb_shr_cache_delent(sharename);
362 	smb_shr_cache_unlock();
363 
364 	smb_shr_unpublish(sharename, container);
365 
366 	/* call kernel to release the hold on the shared file system */
367 	(void) mlsvc_set_share(SMB_SHROP_DELETE, path, sharename);
368 
369 	return (NERR_Success);
370 }
371 
372 /*
373  * Rename a share. Check that the current name exists and the new name
374  * doesn't exist. The rename is performed by deleting the current share
375  * definition and creating a new share with the new name.
376  */
377 uint32_t
378 smb_shr_rename(char *from_name, char *to_name)
379 {
380 	smb_share_t *from_si;
381 	smb_share_t to_si;
382 	uint32_t status;
383 
384 	assert((from_name != NULL) && (to_name != NULL));
385 
386 	if (!smb_shr_chkname(from_name) || !smb_shr_chkname(to_name))
387 		return (ERROR_INVALID_NAME);
388 
389 	if (smb_shr_cache_lock(SMB_SHR_CACHE_WRLOCK) != NERR_Success)
390 		return (NERR_InternalError);
391 
392 	if ((from_si = smb_shr_cache_findent(from_name)) == NULL) {
393 		smb_shr_cache_unlock();
394 		return (NERR_NetNameNotFound);
395 	}
396 
397 	if (from_si->shr_type & STYPE_IPC) {
398 		/* IPC$ share cannot be renamed */
399 		smb_shr_cache_unlock();
400 		return (ERROR_ACCESS_DENIED);
401 	}
402 
403 	if (smb_shr_cache_findent(to_name) != NULL) {
404 		smb_shr_cache_unlock();
405 		return (NERR_DuplicateShare);
406 	}
407 
408 	bcopy(from_si, &to_si, sizeof (smb_share_t));
409 	(void) strlcpy(to_si.shr_name, to_name, sizeof (to_si.shr_name));
410 
411 	if ((status = smb_shr_cache_addent(&to_si)) != NERR_Success) {
412 		smb_shr_cache_unlock();
413 		return (status);
414 	}
415 
416 	smb_shr_cache_delent(from_name);
417 	smb_shr_cache_unlock();
418 
419 	smb_shr_unpublish(from_name, to_si.shr_container);
420 	smb_shr_publish(to_name, to_si.shr_container);
421 
422 	return (NERR_Success);
423 }
424 
425 /*
426  * Load the information for the specified share into the supplied share
427  * info structure.
428  */
429 uint32_t
430 smb_shr_get(char *sharename, smb_share_t *si)
431 {
432 	smb_share_t *cached_si;
433 	uint32_t status = NERR_NetNameNotFound;
434 
435 	if (sharename == NULL || *sharename == '\0')
436 		return (NERR_NetNameNotFound);
437 
438 	if (smb_shr_cache_lock(SMB_SHR_CACHE_RDLOCK) == NERR_Success) {
439 		cached_si = smb_shr_cache_findent(sharename);
440 		if (cached_si != NULL) {
441 			bcopy(cached_si, si, sizeof (smb_share_t));
442 			status = NERR_Success;
443 		}
444 
445 		smb_shr_cache_unlock();
446 	}
447 
448 	return (status);
449 }
450 
451 /*
452  * Modifies an existing share. Properties that can be modified are:
453  *
454  *   o comment
455  *   o AD container
456  *   o host access
457  */
458 uint32_t
459 smb_shr_modify(smb_share_t *new_si)
460 {
461 	smb_share_t *si;
462 	boolean_t adc_changed = B_FALSE;
463 	char old_container[MAXPATHLEN];
464 	uint32_t access;
465 
466 	assert(new_si != NULL);
467 
468 	if (smb_shr_cache_lock(SMB_SHR_CACHE_WRLOCK) != NERR_Success)
469 		return (NERR_InternalError);
470 
471 	if ((si = smb_shr_cache_findent(new_si->shr_name)) == NULL) {
472 		smb_shr_cache_unlock();
473 		return (NERR_NetNameNotFound);
474 	}
475 
476 	if (si->shr_type & STYPE_IPC) {
477 		/* IPC$ share cannot be modified */
478 		smb_shr_cache_unlock();
479 		return (ERROR_ACCESS_DENIED);
480 	}
481 
482 	if (strcmp(new_si->shr_cmnt, si->shr_cmnt) != 0)
483 		(void) strlcpy(si->shr_cmnt, new_si->shr_cmnt,
484 		    sizeof (si->shr_cmnt));
485 
486 	adc_changed = (strcmp(new_si->shr_container, si->shr_container) != 0);
487 	if (adc_changed) {
488 		/* save current container - needed for unpublishing */
489 		(void) strlcpy(old_container, si->shr_container,
490 		    sizeof (old_container));
491 		(void) strlcpy(si->shr_container, new_si->shr_container,
492 		    sizeof (si->shr_container));
493 	}
494 
495 	access = (new_si->shr_flags & SMB_SHRF_ACC_ALL);
496 	si->shr_flags |= access;
497 
498 	if (access & SMB_SHRF_ACC_NONE)
499 		(void) strlcpy(si->shr_access_none, new_si->shr_access_none,
500 		    sizeof (si->shr_access_none));
501 
502 	if (access & SMB_SHRF_ACC_RO)
503 		(void) strlcpy(si->shr_access_ro, new_si->shr_access_ro,
504 		    sizeof (si->shr_access_ro));
505 
506 	if (access & SMB_SHRF_ACC_RW)
507 		(void) strlcpy(si->shr_access_rw, new_si->shr_access_rw,
508 		    sizeof (si->shr_access_rw));
509 
510 	smb_shr_cache_unlock();
511 
512 	if (adc_changed) {
513 		smb_shr_unpublish(new_si->shr_name, old_container);
514 		smb_shr_publish(new_si->shr_name, new_si->shr_container);
515 	}
516 
517 	return (NERR_Success);
518 }
519 
520 /*
521  * smb_shr_exists
522  *
523  * Returns B_TRUE if the share exists. Otherwise returns B_FALSE
524  */
525 boolean_t
526 smb_shr_exists(char *sharename)
527 {
528 	boolean_t exists = B_FALSE;
529 
530 	if (sharename == NULL || *sharename == '\0')
531 		return (B_FALSE);
532 
533 	if (smb_shr_cache_lock(SMB_SHR_CACHE_RDLOCK) == NERR_Success) {
534 		exists = (smb_shr_cache_findent(sharename) != NULL);
535 		smb_shr_cache_unlock();
536 	}
537 
538 	return (exists);
539 }
540 
541 /*
542  * If the shared directory does not begin with a /, one will be
543  * inserted as a prefix. If ipaddr is not zero, then also return
544  * information about access based on the host level access lists, if
545  * present. Also return access check if there is an IP address and
546  * shr_accflags.
547  *
548  * The value of smb_chk_hostaccess is checked for an access match.
549  * -1 is wildcard match
550  * 0 is no match
551  * 1 is match
552  *
553  * Precedence is none is checked first followed by ro then rw if
554  * needed.  If x is wildcard (< 0) then check to see if the other
555  * values are a match. If a match, that wins.
556  */
557 void
558 smb_shr_hostaccess(smb_share_t *si, ipaddr_t ipaddr)
559 {
560 	int acc = SMB_SHRF_ACC_OPEN;
561 
562 	/*
563 	 * Check to see if there area any share level access
564 	 * restrictions.
565 	 */
566 	if (ipaddr != 0 && (si->shr_flags & SMB_SHRF_ACC_ALL) != 0) {
567 		int none = SMB_SHRF_ACC_OPEN;
568 		int rw = SMB_SHRF_ACC_OPEN;
569 		int ro = SMB_SHRF_ACC_OPEN;
570 
571 		if (si->shr_flags & SMB_SHRF_ACC_NONE)
572 			none = smb_chk_hostaccess(ipaddr, si->shr_access_none);
573 		if (si->shr_flags & SMB_SHRF_ACC_RW)
574 			rw = smb_chk_hostaccess(ipaddr, si->shr_access_rw);
575 		if (si->shr_flags & SMB_SHRF_ACC_RO)
576 			ro = smb_chk_hostaccess(ipaddr, si->shr_access_ro);
577 
578 		/* make first pass to get basic value */
579 		if (none != 0)
580 			acc = SMB_SHRF_ACC_NONE;
581 		else if (ro != 0)
582 			acc = SMB_SHRF_ACC_RO;
583 		else if (rw != 0)
584 			acc = SMB_SHRF_ACC_RW;
585 
586 		/* make second pass to handle '*' case */
587 		if (none < 0) {
588 			acc = SMB_SHRF_ACC_NONE;
589 			if (ro > 0)
590 				acc = SMB_SHRF_ACC_RO;
591 			else if (rw > 0)
592 				acc = SMB_SHRF_ACC_RW;
593 		} else if (ro < 0) {
594 			acc = SMB_SHRF_ACC_RO;
595 			if (none > 0)
596 				acc = SMB_SHRF_ACC_NONE;
597 			else if (rw > 0)
598 				acc = SMB_SHRF_ACC_RW;
599 		} else if (rw < 0) {
600 			acc = SMB_SHRF_ACC_RW;
601 			if (none > 0)
602 				acc = SMB_SHRF_ACC_NONE;
603 			else if (ro > 0)
604 				acc = SMB_SHRF_ACC_RO;
605 		}
606 	}
607 	si->shr_access_value = acc;	/* return access here */
608 }
609 
610 /*
611  * smb_shr_is_special
612  *
613  * Special share reserved for interprocess communication (IPC$) or
614  * remote administration of the server (ADMIN$). Can also refer to
615  * administrative shares such as C$, D$, E$, and so forth.
616  */
617 int
618 smb_shr_is_special(char *sharename)
619 {
620 	int len;
621 
622 	if (sharename == NULL)
623 		return (0);
624 
625 	if ((len = strlen(sharename)) == 0)
626 		return (0);
627 
628 	if (sharename[len - 1] == '$')
629 		return (STYPE_SPECIAL);
630 
631 	return (0);
632 }
633 
634 /*
635  * smb_shr_is_restricted
636  *
637  * Check whether or not there is a restriction on a share. Restricted
638  * shares are generally STYPE_SPECIAL, for example, IPC$. All the
639  * administration share names are restricted: C$, D$ etc. Returns B_TRUE
640  * if the share is restricted. Otherwise B_FALSE is returned to indicate
641  * that there are no restrictions.
642  */
643 boolean_t
644 smb_shr_is_restricted(char *sharename)
645 {
646 	static char *restricted[] = {
647 		"IPC$"
648 	};
649 
650 	int i;
651 
652 	if (sharename == NULL)
653 		return (B_FALSE);
654 
655 	for (i = 0; i < sizeof (restricted)/sizeof (restricted[0]); i++) {
656 		if (utf8_strcasecmp(restricted[i], sharename) == 0)
657 			return (B_TRUE);
658 	}
659 
660 	return (smb_shr_is_admin(sharename));
661 }
662 
663 /*
664  * smb_shr_is_admin
665  *
666  * Check whether or not access to the share should be restricted to
667  * administrators. This is a bit of a hack because what we're doing
668  * is checking for the default admin shares: C$, D$ etc.. There are
669  * other shares that have restrictions: see smb_shr_is_restricted().
670  *
671  * Returns B_TRUE if the shares is an admin share. Otherwise B_FALSE
672  * is returned to indicate that there are no restrictions.
673  */
674 boolean_t
675 smb_shr_is_admin(char *sharename)
676 {
677 	if (sharename == NULL)
678 		return (B_FALSE);
679 
680 	if (strlen(sharename) == 2 &&
681 	    mts_isalpha(sharename[0]) && sharename[1] == '$') {
682 		return (B_TRUE);
683 	}
684 
685 	return (B_FALSE);
686 }
687 
688 /*
689  * smb_shr_chkname
690  *
691  * Check for invalid characters in a share name.  The list of invalid
692  * characters includes control characters and the following:
693  *
694  * " / \ [ ] : | < > + ; , ? * =
695  */
696 boolean_t
697 smb_shr_chkname(char *sharename)
698 {
699 	char *invalid = "\"/\\[]:|<>+;,?*=";
700 	char *cp;
701 
702 	if (sharename == NULL)
703 		return (B_FALSE);
704 
705 	if (strpbrk(sharename, invalid))
706 		return (B_FALSE);
707 
708 	for (cp = sharename; *cp != '\0'; cp++) {
709 		if (iscntrl(*cp))
710 			return (B_FALSE);
711 	}
712 
713 	return (B_TRUE);
714 }
715 
716 /*
717  * smb_shr_get_realpath
718  *
719  * Derive the real path for a share from the path provided by a client.
720  * For instance, the real path of C:\ may be /cvol or the real path of
721  * F:\home may be /vol1/home.
722  *
723  * clntpath - path provided by the Windows client is in the
724  *            format of <drive letter>:\<dir>
725  * realpath - path that will be stored as the directory field of
726  *            the smb_share_t structure of the share.
727  * maxlen   - maximum length of the realpath buffer
728  *
729  * Return LAN Manager network error code.
730  */
731 uint32_t
732 smb_shr_get_realpath(const char *clntpath, char *realpath, int maxlen)
733 {
734 	const char *p;
735 	int len;
736 
737 	if ((p = strchr(clntpath, ':')) != NULL)
738 		++p;
739 	else
740 		p = clntpath;
741 
742 	(void) strlcpy(realpath, p, maxlen);
743 	(void) strcanon(realpath, "/\\");
744 	(void) strsubst(realpath, '\\', '/');
745 
746 	len = strlen(realpath);
747 	if ((len > 1) && (realpath[len - 1] == '/'))
748 		realpath[len - 1] = '\0';
749 
750 	return (NERR_Success);
751 }
752 
753 void
754 smb_shr_list(int offset, smb_shrlist_t *list)
755 {
756 	smb_shriter_t iterator;
757 	smb_share_t *si;
758 	int n = 0;
759 
760 	bzero(list, sizeof (smb_shrlist_t));
761 	smb_shr_iterinit(&iterator);
762 
763 	while ((si = smb_shr_iterate(&iterator)) != NULL) {
764 		if (--offset > 0)
765 			continue;
766 
767 		if ((si->shr_flags & SMB_SHRF_TRANS) &&
768 		    ((si->shr_type & STYPE_IPC) == 0)) {
769 			bcopy(si, &list->sl_shares[n], sizeof (smb_share_t));
770 			if (++n == LMSHARES_PER_REQUEST)
771 				break;
772 		}
773 	}
774 
775 	list->sl_cnt = n;
776 }
777 
778 /*
779  * ============================================
780  * Private helper/utility functions
781  * ============================================
782  */
783 
784 /*
785  * Add IPC$ to the cache upon startup.
786  */
787 static uint32_t
788 smb_shr_addipc(void)
789 {
790 	smb_share_t ipc;
791 	uint32_t status = NERR_InternalError;
792 
793 	bzero(&ipc, sizeof (smb_share_t));
794 	(void) strcpy(ipc.shr_name, "IPC$");
795 	(void) strcpy(ipc.shr_cmnt, "Remote IPC");
796 	ipc.shr_flags = SMB_SHRF_TRANS;
797 	ipc.shr_type = STYPE_IPC;
798 
799 	if (smb_shr_cache_lock(SMB_SHR_CACHE_WRLOCK) == NERR_Success) {
800 		status = smb_shr_cache_addent(&ipc);
801 		smb_shr_cache_unlock();
802 	}
803 
804 	return (status);
805 }
806 
807 /*
808  * smb_shr_set_oemname
809  *
810  * Generate the OEM name for the specified share.  If the name is
811  * shorter than 13 bytes the oemname will be saved in si->shr_oemname.
812  * Otherwise si->shr_oemname will be empty and SMB_SHRF_LONGNAME will
813  * be set in si->shr_flags.
814  */
815 static void
816 smb_shr_set_oemname(smb_share_t *si)
817 {
818 	unsigned int cpid = oem_get_smb_cpid();
819 	mts_wchar_t *unibuf;
820 	char *oem_name;
821 	int length;
822 
823 	length = strlen(si->shr_name) + 1;
824 
825 	oem_name = malloc(length);
826 	unibuf = malloc(length * sizeof (mts_wchar_t));
827 	if ((oem_name == NULL) || (unibuf == NULL)) {
828 		free(oem_name);
829 		free(unibuf);
830 		return;
831 	}
832 
833 	(void) mts_mbstowcs(unibuf, si->shr_name, length);
834 
835 	if (unicodestooems(oem_name, unibuf, length, cpid) == 0)
836 		(void) strcpy(oem_name, si->shr_name);
837 
838 	free(unibuf);
839 
840 	if (strlen(oem_name) + 1 > SMB_SHARE_OEMNAME_MAX) {
841 		si->shr_flags |= SMB_SHRF_LONGNAME;
842 		*si->shr_oemname = '\0';
843 	} else {
844 		si->shr_flags &= ~SMB_SHRF_LONGNAME;
845 		(void) strlcpy(si->shr_oemname, oem_name,
846 		    SMB_SHARE_OEMNAME_MAX);
847 	}
848 
849 	free(oem_name);
850 }
851 
852 /*
853  * ============================================
854  * Cache management functions
855  *
856  * All cache functions are private
857  * ============================================
858  */
859 
860 /*
861  * Create the share cache (hash table).
862  */
863 static uint32_t
864 smb_shr_cache_create(void)
865 {
866 	uint32_t status = NERR_Success;
867 
868 	(void) mutex_lock(&smb_shr_cache.sc_mtx);
869 	switch (smb_shr_cache.sc_state) {
870 	case SMB_SHR_CACHE_STATE_NONE:
871 		smb_shr_cache.sc_cache = ht_create_table(SMB_SHR_HTAB_SZ,
872 		    MAXNAMELEN, 0);
873 		if (smb_shr_cache.sc_cache == NULL) {
874 			status = NERR_InternalError;
875 			break;
876 		}
877 
878 		(void) ht_register_callback(smb_shr_cache.sc_cache,
879 		    smb_shr_cache_freent);
880 		smb_shr_cache.sc_nops = 0;
881 		smb_shr_cache.sc_state = SMB_SHR_CACHE_STATE_CREATED;
882 		break;
883 
884 	default:
885 		assert(0);
886 		status = NERR_InternalError;
887 		break;
888 	}
889 	(void) mutex_unlock(&smb_shr_cache.sc_mtx);
890 
891 	return (status);
892 }
893 
894 /*
895  * Destroy the share cache (hash table).
896  * Wait for inflight/pending operations to finish or abort before
897  * destroying the cache.
898  */
899 static void
900 smb_shr_cache_destroy(void)
901 {
902 	(void) mutex_lock(&smb_shr_cache.sc_mtx);
903 	if (smb_shr_cache.sc_state == SMB_SHR_CACHE_STATE_CREATED) {
904 		smb_shr_cache.sc_state = SMB_SHR_CACHE_STATE_DESTROYING;
905 		while (smb_shr_cache.sc_nops > 0)
906 			(void) cond_wait(&smb_shr_cache.sc_cv,
907 			    &smb_shr_cache.sc_mtx);
908 
909 		smb_shr_cache.sc_cache = NULL;
910 		smb_shr_cache.sc_state = SMB_SHR_CACHE_STATE_NONE;
911 	}
912 	(void) mutex_unlock(&smb_shr_cache.sc_mtx);
913 }
914 
915 /*
916  * If the cache is in "created" state, lock the cache for read
917  * or read/write based on the specified mode.
918  *
919  * Whenever a lock is granted, the number of inflight cache
920  * operations is incremented.
921  */
922 static uint32_t
923 smb_shr_cache_lock(int mode)
924 {
925 	(void) mutex_lock(&smb_shr_cache.sc_mtx);
926 	switch (smb_shr_cache.sc_state) {
927 	case SMB_SHR_CACHE_STATE_CREATED:
928 		smb_shr_cache.sc_nops++;
929 		break;
930 
931 	case SMB_SHR_CACHE_STATE_DESTROYING:
932 		(void) mutex_unlock(&smb_shr_cache.sc_mtx);
933 		return (NERR_InternalError);
934 
935 	case SMB_SHR_CACHE_STATE_NONE:
936 	default:
937 		assert(0);
938 		(void) mutex_unlock(&smb_shr_cache.sc_mtx);
939 		return (NERR_InternalError);
940 
941 	}
942 	(void) mutex_unlock(&smb_shr_cache.sc_mtx);
943 
944 	/*
945 	 * Lock has to be taken outside the mutex otherwise
946 	 * there could be a deadlock
947 	 */
948 	if (mode == SMB_SHR_CACHE_RDLOCK)
949 		(void) rw_rdlock(&smb_shr_cache.sc_cache_lck);
950 	else
951 		(void) rw_wrlock(&smb_shr_cache.sc_cache_lck);
952 
953 	return (NERR_Success);
954 }
955 
956 /*
957  * Decrement the number of inflight operations and then unlock.
958  */
959 static void
960 smb_shr_cache_unlock(void)
961 {
962 	(void) mutex_lock(&smb_shr_cache.sc_mtx);
963 	assert(smb_shr_cache.sc_nops > 0);
964 	smb_shr_cache.sc_nops--;
965 	(void) cond_broadcast(&smb_shr_cache.sc_cv);
966 	(void) mutex_unlock(&smb_shr_cache.sc_mtx);
967 
968 	(void) rw_unlock(&smb_shr_cache.sc_cache_lck);
969 }
970 
971 /*
972  * Return the total number of shares
973  */
974 static int
975 smb_shr_cache_count(void)
976 {
977 	return (ht_get_total_items(smb_shr_cache.sc_cache));
978 }
979 
980 /*
981  * looks up the given share name in the cache and if it
982  * finds a match returns a pointer to the cached entry.
983  * Note that since a pointer is returned this function
984  * MUST be protected by smb_shr_cache_lock/unlock pair
985  */
986 static smb_share_t *
987 smb_shr_cache_findent(char *sharename)
988 {
989 	HT_ITEM *item;
990 
991 	(void) utf8_strlwr(sharename);
992 	item = ht_find_item(smb_shr_cache.sc_cache, sharename);
993 	if (item && item->hi_data)
994 		return ((smb_share_t *)item->hi_data);
995 
996 	return (NULL);
997 }
998 
999 /*
1000  * Return a pointer to the first/next entry in
1001  * the cache based on the given iterator.
1002  *
1003  * Calls to this function MUST be protected by
1004  * smb_shr_cache_lock/unlock.
1005  */
1006 static smb_share_t *
1007 smb_shr_cache_iterate(smb_shriter_t *shi)
1008 {
1009 	HT_ITEM *item;
1010 
1011 	if (shi->si_first) {
1012 		item = ht_findfirst(smb_shr_cache.sc_cache, &shi->si_hashiter);
1013 		shi->si_first = B_FALSE;
1014 	} else {
1015 		item = ht_findnext(&shi->si_hashiter);
1016 	}
1017 
1018 	if (item && item->hi_data)
1019 		return ((smb_share_t *)item->hi_data);
1020 
1021 	return (NULL);
1022 }
1023 
1024 /*
1025  * Add the specified share to the cache.  Memory needs to be allocated
1026  * for the cache entry and the passed information is copied to the
1027  * allocated space.
1028  */
1029 static uint32_t
1030 smb_shr_cache_addent(smb_share_t *si)
1031 {
1032 	smb_share_t *cache_ent;
1033 	uint32_t status = NERR_Success;
1034 
1035 	if ((cache_ent = malloc(sizeof (smb_share_t))) == NULL)
1036 		return (ERROR_NOT_ENOUGH_MEMORY);
1037 
1038 	bcopy(si, cache_ent, sizeof (smb_share_t));
1039 
1040 	(void) utf8_strlwr(cache_ent->shr_name);
1041 	smb_shr_set_oemname(cache_ent);
1042 
1043 	if ((si->shr_type & STYPE_IPC) == 0)
1044 		cache_ent->shr_type = STYPE_DISKTREE;
1045 	cache_ent->shr_type |= smb_shr_is_special(cache_ent->shr_name);
1046 
1047 	if (smb_shr_is_admin(cache_ent->shr_name))
1048 		cache_ent->shr_flags |= SMB_SHRF_ADMIN;
1049 
1050 	if (si->shr_flags & SMB_SHRF_AUTOHOME)
1051 		cache_ent->shr_refcnt = 1;
1052 
1053 	if (ht_add_item(smb_shr_cache.sc_cache, cache_ent->shr_name, cache_ent)
1054 	    == NULL) {
1055 		syslog(LOG_DEBUG, "share: %s: cache update failed",
1056 		    cache_ent->shr_name);
1057 		free(cache_ent);
1058 		status = NERR_InternalError;
1059 	}
1060 
1061 	return (status);
1062 }
1063 
1064 /*
1065  * Delete the specified share from the cache.
1066  */
1067 static void
1068 smb_shr_cache_delent(char *sharename)
1069 {
1070 	(void) utf8_strlwr(sharename);
1071 	(void) ht_remove_item(smb_shr_cache.sc_cache, sharename);
1072 }
1073 
1074 /*
1075  * Call back to free the given cache entry.
1076  */
1077 static void
1078 smb_shr_cache_freent(HT_ITEM *item)
1079 {
1080 	if (item && item->hi_data)
1081 		free(item->hi_data);
1082 }
1083 
1084 /*
1085  * ============================================
1086  * Interfaces to sharemgr
1087  *
1088  * All functions in this section are private
1089  * ============================================
1090  */
1091 
1092 /*
1093  * Load shares from sharemgr
1094  */
1095 /*ARGSUSED*/
1096 static void *
1097 smb_shr_sa_loadall(void *args)
1098 {
1099 	sa_handle_t handle;
1100 	sa_group_t group, subgroup;
1101 	char *gstate;
1102 	boolean_t gdisabled;
1103 
1104 	if ((handle = sa_init(SA_INIT_SHARE_API)) == NULL) {
1105 		syslog(LOG_ERR, "share: failed to get libshare API handle");
1106 		return (NULL);
1107 	}
1108 
1109 	for (group = sa_get_group(handle, NULL);
1110 	    group != NULL; group = sa_get_next_group(group)) {
1111 		gstate = sa_get_group_attr(group, "state");
1112 		if (gstate == NULL)
1113 			continue;
1114 
1115 		gdisabled = (strcasecmp(gstate, "disabled") == 0);
1116 		sa_free_attr_string(gstate);
1117 		if (gdisabled)
1118 			continue;
1119 
1120 		smb_shr_sa_loadgrp(group);
1121 
1122 		for (subgroup = sa_get_sub_group(group);
1123 		    subgroup != NULL;
1124 		    subgroup = sa_get_next_group(subgroup)) {
1125 			smb_shr_sa_loadgrp(subgroup);
1126 		}
1127 
1128 	}
1129 
1130 	sa_fini(handle);
1131 	return (NULL);
1132 }
1133 
1134 /*
1135  * Load the shares contained in the specified group.
1136  *
1137  * Don't process groups on which the smb protocol is disabled.
1138  * The top level ZFS group won't have the smb protocol enabled
1139  * but sub-groups will.
1140  *
1141  * We will tolerate a limited number of errors and then give
1142  * up on the current group.  A typical error might be that the
1143  * shared directory no longer exists.
1144  */
1145 static void
1146 smb_shr_sa_loadgrp(sa_group_t group)
1147 {
1148 	sa_share_t share;
1149 	sa_resource_t resource;
1150 	int error_count = 0;
1151 
1152 	if (sa_get_optionset(group, SMB_PROTOCOL_NAME) == NULL)
1153 		return;
1154 
1155 	for (share = sa_get_share(group, NULL);
1156 	    share != NULL;
1157 	    share = sa_get_next_share(share)) {
1158 		for (resource = sa_get_share_resource(share, NULL);
1159 		    resource != NULL;
1160 		    resource = sa_get_next_resource(resource)) {
1161 			if (smb_shr_sa_load(share, resource))
1162 				++error_count;
1163 
1164 			if (error_count > SMB_SHR_ERROR_THRESHOLD)
1165 				break;
1166 		}
1167 
1168 		if (error_count > SMB_SHR_ERROR_THRESHOLD)
1169 			break;
1170 	}
1171 }
1172 
1173 /*
1174  * Load a share definition from sharemgr and add it to the cache.
1175  */
1176 static uint32_t
1177 smb_shr_sa_load(sa_share_t share, sa_resource_t resource)
1178 {
1179 	smb_share_t si;
1180 	uint32_t status;
1181 
1182 	if ((status = smb_shr_sa_get(share, resource, &si)) != NERR_Success) {
1183 		syslog(LOG_DEBUG, "share: failed to load %s (%d)",
1184 		    si.shr_name, status);
1185 		return (status);
1186 	}
1187 
1188 	if ((status = smb_shr_add(&si)) != NERR_Success) {
1189 		syslog(LOG_DEBUG, "share: failed to cache %s (%d)",
1190 		    si.shr_name, status);
1191 		return (status);
1192 	}
1193 
1194 	return (NERR_Success);
1195 }
1196 
1197 /*
1198  * Read the specified share information from sharemgr and return
1199  * it in the given smb_share_t structure.
1200  *
1201  * Shares read from sharemgr are marked as permanent/persistent.
1202  */
1203 static uint32_t
1204 smb_shr_sa_get(sa_share_t share, sa_resource_t resource, smb_share_t *si)
1205 {
1206 	sa_property_t prop;
1207 	sa_optionset_t opts;
1208 	char *val = NULL;
1209 	char *path;
1210 	char *rname;
1211 
1212 	if ((path = sa_get_share_attr(share, "path")) == NULL)
1213 		return (NERR_InternalError);
1214 
1215 	if ((rname = sa_get_resource_attr(resource, "name")) == NULL) {
1216 		sa_free_attr_string(path);
1217 		return (NERR_InternalError);
1218 	}
1219 
1220 	bzero(si, sizeof (smb_share_t));
1221 	si->shr_flags = SMB_SHRF_PERM;
1222 
1223 	(void) strlcpy(si->shr_path, path, sizeof (si->shr_path));
1224 	(void) strlcpy(si->shr_name, rname, sizeof (si->shr_name));
1225 
1226 	sa_free_attr_string(path);
1227 	sa_free_attr_string(rname);
1228 
1229 	val = sa_get_resource_description(resource);
1230 	if (val == NULL)
1231 		val = sa_get_share_description(share);
1232 
1233 	if (val != NULL) {
1234 		(void) strlcpy(si->shr_cmnt, val, sizeof (si->shr_cmnt));
1235 		sa_free_share_description(val);
1236 	}
1237 
1238 	opts = sa_get_derived_optionset(resource, SMB_PROTOCOL_NAME, 1);
1239 	if (opts == NULL)
1240 		return (NERR_Success);
1241 
1242 	prop = (sa_property_t)sa_get_property(opts, SMB_SHROPT_AD_CONTAINER);
1243 	if (prop != NULL) {
1244 		if ((val = sa_get_property_attr(prop, "value")) != NULL) {
1245 			(void) strlcpy(si->shr_container, val,
1246 			    sizeof (si->shr_container));
1247 			free(val);
1248 		}
1249 	}
1250 
1251 	prop = (sa_property_t)sa_get_property(opts, SHOPT_NONE);
1252 	if (prop != NULL) {
1253 		if ((val = sa_get_property_attr(prop, "value")) != NULL) {
1254 			(void) strlcpy(si->shr_access_none, val,
1255 			    sizeof (si->shr_access_none));
1256 			free(val);
1257 			si->shr_flags |= SMB_SHRF_ACC_NONE;
1258 		}
1259 	}
1260 
1261 	prop = (sa_property_t)sa_get_property(opts, SHOPT_RO);
1262 	if (prop != NULL) {
1263 		if ((val = sa_get_property_attr(prop, "value")) != NULL) {
1264 			(void) strlcpy(si->shr_access_ro, val,
1265 			    sizeof (si->shr_access_ro));
1266 			free(val);
1267 			si->shr_flags |= SMB_SHRF_ACC_RO;
1268 		}
1269 	}
1270 
1271 	prop = (sa_property_t)sa_get_property(opts, SHOPT_RW);
1272 	if (prop != NULL) {
1273 		if ((val = sa_get_property_attr(prop, "value")) != NULL) {
1274 			(void) strlcpy(si->shr_access_rw, val,
1275 			    sizeof (si->shr_access_rw));
1276 			free(val);
1277 			si->shr_flags |= SMB_SHRF_ACC_RW;
1278 		}
1279 	}
1280 
1281 	sa_free_derived_optionset(opts);
1282 	return (NERR_Success);
1283 }
1284 
1285 /*
1286  * ============================================
1287  * Share publishing functions
1288  *
1289  * All the functions are private
1290  * ============================================
1291  */
1292 
1293 static void
1294 smb_shr_publish(const char *sharename, const char *container)
1295 {
1296 	smb_shr_publisher_queue(sharename, container, SMB_SHR_PUBLISH);
1297 }
1298 
1299 static void
1300 smb_shr_unpublish(const char *sharename, const char *container)
1301 {
1302 	smb_shr_publisher_queue(sharename, container, SMB_SHR_UNPUBLISH);
1303 }
1304 
1305 /*
1306  * In domain mode, put a share on the publisher queue.
1307  * This is a no-op if the smb service is in Workgroup mode.
1308  */
1309 static void
1310 smb_shr_publisher_queue(const char *sharename, const char *container, char op)
1311 {
1312 	smb_shr_pitem_t *item = NULL;
1313 
1314 	if (container == NULL || *container == '\0')
1315 		return;
1316 
1317 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
1318 		return;
1319 
1320 	(void) mutex_lock(&ad_queue.spq_mtx);
1321 	switch (ad_queue.spq_state) {
1322 	case SMB_SHR_PQS_READY:
1323 	case SMB_SHR_PQS_PUBLISHING:
1324 		break;
1325 	default:
1326 		(void) mutex_unlock(&ad_queue.spq_mtx);
1327 		return;
1328 	}
1329 	(void) mutex_unlock(&ad_queue.spq_mtx);
1330 
1331 	if ((item = malloc(sizeof (smb_shr_pitem_t))) == NULL)
1332 		return;
1333 
1334 	item->spi_op = op;
1335 	(void) strlcpy(item->spi_name, sharename, sizeof (item->spi_name));
1336 	(void) strlcpy(item->spi_container, container,
1337 	    sizeof (item->spi_container));
1338 
1339 	(void) mutex_lock(&ad_queue.spq_mtx);
1340 	list_insert_tail(&ad_queue.spq_list, item);
1341 	(void) cond_signal(&ad_queue.spq_cv);
1342 	(void) mutex_unlock(&ad_queue.spq_mtx);
1343 }
1344 
1345 /*
1346  * Publishing won't be activated if the smb service is running in
1347  * Workgroup mode.
1348  */
1349 static int
1350 smb_shr_publisher_start(void)
1351 {
1352 	pthread_t publish_thr;
1353 	pthread_attr_t tattr;
1354 	int rc;
1355 
1356 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
1357 		return (0);
1358 
1359 	(void) mutex_lock(&ad_queue.spq_mtx);
1360 	if (ad_queue.spq_state != SMB_SHR_PQS_NOQUEUE) {
1361 		(void) mutex_unlock(&ad_queue.spq_mtx);
1362 		errno = EINVAL;
1363 		return (-1);
1364 	}
1365 
1366 	list_create(&ad_queue.spq_list, sizeof (smb_shr_pitem_t),
1367 	    offsetof(smb_shr_pitem_t, spi_lnd));
1368 	ad_queue.spq_state = SMB_SHR_PQS_READY;
1369 	(void) mutex_unlock(&ad_queue.spq_mtx);
1370 
1371 	(void) pthread_attr_init(&tattr);
1372 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1373 	rc = pthread_create(&publish_thr, &tattr, smb_shr_publisher, 0);
1374 	(void) pthread_attr_destroy(&tattr);
1375 
1376 	return (rc);
1377 }
1378 
1379 static void
1380 smb_shr_publisher_stop(void)
1381 {
1382 	if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
1383 		return;
1384 
1385 	(void) mutex_lock(&ad_queue.spq_mtx);
1386 	switch (ad_queue.spq_state) {
1387 	case SMB_SHR_PQS_READY:
1388 	case SMB_SHR_PQS_PUBLISHING:
1389 		ad_queue.spq_state = SMB_SHR_PQS_STOPPING;
1390 		(void) cond_signal(&ad_queue.spq_cv);
1391 		break;
1392 	default:
1393 		break;
1394 	}
1395 	(void) mutex_unlock(&ad_queue.spq_mtx);
1396 }
1397 
1398 /*
1399  * This is the publisher daemon thread.  While running, the thread waits
1400  * on a conditional variable until notified that a share needs to be
1401  * [un]published or that the thread should be terminated.
1402  *
1403  * Entries may remain in the outgoing queue if the Active Directory
1404  * service is inaccessible, in which case the thread wakes up every 60
1405  * seconds to retry.
1406  */
1407 /*ARGSUSED*/
1408 static void *
1409 smb_shr_publisher(void *arg)
1410 {
1411 	smb_ads_handle_t *ah;
1412 	smb_shr_pitem_t *shr;
1413 	list_t publist;
1414 	timestruc_t pubretry;
1415 	char hostname[MAXHOSTNAMELEN];
1416 
1417 	(void) mutex_lock(&ad_queue.spq_mtx);
1418 	if (ad_queue.spq_state != SMB_SHR_PQS_READY) {
1419 		(void) mutex_unlock(&ad_queue.spq_mtx);
1420 		return (NULL);
1421 	}
1422 	ad_queue.spq_state = SMB_SHR_PQS_PUBLISHING;
1423 	(void) mutex_unlock(&ad_queue.spq_mtx);
1424 
1425 	(void) smb_gethostname(hostname, MAXHOSTNAMELEN, 0);
1426 
1427 	list_create(&publist, sizeof (smb_shr_pitem_t),
1428 	    offsetof(smb_shr_pitem_t, spi_lnd));
1429 
1430 	for (;;) {
1431 		(void) mutex_lock(&ad_queue.spq_mtx);
1432 
1433 		while (list_is_empty(&ad_queue.spq_list) &&
1434 		    (ad_queue.spq_state == SMB_SHR_PQS_PUBLISHING)) {
1435 			if (list_is_empty(&publist)) {
1436 				(void) cond_wait(&ad_queue.spq_cv,
1437 				    &ad_queue.spq_mtx);
1438 			} else {
1439 				pubretry.tv_sec = 60;
1440 				pubretry.tv_nsec = 0;
1441 				(void) cond_reltimedwait(&ad_queue.spq_cv,
1442 				    &ad_queue.spq_mtx, &pubretry);
1443 				break;
1444 			}
1445 		}
1446 
1447 		if (ad_queue.spq_state != SMB_SHR_PQS_PUBLISHING) {
1448 			(void) mutex_unlock(&ad_queue.spq_mtx);
1449 			break;
1450 		}
1451 
1452 		/*
1453 		 * Transfer queued items to the local list so that
1454 		 * the mutex can be released.
1455 		 */
1456 		while ((shr = list_head(&ad_queue.spq_list)) != NULL) {
1457 			list_remove(&ad_queue.spq_list, shr);
1458 			list_insert_tail(&publist, shr);
1459 		}
1460 
1461 		(void) mutex_unlock(&ad_queue.spq_mtx);
1462 
1463 		if ((ah = smb_ads_open()) != NULL) {
1464 			smb_shr_publisher_send(ah, &publist, hostname);
1465 			smb_ads_close(ah);
1466 		}
1467 	}
1468 
1469 	(void) mutex_lock(&ad_queue.spq_mtx);
1470 	smb_shr_publisher_flush(&ad_queue.spq_list);
1471 	list_destroy(&ad_queue.spq_list);
1472 	ad_queue.spq_state = SMB_SHR_PQS_NOQUEUE;
1473 	(void) mutex_unlock(&ad_queue.spq_mtx);
1474 
1475 	smb_shr_publisher_flush(&publist);
1476 	list_destroy(&publist);
1477 	return (NULL);
1478 }
1479 
1480 /*
1481  * Remove items from the specified queue and [un]publish them.
1482  */
1483 static void
1484 smb_shr_publisher_send(smb_ads_handle_t *ah, list_t *publist, const char *host)
1485 {
1486 	smb_shr_pitem_t *shr;
1487 
1488 	while ((shr = list_head(publist)) != NULL) {
1489 		(void) mutex_lock(&ad_queue.spq_mtx);
1490 		if (ad_queue.spq_state != SMB_SHR_PQS_PUBLISHING) {
1491 			(void) mutex_unlock(&ad_queue.spq_mtx);
1492 			return;
1493 		}
1494 		(void) mutex_unlock(&ad_queue.spq_mtx);
1495 
1496 		list_remove(publist, shr);
1497 
1498 		if (shr->spi_op == SMB_SHR_PUBLISH)
1499 			(void) smb_ads_publish_share(ah, shr->spi_name,
1500 			    NULL, shr->spi_container, host);
1501 		else
1502 			(void) smb_ads_remove_share(ah, shr->spi_name,
1503 			    NULL, shr->spi_container, host);
1504 
1505 		free(shr);
1506 	}
1507 }
1508 
1509 /*
1510  * Flush all remaining items from the specified list/queue.
1511  */
1512 static void
1513 smb_shr_publisher_flush(list_t *lst)
1514 {
1515 	smb_shr_pitem_t *shr;
1516 
1517 	while ((shr = list_head(lst)) != NULL) {
1518 		list_remove(lst, shr);
1519 		free(shr);
1520 	}
1521 }
1522