xref: /onnv-gate/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c (revision 5331:3047ad28a67b)
1*5331Samw /*
2*5331Samw  * CDDL HEADER START
3*5331Samw  *
4*5331Samw  * The contents of this file are subject to the terms of the
5*5331Samw  * Common Development and Distribution License (the "License").
6*5331Samw  * You may not use this file except in compliance with the License.
7*5331Samw  *
8*5331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5331Samw  * or http://www.opensolaris.org/os/licensing.
10*5331Samw  * See the License for the specific language governing permissions
11*5331Samw  * and limitations under the License.
12*5331Samw  *
13*5331Samw  * When distributing Covered Code, include this CDDL HEADER in each
14*5331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5331Samw  * If applicable, add the following below this CDDL HEADER, with the
16*5331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
17*5331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5331Samw  *
19*5331Samw  * CDDL HEADER END
20*5331Samw  */
21*5331Samw /*
22*5331Samw  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*5331Samw  * Use is subject to license terms.
24*5331Samw  */
25*5331Samw 
26*5331Samw #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5331Samw 
28*5331Samw /*
29*5331Samw  * Net DFS server side RPC service.
30*5331Samw  */
31*5331Samw 
32*5331Samw #include <sys/types.h>
33*5331Samw #include <strings.h>
34*5331Samw #include <string.h>
35*5331Samw 
36*5331Samw #include <smbsrv/libsmb.h>
37*5331Samw #include <smbsrv/lmerr.h>
38*5331Samw #include <smbsrv/lmdfs.h>
39*5331Samw #include <smbsrv/nmpipes.h>
40*5331Samw #include <smbsrv/nterror.h>
41*5331Samw #include <smbsrv/mlrpc.h>
42*5331Samw #include <smbsrv/ndl/netdfs.ndl>
43*5331Samw 
44*5331Samw typedef struct {
45*5331Samw 	char *server;
46*5331Samw 	char *share;
47*5331Samw 	char *path;
48*5331Samw 	char *buf;
49*5331Samw } netdfs_unc_t;
50*5331Samw 
51*5331Samw static int netdfs_unc_parse(struct mlrpc_xaction *, const char *,
52*5331Samw     netdfs_unc_t *);
53*5331Samw 
54*5331Samw static int netdfs_s_getver(void *, struct mlrpc_xaction *);
55*5331Samw static int netdfs_s_add(void *, struct mlrpc_xaction *);
56*5331Samw static int netdfs_s_remove(void *, struct mlrpc_xaction *);
57*5331Samw static int netdfs_s_setinfo(void *, struct mlrpc_xaction *);
58*5331Samw static int netdfs_s_getinfo(void *, struct mlrpc_xaction *);
59*5331Samw static int netdfs_s_enum(void *, struct mlrpc_xaction *);
60*5331Samw static int netdfs_s_move(void *, struct mlrpc_xaction *);
61*5331Samw static int netdfs_s_rename(void *, struct mlrpc_xaction *);
62*5331Samw static int netdfs_s_addstdroot(void *, struct mlrpc_xaction *);
63*5331Samw static int netdfs_s_remstdroot(void *, struct mlrpc_xaction *);
64*5331Samw static int netdfs_s_enumex(void *, struct mlrpc_xaction *);
65*5331Samw 
66*5331Samw static mlrpc_stub_table_t netdfs_stub_table[] = {
67*5331Samw 	{ netdfs_s_getver,	NETDFS_OPNUM_GETVER },
68*5331Samw 	{ netdfs_s_add,		NETDFS_OPNUM_ADD },
69*5331Samw 	{ netdfs_s_remove,	NETDFS_OPNUM_REMOVE },
70*5331Samw 	{ netdfs_s_setinfo,	NETDFS_OPNUM_SETINFO },
71*5331Samw 	{ netdfs_s_getinfo,	NETDFS_OPNUM_GETINFO },
72*5331Samw 	{ netdfs_s_enum,	NETDFS_OPNUM_ENUM },
73*5331Samw 	{ netdfs_s_rename,	NETDFS_OPNUM_RENAME },
74*5331Samw 	{ netdfs_s_move,	NETDFS_OPNUM_MOVE },
75*5331Samw 	{ netdfs_s_addstdroot,	NETDFS_OPNUM_ADDSTDROOT },
76*5331Samw 	{ netdfs_s_remstdroot,	NETDFS_OPNUM_REMSTDROOT },
77*5331Samw 	{ netdfs_s_enumex,	NETDFS_OPNUM_ENUMEX },
78*5331Samw 	{0}
79*5331Samw };
80*5331Samw 
81*5331Samw static mlrpc_service_t netdfs_service = {
82*5331Samw 	"NETDFS",			/* name */
83*5331Samw 	"DFS",				/* desc */
84*5331Samw 	"\\dfs",			/* endpoint */
85*5331Samw 	PIPE_NTSVCS,			/* sec_addr_port */
86*5331Samw 	NETDFS_ABSTRACT_UUID,	NETDFS_ABSTRACT_VERS,
87*5331Samw 	NETDFS_TRANSFER_UUID,	NETDFS_TRANSFER_VERS,
88*5331Samw 
89*5331Samw 	0,				/* no bind_instance_size */
90*5331Samw 	0,				/* no bind_req() */
91*5331Samw 	0,				/* no unbind_and_close() */
92*5331Samw 	0,				/* use generic_call_stub() */
93*5331Samw 
94*5331Samw 	&TYPEINFO(netdfs_interface),	/* interface ti */
95*5331Samw 	netdfs_stub_table		/* stub_table */
96*5331Samw };
97*5331Samw 
98*5331Samw /*
99*5331Samw  * Register the NETDFS RPC interface with the RPC runtime library.
100*5331Samw  * The service must be registered in order to use either the client
101*5331Samw  * side or the server side functions.
102*5331Samw  */
103*5331Samw void
104*5331Samw netdfs_initialize(void)
105*5331Samw {
106*5331Samw 	(void) mlrpc_register_service(&netdfs_service);
107*5331Samw }
108*5331Samw 
109*5331Samw /*
110*5331Samw  * Return the version.
111*5331Samw  *
112*5331Samw  * We have to indicate that we emulate a Windows 2003 Server or the
113*5331Samw  * client will not use the EnumEx RPC and this would limit support
114*5331Samw  * to a single DFS root.
115*5331Samw  */
116*5331Samw /*ARGSUSED*/
117*5331Samw static int
118*5331Samw netdfs_s_getver(void *arg, struct mlrpc_xaction *mxa)
119*5331Samw {
120*5331Samw 	struct netdfs_getver *param = arg;
121*5331Samw 
122*5331Samw 	param->version = DFS_MANAGER_VERSION_W2K3;
123*5331Samw 	return (MLRPC_DRC_OK);
124*5331Samw }
125*5331Samw 
126*5331Samw /*
127*5331Samw  * Add a new volume or additional storage for an existing volume at
128*5331Samw  * dfs_path.
129*5331Samw  */
130*5331Samw static int
131*5331Samw netdfs_s_add(void *arg, struct mlrpc_xaction *mxa)
132*5331Samw {
133*5331Samw 	struct netdfs_add *param = arg;
134*5331Samw 	netdfs_unc_t unc;
135*5331Samw 	DWORD status = ERROR_SUCCESS;
136*5331Samw 
137*5331Samw 	if (param->dfs_path == NULL || param->server == NULL ||
138*5331Samw 	    param->share == NULL) {
139*5331Samw 		bzero(param, sizeof (struct netdfs_add));
140*5331Samw 		param->status = ERROR_INVALID_PARAMETER;
141*5331Samw 		return (MLRPC_DRC_OK);
142*5331Samw 	}
143*5331Samw 
144*5331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
145*5331Samw 		status = ERROR_INVALID_PARAMETER;
146*5331Samw 	} else {
147*5331Samw 		if (unc.path == NULL)
148*5331Samw 			status = ERROR_BAD_PATHNAME;
149*5331Samw 
150*5331Samw 		if (unc.share == NULL)
151*5331Samw 			status = ERROR_INVALID_SHARENAME;
152*5331Samw 	}
153*5331Samw 
154*5331Samw 	if (param->status != ERROR_SUCCESS) {
155*5331Samw 		bzero(param, sizeof (struct netdfs_add));
156*5331Samw 		param->status = status;
157*5331Samw 		return (MLRPC_DRC_OK);
158*5331Samw 	}
159*5331Samw 
160*5331Samw 	bzero(param, sizeof (struct netdfs_add));
161*5331Samw 	param->status = ERROR_ACCESS_DENIED;
162*5331Samw 	return (MLRPC_DRC_OK);
163*5331Samw }
164*5331Samw 
165*5331Samw /*
166*5331Samw  * netdfs_s_remove
167*5331Samw  *
168*5331Samw  * Remove a volume or additional storage for volume from the DFS at
169*5331Samw  * dfs_path. When applied to the last storage in a volume, removes
170*5331Samw  * the volume from the DFS.
171*5331Samw  */
172*5331Samw static int
173*5331Samw netdfs_s_remove(void *arg, struct mlrpc_xaction *mxa)
174*5331Samw {
175*5331Samw 	struct netdfs_remove *param = arg;
176*5331Samw 	netdfs_unc_t unc;
177*5331Samw 	DWORD status = ERROR_SUCCESS;
178*5331Samw 
179*5331Samw 	if (param->dfs_path == NULL || param->server == NULL ||
180*5331Samw 	    param->share == NULL) {
181*5331Samw 		bzero(param, sizeof (struct netdfs_remove));
182*5331Samw 		param->status = ERROR_INVALID_PARAMETER;
183*5331Samw 		return (MLRPC_DRC_OK);
184*5331Samw 	}
185*5331Samw 
186*5331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
187*5331Samw 		status = ERROR_INVALID_PARAMETER;
188*5331Samw 	} else {
189*5331Samw 		if (unc.path == NULL)
190*5331Samw 			status = ERROR_BAD_PATHNAME;
191*5331Samw 
192*5331Samw 		if (unc.share == NULL)
193*5331Samw 			status = ERROR_INVALID_SHARENAME;
194*5331Samw 	}
195*5331Samw 
196*5331Samw 	if (param->status != ERROR_SUCCESS) {
197*5331Samw 		bzero(param, sizeof (struct netdfs_remove));
198*5331Samw 		param->status = status;
199*5331Samw 		return (MLRPC_DRC_OK);
200*5331Samw 	}
201*5331Samw 
202*5331Samw 	bzero(param, sizeof (struct netdfs_remove));
203*5331Samw 	param->status = ERROR_ACCESS_DENIED;
204*5331Samw 	return (MLRPC_DRC_OK);
205*5331Samw }
206*5331Samw 
207*5331Samw /*
208*5331Samw  * Set information about the volume or storage. If the server and share
209*5331Samw  * are specified, the information set is specific to that server and
210*5331Samw  * share. Otherwise the information is specific to the volume as a whole.
211*5331Samw  *
212*5331Samw  * Valid levels are 100-102.
213*5331Samw  */
214*5331Samw /*ARGSUSED*/
215*5331Samw static int
216*5331Samw netdfs_s_setinfo(void *arg, struct mlrpc_xaction *mxa)
217*5331Samw {
218*5331Samw 	struct netdfs_setinfo *param = arg;
219*5331Samw 	netdfs_unc_t unc;
220*5331Samw 	DWORD status = ERROR_SUCCESS;
221*5331Samw 
222*5331Samw 	if (param->dfs_path == NULL) {
223*5331Samw 		bzero(param, sizeof (struct netdfs_setinfo));
224*5331Samw 		param->status = ERROR_INVALID_PARAMETER;
225*5331Samw 		return (MLRPC_DRC_OK);
226*5331Samw 	}
227*5331Samw 
228*5331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
229*5331Samw 		status = ERROR_INVALID_PARAMETER;
230*5331Samw 	} else {
231*5331Samw 		if (unc.share == NULL)
232*5331Samw 			status = ERROR_INVALID_SHARENAME;
233*5331Samw 	}
234*5331Samw 
235*5331Samw 	if (param->status != ERROR_SUCCESS) {
236*5331Samw 		bzero(param, sizeof (struct netdfs_setinfo));
237*5331Samw 		param->status = status;
238*5331Samw 		return (MLRPC_DRC_OK);
239*5331Samw 	}
240*5331Samw 
241*5331Samw 	switch (param->info.level) {
242*5331Samw 	case 100:
243*5331Samw 	case 101:
244*5331Samw 	case 102:
245*5331Samw 		break;
246*5331Samw 
247*5331Samw 	default:
248*5331Samw 		bzero(param, sizeof (struct netdfs_setinfo));
249*5331Samw 		param->status = ERROR_INVALID_LEVEL;
250*5331Samw 		return (MLRPC_DRC_OK);
251*5331Samw 	}
252*5331Samw 
253*5331Samw 	bzero(param, sizeof (struct netdfs_setinfo));
254*5331Samw 	param->status = ERROR_ACCESS_DENIED;
255*5331Samw 	return (MLRPC_DRC_OK);
256*5331Samw }
257*5331Samw 
258*5331Samw /*
259*5331Samw  * Get information about the volume or storage. If the server and share
260*5331Samw  * are specified, the information returned is specific to that server
261*5331Samw  * and share. Otherwise the information is specific to the volume as a
262*5331Samw  * whole.
263*5331Samw  *
264*5331Samw  * Valid levels are 1-4, 100-104.
265*5331Samw  */
266*5331Samw /*ARGSUSED*/
267*5331Samw static int
268*5331Samw netdfs_s_getinfo(void *arg, struct mlrpc_xaction *mxa)
269*5331Samw {
270*5331Samw 	struct netdfs_getinfo *param = arg;
271*5331Samw 	netdfs_unc_t unc;
272*5331Samw 	DWORD status = ERROR_SUCCESS;
273*5331Samw 
274*5331Samw 	if (param->dfs_path == NULL) {
275*5331Samw 		bzero(param, sizeof (struct netdfs_getinfo));
276*5331Samw 		param->status = ERROR_INVALID_PARAMETER;
277*5331Samw 		return (MLRPC_DRC_OK);
278*5331Samw 	}
279*5331Samw 
280*5331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
281*5331Samw 		status = ERROR_INVALID_PARAMETER;
282*5331Samw 	} else {
283*5331Samw 		if (unc.share == NULL)
284*5331Samw 			status = ERROR_INVALID_SHARENAME;
285*5331Samw 	}
286*5331Samw 
287*5331Samw 	if (param->status != ERROR_SUCCESS) {
288*5331Samw 		bzero(param, sizeof (struct netdfs_getinfo));
289*5331Samw 		param->status = status;
290*5331Samw 		return (MLRPC_DRC_OK);
291*5331Samw 	}
292*5331Samw 
293*5331Samw 	switch (param->level) {
294*5331Samw 	case 1:
295*5331Samw 	case 2:
296*5331Samw 	case 3:
297*5331Samw 	case 4:
298*5331Samw 	case 100:
299*5331Samw 	case 101:
300*5331Samw 	case 102:
301*5331Samw 	case 103:
302*5331Samw 	case 104:
303*5331Samw 		break;
304*5331Samw 
305*5331Samw 	default:
306*5331Samw 		bzero(param, sizeof (struct netdfs_getinfo));
307*5331Samw 		param->status = ERROR_INVALID_LEVEL;
308*5331Samw 		return (MLRPC_DRC_OK);
309*5331Samw 	}
310*5331Samw 
311*5331Samw 	bzero(param, sizeof (struct netdfs_getinfo));
312*5331Samw 	param->status = ERROR_ACCESS_DENIED;
313*5331Samw 	return (MLRPC_DRC_OK);
314*5331Samw }
315*5331Samw 
316*5331Samw /*
317*5331Samw  * Get information about all of the volumes in the DFS. dfs_name is
318*5331Samw  * the "server" part of the UNC name used to refer to this particular
319*5331Samw  * DFS.
320*5331Samw  *
321*5331Samw  * Valid levels are 1-3.
322*5331Samw  */
323*5331Samw /*ARGSUSED*/
324*5331Samw static int
325*5331Samw netdfs_s_enum(void *arg, struct mlrpc_xaction *mxa)
326*5331Samw {
327*5331Samw 	struct netdfs_enum *param = arg;
328*5331Samw 
329*5331Samw 	switch (param->level) {
330*5331Samw 	case 1:
331*5331Samw 	case 2:
332*5331Samw 	case 3:
333*5331Samw 		break;
334*5331Samw 
335*5331Samw 	default:
336*5331Samw 		(void) bzero(param, sizeof (struct netdfs_enum));
337*5331Samw 		param->status = ERROR_INVALID_LEVEL;
338*5331Samw 		return (MLRPC_DRC_OK);
339*5331Samw 	}
340*5331Samw 
341*5331Samw 	(void) bzero(param, sizeof (struct netdfs_enum));
342*5331Samw 	param->status = ERROR_ACCESS_DENIED;
343*5331Samw 	return (MLRPC_DRC_OK);
344*5331Samw }
345*5331Samw 
346*5331Samw /*
347*5331Samw  * Move a DFS volume and all subordinate volumes from one place in the
348*5331Samw  * DFS to another place in the DFS.
349*5331Samw  */
350*5331Samw /*ARGSUSED*/
351*5331Samw static int
352*5331Samw netdfs_s_move(void *arg, struct mlrpc_xaction *mxa)
353*5331Samw {
354*5331Samw 	struct netdfs_move *param = arg;
355*5331Samw 
356*5331Samw 	if (param->dfs_path == NULL || param->new_path == NULL) {
357*5331Samw 		bzero(param, sizeof (struct netdfs_move));
358*5331Samw 		param->status = ERROR_INVALID_PARAMETER;
359*5331Samw 		return (MLRPC_DRC_OK);
360*5331Samw 	}
361*5331Samw 
362*5331Samw 	bzero(param, sizeof (struct netdfs_move));
363*5331Samw 	param->status = ERROR_ACCESS_DENIED;
364*5331Samw 	return (MLRPC_DRC_OK);
365*5331Samw }
366*5331Samw 
367*5331Samw /*
368*5331Samw  * Rename the current path in a DFS to a new path in the same DFS.
369*5331Samw  */
370*5331Samw /*ARGSUSED*/
371*5331Samw static int
372*5331Samw netdfs_s_rename(void *arg, struct mlrpc_xaction *mxa)
373*5331Samw {
374*5331Samw 	struct netdfs_rename *param = arg;
375*5331Samw 
376*5331Samw 	if (param->dfs_path == NULL || param->new_path == NULL) {
377*5331Samw 		bzero(param, sizeof (struct netdfs_rename));
378*5331Samw 		param->status = ERROR_INVALID_PARAMETER;
379*5331Samw 		return (MLRPC_DRC_OK);
380*5331Samw 	}
381*5331Samw 
382*5331Samw 	bzero(param, sizeof (struct netdfs_rename));
383*5331Samw 	param->status = ERROR_ACCESS_DENIED;
384*5331Samw 	return (MLRPC_DRC_OK);
385*5331Samw }
386*5331Samw 
387*5331Samw /*
388*5331Samw  * Add a DFS root share.
389*5331Samw  */
390*5331Samw /*ARGSUSED*/
391*5331Samw static int
392*5331Samw netdfs_s_addstdroot(void *arg, struct mlrpc_xaction *mxa)
393*5331Samw {
394*5331Samw 	struct netdfs_addstdroot *param = arg;
395*5331Samw 
396*5331Samw 	bzero(param, sizeof (struct netdfs_addstdroot));
397*5331Samw 	param->status = ERROR_INVALID_PARAMETER;
398*5331Samw 	return (MLRPC_DRC_OK);
399*5331Samw }
400*5331Samw 
401*5331Samw /*
402*5331Samw  * Remove a DFS root share.
403*5331Samw  */
404*5331Samw /*ARGSUSED*/
405*5331Samw static int
406*5331Samw netdfs_s_remstdroot(void *arg, struct mlrpc_xaction *mxa)
407*5331Samw {
408*5331Samw 	struct netdfs_remstdroot *param = arg;
409*5331Samw 
410*5331Samw 	bzero(param, sizeof (struct netdfs_remstdroot));
411*5331Samw 	param->status = ERROR_INVALID_PARAMETER;
412*5331Samw 	return (MLRPC_DRC_OK);
413*5331Samw }
414*5331Samw 
415*5331Samw /*
416*5331Samw  * Get information about all of the volumes in the DFS. dfs_path is
417*5331Samw  * the "server" part of the UNC name used to refer to this particular
418*5331Samw  * DFS.
419*5331Samw  *
420*5331Samw  * Valid levels are 1-3, 300.
421*5331Samw  */
422*5331Samw static int
423*5331Samw netdfs_s_enumex(void *arg, struct mlrpc_xaction *mxa)
424*5331Samw {
425*5331Samw 	struct netdfs_enumex *param = arg;
426*5331Samw 	netdfs_unc_t unc;
427*5331Samw 	DWORD status = ERROR_SUCCESS;
428*5331Samw 
429*5331Samw 	if (param->dfs_path == NULL) {
430*5331Samw 		bzero(param, sizeof (struct netdfs_enumex));
431*5331Samw 		param->status = ERROR_INVALID_PARAMETER;
432*5331Samw 		return (MLRPC_DRC_OK);
433*5331Samw 	}
434*5331Samw 
435*5331Samw 	if (param->resume_handle == NULL)
436*5331Samw 		param->resume_handle = MLRPC_HEAP_NEW(mxa, DWORD);
437*5331Samw 
438*5331Samw 	if (param->resume_handle)
439*5331Samw 		*(param->resume_handle) = 0;
440*5331Samw 
441*5331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
442*5331Samw 		status = ERROR_INVALID_PARAMETER;
443*5331Samw 	} else {
444*5331Samw 		if (unc.path == NULL)
445*5331Samw 			status = ERROR_BAD_PATHNAME;
446*5331Samw 
447*5331Samw 		if (unc.share == NULL)
448*5331Samw 			status = ERROR_INVALID_SHARENAME;
449*5331Samw 	}
450*5331Samw 
451*5331Samw 	if (param->status != ERROR_SUCCESS) {
452*5331Samw 		bzero(param, sizeof (struct netdfs_enumex));
453*5331Samw 		param->status = status;
454*5331Samw 		return (MLRPC_DRC_OK);
455*5331Samw 	}
456*5331Samw 
457*5331Samw 	param->info = MLRPC_HEAP_NEW(mxa, struct netdfs_enum_info);
458*5331Samw 	if (param->info == NULL) {
459*5331Samw 		bzero(param, sizeof (struct netdfs_enumex));
460*5331Samw 		param->status = ERROR_NOT_ENOUGH_MEMORY;
461*5331Samw 		return (MLRPC_DRC_OK);
462*5331Samw 	}
463*5331Samw 
464*5331Samw 	bzero(param->info, sizeof (struct netdfs_enumex));
465*5331Samw 	param->status = ERROR_SUCCESS;
466*5331Samw 	return (MLRPC_DRC_OK);
467*5331Samw }
468*5331Samw 
469*5331Samw /*
470*5331Samw  * Parse a UNC path (\\server\share\path) into components.
471*5331Samw  * Path separators are converted to forward slashes.
472*5331Samw  *
473*5331Samw  * Returns 0 on success, otherwise -1 to indicate an error.
474*5331Samw  */
475*5331Samw static int
476*5331Samw netdfs_unc_parse(struct mlrpc_xaction *mxa, const char *path, netdfs_unc_t *unc)
477*5331Samw {
478*5331Samw 	char *p;
479*5331Samw 
480*5331Samw 	if (path == NULL || unc == NULL)
481*5331Samw 		return (-1);
482*5331Samw 
483*5331Samw 	if ((unc->buf = MLRPC_HEAP_STRSAVE(mxa, (char *)path)) == NULL)
484*5331Samw 		return (-1);
485*5331Samw 
486*5331Samw 	if ((p = strchr(unc->buf, '\n')) != NULL)
487*5331Samw 		*p = '\0';
488*5331Samw 
489*5331Samw 	(void) strsubst(unc->buf, '\\', '/');
490*5331Samw 	(void) strcanon(unc->buf, "/");
491*5331Samw 
492*5331Samw 	unc->server = unc->buf;
493*5331Samw 	unc->server += strspn(unc->buf, "/");
494*5331Samw 
495*5331Samw 	if (unc->server) {
496*5331Samw 		unc->share = strchr(unc->server, '/');
497*5331Samw 		if ((p = unc->share) != NULL) {
498*5331Samw 			unc->share += strspn(unc->share, "/");
499*5331Samw 			*p = '\0';
500*5331Samw 		}
501*5331Samw 	}
502*5331Samw 
503*5331Samw 	if (unc->share) {
504*5331Samw 		unc->path = strchr(unc->share, '/');
505*5331Samw 		if ((p = unc->path) != NULL) {
506*5331Samw 			unc->path += strspn(unc->path, "/");
507*5331Samw 			*p = '\0';
508*5331Samw 		}
509*5331Samw 	}
510*5331Samw 
511*5331Samw 	if (unc->path) {
512*5331Samw 		if ((p = strchr(unc->path, '\0')) != NULL) {
513*5331Samw 			if (*(--p) == '/')
514*5331Samw 				*p = '\0';
515*5331Samw 		}
516*5331Samw 	}
517*5331Samw 
518*5331Samw 	return (0);
519*5331Samw }
520