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