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
64static void *dfs_intr_hdl = NULL;
65
66static 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 */
75static smb_cache_t dfs_nscache;
76static 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 */
83static char dfs_cached_ns[MAXNAMELEN];
84static mutex_t dfs_nsmtx;
85
86/*
87 * Lock for accessing root information (extended attribute)
88 */
89static rwlock_t dfs_root_rwl;
90
91extern uint32_t srvsvc_shr_setdfsroot(smb_share_t *, boolean_t);
92
93/*
94 * Namespace functions
95 */
96static boolean_t dfs_namespace_findlink(const char *, char *, char *, size_t);
97static void *dfs_namespace_cache(void *);
98static boolean_t dfs_namespace_iscached(const char *);
99
100/*
101 * Root functions
102 */
103static int dfs_root_add(const char *, dfs_info_t *);
104static uint32_t dfs_root_remove(const char *);
105static uint32_t dfs_root_encode(dfs_info_t *, char **, size_t *);
106static uint32_t dfs_root_decode(dfs_info_t *, char *, size_t, uint32_t);
107static uint32_t dfs_root_isvalidstate(uint32_t);
108
109static int dfs_root_xopen(const char *, int);
110static void dfs_root_xclose(int);
111static uint32_t dfs_root_xwrite(int, dfs_info_t *);
112static uint32_t dfs_root_xread(int, dfs_info_t *, uint32_t);
113
114/*
115 * Link functions
116 */
117static uint32_t dfs_link_encode(dfs_info_t *, char *, size_t);
118static uint32_t dfs_link_decode(dfs_info_t *, char *, uint32_t);
119static uint32_t dfs_link_commit(const char *, dfs_info_t *);
120static boolean_t dfs_link_isvalidstate(uint32_t);
121
122/*
123 * Target functions
124 */
125static void dfs_target_init(dfs_target_t *, const char *, const char *,
126    uint32_t);
127static int dfs_target_find(dfs_target_t *, uint32_t, const char *,
128    const char *);
129static boolean_t dfs_target_isvalidstate(uint32_t);
130
131/*
132 * Cache functions
133 */
134static uint32_t dfs_cache_add_byunc(const char *, const char *, uint32_t);
135static void dfs_cache_populate(const char *, const char *);
136static int dfs_cache_cmp(const void *, const void *);
137static void dfs_cache_flush(const char *);
138static uint32_t dfs_cache_nscount(void);
139
140/*
141 * Utility functions
142 */
143static boolean_t dfs_path_isdir(const char *);
144static 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 */
152void
153dfs_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 */
181void
182dfs_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 */
198void
199dfs_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 */
217void
218dfs_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 */
244void /*ARGSUSED*/
245dfs_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 */
256uint32_t
257dfs_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 */
280uint32_t
281dfs_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 */
310uint32_t
311dfs_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 */
377uint32_t
378dfs_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 */
422uint32_t
423dfs_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 */
451uint32_t
452dfs_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 */
480uint32_t
481dfs_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 */
520uint32_t
521dfs_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 */
549uint32_t
550dfs_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 */
631uint32_t
632dfs_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 */
688uint32_t
689dfs_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 */
715uint32_t
716dfs_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 */
759uint32_t
760dfs_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 */
790void
791dfs_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 */
807uint32_t
808dfs_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 */
830uint32_t
831dfs_cache_num(void)
832{
833	return (smb_cache_num(&dfs_nscache));
834}
835
836void
837dfs_cache_iterinit(smb_cache_cursor_t *cursor)
838{
839	smb_cache_iterinit(&dfs_nscache, cursor);
840}
841
842boolean_t
843dfs_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 */
864uint32_t
865dfs_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 */
935uint32_t
936dfs_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 */
998void
999dfs_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 */
1009void
1010dfs_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 */
1019void
1020dfs_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 */
1053static boolean_t
1054dfs_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 */
1087static void *
1088dfs_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 */
1130static boolean_t
1131dfs_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
1142static int
1143dfs_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 */
1161static uint32_t
1162dfs_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 */
1193static int
1194dfs_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 */
1220static void
1221dfs_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 */
1230static uint32_t
1231dfs_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 */
1252static uint32_t
1253dfs_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 */
1280static uint32_t
1281dfs_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 */
1316static uint32_t
1317dfs_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 */
1408static uint32_t
1409dfs_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 */
1435static uint32_t
1436dfs_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 */
1517static uint32_t
1518dfs_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 */
1566static uint32_t
1567dfs_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 */
1586static boolean_t
1587dfs_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 */
1597static void
1598dfs_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 */
1613static int
1614dfs_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 */
1632static boolean_t
1633dfs_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 */
1642static int
1643dfs_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 */
1666static uint32_t
1667dfs_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 */
1690static void
1691dfs_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 */
1739static void
1740dfs_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 */
1757static uint32_t
1758dfs_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 */
1772static boolean_t
1773dfs_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 */
1787static uint32_t
1788dfs_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 */
1860static uint32_t
1861dfs_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 */
1885static uint32_t
1886dfs_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