xref: /onnv-gate/usr/src/lib/smbsrv/libmlsvc/common/dfs.c (revision 12508:edb7861a1533)
111963SAfshin.Ardakani@Sun.COM /*
211963SAfshin.Ardakani@Sun.COM  * CDDL HEADER START
311963SAfshin.Ardakani@Sun.COM  *
411963SAfshin.Ardakani@Sun.COM  * The contents of this file are subject to the terms of the
511963SAfshin.Ardakani@Sun.COM  * Common Development and Distribution License (the "License").
611963SAfshin.Ardakani@Sun.COM  * You may not use this file except in compliance with the License.
711963SAfshin.Ardakani@Sun.COM  *
811963SAfshin.Ardakani@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911963SAfshin.Ardakani@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011963SAfshin.Ardakani@Sun.COM  * See the License for the specific language governing permissions
1111963SAfshin.Ardakani@Sun.COM  * and limitations under the License.
1211963SAfshin.Ardakani@Sun.COM  *
1311963SAfshin.Ardakani@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411963SAfshin.Ardakani@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511963SAfshin.Ardakani@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611963SAfshin.Ardakani@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711963SAfshin.Ardakani@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811963SAfshin.Ardakani@Sun.COM  *
1911963SAfshin.Ardakani@Sun.COM  * CDDL HEADER END
2011963SAfshin.Ardakani@Sun.COM  */
21*12508Samw@Sun.COM 
2211963SAfshin.Ardakani@Sun.COM /*
23*12508Samw@Sun.COM  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
2411963SAfshin.Ardakani@Sun.COM  */
2511963SAfshin.Ardakani@Sun.COM 
2611963SAfshin.Ardakani@Sun.COM #include <strings.h>
2711963SAfshin.Ardakani@Sun.COM #include <errno.h>
2811963SAfshin.Ardakani@Sun.COM #include <unistd.h>
2911963SAfshin.Ardakani@Sun.COM #include <fcntl.h>
3011963SAfshin.Ardakani@Sun.COM #include <dirent.h>
31*12508Samw@Sun.COM #include <dlfcn.h>
3211963SAfshin.Ardakani@Sun.COM #include <pthread.h>
3311963SAfshin.Ardakani@Sun.COM #include <syslog.h>
3411963SAfshin.Ardakani@Sun.COM #include <sys/fs_reparse.h>
3511963SAfshin.Ardakani@Sun.COM #include <uuid/uuid.h>
3611963SAfshin.Ardakani@Sun.COM 
37*12508Samw@Sun.COM #include <smbsrv/libsmb.h>
38*12508Samw@Sun.COM #include <smbsrv/libmlsvc.h>
3911963SAfshin.Ardakani@Sun.COM #include <smbsrv/smb_dfs.h>
4011963SAfshin.Ardakani@Sun.COM #include <smbsrv/smb_share.h>
4111963SAfshin.Ardakani@Sun.COM #include <dfs.h>
4211963SAfshin.Ardakani@Sun.COM 
4311963SAfshin.Ardakani@Sun.COM /*
4411963SAfshin.Ardakani@Sun.COM  * default timeout (TTL) values (in second) for root and link
4511963SAfshin.Ardakani@Sun.COM  */
4611963SAfshin.Ardakani@Sun.COM #define	DFS_ROOT_TIMEOUT		300
4711963SAfshin.Ardakani@Sun.COM #define	DFS_LINK_TIMEOUT		1800
4811963SAfshin.Ardakani@Sun.COM 
4911963SAfshin.Ardakani@Sun.COM /*
5011963SAfshin.Ardakani@Sun.COM  * DFS link data format in reparse point
5111963SAfshin.Ardakani@Sun.COM  *
5211963SAfshin.Ardakani@Sun.COM  * ver:state:prop:timeout:guid:ntarget:cmntlen:comment
5311963SAfshin.Ardakani@Sun.COM  *    [[:tserver:tshare:tstate:pclass:prank]...]
5411963SAfshin.Ardakani@Sun.COM  */
5511963SAfshin.Ardakani@Sun.COM #define	DFS_LINK_V1			1
5611963SAfshin.Ardakani@Sun.COM #define	DFS_LINK_HDR_NFIELDS		7	/* # fields in header section */
5711963SAfshin.Ardakani@Sun.COM #define	DFS_LINK_TRGT_NFIELDS		5	/* # fields for each target */
5811963SAfshin.Ardakani@Sun.COM 
5911963SAfshin.Ardakani@Sun.COM #define	DFS_ROOT_XATTR			"SUNWdfs.rootinfo"
6011963SAfshin.Ardakani@Sun.COM 
6111963SAfshin.Ardakani@Sun.COM #define	DFS_INFO_ALL			0
6211963SAfshin.Ardakani@Sun.COM 
63*12508Samw@Sun.COM static void *dfs_intr_hdl = NULL;
64*12508Samw@Sun.COM 
65*12508Samw@Sun.COM static struct {
66*12508Samw@Sun.COM 	int (*dfsops_remote_count)(uint32_t *);
67*12508Samw@Sun.COM } dfs_intr_ops;
68*12508Samw@Sun.COM 
6911963SAfshin.Ardakani@Sun.COM /*
7011963SAfshin.Ardakani@Sun.COM  * Namespace cache
7111963SAfshin.Ardakani@Sun.COM  *
7211963SAfshin.Ardakani@Sun.COM  * Caches links' UNC and filesystem path where the key is the UNC path.
7311963SAfshin.Ardakani@Sun.COM  */
7411963SAfshin.Ardakani@Sun.COM static smb_cache_t dfs_nscache;
7511963SAfshin.Ardakani@Sun.COM static char dfs_nbname[NETBIOS_NAME_SZ];
7611963SAfshin.Ardakani@Sun.COM 
7711963SAfshin.Ardakani@Sun.COM /*
7811963SAfshin.Ardakani@Sun.COM  * Lock for accessing root information (extended attribute)
7911963SAfshin.Ardakani@Sun.COM  */
8011963SAfshin.Ardakani@Sun.COM static rwlock_t dfs_root_rwl;
8111963SAfshin.Ardakani@Sun.COM 
8211963SAfshin.Ardakani@Sun.COM extern uint32_t srvsvc_shr_setdfsroot(smb_share_t *, boolean_t);
8311963SAfshin.Ardakani@Sun.COM 
8411963SAfshin.Ardakani@Sun.COM /*
8511963SAfshin.Ardakani@Sun.COM  * Namespace functions
8611963SAfshin.Ardakani@Sun.COM  */
8711963SAfshin.Ardakani@Sun.COM static boolean_t dfs_namespace_findlink(const char *, char *, char *, size_t);
8811963SAfshin.Ardakani@Sun.COM static void *dfs_namespace_cache(void *);
8911963SAfshin.Ardakani@Sun.COM 
9011963SAfshin.Ardakani@Sun.COM /*
9111963SAfshin.Ardakani@Sun.COM  * Root functions
9211963SAfshin.Ardakani@Sun.COM  */
9311963SAfshin.Ardakani@Sun.COM static int dfs_root_add(const char *, dfs_info_t *);
9411963SAfshin.Ardakani@Sun.COM static uint32_t dfs_root_remove(const char *);
9511963SAfshin.Ardakani@Sun.COM static uint32_t dfs_root_encode(dfs_info_t *, char **, size_t *);
9611963SAfshin.Ardakani@Sun.COM static uint32_t dfs_root_decode(dfs_info_t *, char *, size_t, uint32_t);
9711963SAfshin.Ardakani@Sun.COM static uint32_t dfs_root_isvalidstate(uint32_t);
9811963SAfshin.Ardakani@Sun.COM 
9911963SAfshin.Ardakani@Sun.COM static int dfs_root_xopen(const char *, int);
10011963SAfshin.Ardakani@Sun.COM static void dfs_root_xclose(int);
10111963SAfshin.Ardakani@Sun.COM static uint32_t dfs_root_xwrite(int, dfs_info_t *);
10211963SAfshin.Ardakani@Sun.COM static uint32_t dfs_root_xread(int, dfs_info_t *, uint32_t);
10311963SAfshin.Ardakani@Sun.COM 
10411963SAfshin.Ardakani@Sun.COM /*
10511963SAfshin.Ardakani@Sun.COM  * Link functions
10611963SAfshin.Ardakani@Sun.COM  */
10711963SAfshin.Ardakani@Sun.COM static uint32_t dfs_link_encode(dfs_info_t *, char *, size_t);
10811963SAfshin.Ardakani@Sun.COM static uint32_t dfs_link_decode(dfs_info_t *, char *, uint32_t);
10911963SAfshin.Ardakani@Sun.COM static uint32_t dfs_link_commit(const char *, dfs_info_t *);
11011963SAfshin.Ardakani@Sun.COM static boolean_t dfs_link_isvalidstate(uint32_t);
11111963SAfshin.Ardakani@Sun.COM 
11211963SAfshin.Ardakani@Sun.COM /*
11311963SAfshin.Ardakani@Sun.COM  * Target functions
11411963SAfshin.Ardakani@Sun.COM  */
11511963SAfshin.Ardakani@Sun.COM static void dfs_target_init(dfs_target_t *, const char *, const char *,
11611963SAfshin.Ardakani@Sun.COM     uint32_t);
11711963SAfshin.Ardakani@Sun.COM static int dfs_target_find(dfs_target_t *, uint32_t, const char *,
11811963SAfshin.Ardakani@Sun.COM     const char *);
11911963SAfshin.Ardakani@Sun.COM static boolean_t dfs_target_isvalidstate(uint32_t);
12011963SAfshin.Ardakani@Sun.COM 
12111963SAfshin.Ardakani@Sun.COM /*
12211963SAfshin.Ardakani@Sun.COM  * Cache functions
12311963SAfshin.Ardakani@Sun.COM  */
12411963SAfshin.Ardakani@Sun.COM static uint32_t dfs_cache_add_byunc(const char *, const char *, uint32_t);
12511963SAfshin.Ardakani@Sun.COM static void dfs_cache_populate(const char *, const char *);
12611963SAfshin.Ardakani@Sun.COM static int dfs_cache_cmp(const void *, const void *);
12711963SAfshin.Ardakani@Sun.COM 
12811963SAfshin.Ardakani@Sun.COM /*
12911963SAfshin.Ardakani@Sun.COM  * Utility functions
13011963SAfshin.Ardakani@Sun.COM  */
13111963SAfshin.Ardakani@Sun.COM static boolean_t dfs_path_isdir(const char *);
13211963SAfshin.Ardakani@Sun.COM static uint32_t dfs_modinfo(uint32_t, dfs_info_t *, dfs_info_t *, uint32_t);
13311963SAfshin.Ardakani@Sun.COM 
13411963SAfshin.Ardakani@Sun.COM /*
13511963SAfshin.Ardakani@Sun.COM  * DFS module initializationr:
13611963SAfshin.Ardakani@Sun.COM  *
13711963SAfshin.Ardakani@Sun.COM  * - creates the namespace cache
13811963SAfshin.Ardakani@Sun.COM  * - gets system's NetBIOS name
13911963SAfshin.Ardakani@Sun.COM  */
14011963SAfshin.Ardakani@Sun.COM void
14111963SAfshin.Ardakani@Sun.COM dfs_init(void)
14211963SAfshin.Ardakani@Sun.COM {
14311963SAfshin.Ardakani@Sun.COM 	smb_domain_t di;
14411963SAfshin.Ardakani@Sun.COM 
14511963SAfshin.Ardakani@Sun.COM 	smb_cache_create(&dfs_nscache, 0, dfs_cache_cmp, free, bcopy,
14611963SAfshin.Ardakani@Sun.COM 	    sizeof (dfs_nscnode_t));
14711963SAfshin.Ardakani@Sun.COM 
14811963SAfshin.Ardakani@Sun.COM 	if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
14911963SAfshin.Ardakani@Sun.COM 		return;
15011963SAfshin.Ardakani@Sun.COM 
15111963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(dfs_nbname, di.di_nbname, NETBIOS_NAME_SZ);
152*12508Samw@Sun.COM 
153*12508Samw@Sun.COM 	bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops));
154*12508Samw@Sun.COM 
155*12508Samw@Sun.COM 	if ((dfs_intr_hdl = smb_dlopen()) == NULL)
156*12508Samw@Sun.COM 		return;
157*12508Samw@Sun.COM 
158*12508Samw@Sun.COM 	if ((dfs_intr_ops.dfsops_remote_count =
159*12508Samw@Sun.COM 	    (int (*)())dlsym(dfs_intr_hdl, "smb_dfs_remote_count")) == NULL) {
160*12508Samw@Sun.COM 		smb_dlclose(dfs_intr_hdl);
161*12508Samw@Sun.COM 		dfs_intr_hdl = NULL;
162*12508Samw@Sun.COM 		bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops));
163*12508Samw@Sun.COM 	}
16411963SAfshin.Ardakani@Sun.COM }
16511963SAfshin.Ardakani@Sun.COM 
16611963SAfshin.Ardakani@Sun.COM /*
16711963SAfshin.Ardakani@Sun.COM  * DFS module cleanup:
16811963SAfshin.Ardakani@Sun.COM  *
16911963SAfshin.Ardakani@Sun.COM  * - destroys the namespace cache
17011963SAfshin.Ardakani@Sun.COM  */
17111963SAfshin.Ardakani@Sun.COM void
17211963SAfshin.Ardakani@Sun.COM dfs_fini(void)
17311963SAfshin.Ardakani@Sun.COM {
174*12508Samw@Sun.COM 	smb_dlclose(dfs_intr_hdl);
17511963SAfshin.Ardakani@Sun.COM 	smb_cache_destroy(&dfs_nscache);
17611963SAfshin.Ardakani@Sun.COM }
17711963SAfshin.Ardakani@Sun.COM 
17811963SAfshin.Ardakani@Sun.COM /*
17911963SAfshin.Ardakani@Sun.COM  * To successfully handle some of link/root requests, some
18011963SAfshin.Ardakani@Sun.COM  * file system operations need to be performed. These operations
18111963SAfshin.Ardakani@Sun.COM  * should take place on behalf of the connected user (typically
18211963SAfshin.Ardakani@Sun.COM  * Administrator) and to do so we need to have an infrastructure
18311963SAfshin.Ardakani@Sun.COM  * in place so that smbd can act as a client and sends request to
18411963SAfshin.Ardakani@Sun.COM  * the kernel. Right now, we lack this infrastructure, so we make
18511963SAfshin.Ardakani@Sun.COM  * a compromise by temporarily enabling some privileges for smbd
18611963SAfshin.Ardakani@Sun.COM  * to be able to fulfill various link/root requests.
18711963SAfshin.Ardakani@Sun.COM  */
18811963SAfshin.Ardakani@Sun.COM void
18911963SAfshin.Ardakani@Sun.COM dfs_setpriv(priv_op_t op)
19011963SAfshin.Ardakani@Sun.COM {
19111963SAfshin.Ardakani@Sun.COM 	(void) priv_set(op, PRIV_EFFECTIVE,
19211963SAfshin.Ardakani@Sun.COM 	    PRIV_FILE_DAC_READ,
19311963SAfshin.Ardakani@Sun.COM 	    PRIV_FILE_DAC_WRITE,
19411963SAfshin.Ardakani@Sun.COM 	    PRIV_FILE_DAC_EXECUTE,
19511963SAfshin.Ardakani@Sun.COM 	    PRIV_FILE_DAC_SEARCH, NULL);
19611963SAfshin.Ardakani@Sun.COM }
19711963SAfshin.Ardakani@Sun.COM 
19811963SAfshin.Ardakani@Sun.COM /*
19911963SAfshin.Ardakani@Sun.COM  * ========================
20011963SAfshin.Ardakani@Sun.COM  * Namespace API (public)
20111963SAfshin.Ardakani@Sun.COM  * ========================
20211963SAfshin.Ardakani@Sun.COM  */
20311963SAfshin.Ardakani@Sun.COM 
20411963SAfshin.Ardakani@Sun.COM /*
20511963SAfshin.Ardakani@Sun.COM  * Launches a thread to cache the specified namespace
20611963SAfshin.Ardakani@Sun.COM  */
20711963SAfshin.Ardakani@Sun.COM void
20811963SAfshin.Ardakani@Sun.COM dfs_namespace_load(const char *name)
20911963SAfshin.Ardakani@Sun.COM {
21011963SAfshin.Ardakani@Sun.COM 	pthread_t thr;
21111963SAfshin.Ardakani@Sun.COM 	pthread_attr_t tattr;
21211963SAfshin.Ardakani@Sun.COM 	char *rootshr;
21311963SAfshin.Ardakani@Sun.COM 	int rc;
21411963SAfshin.Ardakani@Sun.COM 
21511963SAfshin.Ardakani@Sun.COM 	if ((rootshr = strdup(name)) == NULL) {
21611963SAfshin.Ardakani@Sun.COM 		syslog(LOG_ERR, "dfs: failed to load %s namespace (no memory)",
21711963SAfshin.Ardakani@Sun.COM 		    name);
21811963SAfshin.Ardakani@Sun.COM 		return;
21911963SAfshin.Ardakani@Sun.COM 	}
22011963SAfshin.Ardakani@Sun.COM 
22111963SAfshin.Ardakani@Sun.COM 	(void) pthread_attr_init(&tattr);
22211963SAfshin.Ardakani@Sun.COM 	(void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
22311963SAfshin.Ardakani@Sun.COM 	rc = pthread_create(&thr, &tattr, dfs_namespace_cache, rootshr);
22411963SAfshin.Ardakani@Sun.COM 	(void) pthread_attr_destroy(&tattr);
22511963SAfshin.Ardakani@Sun.COM 
22611963SAfshin.Ardakani@Sun.COM 	if (rc != 0)
22711963SAfshin.Ardakani@Sun.COM 		syslog(LOG_ERR, "dfs: fail to loading %s namespace (%d)",
22811963SAfshin.Ardakani@Sun.COM 		    name, rc);
22911963SAfshin.Ardakani@Sun.COM }
23011963SAfshin.Ardakani@Sun.COM 
23111963SAfshin.Ardakani@Sun.COM /*
23211963SAfshin.Ardakani@Sun.COM  * Flushes the cache when a DFS root share is removed
23311963SAfshin.Ardakani@Sun.COM  */
23411963SAfshin.Ardakani@Sun.COM void /*ARGSUSED*/
23511963SAfshin.Ardakani@Sun.COM dfs_namespace_unload(const char *name)
23611963SAfshin.Ardakani@Sun.COM {
23711963SAfshin.Ardakani@Sun.COM 	smb_cache_flush(&dfs_nscache);
23811963SAfshin.Ardakani@Sun.COM }
23911963SAfshin.Ardakani@Sun.COM 
24011963SAfshin.Ardakani@Sun.COM /*
24111963SAfshin.Ardakani@Sun.COM  * Returns the file system path for the given share if it
24211963SAfshin.Ardakani@Sun.COM  * is a DFS root share.
24311963SAfshin.Ardakani@Sun.COM  * If 'path' is NULL, this function only indicates whether
24411963SAfshin.Ardakani@Sun.COM  * or not the given share represents a DFS namespace
24511963SAfshin.Ardakani@Sun.COM  */
24611963SAfshin.Ardakani@Sun.COM uint32_t
24711963SAfshin.Ardakani@Sun.COM dfs_namespace_path(const char *name, char *path, size_t pathsz)
24811963SAfshin.Ardakani@Sun.COM {
24911963SAfshin.Ardakani@Sun.COM 	smb_share_t si;
25011963SAfshin.Ardakani@Sun.COM 
25111963SAfshin.Ardakani@Sun.COM 	if (smb_shr_get((char *)name, &si) != NERR_Success)
25211963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_FOUND);
25311963SAfshin.Ardakani@Sun.COM 
25411963SAfshin.Ardakani@Sun.COM 	if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0)
25511963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_FOUND);
25611963SAfshin.Ardakani@Sun.COM 
25711963SAfshin.Ardakani@Sun.COM 	if (path != NULL)
25811963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(path, si.shr_path, pathsz);
25911963SAfshin.Ardakani@Sun.COM 
26011963SAfshin.Ardakani@Sun.COM 	return (ERROR_SUCCESS);
26111963SAfshin.Ardakani@Sun.COM }
26211963SAfshin.Ardakani@Sun.COM 
26311963SAfshin.Ardakani@Sun.COM /*
26411963SAfshin.Ardakani@Sun.COM  * Returns the number of DFS root shares i.e. the number
26511963SAfshin.Ardakani@Sun.COM  * of standalone namespaces.
26611963SAfshin.Ardakani@Sun.COM  */
26711963SAfshin.Ardakani@Sun.COM uint32_t
26811963SAfshin.Ardakani@Sun.COM dfs_namespace_count(void)
26911963SAfshin.Ardakani@Sun.COM {
27011963SAfshin.Ardakani@Sun.COM 	smb_shriter_t shi;
27111963SAfshin.Ardakani@Sun.COM 	smb_share_t *si;
27211963SAfshin.Ardakani@Sun.COM 	uint32_t nroot = 0;
273*12508Samw@Sun.COM 	int rc;
274*12508Samw@Sun.COM 
275*12508Samw@Sun.COM 	if (dfs_intr_ops.dfsops_remote_count != NULL &&
276*12508Samw@Sun.COM 	    (rc = dfs_intr_ops.dfsops_remote_count(&nroot)) != 0) {
277*12508Samw@Sun.COM 		/*
278*12508Samw@Sun.COM 		 * If this call fails, let's assume there's at least one root
279*12508Samw@Sun.COM 		 * namespace already configured.  The interposer library cannot
280*12508Samw@Sun.COM 		 * confirm or deny the presence of a namespace, so let's take
281*12508Samw@Sun.COM 		 * the safe approach and assume one exists.
282*12508Samw@Sun.COM 		 */
283*12508Samw@Sun.COM 		nroot = 1;
284*12508Samw@Sun.COM 		syslog(LOG_WARNING, "dfs: dfsops_remote_count() failed: %d, "
285*12508Samw@Sun.COM 		    "assuming one namespace exists", rc);
286*12508Samw@Sun.COM 	}
28711963SAfshin.Ardakani@Sun.COM 
28811963SAfshin.Ardakani@Sun.COM 	smb_shr_iterinit(&shi);
28911963SAfshin.Ardakani@Sun.COM 	while ((si = smb_shr_iterate(&shi)) != NULL) {
29011963SAfshin.Ardakani@Sun.COM 		if ((si->shr_flags & SMB_SHRF_DFSROOT) != 0)
29111963SAfshin.Ardakani@Sun.COM 			nroot++;
29211963SAfshin.Ardakani@Sun.COM 	}
29311963SAfshin.Ardakani@Sun.COM 
29411963SAfshin.Ardakani@Sun.COM 	return (nroot);
29511963SAfshin.Ardakani@Sun.COM }
29611963SAfshin.Ardakani@Sun.COM 
29711963SAfshin.Ardakani@Sun.COM /*
29811963SAfshin.Ardakani@Sun.COM  * Creates a DFS root with the given name and comment.
29911963SAfshin.Ardakani@Sun.COM  *
30011963SAfshin.Ardakani@Sun.COM  * This function does not create the root share, it
30111963SAfshin.Ardakani@Sun.COM  * should already exist.
30211963SAfshin.Ardakani@Sun.COM  */
30311963SAfshin.Ardakani@Sun.COM uint32_t
30411963SAfshin.Ardakani@Sun.COM dfs_namespace_add(const char *rootshr, const char *cmnt)
30511963SAfshin.Ardakani@Sun.COM {
30611963SAfshin.Ardakani@Sun.COM 	dfs_info_t info;
30711963SAfshin.Ardakani@Sun.COM 	dfs_target_t t;
30811963SAfshin.Ardakani@Sun.COM 	smb_share_t si;
30911963SAfshin.Ardakani@Sun.COM 	uuid_t uuid;
31011963SAfshin.Ardakani@Sun.COM 	uint32_t status;
31111963SAfshin.Ardakani@Sun.COM 
31211963SAfshin.Ardakani@Sun.COM 	if (*rootshr == '\\') {
31311963SAfshin.Ardakani@Sun.COM 		/* Windows has a special case here! */
31411963SAfshin.Ardakani@Sun.COM 		return (ERROR_BAD_PATHNAME);
31511963SAfshin.Ardakani@Sun.COM 	}
31611963SAfshin.Ardakani@Sun.COM 
31711963SAfshin.Ardakani@Sun.COM 	if (smb_shr_get((char *)rootshr, &si) != NERR_Success)
31811963SAfshin.Ardakani@Sun.COM 		return (NERR_NetNameNotFound);
31911963SAfshin.Ardakani@Sun.COM 
32011963SAfshin.Ardakani@Sun.COM 	if (si.shr_flags & SMB_SHRF_DFSROOT) {
32111963SAfshin.Ardakani@Sun.COM 		/* Share is already a DFS root */
32211963SAfshin.Ardakani@Sun.COM 		return (ERROR_FILE_EXISTS);
32311963SAfshin.Ardakani@Sun.COM 	}
32411963SAfshin.Ardakani@Sun.COM 
32511963SAfshin.Ardakani@Sun.COM 	bzero(&info, sizeof (info));
32611963SAfshin.Ardakani@Sun.COM 	if (cmnt)
32711963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(info.i_comment, cmnt, sizeof (info.i_comment));
32811963SAfshin.Ardakani@Sun.COM 	info.i_state = DFS_VOLUME_STATE_OK | DFS_VOLUME_FLAVOR_STANDALONE;
32911963SAfshin.Ardakani@Sun.COM 	info.i_timeout = DFS_ROOT_TIMEOUT;
33011963SAfshin.Ardakani@Sun.COM 	info.i_propflags = 0;
33111963SAfshin.Ardakani@Sun.COM 
33211963SAfshin.Ardakani@Sun.COM 	uuid_generate_random(uuid);
33311963SAfshin.Ardakani@Sun.COM 	uuid_unparse(uuid, info.i_guid);
33411963SAfshin.Ardakani@Sun.COM 
33511963SAfshin.Ardakani@Sun.COM 	dfs_target_init(&t, dfs_nbname, rootshr, DFS_STORAGE_STATE_ONLINE);
33611963SAfshin.Ardakani@Sun.COM 
33711963SAfshin.Ardakani@Sun.COM 	info.i_ntargets = 1;
33811963SAfshin.Ardakani@Sun.COM 	info.i_targets = &t;
33911963SAfshin.Ardakani@Sun.COM 
34011963SAfshin.Ardakani@Sun.COM 	if ((status = dfs_root_add(si.shr_path, &info)) != ERROR_SUCCESS)
34111963SAfshin.Ardakani@Sun.COM 		return (status);
34211963SAfshin.Ardakani@Sun.COM 
34311963SAfshin.Ardakani@Sun.COM 	status = srvsvc_shr_setdfsroot(&si, B_TRUE);
34411963SAfshin.Ardakani@Sun.COM 	if (status == ERROR_SUCCESS)
34511963SAfshin.Ardakani@Sun.COM 		(void) dfs_cache_add_byname(rootshr, NULL, DFS_OBJECT_ROOT);
34611963SAfshin.Ardakani@Sun.COM 
34711963SAfshin.Ardakani@Sun.COM 	return (status);
34811963SAfshin.Ardakani@Sun.COM }
34911963SAfshin.Ardakani@Sun.COM 
35011963SAfshin.Ardakani@Sun.COM /*
35111963SAfshin.Ardakani@Sun.COM  * Removes the namespace and all the links in it.
35211963SAfshin.Ardakani@Sun.COM  */
35311963SAfshin.Ardakani@Sun.COM uint32_t
35411963SAfshin.Ardakani@Sun.COM dfs_namespace_remove(const char *name)
35511963SAfshin.Ardakani@Sun.COM {
35611963SAfshin.Ardakani@Sun.COM 	smb_cache_cursor_t cursor;
35711963SAfshin.Ardakani@Sun.COM 	dfs_nscnode_t nscnode;
35811963SAfshin.Ardakani@Sun.COM 	smb_share_t si;
35911963SAfshin.Ardakani@Sun.COM 	uint32_t status;
36011963SAfshin.Ardakani@Sun.COM 
36111963SAfshin.Ardakani@Sun.COM 	if (smb_shr_get((char *)name, &si) != NERR_Success)
36211963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_FOUND);
36311963SAfshin.Ardakani@Sun.COM 
36411963SAfshin.Ardakani@Sun.COM 	if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0)
36511963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_FOUND);
36611963SAfshin.Ardakani@Sun.COM 
36711963SAfshin.Ardakani@Sun.COM 	if ((status = dfs_root_remove(si.shr_path)) != ERROR_SUCCESS)
36811963SAfshin.Ardakani@Sun.COM 		return (status);
36911963SAfshin.Ardakani@Sun.COM 
37011963SAfshin.Ardakani@Sun.COM 	status = srvsvc_shr_setdfsroot(&si, B_FALSE);
37111963SAfshin.Ardakani@Sun.COM 	if (status != ERROR_SUCCESS)
37211963SAfshin.Ardakani@Sun.COM 		syslog(LOG_WARNING, "dfs: failed to disable root share %s (%d)",
37311963SAfshin.Ardakani@Sun.COM 		    name, status);
37411963SAfshin.Ardakani@Sun.COM 
37511963SAfshin.Ardakani@Sun.COM 	smb_cache_iterinit(&dfs_nscache, &cursor);
37611963SAfshin.Ardakani@Sun.COM 
37711963SAfshin.Ardakani@Sun.COM 	while (smb_cache_iterate(&dfs_nscache, &cursor, &nscnode)) {
37811963SAfshin.Ardakani@Sun.COM 		if (nscnode.nsc_type == DFS_OBJECT_ROOT)
37911963SAfshin.Ardakani@Sun.COM 			continue;
38011963SAfshin.Ardakani@Sun.COM 		status = dfs_link_remove(nscnode.nsc_fspath, NULL, NULL);
38111963SAfshin.Ardakani@Sun.COM 		if (status != ERROR_SUCCESS)
38211963SAfshin.Ardakani@Sun.COM 			syslog(LOG_WARNING, "dfs: failed to remove %s (%d)",
38311963SAfshin.Ardakani@Sun.COM 			    nscnode.nsc_fspath, status);
38411963SAfshin.Ardakani@Sun.COM 	}
38511963SAfshin.Ardakani@Sun.COM 
38611963SAfshin.Ardakani@Sun.COM 	smb_cache_flush(&dfs_nscache);
38711963SAfshin.Ardakani@Sun.COM 
38811963SAfshin.Ardakani@Sun.COM 	/* TODO: remove empty dirs */
38911963SAfshin.Ardakani@Sun.COM 	return (ERROR_SUCCESS);
39011963SAfshin.Ardakani@Sun.COM }
39111963SAfshin.Ardakani@Sun.COM 
39211963SAfshin.Ardakani@Sun.COM /*
39311963SAfshin.Ardakani@Sun.COM  * ==================
39411963SAfshin.Ardakani@Sun.COM  * Root API (public)
39511963SAfshin.Ardakani@Sun.COM  * ==================
39611963SAfshin.Ardakani@Sun.COM  */
39711963SAfshin.Ardakani@Sun.COM 
39811963SAfshin.Ardakani@Sun.COM /*
39911963SAfshin.Ardakani@Sun.COM  * Retrieves the information of the root specified by its path.
40011963SAfshin.Ardakani@Sun.COM  *
40111963SAfshin.Ardakani@Sun.COM  * Info level (1) only needs the UNC path which is not stored,
40211963SAfshin.Ardakani@Sun.COM  * it is constructed so the function will return without
40311963SAfshin.Ardakani@Sun.COM  * accessing the backend storage.
40411963SAfshin.Ardakani@Sun.COM  */
40511963SAfshin.Ardakani@Sun.COM uint32_t
40611963SAfshin.Ardakani@Sun.COM dfs_root_getinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl)
40711963SAfshin.Ardakani@Sun.COM {
40811963SAfshin.Ardakani@Sun.COM 	uint32_t status = ERROR_INTERNAL_ERROR;
40911963SAfshin.Ardakani@Sun.COM 	int xfd;
41011963SAfshin.Ardakani@Sun.COM 
41111963SAfshin.Ardakani@Sun.COM 	bzero(info, sizeof (dfs_info_t));
41211963SAfshin.Ardakani@Sun.COM 	info->i_type = DFS_OBJECT_ROOT;
41311963SAfshin.Ardakani@Sun.COM 
41411963SAfshin.Ardakani@Sun.COM 	if (infolvl == 1)
41511963SAfshin.Ardakani@Sun.COM 		return (ERROR_SUCCESS);
41611963SAfshin.Ardakani@Sun.COM 
41711963SAfshin.Ardakani@Sun.COM 	(void) rw_rdlock(&dfs_root_rwl);
41811963SAfshin.Ardakani@Sun.COM 	if ((xfd = dfs_root_xopen(rootdir, O_RDONLY)) > 0) {
41911963SAfshin.Ardakani@Sun.COM 		status = dfs_root_xread(xfd, info, infolvl);
42011963SAfshin.Ardakani@Sun.COM 		dfs_root_xclose(xfd);
42111963SAfshin.Ardakani@Sun.COM 	}
42211963SAfshin.Ardakani@Sun.COM 	(void) rw_unlock(&dfs_root_rwl);
42311963SAfshin.Ardakani@Sun.COM 
42411963SAfshin.Ardakani@Sun.COM 	return (status);
42511963SAfshin.Ardakani@Sun.COM }
42611963SAfshin.Ardakani@Sun.COM 
42711963SAfshin.Ardakani@Sun.COM /*
42811963SAfshin.Ardakani@Sun.COM  * Sets the provided information for the specified root or root target.
42911963SAfshin.Ardakani@Sun.COM  * Root is specified by 'rootdir' and the target is specified by
43011963SAfshin.Ardakani@Sun.COM  * (t_server, t_share) pair. Only information items needed for given
43111963SAfshin.Ardakani@Sun.COM  * information level (infolvl) is valid in the passed DFS info structure
43211963SAfshin.Ardakani@Sun.COM  * 'info'.
43311963SAfshin.Ardakani@Sun.COM  */
43411963SAfshin.Ardakani@Sun.COM uint32_t
43511963SAfshin.Ardakani@Sun.COM dfs_root_setinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl)
43611963SAfshin.Ardakani@Sun.COM {
43711963SAfshin.Ardakani@Sun.COM 	dfs_info_t curinfo;
43811963SAfshin.Ardakani@Sun.COM 	uint32_t status = ERROR_SUCCESS;
43911963SAfshin.Ardakani@Sun.COM 	int xfd;
44011963SAfshin.Ardakani@Sun.COM 
44111963SAfshin.Ardakani@Sun.COM 	(void) rw_wrlock(&dfs_root_rwl);
44211963SAfshin.Ardakani@Sun.COM 	if ((xfd = dfs_root_xopen(rootdir, O_RDWR)) < 0) {
44311963SAfshin.Ardakani@Sun.COM 		(void) rw_unlock(&dfs_root_rwl);
44411963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
44511963SAfshin.Ardakani@Sun.COM 	}
44611963SAfshin.Ardakani@Sun.COM 
44711963SAfshin.Ardakani@Sun.COM 	status = dfs_root_xread(xfd, &curinfo, DFS_INFO_ALL);
44811963SAfshin.Ardakani@Sun.COM 	if (status != ERROR_SUCCESS) {
44911963SAfshin.Ardakani@Sun.COM 		dfs_root_xclose(xfd);
45011963SAfshin.Ardakani@Sun.COM 		(void) rw_unlock(&dfs_root_rwl);
45111963SAfshin.Ardakani@Sun.COM 		return (status);
45211963SAfshin.Ardakani@Sun.COM 	}
45311963SAfshin.Ardakani@Sun.COM 
45411963SAfshin.Ardakani@Sun.COM 	status = dfs_modinfo(DFS_OBJECT_ROOT, &curinfo, info, infolvl);
45511963SAfshin.Ardakani@Sun.COM 	if (status == ERROR_SUCCESS)
45611963SAfshin.Ardakani@Sun.COM 		status = dfs_root_xwrite(xfd, &curinfo);
45711963SAfshin.Ardakani@Sun.COM 
45811963SAfshin.Ardakani@Sun.COM 	dfs_root_xclose(xfd);
45911963SAfshin.Ardakani@Sun.COM 	(void) rw_unlock(&dfs_root_rwl);
46011963SAfshin.Ardakani@Sun.COM 
46111963SAfshin.Ardakani@Sun.COM 	dfs_info_free(&curinfo);
46211963SAfshin.Ardakani@Sun.COM 	return (status);
46311963SAfshin.Ardakani@Sun.COM }
46411963SAfshin.Ardakani@Sun.COM 
46511963SAfshin.Ardakani@Sun.COM /*
46611963SAfshin.Ardakani@Sun.COM  * ==================
46711963SAfshin.Ardakani@Sun.COM  * Link API (public)
46811963SAfshin.Ardakani@Sun.COM  * ==================
46911963SAfshin.Ardakani@Sun.COM  */
47011963SAfshin.Ardakani@Sun.COM 
47111963SAfshin.Ardakani@Sun.COM /*
47211963SAfshin.Ardakani@Sun.COM  * Gets the status of the given path as a link
47311963SAfshin.Ardakani@Sun.COM  */
47411963SAfshin.Ardakani@Sun.COM uint32_t
47511963SAfshin.Ardakani@Sun.COM dfs_link_stat(const char *path, uint32_t *stat)
47611963SAfshin.Ardakani@Sun.COM {
47711963SAfshin.Ardakani@Sun.COM 	if (smb_reparse_stat(path, stat) != 0)
47811963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
47911963SAfshin.Ardakani@Sun.COM 
48011963SAfshin.Ardakani@Sun.COM 	switch (*stat) {
48111963SAfshin.Ardakani@Sun.COM 	case SMB_REPARSE_NOTFOUND:
48211963SAfshin.Ardakani@Sun.COM 		*stat = DFS_STAT_NOTFOUND;
48311963SAfshin.Ardakani@Sun.COM 		break;
48411963SAfshin.Ardakani@Sun.COM 	case SMB_REPARSE_NOTREPARSE:
48511963SAfshin.Ardakani@Sun.COM 		*stat = DFS_STAT_NOTLINK;
48611963SAfshin.Ardakani@Sun.COM 		break;
48711963SAfshin.Ardakani@Sun.COM 	case SMB_REPARSE_ISREPARSE:
48811963SAfshin.Ardakani@Sun.COM 		*stat = DFS_STAT_ISREPARSE;
48911963SAfshin.Ardakani@Sun.COM 		if (smb_reparse_svcget(path, DFS_REPARSE_SVCTYPE, NULL) == 0)
49011963SAfshin.Ardakani@Sun.COM 			*stat = DFS_STAT_ISDFS;
49111963SAfshin.Ardakani@Sun.COM 		break;
49211963SAfshin.Ardakani@Sun.COM 	default:
49311963SAfshin.Ardakani@Sun.COM 		*stat = DFS_STAT_UNKNOWN;
49411963SAfshin.Ardakani@Sun.COM 		break;
49511963SAfshin.Ardakani@Sun.COM 	}
49611963SAfshin.Ardakani@Sun.COM 
49711963SAfshin.Ardakani@Sun.COM 	return (ERROR_SUCCESS);
49811963SAfshin.Ardakani@Sun.COM }
49911963SAfshin.Ardakani@Sun.COM 
50011963SAfshin.Ardakani@Sun.COM /*
50111963SAfshin.Ardakani@Sun.COM  * Creates a new DFS link or adds a new target to an existing link
50211963SAfshin.Ardakani@Sun.COM  */
50311963SAfshin.Ardakani@Sun.COM uint32_t
50411963SAfshin.Ardakani@Sun.COM dfs_link_add(const char *path, const char *server, const char *share,
50511963SAfshin.Ardakani@Sun.COM     const char *cmnt, uint32_t flags, boolean_t *newlink)
50611963SAfshin.Ardakani@Sun.COM {
50711963SAfshin.Ardakani@Sun.COM 	dfs_info_t info;
50811963SAfshin.Ardakani@Sun.COM 	dfs_target_t *t;
50911963SAfshin.Ardakani@Sun.COM 	int ntargets;
51011963SAfshin.Ardakani@Sun.COM 	uint32_t status;
51111963SAfshin.Ardakani@Sun.COM 	uint32_t stat;
51211963SAfshin.Ardakani@Sun.COM 
51311963SAfshin.Ardakani@Sun.COM 	*newlink = B_FALSE;
51411963SAfshin.Ardakani@Sun.COM 
51511963SAfshin.Ardakani@Sun.COM 	if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS)
51611963SAfshin.Ardakani@Sun.COM 		return (status);
51711963SAfshin.Ardakani@Sun.COM 
51811963SAfshin.Ardakani@Sun.COM 	switch (stat) {
51911963SAfshin.Ardakani@Sun.COM 	case DFS_STAT_NOTFOUND:
52011963SAfshin.Ardakani@Sun.COM 	case DFS_STAT_ISREPARSE:
52111963SAfshin.Ardakani@Sun.COM 		/* Create a new DFS link */
52211963SAfshin.Ardakani@Sun.COM 
52311963SAfshin.Ardakani@Sun.COM 		status = dfs_link_getinfo(NULL, &info, DFS_INFO_ALL);
52411963SAfshin.Ardakani@Sun.COM 		if (status != ERROR_SUCCESS)
52511963SAfshin.Ardakani@Sun.COM 			return (status);
52611963SAfshin.Ardakani@Sun.COM 
52711963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(info.i_comment, (cmnt) ? cmnt : "",
52811963SAfshin.Ardakani@Sun.COM 		    sizeof (info.i_comment));
52911963SAfshin.Ardakani@Sun.COM 		*newlink = B_TRUE;
53011963SAfshin.Ardakani@Sun.COM 		break;
53111963SAfshin.Ardakani@Sun.COM 
53211963SAfshin.Ardakani@Sun.COM 	case DFS_STAT_ISDFS:
53311963SAfshin.Ardakani@Sun.COM 		/* Add a target to an existing link */
53411963SAfshin.Ardakani@Sun.COM 
53511963SAfshin.Ardakani@Sun.COM 		if (flags & DFS_ADD_VOLUME)
53611963SAfshin.Ardakani@Sun.COM 			return (ERROR_FILE_EXISTS);
53711963SAfshin.Ardakani@Sun.COM 
53811963SAfshin.Ardakani@Sun.COM 		status = dfs_link_getinfo(path, &info, DFS_INFO_ALL);
53911963SAfshin.Ardakani@Sun.COM 		if (status != ERROR_SUCCESS)
54011963SAfshin.Ardakani@Sun.COM 			return (status);
54111963SAfshin.Ardakani@Sun.COM 
54211963SAfshin.Ardakani@Sun.COM 		break;
54311963SAfshin.Ardakani@Sun.COM 
54411963SAfshin.Ardakani@Sun.COM 	case DFS_STAT_NOTLINK:
54511963SAfshin.Ardakani@Sun.COM 		/* specified path points to a non-reparse object */
54611963SAfshin.Ardakani@Sun.COM 		return (ERROR_FILE_EXISTS);
54711963SAfshin.Ardakani@Sun.COM 
54811963SAfshin.Ardakani@Sun.COM 	default:
54911963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
55011963SAfshin.Ardakani@Sun.COM 	}
55111963SAfshin.Ardakani@Sun.COM 
55211963SAfshin.Ardakani@Sun.COM 	/* checks to see if the target already exists */
55311963SAfshin.Ardakani@Sun.COM 	ntargets = info.i_ntargets;
55411963SAfshin.Ardakani@Sun.COM 	if (dfs_target_find(info.i_targets, ntargets, server, share) != -1) {
55511963SAfshin.Ardakani@Sun.COM 		dfs_info_free(&info);
55611963SAfshin.Ardakani@Sun.COM 		return (ERROR_FILE_EXISTS);
55711963SAfshin.Ardakani@Sun.COM 	}
55811963SAfshin.Ardakani@Sun.COM 
55911963SAfshin.Ardakani@Sun.COM 	/* add the new target */
56011963SAfshin.Ardakani@Sun.COM 	t = realloc(info.i_targets, (ntargets + 1) * sizeof (dfs_target_t));
56111963SAfshin.Ardakani@Sun.COM 	if (t == NULL) {
56211963SAfshin.Ardakani@Sun.COM 		dfs_info_free(&info);
56311963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_ENOUGH_MEMORY);
56411963SAfshin.Ardakani@Sun.COM 	}
56511963SAfshin.Ardakani@Sun.COM 
56611963SAfshin.Ardakani@Sun.COM 	info.i_targets = t;
56711963SAfshin.Ardakani@Sun.COM 	dfs_target_init(&info.i_targets[ntargets], server, share,
56811963SAfshin.Ardakani@Sun.COM 	    DFS_STORAGE_STATE_ONLINE);
56911963SAfshin.Ardakani@Sun.COM 	info.i_ntargets++;
57011963SAfshin.Ardakani@Sun.COM 
57111963SAfshin.Ardakani@Sun.COM 	status = dfs_link_commit(path, &info);
57211963SAfshin.Ardakani@Sun.COM 
57311963SAfshin.Ardakani@Sun.COM 	dfs_info_free(&info);
57411963SAfshin.Ardakani@Sun.COM 	return (status);
57511963SAfshin.Ardakani@Sun.COM }
57611963SAfshin.Ardakani@Sun.COM 
57711963SAfshin.Ardakani@Sun.COM /*
57811963SAfshin.Ardakani@Sun.COM  * Removes a link or a link target from a DFS namespace. A link can be
57911963SAfshin.Ardakani@Sun.COM  * removed regardless of the number of targets associated with it.
58011963SAfshin.Ardakani@Sun.COM  *
58111963SAfshin.Ardakani@Sun.COM  * 'server' and 'share' parameters specify a target, so if they are NULL
58211963SAfshin.Ardakani@Sun.COM  * it means the link should be removed, otherwise the specified target
58311963SAfshin.Ardakani@Sun.COM  * is removed if found.
58411963SAfshin.Ardakani@Sun.COM  */
58511963SAfshin.Ardakani@Sun.COM uint32_t
58611963SAfshin.Ardakani@Sun.COM dfs_link_remove(const char *path, const char *server, const char *share)
58711963SAfshin.Ardakani@Sun.COM {
58811963SAfshin.Ardakani@Sun.COM 	dfs_info_t info;
58911963SAfshin.Ardakani@Sun.COM 	uint32_t status, stat;
59011963SAfshin.Ardakani@Sun.COM 	int rc, idx;
59111963SAfshin.Ardakani@Sun.COM 
59211963SAfshin.Ardakani@Sun.COM 	if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS)
59311963SAfshin.Ardakani@Sun.COM 		return (status);
59411963SAfshin.Ardakani@Sun.COM 
59511963SAfshin.Ardakani@Sun.COM 	if (stat != DFS_STAT_ISDFS)
59611963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_FOUND);
59711963SAfshin.Ardakani@Sun.COM 
59811963SAfshin.Ardakani@Sun.COM 	if (server == NULL && share == NULL) {
59911963SAfshin.Ardakani@Sun.COM 		/* remove the link */
60011963SAfshin.Ardakani@Sun.COM 		if (smb_reparse_svcdel(path, DFS_REPARSE_SVCTYPE) != 0)
60111963SAfshin.Ardakani@Sun.COM 			return (ERROR_INTERNAL_ERROR);
60211963SAfshin.Ardakani@Sun.COM 
60311963SAfshin.Ardakani@Sun.COM 		return (ERROR_SUCCESS);
60411963SAfshin.Ardakani@Sun.COM 	}
60511963SAfshin.Ardakani@Sun.COM 
60611963SAfshin.Ardakani@Sun.COM 	/* remove the specified target in the link */
60711963SAfshin.Ardakani@Sun.COM 
60811963SAfshin.Ardakani@Sun.COM 	status = dfs_link_getinfo(path, &info, DFS_INFO_ALL);
60911963SAfshin.Ardakani@Sun.COM 	if (status != ERROR_SUCCESS)
61011963SAfshin.Ardakani@Sun.COM 		return (status);
61111963SAfshin.Ardakani@Sun.COM 
61211963SAfshin.Ardakani@Sun.COM 	/* checks to see if the target exists */
61311963SAfshin.Ardakani@Sun.COM 	idx = dfs_target_find(info.i_targets, info.i_ntargets, server, share);
61411963SAfshin.Ardakani@Sun.COM 	if (idx != -1) {
61511963SAfshin.Ardakani@Sun.COM 		bcopy(&info.i_targets[idx + 1], &info.i_targets[idx],
61611963SAfshin.Ardakani@Sun.COM 		    (info.i_ntargets - idx - 1) * sizeof (dfs_target_t));
61711963SAfshin.Ardakani@Sun.COM 		info.i_ntargets--;
61811963SAfshin.Ardakani@Sun.COM 	} else {
61911963SAfshin.Ardakani@Sun.COM 		dfs_info_free(&info);
62011963SAfshin.Ardakani@Sun.COM 		return (ERROR_FILE_NOT_FOUND);
62111963SAfshin.Ardakani@Sun.COM 	}
62211963SAfshin.Ardakani@Sun.COM 
62311963SAfshin.Ardakani@Sun.COM 	if (info.i_ntargets == 0) {
62411963SAfshin.Ardakani@Sun.COM 		/* if last target, then remove the link */
62511963SAfshin.Ardakani@Sun.COM 		rc = smb_reparse_svcdel(path, DFS_REPARSE_SVCTYPE);
62611963SAfshin.Ardakani@Sun.COM 		status = (rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
62711963SAfshin.Ardakani@Sun.COM 	} else {
62811963SAfshin.Ardakani@Sun.COM 		status = dfs_link_commit(path, &info);
62911963SAfshin.Ardakani@Sun.COM 	}
63011963SAfshin.Ardakani@Sun.COM 
63111963SAfshin.Ardakani@Sun.COM 	dfs_info_free(&info);
63211963SAfshin.Ardakani@Sun.COM 	return (status);
63311963SAfshin.Ardakani@Sun.COM }
63411963SAfshin.Ardakani@Sun.COM 
63511963SAfshin.Ardakani@Sun.COM /*
63611963SAfshin.Ardakani@Sun.COM  * Sets the provided information for the specified link or link target.
63711963SAfshin.Ardakani@Sun.COM  * Link is specified by 'path' and the target is specified by
63811963SAfshin.Ardakani@Sun.COM  * (t_server, t_share) pair. Only information items needed for given
63911963SAfshin.Ardakani@Sun.COM  * information level (infolvl) is valid in the passed DFS info structure
64011963SAfshin.Ardakani@Sun.COM  * 'info'.
64111963SAfshin.Ardakani@Sun.COM  */
64211963SAfshin.Ardakani@Sun.COM uint32_t
64311963SAfshin.Ardakani@Sun.COM dfs_link_setinfo(const char *path, dfs_info_t *info, uint32_t infolvl)
64411963SAfshin.Ardakani@Sun.COM {
64511963SAfshin.Ardakani@Sun.COM 	dfs_info_t curinfo;
64611963SAfshin.Ardakani@Sun.COM 	uint32_t status;
64711963SAfshin.Ardakani@Sun.COM 
64811963SAfshin.Ardakani@Sun.COM 	status = dfs_link_getinfo(path, &curinfo, DFS_INFO_ALL);
64911963SAfshin.Ardakani@Sun.COM 	if (status != ERROR_SUCCESS)
65011963SAfshin.Ardakani@Sun.COM 		return (status);
65111963SAfshin.Ardakani@Sun.COM 
65211963SAfshin.Ardakani@Sun.COM 	status = dfs_modinfo(DFS_OBJECT_LINK, &curinfo, info, infolvl);
65311963SAfshin.Ardakani@Sun.COM 	if (status == ERROR_SUCCESS)
65411963SAfshin.Ardakani@Sun.COM 		status = dfs_link_commit(path, &curinfo);
65511963SAfshin.Ardakani@Sun.COM 
65611963SAfshin.Ardakani@Sun.COM 	dfs_info_free(&curinfo);
65711963SAfshin.Ardakani@Sun.COM 	return (status);
65811963SAfshin.Ardakani@Sun.COM }
65911963SAfshin.Ardakani@Sun.COM 
66011963SAfshin.Ardakani@Sun.COM /*
66111963SAfshin.Ardakani@Sun.COM  * Gets the DFS link info.
66211963SAfshin.Ardakani@Sun.COM  *
66311963SAfshin.Ardakani@Sun.COM  * If path is NULL, it just does some initialization.
66411963SAfshin.Ardakani@Sun.COM  *
66511963SAfshin.Ardakani@Sun.COM  * Info level (1) only needs the UNC path which is not
66611963SAfshin.Ardakani@Sun.COM  * stored, it is constructed so the function will return
66711963SAfshin.Ardakani@Sun.COM  * without accessing the backend storage.
66811963SAfshin.Ardakani@Sun.COM  */
66911963SAfshin.Ardakani@Sun.COM uint32_t
67011963SAfshin.Ardakani@Sun.COM dfs_link_getinfo(const char *path, dfs_info_t *info, uint32_t infolvl)
67111963SAfshin.Ardakani@Sun.COM {
67211963SAfshin.Ardakani@Sun.COM 	char *link_data;
67311963SAfshin.Ardakani@Sun.COM 	uint32_t status;
67411963SAfshin.Ardakani@Sun.COM 	uuid_t uuid;
67511963SAfshin.Ardakani@Sun.COM 	int rc;
67611963SAfshin.Ardakani@Sun.COM 
67711963SAfshin.Ardakani@Sun.COM 	bzero(info, sizeof (dfs_info_t));
67811963SAfshin.Ardakani@Sun.COM 	info->i_type = DFS_OBJECT_LINK;
67911963SAfshin.Ardakani@Sun.COM 
68011963SAfshin.Ardakani@Sun.COM 	if (path == NULL) {
68111963SAfshin.Ardakani@Sun.COM 		info->i_state = DFS_VOLUME_STATE_OK;
68211963SAfshin.Ardakani@Sun.COM 		info->i_timeout = DFS_LINK_TIMEOUT;
68311963SAfshin.Ardakani@Sun.COM 		info->i_propflags = 0;
68411963SAfshin.Ardakani@Sun.COM 		uuid_generate_random(uuid);
68511963SAfshin.Ardakani@Sun.COM 		uuid_unparse(uuid, info->i_guid);
68611963SAfshin.Ardakani@Sun.COM 		return (ERROR_SUCCESS);
68711963SAfshin.Ardakani@Sun.COM 	}
68811963SAfshin.Ardakani@Sun.COM 
68911963SAfshin.Ardakani@Sun.COM 	if (infolvl == 1)
69011963SAfshin.Ardakani@Sun.COM 		return (ERROR_SUCCESS);
69111963SAfshin.Ardakani@Sun.COM 
69211963SAfshin.Ardakani@Sun.COM 	rc = smb_reparse_svcget(path, DFS_REPARSE_SVCTYPE, &link_data);
69311963SAfshin.Ardakani@Sun.COM 	if (rc != 0)
69411963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
69511963SAfshin.Ardakani@Sun.COM 
69611963SAfshin.Ardakani@Sun.COM 	status = dfs_link_decode(info, link_data, infolvl);
69711963SAfshin.Ardakani@Sun.COM 	free(link_data);
69811963SAfshin.Ardakani@Sun.COM 
69911963SAfshin.Ardakani@Sun.COM 	return (status);
70011963SAfshin.Ardakani@Sun.COM }
70111963SAfshin.Ardakani@Sun.COM 
70211963SAfshin.Ardakani@Sun.COM /*
70311963SAfshin.Ardakani@Sun.COM  * ===================
70411963SAfshin.Ardakani@Sun.COM  * Cache API (public)
70511963SAfshin.Ardakani@Sun.COM  * ===================
70611963SAfshin.Ardakani@Sun.COM  */
70711963SAfshin.Ardakani@Sun.COM 
70811963SAfshin.Ardakani@Sun.COM /*
70911963SAfshin.Ardakani@Sun.COM  * Adds an entry with given DFS name (root sharename) and relative path
71011963SAfshin.Ardakani@Sun.COM  * to the share (relpath) and the specified entry type (i.e. root/link)
71111963SAfshin.Ardakani@Sun.COM  * to the namespace cache.
71211963SAfshin.Ardakani@Sun.COM  */
71311963SAfshin.Ardakani@Sun.COM uint32_t
71411963SAfshin.Ardakani@Sun.COM dfs_cache_add_byname(const char *name, const char *relpath, uint32_t type)
71511963SAfshin.Ardakani@Sun.COM {
71611963SAfshin.Ardakani@Sun.COM 	char uncpath[DFS_PATH_MAX];
71711963SAfshin.Ardakani@Sun.COM 	char fspath[DFS_PATH_MAX];
71811963SAfshin.Ardakani@Sun.COM 	smb_share_t si;
71911963SAfshin.Ardakani@Sun.COM 
72011963SAfshin.Ardakani@Sun.COM 	if (smb_shr_get((char *)name, &si) != NERR_Success)
72111963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_FOUND);
72211963SAfshin.Ardakani@Sun.COM 
72311963SAfshin.Ardakani@Sun.COM 	if (type == DFS_OBJECT_ROOT) {
72411963SAfshin.Ardakani@Sun.COM 		(void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s",
72511963SAfshin.Ardakani@Sun.COM 		    dfs_nbname, name);
72611963SAfshin.Ardakani@Sun.COM 		return (dfs_cache_add_byunc(uncpath, si.shr_path, type));
72711963SAfshin.Ardakani@Sun.COM 	}
72811963SAfshin.Ardakani@Sun.COM 
72911963SAfshin.Ardakani@Sun.COM 	/* add link entry */
73011963SAfshin.Ardakani@Sun.COM 	(void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", si.shr_path, relpath);
73111963SAfshin.Ardakani@Sun.COM 	(void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s\\%s", dfs_nbname,
73211963SAfshin.Ardakani@Sun.COM 	    name, relpath);
73311963SAfshin.Ardakani@Sun.COM 
73411963SAfshin.Ardakani@Sun.COM 	/* relpath may contain '/' */
73511963SAfshin.Ardakani@Sun.COM 	(void) strsubst(uncpath, '/', '\\');
73611963SAfshin.Ardakani@Sun.COM 
73711963SAfshin.Ardakani@Sun.COM 	return (dfs_cache_add_byunc(uncpath, fspath, type));
73811963SAfshin.Ardakani@Sun.COM }
73911963SAfshin.Ardakani@Sun.COM 
74011963SAfshin.Ardakani@Sun.COM /*
74111963SAfshin.Ardakani@Sun.COM  * Removes the namespace cache entry for the given link
74211963SAfshin.Ardakani@Sun.COM  * in the namespace ('name') with 'relpath'
74311963SAfshin.Ardakani@Sun.COM  */
74411963SAfshin.Ardakani@Sun.COM void
74511963SAfshin.Ardakani@Sun.COM dfs_cache_remove(const char *name, const char *relpath)
74611963SAfshin.Ardakani@Sun.COM {
74711963SAfshin.Ardakani@Sun.COM 	dfs_nscnode_t dn;
74811963SAfshin.Ardakani@Sun.COM 
74911963SAfshin.Ardakani@Sun.COM 	(void) snprintf(dn.nsc_uncpath, sizeof (dn.nsc_uncpath),
75011963SAfshin.Ardakani@Sun.COM 	    "\\\\%s\\%s\\%s", dfs_nbname, name, relpath);
75111963SAfshin.Ardakani@Sun.COM 
75211963SAfshin.Ardakani@Sun.COM 	/* relpath may contain '/' */
75311963SAfshin.Ardakani@Sun.COM 	(void) strsubst(dn.nsc_uncpath, '/', '\\');
75411963SAfshin.Ardakani@Sun.COM 
75511963SAfshin.Ardakani@Sun.COM 	smb_cache_remove(&dfs_nscache, &dn);
75611963SAfshin.Ardakani@Sun.COM }
75711963SAfshin.Ardakani@Sun.COM 
75811963SAfshin.Ardakani@Sun.COM /*
75911963SAfshin.Ardakani@Sun.COM  * Get the DFS data for the specified cache entry
76011963SAfshin.Ardakani@Sun.COM  */
76111963SAfshin.Ardakani@Sun.COM uint32_t
76211963SAfshin.Ardakani@Sun.COM dfs_cache_getinfo(dfs_nscnode_t *dn, dfs_info_t *info, uint32_t infolvl)
76311963SAfshin.Ardakani@Sun.COM {
76411963SAfshin.Ardakani@Sun.COM 	uint32_t status;
76511963SAfshin.Ardakani@Sun.COM 
76611963SAfshin.Ardakani@Sun.COM 	if (dn->nsc_type == DFS_OBJECT_LINK)
76711963SAfshin.Ardakani@Sun.COM 		status = dfs_link_getinfo(dn->nsc_fspath, info, infolvl);
76811963SAfshin.Ardakani@Sun.COM 	else
76911963SAfshin.Ardakani@Sun.COM 		status = dfs_root_getinfo(dn->nsc_fspath, info, infolvl);
77011963SAfshin.Ardakani@Sun.COM 
77111963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(info->i_uncpath, dn->nsc_uncpath,
77211963SAfshin.Ardakani@Sun.COM 	    sizeof (info->i_uncpath));
77311963SAfshin.Ardakani@Sun.COM 
77411963SAfshin.Ardakani@Sun.COM 	if (status == ERROR_SUCCESS)
77511963SAfshin.Ardakani@Sun.COM 		dfs_info_trace("dfs_cache_getinfo", info);
77611963SAfshin.Ardakani@Sun.COM 
77711963SAfshin.Ardakani@Sun.COM 	return (status);
77811963SAfshin.Ardakani@Sun.COM }
77911963SAfshin.Ardakani@Sun.COM 
78011963SAfshin.Ardakani@Sun.COM /*
78111963SAfshin.Ardakani@Sun.COM  * Returns the number of cache entries i.e. the number of
78211963SAfshin.Ardakani@Sun.COM  * root(s) and link(s)
78311963SAfshin.Ardakani@Sun.COM  */
78411963SAfshin.Ardakani@Sun.COM uint32_t
78511963SAfshin.Ardakani@Sun.COM dfs_cache_num(void)
78611963SAfshin.Ardakani@Sun.COM {
78711963SAfshin.Ardakani@Sun.COM 	return (smb_cache_num(&dfs_nscache));
78811963SAfshin.Ardakani@Sun.COM }
78911963SAfshin.Ardakani@Sun.COM 
79011963SAfshin.Ardakani@Sun.COM void
79111963SAfshin.Ardakani@Sun.COM dfs_cache_iterinit(smb_cache_cursor_t *cursor)
79211963SAfshin.Ardakani@Sun.COM {
79311963SAfshin.Ardakani@Sun.COM 	smb_cache_iterinit(&dfs_nscache, cursor);
79411963SAfshin.Ardakani@Sun.COM }
79511963SAfshin.Ardakani@Sun.COM 
79611963SAfshin.Ardakani@Sun.COM boolean_t
79711963SAfshin.Ardakani@Sun.COM dfs_cache_iterate(smb_cache_cursor_t *cursor, dfs_nscnode_t *dn)
79811963SAfshin.Ardakani@Sun.COM {
79911963SAfshin.Ardakani@Sun.COM 	return (smb_cache_iterate(&dfs_nscache, cursor, dn));
80011963SAfshin.Ardakani@Sun.COM }
80111963SAfshin.Ardakani@Sun.COM 
80211963SAfshin.Ardakani@Sun.COM /*
80311963SAfshin.Ardakani@Sun.COM  * ==================
80411963SAfshin.Ardakani@Sun.COM  * Misc API (public)
80511963SAfshin.Ardakani@Sun.COM  * ==================
80611963SAfshin.Ardakani@Sun.COM  */
80711963SAfshin.Ardakani@Sun.COM 
80811963SAfshin.Ardakani@Sun.COM /*
80911963SAfshin.Ardakani@Sun.COM  * This is the function that is called by smbd door server to
81011963SAfshin.Ardakani@Sun.COM  * fullfil a GetReferrals request from smbsrv kernel module
81111963SAfshin.Ardakani@Sun.COM  *
81211963SAfshin.Ardakani@Sun.COM  * 'reftype' specifies the requested referral type. If it is
81311963SAfshin.Ardakani@Sun.COM  * DFS_REFERRAL_ROOT then dfs_path should point to a namespace
81411963SAfshin.Ardakani@Sun.COM  * root. If it is DFS_REFERRAL_LINK then dfs_path should CONTAIN
81511963SAfshin.Ardakani@Sun.COM  * a link, in which case this function will find the link and
81611963SAfshin.Ardakani@Sun.COM  * returns its target information.
81711963SAfshin.Ardakani@Sun.COM  */
81811963SAfshin.Ardakani@Sun.COM uint32_t
81911963SAfshin.Ardakani@Sun.COM dfs_get_referrals(const char *dfs_path, dfs_reftype_t reftype,
82011963SAfshin.Ardakani@Sun.COM     dfs_info_t *referrals)
82111963SAfshin.Ardakani@Sun.COM {
82211963SAfshin.Ardakani@Sun.COM 	dfs_path_t path;
82311963SAfshin.Ardakani@Sun.COM 	smb_unc_t *unc;
82411963SAfshin.Ardakani@Sun.COM 	char linkpath[DFS_PATH_MAX];
82511963SAfshin.Ardakani@Sun.COM 	uint32_t status;
82611963SAfshin.Ardakani@Sun.COM 
82711963SAfshin.Ardakani@Sun.COM 	status = dfs_path_parse(&path, dfs_path, DFS_OBJECT_ANY);
82811963SAfshin.Ardakani@Sun.COM 	if (status != ERROR_SUCCESS)
82911963SAfshin.Ardakani@Sun.COM 		return (status);
83011963SAfshin.Ardakani@Sun.COM 
83111963SAfshin.Ardakani@Sun.COM 	dfs_setpriv(PRIV_ON);
83211963SAfshin.Ardakani@Sun.COM 
83311963SAfshin.Ardakani@Sun.COM 	referrals->i_type = path.p_type;
83411963SAfshin.Ardakani@Sun.COM 
83511963SAfshin.Ardakani@Sun.COM 	switch (reftype) {
83611963SAfshin.Ardakani@Sun.COM 	case DFS_REFERRAL_ROOT:
83711963SAfshin.Ardakani@Sun.COM 		if (path.p_type != DFS_OBJECT_ROOT) {
83811963SAfshin.Ardakani@Sun.COM 			status = ERROR_INVALID_PARAMETER;
83911963SAfshin.Ardakani@Sun.COM 			break;
84011963SAfshin.Ardakani@Sun.COM 		}
84111963SAfshin.Ardakani@Sun.COM 
84211963SAfshin.Ardakani@Sun.COM 		status = dfs_root_getinfo((const char *)path.p_fspath,
84311963SAfshin.Ardakani@Sun.COM 		    referrals, DFS_INFO_ALL);
84411963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(referrals->i_uncpath, dfs_path, DFS_PATH_MAX);
84511963SAfshin.Ardakani@Sun.COM 		break;
84611963SAfshin.Ardakani@Sun.COM 
84711963SAfshin.Ardakani@Sun.COM 	case DFS_REFERRAL_LINK:
84811963SAfshin.Ardakani@Sun.COM 		if (path.p_type != DFS_OBJECT_LINK) {
84911963SAfshin.Ardakani@Sun.COM 			status = ERROR_INVALID_PARAMETER;
85011963SAfshin.Ardakani@Sun.COM 			break;
85111963SAfshin.Ardakani@Sun.COM 		}
85211963SAfshin.Ardakani@Sun.COM 
85311963SAfshin.Ardakani@Sun.COM 		unc = &path.p_unc;
85411963SAfshin.Ardakani@Sun.COM 		if (!dfs_namespace_findlink(unc->unc_share, unc->unc_path,
85511963SAfshin.Ardakani@Sun.COM 		    linkpath, DFS_PATH_MAX)) {
85611963SAfshin.Ardakani@Sun.COM 			status = ERROR_NOT_FOUND;
85711963SAfshin.Ardakani@Sun.COM 			break;
85811963SAfshin.Ardakani@Sun.COM 		}
85911963SAfshin.Ardakani@Sun.COM 
86011963SAfshin.Ardakani@Sun.COM 		status = dfs_link_getinfo(linkpath, referrals, DFS_INFO_ALL);
86111963SAfshin.Ardakani@Sun.COM 		(void) snprintf(referrals->i_uncpath, DFS_PATH_MAX, "/%s/%s/%s",
86211963SAfshin.Ardakani@Sun.COM 		    unc->unc_server, unc->unc_share, unc->unc_path);
86311963SAfshin.Ardakani@Sun.COM 		break;
86411963SAfshin.Ardakani@Sun.COM 
86511963SAfshin.Ardakani@Sun.COM 	default:
86611963SAfshin.Ardakani@Sun.COM 		status = ERROR_INVALID_PARAMETER;
86711963SAfshin.Ardakani@Sun.COM 		break;
86811963SAfshin.Ardakani@Sun.COM 	}
86911963SAfshin.Ardakani@Sun.COM 
87011963SAfshin.Ardakani@Sun.COM 	dfs_setpriv(PRIV_OFF);
87111963SAfshin.Ardakani@Sun.COM 	dfs_path_free(&path);
87211963SAfshin.Ardakani@Sun.COM 	return (status);
87311963SAfshin.Ardakani@Sun.COM }
87411963SAfshin.Ardakani@Sun.COM 
87511963SAfshin.Ardakani@Sun.COM /*
87611963SAfshin.Ardakani@Sun.COM  * Takes a DFS path in UNC format (dfs_path) and parse it into a dfs_path_t
87711963SAfshin.Ardakani@Sun.COM  * structure.
87811963SAfshin.Ardakani@Sun.COM  *
87911963SAfshin.Ardakani@Sun.COM  * dfs_path_free() MUST be called to free the allocated memory in this
88011963SAfshin.Ardakani@Sun.COM  * function.
88111963SAfshin.Ardakani@Sun.COM  *
88211963SAfshin.Ardakani@Sun.COM  * Returns:
88311963SAfshin.Ardakani@Sun.COM  *
88411963SAfshin.Ardakani@Sun.COM  * ERROR_INVALID_PARAMETER	path is not a valid UNC or not valid for the
88511963SAfshin.Ardakani@Sun.COM  * 				specified object type
88611963SAfshin.Ardakani@Sun.COM  * ERROR_NOT_ENOUGH_MEMORY	not enough memory to peform the parse
88711963SAfshin.Ardakani@Sun.COM  * ERROR_NOT_FOUND		namespace specified does not exist
88811963SAfshin.Ardakani@Sun.COM  */
88911963SAfshin.Ardakani@Sun.COM uint32_t
89011963SAfshin.Ardakani@Sun.COM dfs_path_parse(dfs_path_t *path, const char *dfs_path, uint32_t path_type)
89111963SAfshin.Ardakani@Sun.COM {
89211963SAfshin.Ardakani@Sun.COM 	char rootdir[DFS_PATH_MAX];
89311963SAfshin.Ardakani@Sun.COM 	smb_unc_t *unc;
89411963SAfshin.Ardakani@Sun.COM 	uint32_t status = ERROR_SUCCESS;
89511963SAfshin.Ardakani@Sun.COM 	int rc;
89611963SAfshin.Ardakani@Sun.COM 
89711963SAfshin.Ardakani@Sun.COM 	bzero(path, sizeof (dfs_path_t));
89811963SAfshin.Ardakani@Sun.COM 	unc = &path->p_unc;
89911963SAfshin.Ardakani@Sun.COM 
90011963SAfshin.Ardakani@Sun.COM 	rc = smb_unc_init(dfs_path, unc);
90111963SAfshin.Ardakani@Sun.COM 	switch (rc) {
90211963SAfshin.Ardakani@Sun.COM 	case EINVAL:
90311963SAfshin.Ardakani@Sun.COM 		return (ERROR_INVALID_PARAMETER);
90411963SAfshin.Ardakani@Sun.COM 	case ENOMEM:
90511963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_ENOUGH_MEMORY);
90611963SAfshin.Ardakani@Sun.COM 	default:
90711963SAfshin.Ardakani@Sun.COM 		break;
90811963SAfshin.Ardakani@Sun.COM 	}
90911963SAfshin.Ardakani@Sun.COM 
91011963SAfshin.Ardakani@Sun.COM 	if (dfs_namespace_path(unc->unc_share, rootdir, DFS_PATH_MAX)
91111963SAfshin.Ardakani@Sun.COM 	    != ERROR_SUCCESS) {
91211963SAfshin.Ardakani@Sun.COM 		smb_unc_free(unc);
91311963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_FOUND);
91411963SAfshin.Ardakani@Sun.COM 	}
91511963SAfshin.Ardakani@Sun.COM 
91611963SAfshin.Ardakani@Sun.COM 	if (path_type == DFS_OBJECT_ANY)
91711963SAfshin.Ardakani@Sun.COM 		path->p_type = (unc->unc_path != NULL)
91811963SAfshin.Ardakani@Sun.COM 		    ? DFS_OBJECT_LINK : DFS_OBJECT_ROOT;
91911963SAfshin.Ardakani@Sun.COM 	else
92011963SAfshin.Ardakani@Sun.COM 		path->p_type = path_type;
92111963SAfshin.Ardakani@Sun.COM 
92211963SAfshin.Ardakani@Sun.COM 	switch (path->p_type) {
92311963SAfshin.Ardakani@Sun.COM 	case DFS_OBJECT_LINK:
92411963SAfshin.Ardakani@Sun.COM 		if ((unc->unc_path == NULL) || (*unc->unc_path == '\0'))
92511963SAfshin.Ardakani@Sun.COM 			status = ERROR_NOT_FOUND;
92611963SAfshin.Ardakani@Sun.COM 		else
92711963SAfshin.Ardakani@Sun.COM 			(void) snprintf(path->p_fspath, sizeof (path->p_fspath),
92811963SAfshin.Ardakani@Sun.COM 			    "%s/%s", rootdir, unc->unc_path);
92911963SAfshin.Ardakani@Sun.COM 		break;
93011963SAfshin.Ardakani@Sun.COM 
93111963SAfshin.Ardakani@Sun.COM 	case DFS_OBJECT_ROOT:
93211963SAfshin.Ardakani@Sun.COM 		if (unc->unc_path == NULL)
93311963SAfshin.Ardakani@Sun.COM 			(void) strlcpy(path->p_fspath, rootdir,
93411963SAfshin.Ardakani@Sun.COM 			    sizeof (path->p_fspath));
93511963SAfshin.Ardakani@Sun.COM 		else
93611963SAfshin.Ardakani@Sun.COM 			status = ERROR_INVALID_PARAMETER;
93711963SAfshin.Ardakani@Sun.COM 		break;
93811963SAfshin.Ardakani@Sun.COM 
93911963SAfshin.Ardakani@Sun.COM 	default:
94011963SAfshin.Ardakani@Sun.COM 		status = ERROR_INVALID_PARAMETER;
94111963SAfshin.Ardakani@Sun.COM 	}
94211963SAfshin.Ardakani@Sun.COM 
94311963SAfshin.Ardakani@Sun.COM 	if (status != ERROR_SUCCESS)
94411963SAfshin.Ardakani@Sun.COM 		smb_unc_free(unc);
94511963SAfshin.Ardakani@Sun.COM 
94611963SAfshin.Ardakani@Sun.COM 	return (status);
94711963SAfshin.Ardakani@Sun.COM }
94811963SAfshin.Ardakani@Sun.COM 
94911963SAfshin.Ardakani@Sun.COM /*
95011963SAfshin.Ardakani@Sun.COM  * Frees the allocated memory for p_unc field of the passed path
95111963SAfshin.Ardakani@Sun.COM  */
95211963SAfshin.Ardakani@Sun.COM void
95311963SAfshin.Ardakani@Sun.COM dfs_path_free(dfs_path_t *path)
95411963SAfshin.Ardakani@Sun.COM {
95511963SAfshin.Ardakani@Sun.COM 	if (path != NULL)
95611963SAfshin.Ardakani@Sun.COM 		smb_unc_free(&path->p_unc);
95711963SAfshin.Ardakani@Sun.COM }
95811963SAfshin.Ardakani@Sun.COM 
95911963SAfshin.Ardakani@Sun.COM /*
96011963SAfshin.Ardakani@Sun.COM  * Free the allocated memory for targets in the given info
96111963SAfshin.Ardakani@Sun.COM  * structure
96211963SAfshin.Ardakani@Sun.COM  */
96311963SAfshin.Ardakani@Sun.COM void
96411963SAfshin.Ardakani@Sun.COM dfs_info_free(dfs_info_t *info)
96511963SAfshin.Ardakani@Sun.COM {
96611963SAfshin.Ardakani@Sun.COM 	if (info)
96711963SAfshin.Ardakani@Sun.COM 		free(info->i_targets);
96811963SAfshin.Ardakani@Sun.COM }
96911963SAfshin.Ardakani@Sun.COM 
97011963SAfshin.Ardakani@Sun.COM /*
97111963SAfshin.Ardakani@Sun.COM  * Trace the given DFS info structure
97211963SAfshin.Ardakani@Sun.COM  */
97311963SAfshin.Ardakani@Sun.COM void
97411963SAfshin.Ardakani@Sun.COM dfs_info_trace(const char *msg, dfs_info_t *info)
97511963SAfshin.Ardakani@Sun.COM {
97611963SAfshin.Ardakani@Sun.COM 	dfs_target_t *t;
97711963SAfshin.Ardakani@Sun.COM 	int i;
97811963SAfshin.Ardakani@Sun.COM 
97911963SAfshin.Ardakani@Sun.COM 	smb_tracef("%s", msg);
98011963SAfshin.Ardakani@Sun.COM 	if (info == NULL)
98111963SAfshin.Ardakani@Sun.COM 		return;
98211963SAfshin.Ardakani@Sun.COM 
98311963SAfshin.Ardakani@Sun.COM 	smb_tracef("UNC\t%s", info->i_uncpath);
98411963SAfshin.Ardakani@Sun.COM 	smb_tracef("comment\t%s", info->i_comment);
98511963SAfshin.Ardakani@Sun.COM 	smb_tracef("GUID\t%s", info->i_guid);
98611963SAfshin.Ardakani@Sun.COM 	smb_tracef("state\t%X", info->i_state);
98711963SAfshin.Ardakani@Sun.COM 	smb_tracef("timeout\t%d", info->i_timeout);
98811963SAfshin.Ardakani@Sun.COM 	smb_tracef("props\t%X", info->i_propflags);
98911963SAfshin.Ardakani@Sun.COM 	smb_tracef("# targets\t%X", info->i_ntargets);
99011963SAfshin.Ardakani@Sun.COM 
99111963SAfshin.Ardakani@Sun.COM 	if (info->i_targets == NULL)
99211963SAfshin.Ardakani@Sun.COM 		return;
99311963SAfshin.Ardakani@Sun.COM 
99411963SAfshin.Ardakani@Sun.COM 	for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) {
99511963SAfshin.Ardakani@Sun.COM 		smb_tracef("[%d] \\\\%s\\%s", i, t->t_server, t->t_share);
99611963SAfshin.Ardakani@Sun.COM 		smb_tracef("[%d] state\t%X", i, t->t_state);
99711963SAfshin.Ardakani@Sun.COM 		smb_tracef("[%d] priority\t%d:%d", i, t->t_priority.p_class,
99811963SAfshin.Ardakani@Sun.COM 		    t->t_priority.p_rank);
99911963SAfshin.Ardakani@Sun.COM 	}
100011963SAfshin.Ardakani@Sun.COM }
100111963SAfshin.Ardakani@Sun.COM 
100211963SAfshin.Ardakani@Sun.COM /*
100311963SAfshin.Ardakani@Sun.COM  * Search the path specified by 'relpath' to see if it contains
100411963SAfshin.Ardakani@Sun.COM  * a DFS link starting from the last component. If a link is found
100511963SAfshin.Ardakani@Sun.COM  * the full path is returned in 'linkpath'
100611963SAfshin.Ardakani@Sun.COM  */
100711963SAfshin.Ardakani@Sun.COM static boolean_t
100811963SAfshin.Ardakani@Sun.COM dfs_namespace_findlink(const char *name, char *relpath,
100911963SAfshin.Ardakani@Sun.COM     char *linkpath, size_t bufsz)
101011963SAfshin.Ardakani@Sun.COM {
101111963SAfshin.Ardakani@Sun.COM 	char rootdir[DFS_PATH_MAX];
101211963SAfshin.Ardakani@Sun.COM 	uint32_t stat;
101311963SAfshin.Ardakani@Sun.COM 	char *p;
101411963SAfshin.Ardakani@Sun.COM 
101511963SAfshin.Ardakani@Sun.COM 	if (dfs_namespace_path(name, rootdir, DFS_PATH_MAX) != ERROR_SUCCESS)
101611963SAfshin.Ardakani@Sun.COM 		return (B_FALSE);
101711963SAfshin.Ardakani@Sun.COM 
101811963SAfshin.Ardakani@Sun.COM 	(void) snprintf(linkpath, bufsz, "%s/%s", rootdir, relpath);
101911963SAfshin.Ardakani@Sun.COM 
102011963SAfshin.Ardakani@Sun.COM 	for (;;) {
102111963SAfshin.Ardakani@Sun.COM 		if (dfs_link_stat(linkpath, &stat) != ERROR_SUCCESS)
102211963SAfshin.Ardakani@Sun.COM 			return (B_FALSE);
102311963SAfshin.Ardakani@Sun.COM 
102411963SAfshin.Ardakani@Sun.COM 		if (stat == DFS_STAT_ISDFS)
102511963SAfshin.Ardakani@Sun.COM 			return (B_TRUE);
102611963SAfshin.Ardakani@Sun.COM 
102711963SAfshin.Ardakani@Sun.COM 		if ((p = strrchr(relpath, '/')) == NULL)
102811963SAfshin.Ardakani@Sun.COM 			return (B_FALSE);
102911963SAfshin.Ardakani@Sun.COM 		*p = '\0';
103011963SAfshin.Ardakani@Sun.COM 
103111963SAfshin.Ardakani@Sun.COM 		(void) snprintf(linkpath, bufsz, "%s/%s", rootdir, relpath);
103211963SAfshin.Ardakani@Sun.COM 	}
103311963SAfshin.Ardakani@Sun.COM 
103411963SAfshin.Ardakani@Sun.COM 	/*NOTREACHED*/
103511963SAfshin.Ardakani@Sun.COM 	return (B_FALSE);
103611963SAfshin.Ardakani@Sun.COM }
103711963SAfshin.Ardakani@Sun.COM 
103811963SAfshin.Ardakani@Sun.COM /*
103911963SAfshin.Ardakani@Sun.COM  * Caches the specified namespace
104011963SAfshin.Ardakani@Sun.COM  */
104111963SAfshin.Ardakani@Sun.COM static void *
104211963SAfshin.Ardakani@Sun.COM dfs_namespace_cache(void *arg)
104311963SAfshin.Ardakani@Sun.COM {
104411963SAfshin.Ardakani@Sun.COM 	char *share = arg;
104511963SAfshin.Ardakani@Sun.COM 	char uncpath[DFS_PATH_MAX];
104611963SAfshin.Ardakani@Sun.COM 	smb_share_t si;
104711963SAfshin.Ardakani@Sun.COM 
104811963SAfshin.Ardakani@Sun.COM 	if (smb_shr_get(share, &si) != NERR_Success) {
104911963SAfshin.Ardakani@Sun.COM 		free(share);
105011963SAfshin.Ardakani@Sun.COM 		return (NULL);
105111963SAfshin.Ardakani@Sun.COM 	}
105211963SAfshin.Ardakani@Sun.COM 
105311963SAfshin.Ardakani@Sun.COM 	(void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s", dfs_nbname, share);
105411963SAfshin.Ardakani@Sun.COM 	(void) dfs_cache_add_byunc(uncpath, si.shr_path, DFS_OBJECT_ROOT);
105511963SAfshin.Ardakani@Sun.COM 
105611963SAfshin.Ardakani@Sun.COM 	dfs_cache_populate(uncpath, si.shr_path);
105711963SAfshin.Ardakani@Sun.COM 
105811963SAfshin.Ardakani@Sun.COM 	free(share);
105911963SAfshin.Ardakani@Sun.COM 	return (NULL);
106011963SAfshin.Ardakani@Sun.COM }
106111963SAfshin.Ardakani@Sun.COM 
106211963SAfshin.Ardakani@Sun.COM static int
106311963SAfshin.Ardakani@Sun.COM dfs_root_add(const char *rootdir, dfs_info_t *info)
106411963SAfshin.Ardakani@Sun.COM {
106511963SAfshin.Ardakani@Sun.COM 	uint32_t status = ERROR_INTERNAL_ERROR;
106611963SAfshin.Ardakani@Sun.COM 	int xfd;
106711963SAfshin.Ardakani@Sun.COM 
106811963SAfshin.Ardakani@Sun.COM 	(void) rw_wrlock(&dfs_root_rwl);
106911963SAfshin.Ardakani@Sun.COM 	if ((xfd = dfs_root_xopen(rootdir, O_CREAT | O_TRUNC | O_RDWR)) > 0) {
107011963SAfshin.Ardakani@Sun.COM 		status = dfs_root_xwrite(xfd, info);
107111963SAfshin.Ardakani@Sun.COM 		dfs_root_xclose(xfd);
107211963SAfshin.Ardakani@Sun.COM 	}
107311963SAfshin.Ardakani@Sun.COM 	(void) rw_unlock(&dfs_root_rwl);
107411963SAfshin.Ardakani@Sun.COM 
107511963SAfshin.Ardakani@Sun.COM 	return (status);
107611963SAfshin.Ardakani@Sun.COM }
107711963SAfshin.Ardakani@Sun.COM 
107811963SAfshin.Ardakani@Sun.COM /*
107911963SAfshin.Ardakani@Sun.COM  * Deletes the specified root information
108011963SAfshin.Ardakani@Sun.COM  */
108111963SAfshin.Ardakani@Sun.COM static uint32_t
108211963SAfshin.Ardakani@Sun.COM dfs_root_remove(const char *rootdir)
108311963SAfshin.Ardakani@Sun.COM {
108411963SAfshin.Ardakani@Sun.COM 	int attrdirfd;
108511963SAfshin.Ardakani@Sun.COM 	int err = 0;
108611963SAfshin.Ardakani@Sun.COM 
108711963SAfshin.Ardakani@Sun.COM 	(void) rw_wrlock(&dfs_root_rwl);
108811963SAfshin.Ardakani@Sun.COM 
108911963SAfshin.Ardakani@Sun.COM 	if ((attrdirfd = attropen(rootdir, ".", O_RDONLY)) > 0) {
109011963SAfshin.Ardakani@Sun.COM 		if (unlinkat(attrdirfd, DFS_ROOT_XATTR, 0) == -1) {
109111963SAfshin.Ardakani@Sun.COM 			if (errno != ENOENT)
109211963SAfshin.Ardakani@Sun.COM 				err = errno;
109311963SAfshin.Ardakani@Sun.COM 		}
109411963SAfshin.Ardakani@Sun.COM 		(void) close(attrdirfd);
109511963SAfshin.Ardakani@Sun.COM 	} else {
109611963SAfshin.Ardakani@Sun.COM 		err = errno;
109711963SAfshin.Ardakani@Sun.COM 	}
109811963SAfshin.Ardakani@Sun.COM 
109911963SAfshin.Ardakani@Sun.COM 	(void) rw_unlock(&dfs_root_rwl);
110011963SAfshin.Ardakani@Sun.COM 
110111963SAfshin.Ardakani@Sun.COM 	if (err != 0) {
110211963SAfshin.Ardakani@Sun.COM 		syslog(LOG_DEBUG, "dfs: failed to remove root info %s (%d)",
110311963SAfshin.Ardakani@Sun.COM 		    rootdir, err);
110411963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
110511963SAfshin.Ardakani@Sun.COM 	}
110611963SAfshin.Ardakani@Sun.COM 
110711963SAfshin.Ardakani@Sun.COM 	return (ERROR_SUCCESS);
110811963SAfshin.Ardakani@Sun.COM }
110911963SAfshin.Ardakani@Sun.COM 
111011963SAfshin.Ardakani@Sun.COM /*
111111963SAfshin.Ardakani@Sun.COM  * Opens DFS root directory's extended attribute with the given mode.
111211963SAfshin.Ardakani@Sun.COM  */
111311963SAfshin.Ardakani@Sun.COM static int
111411963SAfshin.Ardakani@Sun.COM dfs_root_xopen(const char *rootdir, int oflag)
111511963SAfshin.Ardakani@Sun.COM {
111611963SAfshin.Ardakani@Sun.COM 	int dfd;
111711963SAfshin.Ardakani@Sun.COM 	int xfd = -1;
111811963SAfshin.Ardakani@Sun.COM 	int err = 0;
111911963SAfshin.Ardakani@Sun.COM 
112011963SAfshin.Ardakani@Sun.COM 	if ((dfd = open(rootdir, O_RDONLY)) > 0) {
112111963SAfshin.Ardakani@Sun.COM 		xfd = openat(dfd, DFS_ROOT_XATTR, oflag | O_XATTR, 0600);
112211963SAfshin.Ardakani@Sun.COM 		if (xfd == -1)
112311963SAfshin.Ardakani@Sun.COM 			err = errno;
112411963SAfshin.Ardakani@Sun.COM 		(void) close(dfd);
112511963SAfshin.Ardakani@Sun.COM 	} else {
112611963SAfshin.Ardakani@Sun.COM 		err = errno;
112711963SAfshin.Ardakani@Sun.COM 	}
112811963SAfshin.Ardakani@Sun.COM 
112911963SAfshin.Ardakani@Sun.COM 	if (err != 0) {
113011963SAfshin.Ardakani@Sun.COM 		syslog(LOG_DEBUG, "dfs: failed to open root directory %s (%d)",
113111963SAfshin.Ardakani@Sun.COM 		    rootdir, err);
113211963SAfshin.Ardakani@Sun.COM 	}
113311963SAfshin.Ardakani@Sun.COM 
113411963SAfshin.Ardakani@Sun.COM 	return (xfd);
113511963SAfshin.Ardakani@Sun.COM }
113611963SAfshin.Ardakani@Sun.COM 
113711963SAfshin.Ardakani@Sun.COM /*
113811963SAfshin.Ardakani@Sun.COM  * Closes given extended attribute file descriptor
113911963SAfshin.Ardakani@Sun.COM  */
114011963SAfshin.Ardakani@Sun.COM static void
114111963SAfshin.Ardakani@Sun.COM dfs_root_xclose(int xfd)
114211963SAfshin.Ardakani@Sun.COM {
114311963SAfshin.Ardakani@Sun.COM 	(void) close(xfd);
114411963SAfshin.Ardakani@Sun.COM }
114511963SAfshin.Ardakani@Sun.COM 
114611963SAfshin.Ardakani@Sun.COM /*
114711963SAfshin.Ardakani@Sun.COM  * Writes the given DFS data in the DFS root directory's
114811963SAfshin.Ardakani@Sun.COM  * extended attribute specified with xfd file descriptor.
114911963SAfshin.Ardakani@Sun.COM  */
115011963SAfshin.Ardakani@Sun.COM static uint32_t
115111963SAfshin.Ardakani@Sun.COM dfs_root_xwrite(int xfd, dfs_info_t *info)
115211963SAfshin.Ardakani@Sun.COM {
115311963SAfshin.Ardakani@Sun.COM 	size_t nbytes;
115411963SAfshin.Ardakani@Sun.COM 	char *buf = NULL;
115511963SAfshin.Ardakani@Sun.COM 	size_t buflen;
115611963SAfshin.Ardakani@Sun.COM 	uint32_t status;
115711963SAfshin.Ardakani@Sun.COM 
115811963SAfshin.Ardakani@Sun.COM 	if ((status = dfs_root_encode(info, &buf, &buflen)) != ERROR_SUCCESS)
115911963SAfshin.Ardakani@Sun.COM 		return (status);
116011963SAfshin.Ardakani@Sun.COM 
116111963SAfshin.Ardakani@Sun.COM 	(void) lseek(xfd, 0, SEEK_SET);
116211963SAfshin.Ardakani@Sun.COM 	nbytes = write(xfd, buf, buflen);
116311963SAfshin.Ardakani@Sun.COM 	free(buf);
116411963SAfshin.Ardakani@Sun.COM 
116511963SAfshin.Ardakani@Sun.COM 	return ((nbytes == buflen) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR);
116611963SAfshin.Ardakani@Sun.COM }
116711963SAfshin.Ardakani@Sun.COM 
116811963SAfshin.Ardakani@Sun.COM /*
116911963SAfshin.Ardakani@Sun.COM  * Reads DFS root information from its directory extended attribute
117011963SAfshin.Ardakani@Sun.COM  * and parse it into given dfs_info_t structure
117111963SAfshin.Ardakani@Sun.COM  */
117211963SAfshin.Ardakani@Sun.COM static uint32_t
117311963SAfshin.Ardakani@Sun.COM dfs_root_xread(int xfd, dfs_info_t *info, uint32_t infolvl)
117411963SAfshin.Ardakani@Sun.COM {
117511963SAfshin.Ardakani@Sun.COM 	struct stat statbuf;
117611963SAfshin.Ardakani@Sun.COM 	uint32_t status;
117711963SAfshin.Ardakani@Sun.COM 	char *buf;
117811963SAfshin.Ardakani@Sun.COM 
117911963SAfshin.Ardakani@Sun.COM 	if (fstat(xfd, &statbuf) != 0)
118011963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
118111963SAfshin.Ardakani@Sun.COM 
118211963SAfshin.Ardakani@Sun.COM 	if ((buf = malloc(statbuf.st_size)) == NULL)
118311963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_ENOUGH_MEMORY);
118411963SAfshin.Ardakani@Sun.COM 
118511963SAfshin.Ardakani@Sun.COM 	if (read(xfd, buf, statbuf.st_size) == statbuf.st_size)
118611963SAfshin.Ardakani@Sun.COM 		status = dfs_root_decode(info, buf, statbuf.st_size, infolvl);
118711963SAfshin.Ardakani@Sun.COM 	else
118811963SAfshin.Ardakani@Sun.COM 		status = ERROR_INTERNAL_ERROR;
118911963SAfshin.Ardakani@Sun.COM 
119011963SAfshin.Ardakani@Sun.COM 	free(buf);
119111963SAfshin.Ardakani@Sun.COM 	return (status);
119211963SAfshin.Ardakani@Sun.COM }
119311963SAfshin.Ardakani@Sun.COM 
119411963SAfshin.Ardakani@Sun.COM /*
119511963SAfshin.Ardakani@Sun.COM  * Encodes (packs) DFS information in 'info' into a flat
119611963SAfshin.Ardakani@Sun.COM  * buffer in a name-value format. This function allocates a
119711963SAfshin.Ardakani@Sun.COM  * buffer with appropriate size to contain all the information
119811963SAfshin.Ardakani@Sun.COM  * so the caller MUST free the allocated memory by calling free().
119911963SAfshin.Ardakani@Sun.COM  */
120011963SAfshin.Ardakani@Sun.COM static uint32_t
120111963SAfshin.Ardakani@Sun.COM dfs_root_encode(dfs_info_t *info, char **buf, size_t *bufsz)
120211963SAfshin.Ardakani@Sun.COM {
120311963SAfshin.Ardakani@Sun.COM 	dfs_target_t *t;
120411963SAfshin.Ardakani@Sun.COM 	nvlist_t *nvl;
120511963SAfshin.Ardakani@Sun.COM 	int rc;
120611963SAfshin.Ardakani@Sun.COM 
120711963SAfshin.Ardakani@Sun.COM 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
120811963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_ENOUGH_MEMORY);
120911963SAfshin.Ardakani@Sun.COM 
121011963SAfshin.Ardakani@Sun.COM 	rc = nvlist_add_string(nvl, "comment", info->i_comment);
121111963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_add_string(nvl, "guid", info->i_guid);
121211963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_add_uint32(nvl, "state", info->i_state);
121311963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_add_uint32(nvl, "timeout", info->i_timeout);
121411963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_add_uint32(nvl, "propflags", info->i_propflags);
121511963SAfshin.Ardakani@Sun.COM 	t = info->i_targets;
121611963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_add_string(nvl, "t_server", t->t_server);
121711963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_add_string(nvl, "t_share", t->t_share);
121811963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_add_uint32(nvl, "t_state", t->t_state);
121911963SAfshin.Ardakani@Sun.COM 
122011963SAfshin.Ardakani@Sun.COM 	if (rc == 0)
122111963SAfshin.Ardakani@Sun.COM 		rc = nvlist_pack(nvl, buf, bufsz, NV_ENCODE_NATIVE, 0);
122211963SAfshin.Ardakani@Sun.COM 
122311963SAfshin.Ardakani@Sun.COM 	nvlist_free(nvl);
122411963SAfshin.Ardakani@Sun.COM 
122511963SAfshin.Ardakani@Sun.COM 	return ((rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR);
122611963SAfshin.Ardakani@Sun.COM }
122711963SAfshin.Ardakani@Sun.COM 
122811963SAfshin.Ardakani@Sun.COM /*
122911963SAfshin.Ardakani@Sun.COM  * Decodes (unpack) provided buffer which contains a list of name-value
123011963SAfshin.Ardakani@Sun.COM  * pairs into given dfs_info_t structure
123111963SAfshin.Ardakani@Sun.COM  */
123211963SAfshin.Ardakani@Sun.COM static uint32_t
123311963SAfshin.Ardakani@Sun.COM dfs_root_decode(dfs_info_t *info, char *buf, size_t bufsz, uint32_t infolvl)
123411963SAfshin.Ardakani@Sun.COM {
123511963SAfshin.Ardakani@Sun.COM 	nvlist_t *nvl;
123611963SAfshin.Ardakani@Sun.COM 	char *cmnt, *guid;
123711963SAfshin.Ardakani@Sun.COM 	char *t_server, *t_share;
123811963SAfshin.Ardakani@Sun.COM 	uint32_t t_state;
123911963SAfshin.Ardakani@Sun.COM 	int rc;
124011963SAfshin.Ardakani@Sun.COM 
124111963SAfshin.Ardakani@Sun.COM 	if (nvlist_unpack(buf, bufsz, &nvl, 0) != 0)
124211963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
124311963SAfshin.Ardakani@Sun.COM 
124411963SAfshin.Ardakani@Sun.COM 	rc = nvlist_lookup_string(nvl, "comment", &cmnt);
124511963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_lookup_string(nvl, "guid", &guid);
124611963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_lookup_uint32(nvl, "state", &info->i_state);
124711963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_lookup_uint32(nvl, "timeout", &info->i_timeout);
124811963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_lookup_uint32(nvl, "propflags", &info->i_propflags);
124911963SAfshin.Ardakani@Sun.COM 
125011963SAfshin.Ardakani@Sun.COM 	if (rc != 0) {
125111963SAfshin.Ardakani@Sun.COM 		nvlist_free(nvl);
125211963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
125311963SAfshin.Ardakani@Sun.COM 	}
125411963SAfshin.Ardakani@Sun.COM 
125511963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(info->i_comment, (cmnt) ? cmnt : "",
125611963SAfshin.Ardakani@Sun.COM 	    sizeof (info->i_comment));
125711963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(info->i_guid, (guid) ? guid : "", sizeof (info->i_guid));
125811963SAfshin.Ardakani@Sun.COM 
125911963SAfshin.Ardakani@Sun.COM 	info->i_targets = NULL;
126011963SAfshin.Ardakani@Sun.COM 	info->i_ntargets = 1;
126111963SAfshin.Ardakani@Sun.COM 
126211963SAfshin.Ardakani@Sun.COM 	switch (infolvl) {
126311963SAfshin.Ardakani@Sun.COM 	case DFS_INFO_ALL:
126411963SAfshin.Ardakani@Sun.COM 	case 3:
126511963SAfshin.Ardakani@Sun.COM 	case 4:
126611963SAfshin.Ardakani@Sun.COM 	case 6:
126711963SAfshin.Ardakani@Sun.COM 	case 9:
126811963SAfshin.Ardakani@Sun.COM 		/* need target information */
126911963SAfshin.Ardakani@Sun.COM 		break;
127011963SAfshin.Ardakani@Sun.COM 	default:
127111963SAfshin.Ardakani@Sun.COM 		nvlist_free(nvl);
127211963SAfshin.Ardakani@Sun.COM 		return (ERROR_SUCCESS);
127311963SAfshin.Ardakani@Sun.COM 	}
127411963SAfshin.Ardakani@Sun.COM 
127511963SAfshin.Ardakani@Sun.COM 	info->i_targets = malloc(sizeof (dfs_target_t));
127611963SAfshin.Ardakani@Sun.COM 	if (info->i_targets == NULL) {
127711963SAfshin.Ardakani@Sun.COM 		nvlist_free(nvl);
127811963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_ENOUGH_MEMORY);
127911963SAfshin.Ardakani@Sun.COM 	}
128011963SAfshin.Ardakani@Sun.COM 
128111963SAfshin.Ardakani@Sun.COM 	rc = nvlist_lookup_string(nvl, "t_server", &t_server);
128211963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_lookup_string(nvl, "t_share", &t_share);
128311963SAfshin.Ardakani@Sun.COM 	rc |= nvlist_lookup_uint32(nvl, "t_state", &t_state);
128411963SAfshin.Ardakani@Sun.COM 	if (rc != 0) {
128511963SAfshin.Ardakani@Sun.COM 		nvlist_free(nvl);
128611963SAfshin.Ardakani@Sun.COM 		free(info->i_targets);
128711963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
128811963SAfshin.Ardakani@Sun.COM 	}
128911963SAfshin.Ardakani@Sun.COM 	dfs_target_init(info->i_targets, t_server, t_share, t_state);
129011963SAfshin.Ardakani@Sun.COM 
129111963SAfshin.Ardakani@Sun.COM 	nvlist_free(nvl);
129211963SAfshin.Ardakani@Sun.COM 	return (ERROR_SUCCESS);
129311963SAfshin.Ardakani@Sun.COM }
129411963SAfshin.Ardakani@Sun.COM 
129511963SAfshin.Ardakani@Sun.COM /*
129611963SAfshin.Ardakani@Sun.COM  * Determines if the passed state is valid for a DFS root
129711963SAfshin.Ardakani@Sun.COM  *
129811963SAfshin.Ardakani@Sun.COM  * This is based on test results against Win2003 and in some cases
129911963SAfshin.Ardakani@Sun.COM  * does not match [MS-DFSNM] spec.
130011963SAfshin.Ardakani@Sun.COM  */
130111963SAfshin.Ardakani@Sun.COM static uint32_t
130211963SAfshin.Ardakani@Sun.COM dfs_root_isvalidstate(uint32_t state)
130311963SAfshin.Ardakani@Sun.COM {
130411963SAfshin.Ardakani@Sun.COM 	switch (state) {
130511963SAfshin.Ardakani@Sun.COM 	case DFS_VOLUME_STATE_OK:
130611963SAfshin.Ardakani@Sun.COM 	case DFS_VOLUME_STATE_RESYNCHRONIZE:
130711963SAfshin.Ardakani@Sun.COM 		return (ERROR_SUCCESS);
130811963SAfshin.Ardakani@Sun.COM 
130911963SAfshin.Ardakani@Sun.COM 	case DFS_VOLUME_STATE_INCONSISTENT:
131011963SAfshin.Ardakani@Sun.COM 	case DFS_VOLUME_STATE_FORCE_SYNC:
131111963SAfshin.Ardakani@Sun.COM 		return (ERROR_INVALID_PARAMETER);
131211963SAfshin.Ardakani@Sun.COM 
131311963SAfshin.Ardakani@Sun.COM 	case DFS_VOLUME_STATE_OFFLINE:
131411963SAfshin.Ardakani@Sun.COM 	case DFS_VOLUME_STATE_ONLINE:
131511963SAfshin.Ardakani@Sun.COM 	case DFS_VOLUME_STATE_STANDBY:
131611963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_SUPPORTED);
131711963SAfshin.Ardakani@Sun.COM 	default:
131811963SAfshin.Ardakani@Sun.COM 		break;
131911963SAfshin.Ardakani@Sun.COM 	}
132011963SAfshin.Ardakani@Sun.COM 
132111963SAfshin.Ardakani@Sun.COM 	return (ERROR_INVALID_PARAMETER);
132211963SAfshin.Ardakani@Sun.COM }
132311963SAfshin.Ardakani@Sun.COM 
132411963SAfshin.Ardakani@Sun.COM /*
132511963SAfshin.Ardakani@Sun.COM  * Decodes the link info from given string buffer (buf) into
132611963SAfshin.Ardakani@Sun.COM  * dfs_info_t structure.
132711963SAfshin.Ardakani@Sun.COM  */
132811963SAfshin.Ardakani@Sun.COM static uint32_t
132911963SAfshin.Ardakani@Sun.COM dfs_link_decode(dfs_info_t *info, char *buf, uint32_t infolvl)
133011963SAfshin.Ardakani@Sun.COM {
133111963SAfshin.Ardakani@Sun.COM 	char *lfield[DFS_LINK_HDR_NFIELDS];
133211963SAfshin.Ardakani@Sun.COM 	dfs_target_t *t;
133311963SAfshin.Ardakani@Sun.COM 	uint32_t linkver;
133411963SAfshin.Ardakani@Sun.COM 	uint32_t cmntlen;
133511963SAfshin.Ardakani@Sun.COM 	uint32_t cpylen;
133611963SAfshin.Ardakani@Sun.COM 	int i, j;
133711963SAfshin.Ardakani@Sun.COM 
133811963SAfshin.Ardakani@Sun.COM 	/*
133911963SAfshin.Ardakani@Sun.COM 	 * Header format
134011963SAfshin.Ardakani@Sun.COM 	 * ver:state:prop:timeout:guid:ntarget:cmntlen:comment:
134111963SAfshin.Ardakani@Sun.COM 	 */
134211963SAfshin.Ardakani@Sun.COM 	for (i = 0; i < DFS_LINK_HDR_NFIELDS; i++) {
134311963SAfshin.Ardakani@Sun.COM 		if ((lfield[i] = strsep((char **)&buf, ":")) == NULL)
134411963SAfshin.Ardakani@Sun.COM 			return (ERROR_INVALID_DATA);
134511963SAfshin.Ardakani@Sun.COM 	}
134611963SAfshin.Ardakani@Sun.COM 
134711963SAfshin.Ardakani@Sun.COM 	i = 0;
134811963SAfshin.Ardakani@Sun.COM 	linkver = strtoul(lfield[i++], NULL, 10);
134911963SAfshin.Ardakani@Sun.COM 	if (linkver != DFS_LINK_V1)
135011963SAfshin.Ardakani@Sun.COM 		return (ERROR_INVALID_DATA);
135111963SAfshin.Ardakani@Sun.COM 
135211963SAfshin.Ardakani@Sun.COM 	info->i_state = strtoul(lfield[i++], NULL, 10);
135311963SAfshin.Ardakani@Sun.COM 	info->i_propflags = strtoul(lfield[i++], NULL, 10);
135411963SAfshin.Ardakani@Sun.COM 	info->i_timeout = strtoul(lfield[i++], NULL, 10);
135511963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(info->i_guid, lfield[i++], sizeof (info->i_guid));
135611963SAfshin.Ardakani@Sun.COM 	info->i_ntargets = strtoul(lfield[i++], NULL, 10);
135711963SAfshin.Ardakani@Sun.COM 	info->i_targets = NULL;
135811963SAfshin.Ardakani@Sun.COM 
135911963SAfshin.Ardakani@Sun.COM 	cpylen = cmntlen = strtoul(lfield[i++], NULL, 10);
136011963SAfshin.Ardakani@Sun.COM 
136111963SAfshin.Ardakani@Sun.COM 	if (cmntlen > sizeof (info->i_comment))
136211963SAfshin.Ardakani@Sun.COM 		cpylen = sizeof (info->i_comment);
136311963SAfshin.Ardakani@Sun.COM 	else if (cmntlen != 0)
136411963SAfshin.Ardakani@Sun.COM 		cpylen = cmntlen + 1;
136511963SAfshin.Ardakani@Sun.COM 
136611963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(info->i_comment, buf, cpylen);
136711963SAfshin.Ardakani@Sun.COM 	buf += (cmntlen + 1);
136811963SAfshin.Ardakani@Sun.COM 
136911963SAfshin.Ardakani@Sun.COM 	switch (infolvl) {
137011963SAfshin.Ardakani@Sun.COM 	case DFS_INFO_ALL:
137111963SAfshin.Ardakani@Sun.COM 	case 3:
137211963SAfshin.Ardakani@Sun.COM 	case 4:
137311963SAfshin.Ardakani@Sun.COM 	case 6:
137411963SAfshin.Ardakani@Sun.COM 	case 9:
137511963SAfshin.Ardakani@Sun.COM 		/* need target information */
137611963SAfshin.Ardakani@Sun.COM 		break;
137711963SAfshin.Ardakani@Sun.COM 	default:
137811963SAfshin.Ardakani@Sun.COM 		return (ERROR_SUCCESS);
137911963SAfshin.Ardakani@Sun.COM 	}
138011963SAfshin.Ardakani@Sun.COM 
138111963SAfshin.Ardakani@Sun.COM 	info->i_targets = calloc(info->i_ntargets, sizeof (dfs_target_t));
138211963SAfshin.Ardakani@Sun.COM 	if (info->i_targets == NULL)
138311963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_ENOUGH_MEMORY);
138411963SAfshin.Ardakani@Sun.COM 
138511963SAfshin.Ardakani@Sun.COM 	/*
138611963SAfshin.Ardakani@Sun.COM 	 * Format for each target
138711963SAfshin.Ardakani@Sun.COM 	 * server:share:state:class:rank
138811963SAfshin.Ardakani@Sun.COM 	 */
138911963SAfshin.Ardakani@Sun.COM 	for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) {
139011963SAfshin.Ardakani@Sun.COM 		for (j = 0; j < DFS_LINK_TRGT_NFIELDS; j++) {
139111963SAfshin.Ardakani@Sun.COM 			if ((lfield[j] = strsep((char **)&buf, ":")) == NULL) {
139211963SAfshin.Ardakani@Sun.COM 				dfs_info_free(info);
139311963SAfshin.Ardakani@Sun.COM 				return (ERROR_INVALID_DATA);
139411963SAfshin.Ardakani@Sun.COM 			}
139511963SAfshin.Ardakani@Sun.COM 		}
139611963SAfshin.Ardakani@Sun.COM 
139711963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(t->t_server, lfield[0], sizeof (t->t_server));
139811963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(t->t_share, lfield[1], sizeof (t->t_share));
139911963SAfshin.Ardakani@Sun.COM 		t->t_state = strtoul(lfield[2], NULL, 10);
140011963SAfshin.Ardakani@Sun.COM 		t->t_priority.p_class = strtoul(lfield[3], NULL, 10);
140111963SAfshin.Ardakani@Sun.COM 		t->t_priority.p_rank = strtoul(lfield[4], NULL, 10);
140211963SAfshin.Ardakani@Sun.COM 	}
140311963SAfshin.Ardakani@Sun.COM 
140411963SAfshin.Ardakani@Sun.COM 	return (ERROR_SUCCESS);
140511963SAfshin.Ardakani@Sun.COM }
140611963SAfshin.Ardakani@Sun.COM 
140711963SAfshin.Ardakani@Sun.COM /*
140811963SAfshin.Ardakani@Sun.COM  * Encodes given link information (info)
140911963SAfshin.Ardakani@Sun.COM  */
141011963SAfshin.Ardakani@Sun.COM static uint32_t
141111963SAfshin.Ardakani@Sun.COM dfs_link_encode(dfs_info_t *info, char *buf, size_t bufsz)
141211963SAfshin.Ardakani@Sun.COM {
141311963SAfshin.Ardakani@Sun.COM 	char linkdata[MAXREPARSELEN];
141411963SAfshin.Ardakani@Sun.COM 	dfs_target_t *t;
141511963SAfshin.Ardakani@Sun.COM 	int i, sz;
141611963SAfshin.Ardakani@Sun.COM 
141711963SAfshin.Ardakani@Sun.COM 	/*
141811963SAfshin.Ardakani@Sun.COM 	 * Header format
141911963SAfshin.Ardakani@Sun.COM 	 * ver:state:prop:timeout:guid:ntarget:cmntlen:comment
142011963SAfshin.Ardakani@Sun.COM 	 */
142111963SAfshin.Ardakani@Sun.COM 	sz = snprintf(buf, bufsz, "%u:%u:%u:%u:%s:%u:%u:%s",
142211963SAfshin.Ardakani@Sun.COM 	    DFS_LINK_V1, info->i_state, info->i_propflags, info->i_timeout,
142311963SAfshin.Ardakani@Sun.COM 	    info->i_guid, info->i_ntargets,
142411963SAfshin.Ardakani@Sun.COM 	    strlen(info->i_comment), info->i_comment);
142511963SAfshin.Ardakani@Sun.COM 
142611963SAfshin.Ardakani@Sun.COM 	if (sz > bufsz) {
142711963SAfshin.Ardakani@Sun.COM 		syslog(LOG_WARNING, "dfs: link data is too large");
142811963SAfshin.Ardakani@Sun.COM 		dfs_info_trace("DFS link encode", info);
142911963SAfshin.Ardakani@Sun.COM 		return (ERROR_INTERNAL_ERROR);
143011963SAfshin.Ardakani@Sun.COM 	}
143111963SAfshin.Ardakani@Sun.COM 
143211963SAfshin.Ardakani@Sun.COM 	/*
143311963SAfshin.Ardakani@Sun.COM 	 * Format for each target
143411963SAfshin.Ardakani@Sun.COM 	 * :server:share:state:class:rank
143511963SAfshin.Ardakani@Sun.COM 	 */
143611963SAfshin.Ardakani@Sun.COM 	bufsz -= sz;
143711963SAfshin.Ardakani@Sun.COM 	for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) {
143811963SAfshin.Ardakani@Sun.COM 		if (strchr(t->t_server, ':') || strchr(t->t_share, ':'))
143911963SAfshin.Ardakani@Sun.COM 			return (ERROR_INVALID_NAME);
144011963SAfshin.Ardakani@Sun.COM 
144111963SAfshin.Ardakani@Sun.COM 		sz = snprintf(linkdata, MAXREPARSELEN, ":%s:%s:%u:%u:%u",
144211963SAfshin.Ardakani@Sun.COM 		    t->t_server, t->t_share, t->t_state,
144311963SAfshin.Ardakani@Sun.COM 		    t->t_priority.p_class, t->t_priority.p_rank);
144411963SAfshin.Ardakani@Sun.COM 		if (sz > bufsz) {
144511963SAfshin.Ardakani@Sun.COM 			syslog(LOG_WARNING, "dfs: link data is too large");
144611963SAfshin.Ardakani@Sun.COM 			dfs_info_trace("DFS link encode", info);
144711963SAfshin.Ardakani@Sun.COM 			return (ERROR_INTERNAL_ERROR);
144811963SAfshin.Ardakani@Sun.COM 		}
144911963SAfshin.Ardakani@Sun.COM 		(void) strcat(buf, linkdata);
145011963SAfshin.Ardakani@Sun.COM 		bufsz -= sz;
145111963SAfshin.Ardakani@Sun.COM 	}
145211963SAfshin.Ardakani@Sun.COM 
145311963SAfshin.Ardakani@Sun.COM 	return (ERROR_SUCCESS);
145411963SAfshin.Ardakani@Sun.COM }
145511963SAfshin.Ardakani@Sun.COM 
145611963SAfshin.Ardakani@Sun.COM /*
145711963SAfshin.Ardakani@Sun.COM  * Stores given information for the specified link
145811963SAfshin.Ardakani@Sun.COM  */
145911963SAfshin.Ardakani@Sun.COM static uint32_t
146011963SAfshin.Ardakani@Sun.COM dfs_link_commit(const char *path, dfs_info_t *info)
146111963SAfshin.Ardakani@Sun.COM {
146211963SAfshin.Ardakani@Sun.COM 	char linkdata[MAXREPARSELEN];
146311963SAfshin.Ardakani@Sun.COM 	uint32_t status;
146411963SAfshin.Ardakani@Sun.COM 	int rc;
146511963SAfshin.Ardakani@Sun.COM 
146611963SAfshin.Ardakani@Sun.COM 	status = dfs_link_encode(info, linkdata, MAXREPARSELEN);
146711963SAfshin.Ardakani@Sun.COM 	if (status == ERROR_SUCCESS) {
146811963SAfshin.Ardakani@Sun.COM 		rc = smb_reparse_svcadd(path, DFS_REPARSE_SVCTYPE, linkdata);
146911963SAfshin.Ardakani@Sun.COM 		if (rc != 0)
147011963SAfshin.Ardakani@Sun.COM 			status = ERROR_INTERNAL_ERROR;
147111963SAfshin.Ardakani@Sun.COM 	}
147211963SAfshin.Ardakani@Sun.COM 
147311963SAfshin.Ardakani@Sun.COM 	return (status);
147411963SAfshin.Ardakani@Sun.COM }
147511963SAfshin.Ardakani@Sun.COM 
147611963SAfshin.Ardakani@Sun.COM /*
147711963SAfshin.Ardakani@Sun.COM  * Determines if the passed state is valid for a link
147811963SAfshin.Ardakani@Sun.COM  */
147911963SAfshin.Ardakani@Sun.COM static boolean_t
148011963SAfshin.Ardakani@Sun.COM dfs_link_isvalidstate(uint32_t state)
148111963SAfshin.Ardakani@Sun.COM {
148211963SAfshin.Ardakani@Sun.COM 	return (state == DFS_VOLUME_STATE_OK ||
148311963SAfshin.Ardakani@Sun.COM 	    state == DFS_VOLUME_STATE_OFFLINE ||
148411963SAfshin.Ardakani@Sun.COM 	    state == DFS_VOLUME_STATE_ONLINE);
148511963SAfshin.Ardakani@Sun.COM }
148611963SAfshin.Ardakani@Sun.COM 
148711963SAfshin.Ardakani@Sun.COM /*
148811963SAfshin.Ardakani@Sun.COM  * Initializes the given target structure (t) with provided information.
148911963SAfshin.Ardakani@Sun.COM  */
149011963SAfshin.Ardakani@Sun.COM static void
149111963SAfshin.Ardakani@Sun.COM dfs_target_init(dfs_target_t *t, const char *srv, const char *share,
149211963SAfshin.Ardakani@Sun.COM     uint32_t state)
149311963SAfshin.Ardakani@Sun.COM {
149411963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(t->t_server, (srv) ? srv : "", sizeof (t->t_server));
149511963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(t->t_share, (share) ? share : "", sizeof (t->t_share));
149611963SAfshin.Ardakani@Sun.COM 	t->t_state = state;
149711963SAfshin.Ardakani@Sun.COM 	t->t_priority.p_class = DfsSiteCostNormalPriorityClass;
149811963SAfshin.Ardakani@Sun.COM 	t->t_priority.p_rank = 0;
149911963SAfshin.Ardakani@Sun.COM }
150011963SAfshin.Ardakani@Sun.COM 
150111963SAfshin.Ardakani@Sun.COM /*
150211963SAfshin.Ardakani@Sun.COM  * Lookup the specified target (server, share) in the given
150311963SAfshin.Ardakani@Sun.COM  * target list (targets). If there is a match its index is
150411963SAfshin.Ardakani@Sun.COM  * returned, otherwise -1 will be returned.
150511963SAfshin.Ardakani@Sun.COM  */
150611963SAfshin.Ardakani@Sun.COM static int
150711963SAfshin.Ardakani@Sun.COM dfs_target_find(dfs_target_t *targets, uint32_t ntargets,
150811963SAfshin.Ardakani@Sun.COM     const char *server, const char *share)
150911963SAfshin.Ardakani@Sun.COM {
151011963SAfshin.Ardakani@Sun.COM 	dfs_target_t *t;
151111963SAfshin.Ardakani@Sun.COM 	int i;
151211963SAfshin.Ardakani@Sun.COM 
151311963SAfshin.Ardakani@Sun.COM 	for (i = 0, t = targets; i < ntargets; i++, t++) {
151411963SAfshin.Ardakani@Sun.COM 		if ((smb_strcasecmp(t->t_server, server, 0) == 0) &&
151511963SAfshin.Ardakani@Sun.COM 		    (smb_strcasecmp(t->t_share, share, 0) == 0))
151611963SAfshin.Ardakani@Sun.COM 			return (i);
151711963SAfshin.Ardakani@Sun.COM 	}
151811963SAfshin.Ardakani@Sun.COM 
151911963SAfshin.Ardakani@Sun.COM 	return (-1);
152011963SAfshin.Ardakani@Sun.COM }
152111963SAfshin.Ardakani@Sun.COM 
152211963SAfshin.Ardakani@Sun.COM /*
152311963SAfshin.Ardakani@Sun.COM  * Determines if the passed state is valid for a link/root target
152411963SAfshin.Ardakani@Sun.COM  */
152511963SAfshin.Ardakani@Sun.COM static boolean_t
152611963SAfshin.Ardakani@Sun.COM dfs_target_isvalidstate(uint32_t state)
152711963SAfshin.Ardakani@Sun.COM {
152811963SAfshin.Ardakani@Sun.COM 	return (state == DFS_STORAGE_STATE_ONLINE ||
152911963SAfshin.Ardakani@Sun.COM 	    state == DFS_STORAGE_STATE_OFFLINE);
153011963SAfshin.Ardakani@Sun.COM }
153111963SAfshin.Ardakani@Sun.COM 
153211963SAfshin.Ardakani@Sun.COM /*
153311963SAfshin.Ardakani@Sun.COM  * Cache compare function, the key is UNC path
153411963SAfshin.Ardakani@Sun.COM  */
153511963SAfshin.Ardakani@Sun.COM static int
153611963SAfshin.Ardakani@Sun.COM dfs_cache_cmp(const void *p1, const void *p2)
153711963SAfshin.Ardakani@Sun.COM {
153811963SAfshin.Ardakani@Sun.COM 	smb_cache_node_t *cn1 = (smb_cache_node_t *)p1;
153911963SAfshin.Ardakani@Sun.COM 	smb_cache_node_t *cn2 = (smb_cache_node_t *)p2;
154011963SAfshin.Ardakani@Sun.COM 	dfs_nscnode_t *dn1 = cn1->cn_data;
154111963SAfshin.Ardakani@Sun.COM 	dfs_nscnode_t *dn2 = cn2->cn_data;
154211963SAfshin.Ardakani@Sun.COM 	int rc;
154311963SAfshin.Ardakani@Sun.COM 
154411963SAfshin.Ardakani@Sun.COM 	rc = smb_strcasecmp(dn1->nsc_uncpath, dn2->nsc_uncpath, 0);
154511963SAfshin.Ardakani@Sun.COM 
154611963SAfshin.Ardakani@Sun.COM 	if (rc < 0)
154711963SAfshin.Ardakani@Sun.COM 		return (-1);
154811963SAfshin.Ardakani@Sun.COM 
154911963SAfshin.Ardakani@Sun.COM 	if (rc > 0)
155011963SAfshin.Ardakani@Sun.COM 		return (1);
155111963SAfshin.Ardakani@Sun.COM 
155211963SAfshin.Ardakani@Sun.COM 	return (0);
155311963SAfshin.Ardakani@Sun.COM }
155411963SAfshin.Ardakani@Sun.COM 
155511963SAfshin.Ardakani@Sun.COM /*
155611963SAfshin.Ardakani@Sun.COM  * Adds an entry with given UNC and filesystem path and the specified
155711963SAfshin.Ardakani@Sun.COM  * entry type (i.e. root/link) to the namespace cache.
155811963SAfshin.Ardakani@Sun.COM  */
155911963SAfshin.Ardakani@Sun.COM static uint32_t
156011963SAfshin.Ardakani@Sun.COM dfs_cache_add_byunc(const char *uncpath, const char *fspath, uint32_t type)
156111963SAfshin.Ardakani@Sun.COM {
156211963SAfshin.Ardakani@Sun.COM 	dfs_nscnode_t *dn;
156311963SAfshin.Ardakani@Sun.COM 	uint32_t status = ERROR_SUCCESS;
156411963SAfshin.Ardakani@Sun.COM 
156511963SAfshin.Ardakani@Sun.COM 	if ((dn = malloc(sizeof (dfs_nscnode_t))) == NULL)
156611963SAfshin.Ardakani@Sun.COM 		return (ERROR_NOT_ENOUGH_MEMORY);
156711963SAfshin.Ardakani@Sun.COM 
156811963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(dn->nsc_uncpath, uncpath, sizeof (dn->nsc_uncpath));
156911963SAfshin.Ardakani@Sun.COM 	(void) strlcpy(dn->nsc_fspath, fspath, sizeof (dn->nsc_fspath));
157011963SAfshin.Ardakani@Sun.COM 	dn->nsc_type = type;
157111963SAfshin.Ardakani@Sun.COM 	if (smb_cache_add(&dfs_nscache, dn, SMB_CACHE_ADD) != 0) {
157211963SAfshin.Ardakani@Sun.COM 		free(dn);
157311963SAfshin.Ardakani@Sun.COM 		status = ERROR_INTERNAL_ERROR;
157411963SAfshin.Ardakani@Sun.COM 	}
157511963SAfshin.Ardakani@Sun.COM 
157611963SAfshin.Ardakani@Sun.COM 	return (status);
157711963SAfshin.Ardakani@Sun.COM }
157811963SAfshin.Ardakani@Sun.COM 
157911963SAfshin.Ardakani@Sun.COM /*
158011963SAfshin.Ardakani@Sun.COM  * starting from DFS root directory, scans the tree for DFS links
158111963SAfshin.Ardakani@Sun.COM  * and adds them to the cache.
158211963SAfshin.Ardakani@Sun.COM  */
158311963SAfshin.Ardakani@Sun.COM static void
158411963SAfshin.Ardakani@Sun.COM dfs_cache_populate(const char *unc_prefix, const char *dir)
158511963SAfshin.Ardakani@Sun.COM {
158611963SAfshin.Ardakani@Sun.COM 	char fspath[DFS_PATH_MAX];
158711963SAfshin.Ardakani@Sun.COM 	char uncpath[DFS_PATH_MAX];
158811963SAfshin.Ardakani@Sun.COM 	char *fname;
158911963SAfshin.Ardakani@Sun.COM 	int nentries, i;
159011963SAfshin.Ardakani@Sun.COM 	struct dirent **entry_list;
159111963SAfshin.Ardakani@Sun.COM 	uint32_t stat;
159211963SAfshin.Ardakani@Sun.COM 
159311963SAfshin.Ardakani@Sun.COM 	nentries = scandir(dir, &entry_list, NULL, NULL);
159411963SAfshin.Ardakani@Sun.COM 	if (nentries == -1)
159511963SAfshin.Ardakani@Sun.COM 		return;
159611963SAfshin.Ardakani@Sun.COM 
159711963SAfshin.Ardakani@Sun.COM 	for (i = 0; i < nentries; i++) {
159811963SAfshin.Ardakani@Sun.COM 		fname = entry_list[i]->d_name;
159911963SAfshin.Ardakani@Sun.COM 
160011963SAfshin.Ardakani@Sun.COM 		if (strcmp(fname, ".") == 0 ||
160111963SAfshin.Ardakani@Sun.COM 		    strcmp(fname, "..") == 0) {
160211963SAfshin.Ardakani@Sun.COM 			free(entry_list[i]);
160311963SAfshin.Ardakani@Sun.COM 			continue;
160411963SAfshin.Ardakani@Sun.COM 		}
160511963SAfshin.Ardakani@Sun.COM 
160611963SAfshin.Ardakani@Sun.COM 		(void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", dir, fname);
160711963SAfshin.Ardakani@Sun.COM 		(void) snprintf(uncpath, DFS_PATH_MAX, "%s\\%s", unc_prefix,
160811963SAfshin.Ardakani@Sun.COM 		    fname);
160911963SAfshin.Ardakani@Sun.COM 
161011963SAfshin.Ardakani@Sun.COM 		if (dfs_path_isdir(fspath)) {
161111963SAfshin.Ardakani@Sun.COM 			dfs_cache_populate(uncpath, fspath);
161211963SAfshin.Ardakani@Sun.COM 		} else if (dfs_link_stat(fspath, &stat) == ERROR_SUCCESS) {
161311963SAfshin.Ardakani@Sun.COM 			if (stat == DFS_STAT_ISDFS)
161411963SAfshin.Ardakani@Sun.COM 				(void) dfs_cache_add_byunc(uncpath, fspath,
161511963SAfshin.Ardakani@Sun.COM 				    DFS_OBJECT_LINK);
161611963SAfshin.Ardakani@Sun.COM 		}
161711963SAfshin.Ardakani@Sun.COM 
161811963SAfshin.Ardakani@Sun.COM 		free(entry_list[i]);
161911963SAfshin.Ardakani@Sun.COM 	}
162011963SAfshin.Ardakani@Sun.COM 
162111963SAfshin.Ardakani@Sun.COM 	for (; i < nentries; i++)
162211963SAfshin.Ardakani@Sun.COM 		free(entry_list[i]);
162311963SAfshin.Ardakani@Sun.COM 
162411963SAfshin.Ardakani@Sun.COM 	free(entry_list);
162511963SAfshin.Ardakani@Sun.COM }
162611963SAfshin.Ardakani@Sun.COM 
162711963SAfshin.Ardakani@Sun.COM /*
162811963SAfshin.Ardakani@Sun.COM  * Determines whether the given path is a directory.
162911963SAfshin.Ardakani@Sun.COM  */
163011963SAfshin.Ardakani@Sun.COM static boolean_t
163111963SAfshin.Ardakani@Sun.COM dfs_path_isdir(const char *path)
163211963SAfshin.Ardakani@Sun.COM {
163311963SAfshin.Ardakani@Sun.COM 	struct stat statbuf;
163411963SAfshin.Ardakani@Sun.COM 
163511963SAfshin.Ardakani@Sun.COM 	if (lstat(path, &statbuf) != 0)
163611963SAfshin.Ardakani@Sun.COM 		return (B_FALSE);
163711963SAfshin.Ardakani@Sun.COM 
163811963SAfshin.Ardakani@Sun.COM 	return ((statbuf.st_mode & S_IFMT) == S_IFDIR);
163911963SAfshin.Ardakani@Sun.COM }
164011963SAfshin.Ardakani@Sun.COM 
164111963SAfshin.Ardakani@Sun.COM /*
164211963SAfshin.Ardakani@Sun.COM  * Validates the given state based on the object type (root/link)
164311963SAfshin.Ardakani@Sun.COM  * and whether it is the object's state or its target's state
164411963SAfshin.Ardakani@Sun.COM  */
164511963SAfshin.Ardakani@Sun.COM static uint32_t
164611963SAfshin.Ardakani@Sun.COM dfs_isvalidstate(uint32_t state, uint32_t type, boolean_t target)
164711963SAfshin.Ardakani@Sun.COM {
164811963SAfshin.Ardakani@Sun.COM 	uint32_t status = ERROR_SUCCESS;
164911963SAfshin.Ardakani@Sun.COM 
165011963SAfshin.Ardakani@Sun.COM 	if (type == DFS_OBJECT_ROOT) {
165111963SAfshin.Ardakani@Sun.COM 		if (!target)
165211963SAfshin.Ardakani@Sun.COM 			return (dfs_root_isvalidstate(state));
165311963SAfshin.Ardakani@Sun.COM 
165411963SAfshin.Ardakani@Sun.COM 		if (!dfs_target_isvalidstate(state))
165511963SAfshin.Ardakani@Sun.COM 			status = ERROR_INVALID_PARAMETER;
165611963SAfshin.Ardakani@Sun.COM 		else if (state == DFS_STORAGE_STATE_OFFLINE)
165711963SAfshin.Ardakani@Sun.COM 			status = ERROR_NOT_SUPPORTED;
165811963SAfshin.Ardakani@Sun.COM 	} else {
165911963SAfshin.Ardakani@Sun.COM 		if (!target) {
166011963SAfshin.Ardakani@Sun.COM 			if (!dfs_link_isvalidstate(state))
166111963SAfshin.Ardakani@Sun.COM 				status = ERROR_INVALID_PARAMETER;
166211963SAfshin.Ardakani@Sun.COM 		} else {
166311963SAfshin.Ardakani@Sun.COM 			if (!dfs_target_isvalidstate(state))
166411963SAfshin.Ardakani@Sun.COM 				status = ERROR_INVALID_PARAMETER;
166511963SAfshin.Ardakani@Sun.COM 		}
166611963SAfshin.Ardakani@Sun.COM 	}
166711963SAfshin.Ardakani@Sun.COM 
166811963SAfshin.Ardakani@Sun.COM 	return (status);
166911963SAfshin.Ardakani@Sun.COM }
167011963SAfshin.Ardakani@Sun.COM 
167111963SAfshin.Ardakani@Sun.COM /*
167211963SAfshin.Ardakani@Sun.COM  * Based on the specified information level (infolvl) copy parts of the
167311963SAfshin.Ardakani@Sun.COM  * information provided through newinfo into the existing information
167411963SAfshin.Ardakani@Sun.COM  * (info) for the given object.
167511963SAfshin.Ardakani@Sun.COM  */
167611963SAfshin.Ardakani@Sun.COM static uint32_t
167711963SAfshin.Ardakani@Sun.COM dfs_modinfo(uint32_t type, dfs_info_t *info, dfs_info_t *newinfo,
167811963SAfshin.Ardakani@Sun.COM     uint32_t infolvl)
167911963SAfshin.Ardakani@Sun.COM {
168011963SAfshin.Ardakani@Sun.COM 	boolean_t target_op = B_FALSE;
168111963SAfshin.Ardakani@Sun.COM 	uint32_t status = ERROR_SUCCESS;
168211963SAfshin.Ardakani@Sun.COM 	uint32_t state;
168311963SAfshin.Ardakani@Sun.COM 	int target_idx;
168411963SAfshin.Ardakani@Sun.COM 
168511963SAfshin.Ardakani@Sun.COM 	if (newinfo->i_targets != NULL) {
168611963SAfshin.Ardakani@Sun.COM 		target_idx = dfs_target_find(info->i_targets, info->i_ntargets,
168711963SAfshin.Ardakani@Sun.COM 		    newinfo->i_targets->t_server, newinfo->i_targets->t_share);
168811963SAfshin.Ardakani@Sun.COM 		if (target_idx == -1)
168911963SAfshin.Ardakani@Sun.COM 			return (ERROR_FILE_NOT_FOUND);
169011963SAfshin.Ardakani@Sun.COM 		target_op = B_TRUE;
169111963SAfshin.Ardakani@Sun.COM 	}
169211963SAfshin.Ardakani@Sun.COM 
169311963SAfshin.Ardakani@Sun.COM 	switch (infolvl) {
169411963SAfshin.Ardakani@Sun.COM 	case 100:
169511963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(info->i_comment, newinfo->i_comment,
169611963SAfshin.Ardakani@Sun.COM 		    sizeof (newinfo->i_comment));
169711963SAfshin.Ardakani@Sun.COM 		break;
169811963SAfshin.Ardakani@Sun.COM 
169911963SAfshin.Ardakani@Sun.COM 	case 101:
170011963SAfshin.Ardakani@Sun.COM 		state = (target_op)
170111963SAfshin.Ardakani@Sun.COM 		    ? newinfo->i_targets->t_state : newinfo->i_state;
170211963SAfshin.Ardakani@Sun.COM 		status = dfs_isvalidstate(state, type, target_op);
170311963SAfshin.Ardakani@Sun.COM 		if (status != ERROR_SUCCESS)
170411963SAfshin.Ardakani@Sun.COM 			return (status);
170511963SAfshin.Ardakani@Sun.COM 
170611963SAfshin.Ardakani@Sun.COM 		if (!target_op) {
170711963SAfshin.Ardakani@Sun.COM 			/*
170811963SAfshin.Ardakani@Sun.COM 			 * states specified by this mask should not be stored
170911963SAfshin.Ardakani@Sun.COM 			 */
171011963SAfshin.Ardakani@Sun.COM 			if (state & DFS_VOLUME_STATES_SRV_OPS)
171111963SAfshin.Ardakani@Sun.COM 				return (ERROR_SUCCESS);
171211963SAfshin.Ardakani@Sun.COM 
171311963SAfshin.Ardakani@Sun.COM 			info->i_state = state;
171411963SAfshin.Ardakani@Sun.COM 		} else {
171511963SAfshin.Ardakani@Sun.COM 			info->i_targets[target_idx].t_state = state;
171611963SAfshin.Ardakani@Sun.COM 		}
171711963SAfshin.Ardakani@Sun.COM 		break;
171811963SAfshin.Ardakani@Sun.COM 
171911963SAfshin.Ardakani@Sun.COM 	case 102:
172011963SAfshin.Ardakani@Sun.COM 		info->i_timeout = newinfo->i_timeout;
172111963SAfshin.Ardakani@Sun.COM 		break;
172211963SAfshin.Ardakani@Sun.COM 
172311963SAfshin.Ardakani@Sun.COM 	case 103:
172411963SAfshin.Ardakani@Sun.COM 		info->i_propflags = newinfo->i_propflags;
172511963SAfshin.Ardakani@Sun.COM 		break;
172611963SAfshin.Ardakani@Sun.COM 
172711963SAfshin.Ardakani@Sun.COM 	case 104:
172811963SAfshin.Ardakani@Sun.COM 		info->i_targets[target_idx].t_priority =
172911963SAfshin.Ardakani@Sun.COM 		    newinfo->i_targets->t_priority;
173011963SAfshin.Ardakani@Sun.COM 		break;
173111963SAfshin.Ardakani@Sun.COM 
173211963SAfshin.Ardakani@Sun.COM 	case 105:
173311963SAfshin.Ardakani@Sun.COM 		(void) strlcpy(info->i_comment, newinfo->i_comment,
173411963SAfshin.Ardakani@Sun.COM 		    sizeof (newinfo->i_comment));
173511963SAfshin.Ardakani@Sun.COM 		info->i_state = newinfo->i_state;
173611963SAfshin.Ardakani@Sun.COM 		info->i_timeout = newinfo->i_timeout;
173711963SAfshin.Ardakani@Sun.COM 		info->i_propflags = newinfo->i_propflags;
173811963SAfshin.Ardakani@Sun.COM 		break;
173911963SAfshin.Ardakani@Sun.COM 
174011963SAfshin.Ardakani@Sun.COM 	default:
174111963SAfshin.Ardakani@Sun.COM 		status = ERROR_INVALID_LEVEL;
174211963SAfshin.Ardakani@Sun.COM 	}
174311963SAfshin.Ardakani@Sun.COM 
174411963SAfshin.Ardakani@Sun.COM 	return (status);
174511963SAfshin.Ardakani@Sun.COM }
1746