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