xref: /illumos-gate/usr/src/lib/smbsrv/libmlsvc/common/dfs.c (revision 694c35faa87b858ecdadfe4fc592615f4eefbb07)
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) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <strings.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <fcntl.h>
30 #include <dirent.h>
31 #include <dlfcn.h>
32 #include <pthread.h>
33 #include <syslog.h>
34 #include <sys/fs_reparse.h>
35 #include <uuid/uuid.h>
36 
37 #include <smbsrv/libsmb.h>
38 #include <smbsrv/libmlsvc.h>
39 #include <smbsrv/smb_dfs.h>
40 #include <smbsrv/smb_share.h>
41 #include <dfs.h>
42 
43 /*
44  * default timeout (TTL) values (in second) for root and link
45  */
46 #define	DFS_ROOT_TIMEOUT		300
47 #define	DFS_LINK_TIMEOUT		1800
48 
49 /*
50  * DFS link data format in reparse point
51  *
52  * ver:state:prop:timeout:guid:ntarget:cmntlen:comment
53  *    [[:tserver:tshare:tstate:pclass:prank]...]
54  */
55 #define	DFS_LINK_V1			1
56 #define	DFS_LINK_HDR_NFIELDS		7	/* # fields in header section */
57 #define	DFS_LINK_TRGT_NFIELDS		5	/* # fields for each target */
58 
59 #define	DFS_ROOT_XATTR			"SUNWdfs.rootinfo"
60 
61 #define	DFS_INFO_ALL			0
62 
63 static void *dfs_intr_hdl = NULL;
64 
65 static struct {
66 	int (*dfsops_remote_count)(uint32_t *);
67 } dfs_intr_ops;
68 
69 /*
70  * Namespace cache
71  *
72  * Caches links' UNC and filesystem path where the key is the UNC path.
73  */
74 static smb_cache_t dfs_nscache;
75 static char dfs_nbname[NETBIOS_NAME_SZ];
76 
77 /*
78  * The name of cached namespace. This will be the only
79  * exported namespace until hosting multiple namespaces
80  * is supported
81  */
82 static char dfs_cached_ns[MAXNAMELEN];
83 static mutex_t dfs_nsmtx;
84 
85 /*
86  * Lock for accessing root information (extended attribute)
87  */
88 static rwlock_t dfs_root_rwl;
89 
90 extern uint32_t srvsvc_shr_setdfsroot(smb_share_t *, boolean_t);
91 
92 /*
93  * Namespace functions
94  */
95 static boolean_t dfs_namespace_findlink(const char *, char *, char *, size_t);
96 static void *dfs_namespace_cache(void *);
97 static boolean_t dfs_namespace_iscached(const char *);
98 
99 /*
100  * Root functions
101  */
102 static int dfs_root_add(const char *, dfs_info_t *);
103 static uint32_t dfs_root_remove(const char *);
104 static uint32_t dfs_root_encode(dfs_info_t *, char **, size_t *);
105 static uint32_t dfs_root_decode(dfs_info_t *, char *, size_t, uint32_t);
106 static uint32_t dfs_root_isvalidstate(uint32_t);
107 
108 static int dfs_root_xopen(const char *, int);
109 static void dfs_root_xclose(int);
110 static uint32_t dfs_root_xwrite(int, dfs_info_t *);
111 static uint32_t dfs_root_xread(int, dfs_info_t *, uint32_t);
112 
113 /*
114  * Link functions
115  */
116 static uint32_t dfs_link_encode(dfs_info_t *, char *, size_t);
117 static uint32_t dfs_link_decode(dfs_info_t *, char *, uint32_t);
118 static uint32_t dfs_link_commit(const char *, dfs_info_t *);
119 static boolean_t dfs_link_isvalidstate(uint32_t);
120 
121 /*
122  * Target functions
123  */
124 static void dfs_target_init(dfs_target_t *, const char *, const char *,
125     uint32_t);
126 static int dfs_target_find(dfs_target_t *, uint32_t, const char *,
127     const char *);
128 static boolean_t dfs_target_isvalidstate(uint32_t);
129 
130 /*
131  * Cache functions
132  */
133 static uint32_t dfs_cache_add_byunc(const char *, const char *, uint32_t);
134 static void dfs_cache_populate(const char *, const char *);
135 static int dfs_cache_cmp(const void *, const void *);
136 static void dfs_cache_flush(const char *);
137 static uint32_t dfs_cache_nscount(void);
138 
139 /*
140  * Utility functions
141  */
142 static boolean_t dfs_path_isdir(const char *);
143 static uint32_t dfs_modinfo(uint32_t, dfs_info_t *, dfs_info_t *, uint32_t);
144 
145 /*
146  * DFS module initializationr:
147  *
148  * - creates the namespace cache
149  * - gets system's NetBIOS name
150  */
151 void
152 dfs_init(void)
153 {
154 	smb_domain_t di;
155 
156 	smb_cache_create(&dfs_nscache, 0, dfs_cache_cmp, free, bcopy,
157 	    sizeof (dfs_nscnode_t));
158 
159 	if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
160 		return;
161 
162 	(void) strlcpy(dfs_nbname, di.di_nbname, NETBIOS_NAME_SZ);
163 
164 	bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops));
165 
166 	if ((dfs_intr_hdl = smb_dlopen()) == NULL)
167 		return;
168 
169 	if ((dfs_intr_ops.dfsops_remote_count =
170 	    (int (*)())dlsym(dfs_intr_hdl, "smb_dfs_remote_count")) == NULL) {
171 		smb_dlclose(dfs_intr_hdl);
172 		dfs_intr_hdl = NULL;
173 		bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops));
174 	}
175 }
176 
177 /*
178  * DFS module cleanup:
179  *
180  * - destroys the namespace cache
181  */
182 void
183 dfs_fini(void)
184 {
185 	smb_dlclose(dfs_intr_hdl);
186 	smb_cache_destroy(&dfs_nscache);
187 }
188 
189 /*
190  * To successfully handle some of link/root requests, some
191  * file system operations need to be performed. These operations
192  * should take place on behalf of the connected user (typically
193  * Administrator) and to do so we need to have an infrastructure
194  * in place so that smbd can act as a client and sends request to
195  * the kernel. Right now, we lack this infrastructure, so we make
196  * a compromise by temporarily enabling some privileges for smbd
197  * to be able to fulfill various link/root requests.
198  */
199 void
200 dfs_setpriv(priv_op_t op)
201 {
202 	(void) priv_set(op, PRIV_EFFECTIVE,
203 	    PRIV_FILE_DAC_READ,
204 	    PRIV_FILE_DAC_WRITE,
205 	    PRIV_FILE_DAC_EXECUTE,
206 	    PRIV_FILE_DAC_SEARCH, NULL);
207 }
208 
209 /*
210  * ========================
211  * Namespace API (public)
212  * ========================
213  */
214 
215 /*
216  * Launches a thread to cache the specified namespace
217  */
218 void
219 dfs_namespace_load(const char *name)
220 {
221 	pthread_t thr;
222 	pthread_attr_t tattr;
223 	char *rootshr;
224 	int rc;
225 
226 	if ((rootshr = strdup(name)) == NULL) {
227 		syslog(LOG_ERR, "dfs: failed to load %s namespace (no memory)",
228 		    name);
229 		return;
230 	}
231 
232 	(void) pthread_attr_init(&tattr);
233 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
234 	rc = pthread_create(&thr, &tattr, dfs_namespace_cache, rootshr);
235 	(void) pthread_attr_destroy(&tattr);
236 
237 	if (rc != 0)
238 		syslog(LOG_ERR, "dfs: fail to loading %s namespace (%d)",
239 		    name, rc);
240 }
241 
242 /*
243  * Flushes the cache when a DFS root share is removed
244  */
245 void /*ARGSUSED*/
246 dfs_namespace_unload(const char *name)
247 {
248 	dfs_cache_flush(name);
249 }
250 
251 /*
252  * Returns the file system path for the given share if it
253  * is a DFS root share.
254  * If 'path' is NULL, this function only indicates whether
255  * or not the given share represents a DFS namespace
256  */
257 uint32_t
258 dfs_namespace_path(const char *name, char *path, size_t pathsz)
259 {
260 	smb_share_t si;
261 
262 	if (smb_shr_get((char *)name, &si) != NERR_Success)
263 		return (ERROR_NOT_FOUND);
264 
265 	if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0)
266 		return (ERROR_NOT_FOUND);
267 
268 	if (!dfs_namespace_iscached(name))
269 		return (ERROR_NOT_FOUND);
270 
271 	if (path != NULL)
272 		(void) strlcpy(path, si.shr_path, pathsz);
273 
274 	return (ERROR_SUCCESS);
275 }
276 
277 /*
278  * Returns the number of DFS root shares i.e. the number
279  * of standalone namespaces.
280  */
281 uint32_t
282 dfs_namespace_count(void)
283 {
284 	uint32_t nroot = 0;
285 	int rc;
286 
287 	if (dfs_intr_ops.dfsops_remote_count != NULL &&
288 	    (rc = dfs_intr_ops.dfsops_remote_count(&nroot)) != 0) {
289 		/*
290 		 * If this call fails, let's assume there's at least one root
291 		 * namespace already configured.  The interposer library cannot
292 		 * confirm or deny the presence of a namespace, so let's take
293 		 * the safe approach and assume one exists.
294 		 */
295 		nroot = 1;
296 		syslog(LOG_WARNING, "dfs: dfsops_remote_count() failed: %d, "
297 		    "assuming one namespace exists", rc);
298 	}
299 
300 	nroot += dfs_cache_nscount();
301 
302 	return (nroot);
303 }
304 
305 /*
306  * Creates a DFS root with the given name and comment.
307  *
308  * This function does not create the root share, it
309  * should already exist.
310  */
311 uint32_t
312 dfs_namespace_add(const char *rootshr, const char *cmnt)
313 {
314 	dfs_info_t info;
315 	dfs_target_t t;
316 	smb_share_t si;
317 	uuid_t uuid;
318 	uint32_t status;
319 
320 	if (*rootshr == '\\') {
321 		/* Windows has a special case here! */
322 		return (ERROR_BAD_PATHNAME);
323 	}
324 
325 	if (smb_shr_get((char *)rootshr, &si) != NERR_Success)
326 		return (NERR_NetNameNotFound);
327 
328 	(void) mutex_lock(&dfs_nsmtx);
329 	if (smb_strcasecmp(dfs_cached_ns, rootshr, 0) == 0) {
330 		/* This DFS root is already exported */
331 		(void) mutex_unlock(&dfs_nsmtx);
332 		return (ERROR_FILE_EXISTS);
333 	}
334 
335 	if (*dfs_cached_ns != '\0') {
336 		syslog(LOG_WARNING, "dfs: trying to add %s namespace."
337 		    " Only one standalone namespace is supported."
338 		    " A namespace is already exported for %s",
339 		    rootshr, dfs_cached_ns);
340 		(void) mutex_unlock(&dfs_nsmtx);
341 		return (ERROR_NOT_SUPPORTED);
342 	}
343 
344 	bzero(&info, sizeof (info));
345 	if (cmnt)
346 		(void) strlcpy(info.i_comment, cmnt, sizeof (info.i_comment));
347 	info.i_state = DFS_VOLUME_STATE_OK | DFS_VOLUME_FLAVOR_STANDALONE;
348 	info.i_timeout = DFS_ROOT_TIMEOUT;
349 	info.i_propflags = 0;
350 
351 	uuid_generate_random(uuid);
352 	uuid_unparse(uuid, info.i_guid);
353 
354 	dfs_target_init(&t, dfs_nbname, rootshr, DFS_STORAGE_STATE_ONLINE);
355 
356 	info.i_ntargets = 1;
357 	info.i_targets = &t;
358 
359 	if ((status = dfs_root_add(si.shr_path, &info)) != ERROR_SUCCESS) {
360 		(void) mutex_unlock(&dfs_nsmtx);
361 		return (status);
362 	}
363 
364 	status = srvsvc_shr_setdfsroot(&si, B_TRUE);
365 	if (status == ERROR_SUCCESS) {
366 		(void) dfs_cache_add_byname(rootshr, NULL, DFS_OBJECT_ROOT);
367 		(void) strlcpy(dfs_cached_ns, rootshr, sizeof (dfs_cached_ns));
368 		(void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 1);
369 	}
370 	(void) mutex_unlock(&dfs_nsmtx);
371 
372 	return (status);
373 }
374 
375 /*
376  * Removes the namespace and all the links in it.
377  */
378 uint32_t
379 dfs_namespace_remove(const char *name)
380 {
381 	smb_cache_cursor_t cursor;
382 	dfs_nscnode_t nscnode;
383 	smb_share_t si;
384 	uint32_t status;
385 
386 	if (smb_shr_get((char *)name, &si) != NERR_Success)
387 		return (ERROR_NOT_FOUND);
388 
389 	if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0)
390 		return (ERROR_NOT_FOUND);
391 
392 	if ((status = dfs_root_remove(si.shr_path)) != ERROR_SUCCESS)
393 		return (status);
394 
395 	status = srvsvc_shr_setdfsroot(&si, B_FALSE);
396 	if (status != ERROR_SUCCESS)
397 		syslog(LOG_WARNING, "dfs: failed to disable root share %s (%d)",
398 		    name, status);
399 
400 	if (!dfs_namespace_iscached(name))
401 		return (ERROR_SUCCESS);
402 
403 	smb_cache_iterinit(&dfs_nscache, &cursor);
404 
405 	while (smb_cache_iterate(&dfs_nscache, &cursor, &nscnode)) {
406 		if (nscnode.nsc_type == DFS_OBJECT_ROOT)
407 			continue;
408 		status = dfs_link_remove(nscnode.nsc_fspath, NULL, NULL);
409 		if (status != ERROR_SUCCESS)
410 			syslog(LOG_WARNING, "dfs: failed to remove %s (%d)",
411 			    nscnode.nsc_fspath, status);
412 	}
413 
414 	dfs_cache_flush(name);
415 
416 	/* TODO: remove empty dirs */
417 	return (ERROR_SUCCESS);
418 }
419 
420 /*
421  * Determines the DFS namespace flavor.
422  */
423 uint32_t
424 dfs_namespace_getflavor(const char *name)
425 {
426 	char rootdir[DFS_PATH_MAX];
427 	dfs_info_t info;
428 
429 	if (dfs_namespace_path(name, rootdir, DFS_PATH_MAX) != ERROR_SUCCESS)
430 		return (0);
431 
432 	/* get flavor info from state info (info level 2) */
433 	if (dfs_root_getinfo(rootdir, &info, 2) != ERROR_SUCCESS)
434 		return (0);
435 
436 	return (info.i_state & DFS_VOLUME_FLAVORS);
437 }
438 
439 /*
440  * ==================
441  * Root API (public)
442  * ==================
443  */
444 
445 /*
446  * Retrieves the information of the root specified by its path.
447  *
448  * Info level (1) only needs the UNC path which is not stored,
449  * it is constructed so the function will return without
450  * accessing the backend storage.
451  */
452 uint32_t
453 dfs_root_getinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl)
454 {
455 	uint32_t status = ERROR_INTERNAL_ERROR;
456 	int xfd;
457 
458 	bzero(info, sizeof (dfs_info_t));
459 	info->i_type = DFS_OBJECT_ROOT;
460 
461 	if (infolvl == 1)
462 		return (ERROR_SUCCESS);
463 
464 	(void) rw_rdlock(&dfs_root_rwl);
465 	if ((xfd = dfs_root_xopen(rootdir, O_RDONLY)) > 0) {
466 		status = dfs_root_xread(xfd, info, infolvl);
467 		dfs_root_xclose(xfd);
468 	}
469 	(void) rw_unlock(&dfs_root_rwl);
470 
471 	return (status);
472 }
473 
474 /*
475  * Sets the provided information for the specified root or root target.
476  * Root is specified by 'rootdir' and the target is specified by
477  * (t_server, t_share) pair. Only information items needed for given
478  * information level (infolvl) is valid in the passed DFS info structure
479  * 'info'.
480  */
481 uint32_t
482 dfs_root_setinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl)
483 {
484 	dfs_info_t curinfo;
485 	uint32_t status = ERROR_SUCCESS;
486 	int xfd;
487 
488 	(void) rw_wrlock(&dfs_root_rwl);
489 	if ((xfd = dfs_root_xopen(rootdir, O_RDWR)) < 0) {
490 		(void) rw_unlock(&dfs_root_rwl);
491 		return (ERROR_INTERNAL_ERROR);
492 	}
493 
494 	status = dfs_root_xread(xfd, &curinfo, DFS_INFO_ALL);
495 	if (status != ERROR_SUCCESS) {
496 		dfs_root_xclose(xfd);
497 		(void) rw_unlock(&dfs_root_rwl);
498 		return (status);
499 	}
500 
501 	status = dfs_modinfo(DFS_OBJECT_ROOT, &curinfo, info, infolvl);
502 	if (status == ERROR_SUCCESS)
503 		status = dfs_root_xwrite(xfd, &curinfo);
504 
505 	dfs_root_xclose(xfd);
506 	(void) rw_unlock(&dfs_root_rwl);
507 
508 	dfs_info_free(&curinfo);
509 	return (status);
510 }
511 
512 /*
513  * ==================
514  * Link API (public)
515  * ==================
516  */
517 
518 /*
519  * Gets the status of the given path as a link
520  */
521 uint32_t
522 dfs_link_stat(const char *path, uint32_t *stat)
523 {
524 	if (smb_reparse_stat(path, stat) != 0)
525 		return (ERROR_INTERNAL_ERROR);
526 
527 	switch (*stat) {
528 	case SMB_REPARSE_NOTFOUND:
529 		*stat = DFS_STAT_NOTFOUND;
530 		break;
531 	case SMB_REPARSE_NOTREPARSE:
532 		*stat = DFS_STAT_NOTLINK;
533 		break;
534 	case SMB_REPARSE_ISREPARSE:
535 		*stat = DFS_STAT_ISREPARSE;
536 		if (smb_reparse_svcget(path, DFS_REPARSE_SVCTYPE, NULL) == 0)
537 			*stat = DFS_STAT_ISDFS;
538 		break;
539 	default:
540 		*stat = DFS_STAT_UNKNOWN;
541 		break;
542 	}
543 
544 	return (ERROR_SUCCESS);
545 }
546 
547 /*
548  * Creates a new DFS link or adds a new target to an existing link
549  */
550 uint32_t
551 dfs_link_add(const char *path, const char *server, const char *share,
552     const char *cmnt, uint32_t flags, boolean_t *newlink)
553 {
554 	dfs_info_t info;
555 	dfs_target_t *t;
556 	int ntargets;
557 	uint32_t status;
558 	uint32_t stat;
559 
560 	*newlink = B_FALSE;
561 
562 	if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS)
563 		return (status);
564 
565 	switch (stat) {
566 	case DFS_STAT_NOTFOUND:
567 	case DFS_STAT_ISREPARSE:
568 		/* Create a new DFS link */
569 
570 		status = dfs_link_getinfo(NULL, &info, DFS_INFO_ALL);
571 		if (status != ERROR_SUCCESS)
572 			return (status);
573 
574 		(void) strlcpy(info.i_comment, (cmnt) ? cmnt : "",
575 		    sizeof (info.i_comment));
576 		*newlink = B_TRUE;
577 		break;
578 
579 	case DFS_STAT_ISDFS:
580 		/* Add a target to an existing link */
581 
582 		if (flags & DFS_ADD_VOLUME)
583 			return (ERROR_FILE_EXISTS);
584 
585 		status = dfs_link_getinfo(path, &info, DFS_INFO_ALL);
586 		if (status != ERROR_SUCCESS)
587 			return (status);
588 
589 		break;
590 
591 	case DFS_STAT_NOTLINK:
592 		/* specified path points to a non-reparse object */
593 		return (ERROR_FILE_EXISTS);
594 
595 	default:
596 		return (ERROR_INTERNAL_ERROR);
597 	}
598 
599 	/* checks to see if the target already exists */
600 	ntargets = info.i_ntargets;
601 	if (dfs_target_find(info.i_targets, ntargets, server, share) != -1) {
602 		dfs_info_free(&info);
603 		return (ERROR_FILE_EXISTS);
604 	}
605 
606 	/* add the new target */
607 	t = realloc(info.i_targets, (ntargets + 1) * sizeof (dfs_target_t));
608 	if (t == NULL) {
609 		dfs_info_free(&info);
610 		return (ERROR_NOT_ENOUGH_MEMORY);
611 	}
612 
613 	info.i_targets = t;
614 	dfs_target_init(&info.i_targets[ntargets], server, share,
615 	    DFS_STORAGE_STATE_ONLINE);
616 	info.i_ntargets++;
617 
618 	status = dfs_link_commit(path, &info);
619 
620 	dfs_info_free(&info);
621 	return (status);
622 }
623 
624 /*
625  * Removes a link or a link target from a DFS namespace. A link can be
626  * removed regardless of the number of targets associated with it.
627  *
628  * 'server' and 'share' parameters specify a target, so if they are NULL
629  * it means the link should be removed, otherwise the specified target
630  * is removed if found.
631  */
632 uint32_t
633 dfs_link_remove(const char *path, const char *server, const char *share)
634 {
635 	dfs_info_t info;
636 	uint32_t status, stat;
637 	int rc, idx;
638 
639 	if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS)
640 		return (status);
641 
642 	if (stat != DFS_STAT_ISDFS)
643 		return (ERROR_NOT_FOUND);
644 
645 	if (server == NULL && share == NULL) {
646 		/* remove the link */
647 		if (smb_reparse_svcdel(path, DFS_REPARSE_SVCTYPE) != 0)
648 			return (ERROR_INTERNAL_ERROR);
649 
650 		return (ERROR_SUCCESS);
651 	}
652 
653 	/* remove the specified target in the link */
654 
655 	status = dfs_link_getinfo(path, &info, DFS_INFO_ALL);
656 	if (status != ERROR_SUCCESS)
657 		return (status);
658 
659 	/* checks to see if the target exists */
660 	idx = dfs_target_find(info.i_targets, info.i_ntargets, server, share);
661 	if (idx != -1) {
662 		bcopy(&info.i_targets[idx + 1], &info.i_targets[idx],
663 		    (info.i_ntargets - idx - 1) * sizeof (dfs_target_t));
664 		info.i_ntargets--;
665 	} else {
666 		dfs_info_free(&info);
667 		return (ERROR_FILE_NOT_FOUND);
668 	}
669 
670 	if (info.i_ntargets == 0) {
671 		/* if last target, then remove the link */
672 		rc = smb_reparse_svcdel(path, DFS_REPARSE_SVCTYPE);
673 		status = (rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
674 	} else {
675 		status = dfs_link_commit(path, &info);
676 	}
677 
678 	dfs_info_free(&info);
679 	return (status);
680 }
681 
682 /*
683  * Sets the provided information for the specified link or link target.
684  * Link is specified by 'path' and the target is specified by
685  * (t_server, t_share) pair. Only information items needed for given
686  * information level (infolvl) is valid in the passed DFS info structure
687  * 'info'.
688  */
689 uint32_t
690 dfs_link_setinfo(const char *path, dfs_info_t *info, uint32_t infolvl)
691 {
692 	dfs_info_t curinfo;
693 	uint32_t status;
694 
695 	status = dfs_link_getinfo(path, &curinfo, DFS_INFO_ALL);
696 	if (status != ERROR_SUCCESS)
697 		return (status);
698 
699 	status = dfs_modinfo(DFS_OBJECT_LINK, &curinfo, info, infolvl);
700 	if (status == ERROR_SUCCESS)
701 		status = dfs_link_commit(path, &curinfo);
702 
703 	dfs_info_free(&curinfo);
704 	return (status);
705 }
706 
707 /*
708  * Gets the DFS link info.
709  *
710  * If path is NULL, it just does some initialization.
711  *
712  * Info level (1) only needs the UNC path which is not
713  * stored, it is constructed so the function will return
714  * without accessing the backend storage.
715  */
716 uint32_t
717 dfs_link_getinfo(const char *path, dfs_info_t *info, uint32_t infolvl)
718 {
719 	char *link_data;
720 	uint32_t status;
721 	uuid_t uuid;
722 	int rc;
723 
724 	bzero(info, sizeof (dfs_info_t));
725 	info->i_type = DFS_OBJECT_LINK;
726 
727 	if (path == NULL) {
728 		info->i_state = DFS_VOLUME_STATE_OK;
729 		info->i_timeout = DFS_LINK_TIMEOUT;
730 		info->i_propflags = 0;
731 		uuid_generate_random(uuid);
732 		uuid_unparse(uuid, info->i_guid);
733 		return (ERROR_SUCCESS);
734 	}
735 
736 	if (infolvl == 1)
737 		return (ERROR_SUCCESS);
738 
739 	rc = smb_reparse_svcget(path, DFS_REPARSE_SVCTYPE, &link_data);
740 	if (rc != 0)
741 		return (ERROR_INTERNAL_ERROR);
742 
743 	status = dfs_link_decode(info, link_data, infolvl);
744 	free(link_data);
745 
746 	return (status);
747 }
748 
749 /*
750  * ===================
751  * Cache API (public)
752  * ===================
753  */
754 
755 /*
756  * Adds an entry with given DFS name (root sharename) and relative path
757  * to the share (relpath) and the specified entry type (i.e. root/link)
758  * to the namespace cache.
759  */
760 uint32_t
761 dfs_cache_add_byname(const char *name, const char *relpath, uint32_t type)
762 {
763 	char uncpath[DFS_PATH_MAX];
764 	char fspath[DFS_PATH_MAX];
765 	smb_share_t si;
766 
767 	if (smb_shr_get((char *)name, &si) != NERR_Success)
768 		return (ERROR_NOT_FOUND);
769 
770 	if (type == DFS_OBJECT_ROOT) {
771 		(void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s",
772 		    dfs_nbname, name);
773 		return (dfs_cache_add_byunc(uncpath, si.shr_path, type));
774 	}
775 
776 	/* add link entry */
777 	(void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", si.shr_path, relpath);
778 	(void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s\\%s", dfs_nbname,
779 	    name, relpath);
780 
781 	/* relpath may contain '/' */
782 	(void) strsubst(uncpath, '/', '\\');
783 
784 	return (dfs_cache_add_byunc(uncpath, fspath, type));
785 }
786 
787 /*
788  * Removes the namespace cache entry for the given link
789  * in the namespace ('name') with 'relpath'
790  */
791 void
792 dfs_cache_remove(const char *name, const char *relpath)
793 {
794 	dfs_nscnode_t dn;
795 
796 	(void) snprintf(dn.nsc_uncpath, sizeof (dn.nsc_uncpath),
797 	    "\\\\%s\\%s\\%s", dfs_nbname, name, relpath);
798 
799 	/* relpath may contain '/' */
800 	(void) strsubst(dn.nsc_uncpath, '/', '\\');
801 
802 	smb_cache_remove(&dfs_nscache, &dn);
803 }
804 
805 /*
806  * Get the DFS data for the specified cache entry
807  */
808 uint32_t
809 dfs_cache_getinfo(dfs_nscnode_t *dn, dfs_info_t *info, uint32_t infolvl)
810 {
811 	uint32_t status;
812 
813 	if (dn->nsc_type == DFS_OBJECT_LINK)
814 		status = dfs_link_getinfo(dn->nsc_fspath, info, infolvl);
815 	else
816 		status = dfs_root_getinfo(dn->nsc_fspath, info, infolvl);
817 
818 	(void) strlcpy(info->i_uncpath, dn->nsc_uncpath,
819 	    sizeof (info->i_uncpath));
820 
821 	if (status == ERROR_SUCCESS)
822 		dfs_info_trace("dfs_cache_getinfo", info);
823 
824 	return (status);
825 }
826 
827 /*
828  * Returns the number of cache entries i.e. the number of
829  * root(s) and link(s)
830  */
831 uint32_t
832 dfs_cache_num(void)
833 {
834 	return (smb_cache_num(&dfs_nscache));
835 }
836 
837 void
838 dfs_cache_iterinit(smb_cache_cursor_t *cursor)
839 {
840 	smb_cache_iterinit(&dfs_nscache, cursor);
841 }
842 
843 boolean_t
844 dfs_cache_iterate(smb_cache_cursor_t *cursor, dfs_nscnode_t *dn)
845 {
846 	return (smb_cache_iterate(&dfs_nscache, cursor, dn));
847 }
848 
849 /*
850  * ==================
851  * Misc API (public)
852  * ==================
853  */
854 
855 /*
856  * This is the function that is called by smbd door server to
857  * fullfil a GetReferrals request from smbsrv kernel module
858  *
859  * 'reftype' specifies the requested referral type. If it is
860  * DFS_REFERRAL_ROOT then dfs_path should point to a namespace
861  * root. If it is DFS_REFERRAL_LINK then dfs_path should CONTAIN
862  * a link, in which case this function will find the link and
863  * returns its target information.
864  */
865 uint32_t
866 dfs_get_referrals(const char *dfs_path, dfs_reftype_t reftype,
867     dfs_info_t *referrals)
868 {
869 	dfs_path_t path;
870 	smb_unc_t *unc;
871 	char linkpath[DFS_PATH_MAX];
872 	uint32_t status;
873 
874 	status = dfs_path_parse(&path, dfs_path, DFS_OBJECT_ANY);
875 	if (status != ERROR_SUCCESS)
876 		return (status);
877 
878 	dfs_setpriv(PRIV_ON);
879 
880 	referrals->i_type = path.p_type;
881 
882 	switch (reftype) {
883 	case DFS_REFERRAL_ROOT:
884 		if (path.p_type != DFS_OBJECT_ROOT) {
885 			status = ERROR_INVALID_PARAMETER;
886 			break;
887 		}
888 
889 		status = dfs_root_getinfo((const char *)path.p_fspath,
890 		    referrals, DFS_INFO_ALL);
891 		(void) strlcpy(referrals->i_uncpath, dfs_path, DFS_PATH_MAX);
892 		break;
893 
894 	case DFS_REFERRAL_LINK:
895 		if (path.p_type != DFS_OBJECT_LINK) {
896 			status = ERROR_INVALID_PARAMETER;
897 			break;
898 		}
899 
900 		unc = &path.p_unc;
901 		if (!dfs_namespace_findlink(unc->unc_share, unc->unc_path,
902 		    linkpath, DFS_PATH_MAX)) {
903 			status = ERROR_NOT_FOUND;
904 			break;
905 		}
906 
907 		status = dfs_link_getinfo(linkpath, referrals, DFS_INFO_ALL);
908 		(void) snprintf(referrals->i_uncpath, DFS_PATH_MAX, "/%s/%s/%s",
909 		    unc->unc_server, unc->unc_share, unc->unc_path);
910 		break;
911 
912 	default:
913 		status = ERROR_INVALID_PARAMETER;
914 		break;
915 	}
916 
917 	dfs_setpriv(PRIV_OFF);
918 	dfs_path_free(&path);
919 	return (status);
920 }
921 
922 /*
923  * Takes a DFS path in UNC format (dfs_path) and parse it into a dfs_path_t
924  * structure.
925  *
926  * dfs_path_free() MUST be called to free the allocated memory in this
927  * function.
928  *
929  * Returns:
930  *
931  * ERROR_INVALID_PARAMETER	path is not a valid UNC or not valid for the
932  * 				specified object type
933  * ERROR_NOT_ENOUGH_MEMORY	not enough memory to peform the parse
934  * ERROR_NOT_FOUND		namespace specified does not exist
935  */
936 uint32_t
937 dfs_path_parse(dfs_path_t *path, const char *dfs_path, uint32_t path_type)
938 {
939 	char rootdir[DFS_PATH_MAX];
940 	smb_unc_t *unc;
941 	uint32_t status = ERROR_SUCCESS;
942 	int rc;
943 
944 	bzero(path, sizeof (dfs_path_t));
945 	unc = &path->p_unc;
946 
947 	rc = smb_unc_init(dfs_path, unc);
948 	switch (rc) {
949 	case EINVAL:
950 		return (ERROR_INVALID_PARAMETER);
951 	case ENOMEM:
952 		return (ERROR_NOT_ENOUGH_MEMORY);
953 	default:
954 		break;
955 	}
956 
957 	if (dfs_namespace_path(unc->unc_share, rootdir, DFS_PATH_MAX)
958 	    != ERROR_SUCCESS) {
959 		smb_unc_free(unc);
960 		return (ERROR_NOT_FOUND);
961 	}
962 
963 	if (path_type == DFS_OBJECT_ANY)
964 		path->p_type = (unc->unc_path != NULL)
965 		    ? DFS_OBJECT_LINK : DFS_OBJECT_ROOT;
966 	else
967 		path->p_type = path_type;
968 
969 	switch (path->p_type) {
970 	case DFS_OBJECT_LINK:
971 		if ((unc->unc_path == NULL) || (*unc->unc_path == '\0'))
972 			status = ERROR_NOT_FOUND;
973 		else
974 			(void) snprintf(path->p_fspath, sizeof (path->p_fspath),
975 			    "%s/%s", rootdir, unc->unc_path);
976 		break;
977 
978 	case DFS_OBJECT_ROOT:
979 		if (unc->unc_path == NULL)
980 			(void) strlcpy(path->p_fspath, rootdir,
981 			    sizeof (path->p_fspath));
982 		else
983 			status = ERROR_INVALID_PARAMETER;
984 		break;
985 
986 	default:
987 		status = ERROR_INVALID_PARAMETER;
988 	}
989 
990 	if (status != ERROR_SUCCESS)
991 		smb_unc_free(unc);
992 
993 	return (status);
994 }
995 
996 /*
997  * Frees the allocated memory for p_unc field of the passed path
998  */
999 void
1000 dfs_path_free(dfs_path_t *path)
1001 {
1002 	if (path != NULL)
1003 		smb_unc_free(&path->p_unc);
1004 }
1005 
1006 /*
1007  * Free the allocated memory for targets in the given info
1008  * structure
1009  */
1010 void
1011 dfs_info_free(dfs_info_t *info)
1012 {
1013 	if (info)
1014 		free(info->i_targets);
1015 }
1016 
1017 /*
1018  * Trace the given DFS info structure
1019  */
1020 void
1021 dfs_info_trace(const char *msg, dfs_info_t *info)
1022 {
1023 	dfs_target_t *t;
1024 	int i;
1025 
1026 	smb_tracef("%s", msg);
1027 	if (info == NULL)
1028 		return;
1029 
1030 	smb_tracef("UNC\t%s", info->i_uncpath);
1031 	smb_tracef("comment\t%s", info->i_comment);
1032 	smb_tracef("GUID\t%s", info->i_guid);
1033 	smb_tracef("state\t%X", info->i_state);
1034 	smb_tracef("timeout\t%d", info->i_timeout);
1035 	smb_tracef("props\t%X", info->i_propflags);
1036 	smb_tracef("# targets\t%X", info->i_ntargets);
1037 
1038 	if (info->i_targets == NULL)
1039 		return;
1040 
1041 	for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) {
1042 		smb_tracef("[%d] \\\\%s\\%s", i, t->t_server, t->t_share);
1043 		smb_tracef("[%d] state\t%X", i, t->t_state);
1044 		smb_tracef("[%d] priority\t%d:%d", i, t->t_priority.p_class,
1045 		    t->t_priority.p_rank);
1046 	}
1047 }
1048 
1049 /*
1050  * Search the path specified by 'relpath' to see if it contains
1051  * a DFS link starting from the last component. If a link is found
1052  * the full path is returned in 'linkpath'
1053  */
1054 static boolean_t
1055 dfs_namespace_findlink(const char *name, char *relpath,
1056     char *linkpath, size_t bufsz)
1057 {
1058 	char rootdir[DFS_PATH_MAX];
1059 	uint32_t stat;
1060 	char *p;
1061 
1062 	if (dfs_namespace_path(name, rootdir, DFS_PATH_MAX) != ERROR_SUCCESS)
1063 		return (B_FALSE);
1064 
1065 	(void) snprintf(linkpath, bufsz, "%s/%s", rootdir, relpath);
1066 
1067 	for (;;) {
1068 		if (dfs_link_stat(linkpath, &stat) != ERROR_SUCCESS)
1069 			return (B_FALSE);
1070 
1071 		if (stat == DFS_STAT_ISDFS)
1072 			return (B_TRUE);
1073 
1074 		if ((p = strrchr(relpath, '/')) == NULL)
1075 			return (B_FALSE);
1076 		*p = '\0';
1077 
1078 		(void) snprintf(linkpath, bufsz, "%s/%s", rootdir, relpath);
1079 	}
1080 
1081 	/*NOTREACHED*/
1082 	return (B_FALSE);
1083 }
1084 
1085 /*
1086  * Caches the specified namespace
1087  */
1088 static void *
1089 dfs_namespace_cache(void *arg)
1090 {
1091 	char *share = arg;
1092 	char uncpath[DFS_PATH_MAX];
1093 	smb_share_t si;
1094 
1095 	if (smb_shr_get(share, &si) != NERR_Success) {
1096 		free(share);
1097 		return (NULL);
1098 	}
1099 
1100 	/*
1101 	 * This check should be removed when multiple standalone
1102 	 * namespaces are supported.
1103 	 */
1104 	(void) mutex_lock(&dfs_nsmtx);
1105 	if (*dfs_cached_ns != '\0') {
1106 		syslog(LOG_WARNING, "dfs: trying to load %s namespace."
1107 		    " Only one standalone namespace is supported."
1108 		    " A namespace is already exported for %s",
1109 		    share, dfs_cached_ns);
1110 		(void) mutex_unlock(&dfs_nsmtx);
1111 		free(share);
1112 		return (NULL);
1113 	}
1114 	(void) strlcpy(dfs_cached_ns, share, sizeof (dfs_cached_ns));
1115 	(void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 1);
1116 	(void) mutex_unlock(&dfs_nsmtx);
1117 
1118 	(void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s", dfs_nbname, share);
1119 	(void) dfs_cache_add_byunc(uncpath, si.shr_path, DFS_OBJECT_ROOT);
1120 
1121 	dfs_cache_populate(uncpath, si.shr_path);
1122 
1123 	free(share);
1124 	return (NULL);
1125 }
1126 
1127 /*
1128  * Checks whether the given name matches the name of
1129  * the cached namespace.
1130  */
1131 static boolean_t
1132 dfs_namespace_iscached(const char *name)
1133 {
1134 	boolean_t iscached;
1135 
1136 	(void) mutex_lock(&dfs_nsmtx);
1137 	iscached = (smb_strcasecmp(name, dfs_cached_ns, 0) == 0);
1138 	(void) mutex_unlock(&dfs_nsmtx);
1139 
1140 	return (iscached);
1141 }
1142 
1143 static int
1144 dfs_root_add(const char *rootdir, dfs_info_t *info)
1145 {
1146 	uint32_t status = ERROR_INTERNAL_ERROR;
1147 	int xfd;
1148 
1149 	(void) rw_wrlock(&dfs_root_rwl);
1150 	if ((xfd = dfs_root_xopen(rootdir, O_CREAT | O_TRUNC | O_RDWR)) > 0) {
1151 		status = dfs_root_xwrite(xfd, info);
1152 		dfs_root_xclose(xfd);
1153 	}
1154 	(void) rw_unlock(&dfs_root_rwl);
1155 
1156 	return (status);
1157 }
1158 
1159 /*
1160  * Deletes the specified root information
1161  */
1162 static uint32_t
1163 dfs_root_remove(const char *rootdir)
1164 {
1165 	int attrdirfd;
1166 	int err = 0;
1167 
1168 	(void) rw_wrlock(&dfs_root_rwl);
1169 
1170 	if ((attrdirfd = attropen(rootdir, ".", O_RDONLY)) > 0) {
1171 		if (unlinkat(attrdirfd, DFS_ROOT_XATTR, 0) == -1) {
1172 			if (errno != ENOENT)
1173 				err = errno;
1174 		}
1175 		(void) close(attrdirfd);
1176 	} else {
1177 		err = errno;
1178 	}
1179 
1180 	(void) rw_unlock(&dfs_root_rwl);
1181 
1182 	if (err != 0) {
1183 		syslog(LOG_DEBUG, "dfs: failed to remove root info %s (%d)",
1184 		    rootdir, err);
1185 		return (ERROR_INTERNAL_ERROR);
1186 	}
1187 
1188 	return (ERROR_SUCCESS);
1189 }
1190 
1191 /*
1192  * Opens DFS root directory's extended attribute with the given mode.
1193  */
1194 static int
1195 dfs_root_xopen(const char *rootdir, int oflag)
1196 {
1197 	int dfd;
1198 	int xfd = -1;
1199 	int err = 0;
1200 
1201 	if ((dfd = open(rootdir, O_RDONLY)) > 0) {
1202 		xfd = openat(dfd, DFS_ROOT_XATTR, oflag | O_XATTR, 0600);
1203 		if (xfd == -1)
1204 			err = errno;
1205 		(void) close(dfd);
1206 	} else {
1207 		err = errno;
1208 	}
1209 
1210 	if (err != 0) {
1211 		syslog(LOG_DEBUG, "dfs: failed to open root directory %s (%d)",
1212 		    rootdir, err);
1213 	}
1214 
1215 	return (xfd);
1216 }
1217 
1218 /*
1219  * Closes given extended attribute file descriptor
1220  */
1221 static void
1222 dfs_root_xclose(int xfd)
1223 {
1224 	(void) close(xfd);
1225 }
1226 
1227 /*
1228  * Writes the given DFS data in the DFS root directory's
1229  * extended attribute specified with xfd file descriptor.
1230  */
1231 static uint32_t
1232 dfs_root_xwrite(int xfd, dfs_info_t *info)
1233 {
1234 	size_t nbytes;
1235 	char *buf = NULL;
1236 	size_t buflen;
1237 	uint32_t status;
1238 
1239 	if ((status = dfs_root_encode(info, &buf, &buflen)) != ERROR_SUCCESS)
1240 		return (status);
1241 
1242 	(void) lseek(xfd, 0, SEEK_SET);
1243 	nbytes = write(xfd, buf, buflen);
1244 	free(buf);
1245 
1246 	return ((nbytes == buflen) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR);
1247 }
1248 
1249 /*
1250  * Reads DFS root information from its directory extended attribute
1251  * and parse it into given dfs_info_t structure
1252  */
1253 static uint32_t
1254 dfs_root_xread(int xfd, dfs_info_t *info, uint32_t infolvl)
1255 {
1256 	struct stat statbuf;
1257 	uint32_t status;
1258 	char *buf;
1259 
1260 	if (fstat(xfd, &statbuf) != 0)
1261 		return (ERROR_INTERNAL_ERROR);
1262 
1263 	if ((buf = malloc(statbuf.st_size)) == NULL)
1264 		return (ERROR_NOT_ENOUGH_MEMORY);
1265 
1266 	if (read(xfd, buf, statbuf.st_size) == statbuf.st_size)
1267 		status = dfs_root_decode(info, buf, statbuf.st_size, infolvl);
1268 	else
1269 		status = ERROR_INTERNAL_ERROR;
1270 
1271 	free(buf);
1272 	return (status);
1273 }
1274 
1275 /*
1276  * Encodes (packs) DFS information in 'info' into a flat
1277  * buffer in a name-value format. This function allocates a
1278  * buffer with appropriate size to contain all the information
1279  * so the caller MUST free the allocated memory by calling free().
1280  */
1281 static uint32_t
1282 dfs_root_encode(dfs_info_t *info, char **buf, size_t *bufsz)
1283 {
1284 	dfs_target_t *t;
1285 	nvlist_t *nvl;
1286 	int rc;
1287 
1288 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
1289 		return (ERROR_NOT_ENOUGH_MEMORY);
1290 
1291 	rc = nvlist_add_string(nvl, "comment", info->i_comment);
1292 	rc |= nvlist_add_string(nvl, "guid", info->i_guid);
1293 	rc |= nvlist_add_uint32(nvl, "state", info->i_state);
1294 	rc |= nvlist_add_uint32(nvl, "timeout", info->i_timeout);
1295 	rc |= nvlist_add_uint32(nvl, "propflags", info->i_propflags);
1296 	t = info->i_targets;
1297 	rc |= nvlist_add_string(nvl, "t_server", t->t_server);
1298 	rc |= nvlist_add_string(nvl, "t_share", t->t_share);
1299 	rc |= nvlist_add_uint32(nvl, "t_state", t->t_state);
1300 	rc |= nvlist_add_uint32(nvl, "t_priority_class",
1301 	    t->t_priority.p_class);
1302 	rc |= nvlist_add_uint16(nvl, "t_priority_rank",
1303 	    t->t_priority.p_rank);
1304 
1305 	if (rc == 0)
1306 		rc = nvlist_pack(nvl, buf, bufsz, NV_ENCODE_NATIVE, 0);
1307 
1308 	nvlist_free(nvl);
1309 
1310 	return ((rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR);
1311 }
1312 
1313 /*
1314  * Decodes (unpack) provided buffer which contains a list of name-value
1315  * pairs into given dfs_info_t structure
1316  */
1317 static uint32_t
1318 dfs_root_decode(dfs_info_t *info, char *buf, size_t bufsz, uint32_t infolvl)
1319 {
1320 	nvlist_t *nvl;
1321 	char *cmnt, *guid;
1322 	char *t_server, *t_share;
1323 	uint32_t t_state;
1324 	uint32_t t_priority_class;
1325 	uint16_t t_priority_rank;
1326 	boolean_t decode_priority = B_FALSE;
1327 	int rc;
1328 
1329 	if (nvlist_unpack(buf, bufsz, &nvl, 0) != 0)
1330 		return (ERROR_INTERNAL_ERROR);
1331 
1332 	rc = nvlist_lookup_string(nvl, "comment", &cmnt);
1333 	rc |= nvlist_lookup_string(nvl, "guid", &guid);
1334 	rc |= nvlist_lookup_uint32(nvl, "state", &info->i_state);
1335 	rc |= nvlist_lookup_uint32(nvl, "timeout", &info->i_timeout);
1336 	rc |= nvlist_lookup_uint32(nvl, "propflags", &info->i_propflags);
1337 
1338 	if (rc != 0) {
1339 		nvlist_free(nvl);
1340 		return (ERROR_INTERNAL_ERROR);
1341 	}
1342 
1343 	(void) strlcpy(info->i_comment, (cmnt) ? cmnt : "",
1344 	    sizeof (info->i_comment));
1345 	(void) strlcpy(info->i_guid, (guid) ? guid : "", sizeof (info->i_guid));
1346 
1347 	info->i_targets = NULL;
1348 	info->i_ntargets = 1;
1349 
1350 	switch (infolvl) {
1351 	case DFS_INFO_ALL:
1352 	case 3:
1353 	case 4:
1354 		/* need target information */
1355 		break;
1356 	case 6:
1357 	case 9:
1358 		/* need target and priority information */
1359 		decode_priority = B_TRUE;
1360 		break;
1361 	default:
1362 		nvlist_free(nvl);
1363 		return (ERROR_SUCCESS);
1364 	}
1365 
1366 	info->i_targets = malloc(sizeof (dfs_target_t));
1367 	if (info->i_targets == NULL) {
1368 		nvlist_free(nvl);
1369 		return (ERROR_NOT_ENOUGH_MEMORY);
1370 	}
1371 
1372 	rc = nvlist_lookup_string(nvl, "t_server", &t_server);
1373 	rc |= nvlist_lookup_string(nvl, "t_share", &t_share);
1374 	rc |= nvlist_lookup_uint32(nvl, "t_state", &t_state);
1375 	if (rc != 0) {
1376 		nvlist_free(nvl);
1377 		free(info->i_targets);
1378 		return (ERROR_INTERNAL_ERROR);
1379 	}
1380 	dfs_target_init(info->i_targets, t_server, t_share, t_state);
1381 
1382 	if (decode_priority) {
1383 		rc = nvlist_lookup_uint32(nvl, "t_priority_class",
1384 		    &t_priority_class);
1385 		if (rc == 0)
1386 			rc = nvlist_lookup_uint16(nvl, "t_priority_rank",
1387 			    &t_priority_rank);
1388 
1389 		if (rc != 0 && rc != ENOENT) {
1390 			nvlist_free(nvl);
1391 			free(info->i_targets);
1392 			return (ERROR_INTERNAL_ERROR);
1393 		} else if (rc == 0) {
1394 			info->i_targets->t_priority.p_class = t_priority_class;
1395 			info->i_targets->t_priority.p_rank = t_priority_rank;
1396 		}
1397 	}
1398 
1399 	nvlist_free(nvl);
1400 	return (ERROR_SUCCESS);
1401 }
1402 
1403 /*
1404  * Determines if the passed state is valid for a DFS root
1405  *
1406  * This is based on test results against Win2003 and in some cases
1407  * does not match [MS-DFSNM] spec.
1408  */
1409 static uint32_t
1410 dfs_root_isvalidstate(uint32_t state)
1411 {
1412 	switch (state) {
1413 	case DFS_VOLUME_STATE_OK:
1414 	case DFS_VOLUME_STATE_RESYNCHRONIZE:
1415 		return (ERROR_SUCCESS);
1416 
1417 	case DFS_VOLUME_STATE_INCONSISTENT:
1418 	case DFS_VOLUME_STATE_FORCE_SYNC:
1419 		return (ERROR_INVALID_PARAMETER);
1420 
1421 	case DFS_VOLUME_STATE_OFFLINE:
1422 	case DFS_VOLUME_STATE_ONLINE:
1423 	case DFS_VOLUME_STATE_STANDBY:
1424 		return (ERROR_NOT_SUPPORTED);
1425 	default:
1426 		break;
1427 	}
1428 
1429 	return (ERROR_INVALID_PARAMETER);
1430 }
1431 
1432 /*
1433  * Decodes the link info from given string buffer (buf) into
1434  * dfs_info_t structure.
1435  */
1436 static uint32_t
1437 dfs_link_decode(dfs_info_t *info, char *buf, uint32_t infolvl)
1438 {
1439 	char *lfield[DFS_LINK_HDR_NFIELDS];
1440 	dfs_target_t *t;
1441 	uint32_t linkver;
1442 	uint32_t cmntlen;
1443 	uint32_t cpylen;
1444 	int i, j;
1445 
1446 	/*
1447 	 * Header format
1448 	 * ver:state:prop:timeout:guid:ntarget:cmntlen:comment:
1449 	 */
1450 	for (i = 0; i < DFS_LINK_HDR_NFIELDS; i++) {
1451 		if ((lfield[i] = strsep((char **)&buf, ":")) == NULL)
1452 			return (ERROR_INVALID_DATA);
1453 	}
1454 
1455 	i = 0;
1456 	linkver = strtoul(lfield[i++], NULL, 10);
1457 	if (linkver != DFS_LINK_V1)
1458 		return (ERROR_INVALID_DATA);
1459 
1460 	info->i_state = strtoul(lfield[i++], NULL, 10);
1461 	info->i_propflags = strtoul(lfield[i++], NULL, 10);
1462 	info->i_timeout = strtoul(lfield[i++], NULL, 10);
1463 	(void) strlcpy(info->i_guid, lfield[i++], sizeof (info->i_guid));
1464 	info->i_ntargets = strtoul(lfield[i++], NULL, 10);
1465 	info->i_targets = NULL;
1466 
1467 	cpylen = cmntlen = strtoul(lfield[i++], NULL, 10);
1468 
1469 	if (cmntlen > sizeof (info->i_comment))
1470 		cpylen = sizeof (info->i_comment);
1471 	else if (cmntlen != 0)
1472 		cpylen = cmntlen + 1;
1473 
1474 	(void) strlcpy(info->i_comment, buf, cpylen);
1475 	buf += (cmntlen + 1);
1476 
1477 	switch (infolvl) {
1478 	case DFS_INFO_ALL:
1479 	case 3:
1480 	case 4:
1481 	case 6:
1482 	case 9:
1483 		/* need target information */
1484 		break;
1485 	default:
1486 		return (ERROR_SUCCESS);
1487 	}
1488 
1489 	info->i_targets = calloc(info->i_ntargets, sizeof (dfs_target_t));
1490 	if (info->i_targets == NULL)
1491 		return (ERROR_NOT_ENOUGH_MEMORY);
1492 
1493 	/*
1494 	 * Format for each target
1495 	 * server:share:state:class:rank
1496 	 */
1497 	for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) {
1498 		for (j = 0; j < DFS_LINK_TRGT_NFIELDS; j++) {
1499 			if ((lfield[j] = strsep((char **)&buf, ":")) == NULL) {
1500 				dfs_info_free(info);
1501 				return (ERROR_INVALID_DATA);
1502 			}
1503 		}
1504 
1505 		(void) strlcpy(t->t_server, lfield[0], sizeof (t->t_server));
1506 		(void) strlcpy(t->t_share, lfield[1], sizeof (t->t_share));
1507 		t->t_state = strtoul(lfield[2], NULL, 10);
1508 		t->t_priority.p_class = strtoul(lfield[3], NULL, 10);
1509 		t->t_priority.p_rank = strtoul(lfield[4], NULL, 10);
1510 	}
1511 
1512 	return (ERROR_SUCCESS);
1513 }
1514 
1515 /*
1516  * Encodes given link information (info)
1517  */
1518 static uint32_t
1519 dfs_link_encode(dfs_info_t *info, char *buf, size_t bufsz)
1520 {
1521 	char linkdata[MAXREPARSELEN];
1522 	dfs_target_t *t;
1523 	int i, sz;
1524 
1525 	/*
1526 	 * Header format
1527 	 * ver:state:prop:timeout:guid:ntarget:cmntlen:comment
1528 	 */
1529 	sz = snprintf(buf, bufsz, "%u:%u:%u:%u:%s:%u:%u:%s",
1530 	    DFS_LINK_V1, info->i_state, info->i_propflags, info->i_timeout,
1531 	    info->i_guid, info->i_ntargets,
1532 	    strlen(info->i_comment), info->i_comment);
1533 
1534 	if (sz > bufsz) {
1535 		syslog(LOG_WARNING, "dfs: link data is too large");
1536 		dfs_info_trace("DFS link encode", info);
1537 		return (ERROR_INTERNAL_ERROR);
1538 	}
1539 
1540 	/*
1541 	 * Format for each target
1542 	 * :server:share:state:class:rank
1543 	 */
1544 	bufsz -= sz;
1545 	for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) {
1546 		if (strchr(t->t_server, ':') || strchr(t->t_share, ':'))
1547 			return (ERROR_INVALID_NAME);
1548 
1549 		sz = snprintf(linkdata, MAXREPARSELEN, ":%s:%s:%u:%u:%u",
1550 		    t->t_server, t->t_share, t->t_state,
1551 		    t->t_priority.p_class, t->t_priority.p_rank);
1552 		if (sz > bufsz) {
1553 			syslog(LOG_WARNING, "dfs: link data is too large");
1554 			dfs_info_trace("DFS link encode", info);
1555 			return (ERROR_INTERNAL_ERROR);
1556 		}
1557 		(void) strcat(buf, linkdata);
1558 		bufsz -= sz;
1559 	}
1560 
1561 	return (ERROR_SUCCESS);
1562 }
1563 
1564 /*
1565  * Stores given information for the specified link
1566  */
1567 static uint32_t
1568 dfs_link_commit(const char *path, dfs_info_t *info)
1569 {
1570 	char linkdata[MAXREPARSELEN];
1571 	uint32_t status;
1572 	int rc;
1573 
1574 	status = dfs_link_encode(info, linkdata, MAXREPARSELEN);
1575 	if (status == ERROR_SUCCESS) {
1576 		rc = smb_reparse_svcadd(path, DFS_REPARSE_SVCTYPE, linkdata);
1577 		if (rc != 0)
1578 			status = ERROR_INTERNAL_ERROR;
1579 	}
1580 
1581 	return (status);
1582 }
1583 
1584 /*
1585  * Determines if the passed state is valid for a link
1586  */
1587 static boolean_t
1588 dfs_link_isvalidstate(uint32_t state)
1589 {
1590 	return (state == DFS_VOLUME_STATE_OK ||
1591 	    state == DFS_VOLUME_STATE_OFFLINE ||
1592 	    state == DFS_VOLUME_STATE_ONLINE);
1593 }
1594 
1595 /*
1596  * Initializes the given target structure (t) with provided information.
1597  */
1598 static void
1599 dfs_target_init(dfs_target_t *t, const char *srv, const char *share,
1600     uint32_t state)
1601 {
1602 	(void) strlcpy(t->t_server, (srv) ? srv : "", sizeof (t->t_server));
1603 	(void) strlcpy(t->t_share, (share) ? share : "", sizeof (t->t_share));
1604 	t->t_state = state;
1605 	t->t_priority.p_class = DfsSiteCostNormalPriorityClass;
1606 	t->t_priority.p_rank = 0;
1607 }
1608 
1609 /*
1610  * Lookup the specified target (server, share) in the given
1611  * target list (targets). If there is a match its index is
1612  * returned, otherwise -1 will be returned.
1613  */
1614 static int
1615 dfs_target_find(dfs_target_t *targets, uint32_t ntargets,
1616     const char *server, const char *share)
1617 {
1618 	dfs_target_t *t;
1619 	int i;
1620 
1621 	for (i = 0, t = targets; i < ntargets; i++, t++) {
1622 		if ((smb_strcasecmp(t->t_server, server, 0) == 0) &&
1623 		    (smb_strcasecmp(t->t_share, share, 0) == 0))
1624 			return (i);
1625 	}
1626 
1627 	return (-1);
1628 }
1629 
1630 /*
1631  * Determines if the passed state is valid for a link/root target
1632  */
1633 static boolean_t
1634 dfs_target_isvalidstate(uint32_t state)
1635 {
1636 	return (state == DFS_STORAGE_STATE_ONLINE ||
1637 	    state == DFS_STORAGE_STATE_OFFLINE);
1638 }
1639 
1640 /*
1641  * Cache compare function, the key is UNC path
1642  */
1643 static int
1644 dfs_cache_cmp(const void *p1, const void *p2)
1645 {
1646 	smb_cache_node_t *cn1 = (smb_cache_node_t *)p1;
1647 	smb_cache_node_t *cn2 = (smb_cache_node_t *)p2;
1648 	dfs_nscnode_t *dn1 = cn1->cn_data;
1649 	dfs_nscnode_t *dn2 = cn2->cn_data;
1650 	int rc;
1651 
1652 	rc = smb_strcasecmp(dn1->nsc_uncpath, dn2->nsc_uncpath, 0);
1653 
1654 	if (rc < 0)
1655 		return (-1);
1656 
1657 	if (rc > 0)
1658 		return (1);
1659 
1660 	return (0);
1661 }
1662 
1663 /*
1664  * Adds an entry with given UNC and filesystem path and the specified
1665  * entry type (i.e. root/link) to the namespace cache.
1666  */
1667 static uint32_t
1668 dfs_cache_add_byunc(const char *uncpath, const char *fspath, uint32_t type)
1669 {
1670 	dfs_nscnode_t *dn;
1671 	uint32_t status = ERROR_SUCCESS;
1672 
1673 	if ((dn = malloc(sizeof (dfs_nscnode_t))) == NULL)
1674 		return (ERROR_NOT_ENOUGH_MEMORY);
1675 
1676 	(void) strlcpy(dn->nsc_uncpath, uncpath, sizeof (dn->nsc_uncpath));
1677 	(void) strlcpy(dn->nsc_fspath, fspath, sizeof (dn->nsc_fspath));
1678 	dn->nsc_type = type;
1679 	if (smb_cache_add(&dfs_nscache, dn, SMB_CACHE_ADD) != 0) {
1680 		free(dn);
1681 		status = ERROR_INTERNAL_ERROR;
1682 	}
1683 
1684 	return (status);
1685 }
1686 
1687 /*
1688  * starting from DFS root directory, scans the tree for DFS links
1689  * and adds them to the cache.
1690  */
1691 static void
1692 dfs_cache_populate(const char *unc_prefix, const char *dir)
1693 {
1694 	char fspath[DFS_PATH_MAX];
1695 	char uncpath[DFS_PATH_MAX];
1696 	char *fname;
1697 	int nentries, i;
1698 	struct dirent **entry_list;
1699 	uint32_t stat;
1700 
1701 	nentries = scandir(dir, &entry_list, NULL, NULL);
1702 	if (nentries == -1)
1703 		return;
1704 
1705 	for (i = 0; i < nentries; i++) {
1706 		fname = entry_list[i]->d_name;
1707 
1708 		if (strcmp(fname, ".") == 0 ||
1709 		    strcmp(fname, "..") == 0) {
1710 			free(entry_list[i]);
1711 			continue;
1712 		}
1713 
1714 		(void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", dir, fname);
1715 		(void) snprintf(uncpath, DFS_PATH_MAX, "%s\\%s", unc_prefix,
1716 		    fname);
1717 
1718 		if (dfs_path_isdir(fspath)) {
1719 			dfs_cache_populate(uncpath, fspath);
1720 		} else if (dfs_link_stat(fspath, &stat) == ERROR_SUCCESS) {
1721 			if (stat == DFS_STAT_ISDFS)
1722 				(void) dfs_cache_add_byunc(uncpath, fspath,
1723 				    DFS_OBJECT_LINK);
1724 		}
1725 
1726 		free(entry_list[i]);
1727 	}
1728 
1729 	for (; i < nentries; i++)
1730 		free(entry_list[i]);
1731 
1732 	free(entry_list);
1733 }
1734 
1735 /*
1736  * If this namespace hasn't been cached then return
1737  * without flushing the cache; otherwise clear the
1738  * name and flush the cache.
1739  */
1740 static void
1741 dfs_cache_flush(const char *name)
1742 {
1743 	(void) mutex_lock(&dfs_nsmtx);
1744 	if (smb_strcasecmp(name, dfs_cached_ns, 0) != 0) {
1745 		(void) mutex_unlock(&dfs_nsmtx);
1746 		return;
1747 	}
1748 	*dfs_cached_ns = '\0';
1749 	(void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 0);
1750 	(void) mutex_unlock(&dfs_nsmtx);
1751 
1752 	smb_cache_flush(&dfs_nscache);
1753 }
1754 
1755 /*
1756  * Returns the number of cached namespaces
1757  */
1758 static uint32_t
1759 dfs_cache_nscount(void)
1760 {
1761 	uint32_t nscount;
1762 
1763 	(void) mutex_lock(&dfs_nsmtx);
1764 	nscount = (*dfs_cached_ns == '\0') ? 0 : 1;
1765 	(void) mutex_unlock(&dfs_nsmtx);
1766 
1767 	return (nscount);
1768 }
1769 
1770 /*
1771  * Determines whether the given path is a directory.
1772  */
1773 static boolean_t
1774 dfs_path_isdir(const char *path)
1775 {
1776 	struct stat statbuf;
1777 
1778 	if (lstat(path, &statbuf) != 0)
1779 		return (B_FALSE);
1780 
1781 	return ((statbuf.st_mode & S_IFMT) == S_IFDIR);
1782 }
1783 
1784 /*
1785  * Validates the given state based on the object type (root/link), info
1786  * level, and whether it is the object's state or its target's state
1787  */
1788 static uint32_t
1789 dfs_isvalidstate(uint32_t state, uint32_t type, boolean_t target,
1790     uint32_t infolvl)
1791 {
1792 	uint32_t status = ERROR_SUCCESS;
1793 
1794 	switch (infolvl) {
1795 	case 101:
1796 		if (type == DFS_OBJECT_ROOT) {
1797 			if (!target)
1798 				return (dfs_root_isvalidstate(state));
1799 
1800 			if (!dfs_target_isvalidstate(state))
1801 				status = ERROR_INVALID_PARAMETER;
1802 			else if (state == DFS_STORAGE_STATE_OFFLINE)
1803 				status = ERROR_NOT_SUPPORTED;
1804 		} else {
1805 			if (!target) {
1806 				if (!dfs_link_isvalidstate(state))
1807 					status = ERROR_INVALID_PARAMETER;
1808 			} else {
1809 				if (!dfs_target_isvalidstate(state))
1810 					status = ERROR_INVALID_PARAMETER;
1811 			}
1812 		}
1813 		break;
1814 
1815 	case 105:
1816 		if (state == 0)
1817 			return (ERROR_SUCCESS);
1818 
1819 		if (type == DFS_OBJECT_ROOT) {
1820 			switch (state) {
1821 			case DFS_VOLUME_STATE_OK:
1822 			case DFS_VOLUME_STATE_OFFLINE:
1823 			case DFS_VOLUME_STATE_ONLINE:
1824 			case DFS_VOLUME_STATE_RESYNCHRONIZE:
1825 			case DFS_VOLUME_STATE_STANDBY:
1826 				status = ERROR_NOT_SUPPORTED;
1827 				break;
1828 
1829 			default:
1830 				status = ERROR_INVALID_PARAMETER;
1831 			}
1832 		} else {
1833 			switch (state) {
1834 			case DFS_VOLUME_STATE_OK:
1835 			case DFS_VOLUME_STATE_OFFLINE:
1836 			case DFS_VOLUME_STATE_ONLINE:
1837 				break;
1838 
1839 			case DFS_VOLUME_STATE_RESYNCHRONIZE:
1840 			case DFS_VOLUME_STATE_STANDBY:
1841 				status = ERROR_NOT_SUPPORTED;
1842 				break;
1843 
1844 			default:
1845 				status = ERROR_INVALID_PARAMETER;
1846 			}
1847 		}
1848 		break;
1849 
1850 	default:
1851 		status = ERROR_INVALID_LEVEL;
1852 	}
1853 
1854 	return (status);
1855 }
1856 
1857 /*
1858  * Validates the given property flag mask based on the object
1859  * type (root/link) and namespace flavor.
1860  */
1861 static uint32_t
1862 dfs_isvalidpropflagmask(uint32_t propflag_mask, uint32_t type,
1863     uint32_t flavor)
1864 {
1865 	uint32_t flgs_not_supported;
1866 
1867 	flgs_not_supported = DFS_PROPERTY_FLAG_ROOT_SCALABILITY
1868 	    | DFS_PROPERTY_FLAG_CLUSTER_ENABLED
1869 	    | DFS_PROPERTY_FLAG_ABDE;
1870 
1871 	if (flavor == DFS_VOLUME_FLAVOR_STANDALONE) {
1872 		if (type == DFS_OBJECT_LINK)
1873 			flgs_not_supported |= DFS_PROPERTY_FLAG_SITE_COSTING;
1874 		if (propflag_mask & flgs_not_supported)
1875 			return (ERROR_NOT_SUPPORTED);
1876 	}
1877 
1878 	return (ERROR_SUCCESS);
1879 }
1880 
1881 /*
1882  * Based on the specified information level (infolvl) copy parts of the
1883  * information provided through newinfo into the existing information
1884  * (info) for the given object.
1885  */
1886 static uint32_t
1887 dfs_modinfo(uint32_t type, dfs_info_t *info, dfs_info_t *newinfo,
1888     uint32_t infolvl)
1889 {
1890 	boolean_t target_op = B_FALSE;
1891 	uint32_t status = ERROR_SUCCESS;
1892 	uint32_t state;
1893 	int target_idx;
1894 
1895 	if (newinfo->i_targets != NULL) {
1896 		target_idx = dfs_target_find(info->i_targets, info->i_ntargets,
1897 		    newinfo->i_targets->t_server, newinfo->i_targets->t_share);
1898 		if (target_idx == -1)
1899 			return (ERROR_FILE_NOT_FOUND);
1900 		target_op = B_TRUE;
1901 	}
1902 
1903 	switch (infolvl) {
1904 	case 100:
1905 		(void) strlcpy(info->i_comment, newinfo->i_comment,
1906 		    sizeof (newinfo->i_comment));
1907 		break;
1908 
1909 	case 101:
1910 		state = (target_op)
1911 		    ? newinfo->i_targets->t_state : newinfo->i_state;
1912 		status = dfs_isvalidstate(state, type, target_op, 101);
1913 		if (status != ERROR_SUCCESS)
1914 			return (status);
1915 
1916 		if (!target_op) {
1917 			/*
1918 			 * states specified by this mask should not be stored
1919 			 */
1920 			if (state & DFS_VOLUME_STATES_SRV_OPS)
1921 				return (ERROR_SUCCESS);
1922 
1923 			info->i_state = state;
1924 		} else {
1925 			info->i_targets[target_idx].t_state = state;
1926 		}
1927 		break;
1928 
1929 	case 102:
1930 		info->i_timeout = newinfo->i_timeout;
1931 		break;
1932 
1933 	case 103:
1934 		info->i_propflags = newinfo->i_propflags;
1935 		break;
1936 
1937 	case 104:
1938 		info->i_targets[target_idx].t_priority =
1939 		    newinfo->i_targets->t_priority;
1940 		break;
1941 
1942 	case 105:
1943 		status = dfs_isvalidstate(newinfo->i_state, type, B_FALSE, 105);
1944 		if (status != ERROR_SUCCESS)
1945 			return (status);
1946 
1947 		status = dfs_isvalidpropflagmask(newinfo->i_propflag_mask, type,
1948 		    newinfo->i_flavor);
1949 		if (status != ERROR_SUCCESS)
1950 			return (status);
1951 
1952 		(void) strlcpy(info->i_comment, newinfo->i_comment,
1953 		    sizeof (newinfo->i_comment));
1954 		if (newinfo->i_state != 0)
1955 			info->i_state = newinfo->i_state;
1956 		info->i_timeout = newinfo->i_timeout;
1957 		info->i_propflags = newinfo->i_propflags;
1958 		break;
1959 
1960 	default:
1961 		status = ERROR_INVALID_LEVEL;
1962 	}
1963 
1964 	return (status);
1965 }
1966