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