xref: /onnv-gate/usr/src/lib/smbsrv/libmlsvc/common/netdfs.c (revision 8334:5f1c6a3b0fad)
15331Samw /*
25331Samw  * CDDL HEADER START
35331Samw  *
45331Samw  * The contents of this file are subject to the terms of the
55331Samw  * Common Development and Distribution License (the "License").
65331Samw  * You may not use this file except in compliance with the License.
75331Samw  *
85331Samw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95331Samw  * or http://www.opensolaris.org/os/licensing.
105331Samw  * See the License for the specific language governing permissions
115331Samw  * and limitations under the License.
125331Samw  *
135331Samw  * When distributing Covered Code, include this CDDL HEADER in each
145331Samw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155331Samw  * If applicable, add the following below this CDDL HEADER, with the
165331Samw  * fields enclosed by brackets "[]" replaced with your own identifying
175331Samw  * information: Portions Copyright [yyyy] [name of copyright owner]
185331Samw  *
195331Samw  * CDDL HEADER END
205331Samw  */
215331Samw /*
22*8334SJose.Borrego@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235331Samw  * Use is subject to license terms.
245331Samw  */
255331Samw 
265331Samw /*
275331Samw  * Net DFS server side RPC service.
285331Samw  */
295331Samw 
305331Samw #include <sys/types.h>
315331Samw #include <strings.h>
325331Samw #include <string.h>
335331Samw 
345331Samw #include <smbsrv/libsmb.h>
355331Samw #include <smbsrv/lmerr.h>
365331Samw #include <smbsrv/lmdfs.h>
375331Samw #include <smbsrv/nmpipes.h>
385331Samw #include <smbsrv/nterror.h>
39*8334SJose.Borrego@Sun.COM #include <smbsrv/libmlrpc.h>
405331Samw #include <smbsrv/ndl/netdfs.ndl>
415331Samw 
425331Samw typedef struct {
435331Samw 	char *server;
445331Samw 	char *share;
455331Samw 	char *path;
465331Samw 	char *buf;
475331Samw } netdfs_unc_t;
485331Samw 
49*8334SJose.Borrego@Sun.COM static int netdfs_unc_parse(ndr_xa_t *, const char *,
505331Samw     netdfs_unc_t *);
515331Samw 
52*8334SJose.Borrego@Sun.COM static int netdfs_s_getver(void *, ndr_xa_t *);
53*8334SJose.Borrego@Sun.COM static int netdfs_s_add(void *, ndr_xa_t *);
54*8334SJose.Borrego@Sun.COM static int netdfs_s_remove(void *, ndr_xa_t *);
55*8334SJose.Borrego@Sun.COM static int netdfs_s_setinfo(void *, ndr_xa_t *);
56*8334SJose.Borrego@Sun.COM static int netdfs_s_getinfo(void *, ndr_xa_t *);
57*8334SJose.Borrego@Sun.COM static int netdfs_s_enum(void *, ndr_xa_t *);
58*8334SJose.Borrego@Sun.COM static int netdfs_s_move(void *, ndr_xa_t *);
59*8334SJose.Borrego@Sun.COM static int netdfs_s_rename(void *, ndr_xa_t *);
60*8334SJose.Borrego@Sun.COM static int netdfs_s_addstdroot(void *, ndr_xa_t *);
61*8334SJose.Borrego@Sun.COM static int netdfs_s_remstdroot(void *, ndr_xa_t *);
62*8334SJose.Borrego@Sun.COM static int netdfs_s_enumex(void *, ndr_xa_t *);
635331Samw 
64*8334SJose.Borrego@Sun.COM static ndr_stub_table_t netdfs_stub_table[] = {
655331Samw 	{ netdfs_s_getver,	NETDFS_OPNUM_GETVER },
665331Samw 	{ netdfs_s_add,		NETDFS_OPNUM_ADD },
675331Samw 	{ netdfs_s_remove,	NETDFS_OPNUM_REMOVE },
685331Samw 	{ netdfs_s_setinfo,	NETDFS_OPNUM_SETINFO },
695331Samw 	{ netdfs_s_getinfo,	NETDFS_OPNUM_GETINFO },
705331Samw 	{ netdfs_s_enum,	NETDFS_OPNUM_ENUM },
715331Samw 	{ netdfs_s_rename,	NETDFS_OPNUM_RENAME },
725331Samw 	{ netdfs_s_move,	NETDFS_OPNUM_MOVE },
735331Samw 	{ netdfs_s_addstdroot,	NETDFS_OPNUM_ADDSTDROOT },
745331Samw 	{ netdfs_s_remstdroot,	NETDFS_OPNUM_REMSTDROOT },
755331Samw 	{ netdfs_s_enumex,	NETDFS_OPNUM_ENUMEX },
765331Samw 	{0}
775331Samw };
785331Samw 
79*8334SJose.Borrego@Sun.COM static ndr_service_t netdfs_service = {
805331Samw 	"NETDFS",			/* name */
815331Samw 	"DFS",				/* desc */
825331Samw 	"\\dfs",			/* endpoint */
835331Samw 	PIPE_NTSVCS,			/* sec_addr_port */
845331Samw 	NETDFS_ABSTRACT_UUID,	NETDFS_ABSTRACT_VERS,
855331Samw 	NETDFS_TRANSFER_UUID,	NETDFS_TRANSFER_VERS,
865331Samw 
875331Samw 	0,				/* no bind_instance_size */
885331Samw 	0,				/* no bind_req() */
895331Samw 	0,				/* no unbind_and_close() */
905331Samw 	0,				/* use generic_call_stub() */
915331Samw 
925331Samw 	&TYPEINFO(netdfs_interface),	/* interface ti */
935331Samw 	netdfs_stub_table		/* stub_table */
945331Samw };
955331Samw 
965331Samw /*
975331Samw  * Register the NETDFS RPC interface with the RPC runtime library.
985331Samw  * The service must be registered in order to use either the client
995331Samw  * side or the server side functions.
1005331Samw  */
1015331Samw void
1025331Samw netdfs_initialize(void)
1035331Samw {
104*8334SJose.Borrego@Sun.COM 	(void) ndr_svc_register(&netdfs_service);
1055331Samw }
1065331Samw 
1075331Samw /*
1085331Samw  * Return the version.
1095331Samw  *
1105331Samw  * We have to indicate that we emulate a Windows 2003 Server or the
1115331Samw  * client will not use the EnumEx RPC and this would limit support
1125331Samw  * to a single DFS root.
1135331Samw  */
1145331Samw /*ARGSUSED*/
1155331Samw static int
116*8334SJose.Borrego@Sun.COM netdfs_s_getver(void *arg, ndr_xa_t *mxa)
1175331Samw {
1185331Samw 	struct netdfs_getver *param = arg;
1195331Samw 
1205331Samw 	param->version = DFS_MANAGER_VERSION_W2K3;
121*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
1225331Samw }
1235331Samw 
1245331Samw /*
1255331Samw  * Add a new volume or additional storage for an existing volume at
1265331Samw  * dfs_path.
1275331Samw  */
1285331Samw static int
129*8334SJose.Borrego@Sun.COM netdfs_s_add(void *arg, ndr_xa_t *mxa)
1305331Samw {
1315331Samw 	struct netdfs_add *param = arg;
1325331Samw 	netdfs_unc_t unc;
1335331Samw 	DWORD status = ERROR_SUCCESS;
1345331Samw 
1355331Samw 	if (param->dfs_path == NULL || param->server == NULL ||
1365331Samw 	    param->share == NULL) {
1375331Samw 		bzero(param, sizeof (struct netdfs_add));
1385331Samw 		param->status = ERROR_INVALID_PARAMETER;
139*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
1405331Samw 	}
1415331Samw 
1425331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
1435331Samw 		status = ERROR_INVALID_PARAMETER;
1445331Samw 	} else {
1455331Samw 		if (unc.path == NULL)
1465331Samw 			status = ERROR_BAD_PATHNAME;
1475331Samw 
1485331Samw 		if (unc.share == NULL)
1495331Samw 			status = ERROR_INVALID_SHARENAME;
1505331Samw 	}
1515331Samw 
1525331Samw 	if (param->status != ERROR_SUCCESS) {
1535331Samw 		bzero(param, sizeof (struct netdfs_add));
1545331Samw 		param->status = status;
155*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
1565331Samw 	}
1575331Samw 
1585331Samw 	bzero(param, sizeof (struct netdfs_add));
1595331Samw 	param->status = ERROR_ACCESS_DENIED;
160*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
1615331Samw }
1625331Samw 
1635331Samw /*
1645331Samw  * netdfs_s_remove
1655331Samw  *
1665331Samw  * Remove a volume or additional storage for volume from the DFS at
1675331Samw  * dfs_path. When applied to the last storage in a volume, removes
1685331Samw  * the volume from the DFS.
1695331Samw  */
1705331Samw static int
171*8334SJose.Borrego@Sun.COM netdfs_s_remove(void *arg, ndr_xa_t *mxa)
1725331Samw {
1735331Samw 	struct netdfs_remove *param = arg;
1745331Samw 	netdfs_unc_t unc;
1755331Samw 	DWORD status = ERROR_SUCCESS;
1765331Samw 
1775331Samw 	if (param->dfs_path == NULL || param->server == NULL ||
1785331Samw 	    param->share == NULL) {
1795331Samw 		bzero(param, sizeof (struct netdfs_remove));
1805331Samw 		param->status = ERROR_INVALID_PARAMETER;
181*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
1825331Samw 	}
1835331Samw 
1845331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
1855331Samw 		status = ERROR_INVALID_PARAMETER;
1865331Samw 	} else {
1875331Samw 		if (unc.path == NULL)
1885331Samw 			status = ERROR_BAD_PATHNAME;
1895331Samw 
1905331Samw 		if (unc.share == NULL)
1915331Samw 			status = ERROR_INVALID_SHARENAME;
1925331Samw 	}
1935331Samw 
1945331Samw 	if (param->status != ERROR_SUCCESS) {
1955331Samw 		bzero(param, sizeof (struct netdfs_remove));
1965331Samw 		param->status = status;
197*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
1985331Samw 	}
1995331Samw 
2005331Samw 	bzero(param, sizeof (struct netdfs_remove));
2015331Samw 	param->status = ERROR_ACCESS_DENIED;
202*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
2035331Samw }
2045331Samw 
2055331Samw /*
2065331Samw  * Set information about the volume or storage. If the server and share
2075331Samw  * are specified, the information set is specific to that server and
2085331Samw  * share. Otherwise the information is specific to the volume as a whole.
2095331Samw  *
2105331Samw  * Valid levels are 100-102.
2115331Samw  */
2125331Samw /*ARGSUSED*/
2135331Samw static int
214*8334SJose.Borrego@Sun.COM netdfs_s_setinfo(void *arg, ndr_xa_t *mxa)
2155331Samw {
2165331Samw 	struct netdfs_setinfo *param = arg;
2175331Samw 	netdfs_unc_t unc;
2185331Samw 	DWORD status = ERROR_SUCCESS;
2195331Samw 
2205331Samw 	if (param->dfs_path == NULL) {
2215331Samw 		bzero(param, sizeof (struct netdfs_setinfo));
2225331Samw 		param->status = ERROR_INVALID_PARAMETER;
223*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
2245331Samw 	}
2255331Samw 
2265331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
2275331Samw 		status = ERROR_INVALID_PARAMETER;
2285331Samw 	} else {
2295331Samw 		if (unc.share == NULL)
2305331Samw 			status = ERROR_INVALID_SHARENAME;
2315331Samw 	}
2325331Samw 
2335331Samw 	if (param->status != ERROR_SUCCESS) {
2345331Samw 		bzero(param, sizeof (struct netdfs_setinfo));
2355331Samw 		param->status = status;
236*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
2375331Samw 	}
2385331Samw 
2395331Samw 	switch (param->info.level) {
2405331Samw 	case 100:
2415331Samw 	case 101:
2425331Samw 	case 102:
2435331Samw 		break;
2445331Samw 
2455331Samw 	default:
2465331Samw 		bzero(param, sizeof (struct netdfs_setinfo));
2475331Samw 		param->status = ERROR_INVALID_LEVEL;
248*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
2495331Samw 	}
2505331Samw 
2515331Samw 	bzero(param, sizeof (struct netdfs_setinfo));
2525331Samw 	param->status = ERROR_ACCESS_DENIED;
253*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
2545331Samw }
2555331Samw 
2565331Samw /*
2575331Samw  * Get information about the volume or storage. If the server and share
2585331Samw  * are specified, the information returned is specific to that server
2595331Samw  * and share. Otherwise the information is specific to the volume as a
2605331Samw  * whole.
2615331Samw  *
2625331Samw  * Valid levels are 1-4, 100-104.
2635331Samw  */
2645331Samw /*ARGSUSED*/
2655331Samw static int
266*8334SJose.Borrego@Sun.COM netdfs_s_getinfo(void *arg, ndr_xa_t *mxa)
2675331Samw {
2685331Samw 	struct netdfs_getinfo *param = arg;
2695331Samw 	netdfs_unc_t unc;
2705331Samw 	DWORD status = ERROR_SUCCESS;
2715331Samw 
2725331Samw 	if (param->dfs_path == NULL) {
2735331Samw 		bzero(param, sizeof (struct netdfs_getinfo));
2745331Samw 		param->status = ERROR_INVALID_PARAMETER;
275*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
2765331Samw 	}
2775331Samw 
2785331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
2795331Samw 		status = ERROR_INVALID_PARAMETER;
2805331Samw 	} else {
2815331Samw 		if (unc.share == NULL)
2825331Samw 			status = ERROR_INVALID_SHARENAME;
2835331Samw 	}
2845331Samw 
2855331Samw 	if (param->status != ERROR_SUCCESS) {
2865331Samw 		bzero(param, sizeof (struct netdfs_getinfo));
2875331Samw 		param->status = status;
288*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
2895331Samw 	}
2905331Samw 
2915331Samw 	switch (param->level) {
2925331Samw 	case 1:
2935331Samw 	case 2:
2945331Samw 	case 3:
2955331Samw 	case 4:
2965331Samw 	case 100:
2975331Samw 	case 101:
2985331Samw 	case 102:
2995331Samw 	case 103:
3005331Samw 	case 104:
3015331Samw 		break;
3025331Samw 
3035331Samw 	default:
3045331Samw 		bzero(param, sizeof (struct netdfs_getinfo));
3055331Samw 		param->status = ERROR_INVALID_LEVEL;
306*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
3075331Samw 	}
3085331Samw 
3095331Samw 	bzero(param, sizeof (struct netdfs_getinfo));
3105331Samw 	param->status = ERROR_ACCESS_DENIED;
311*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
3125331Samw }
3135331Samw 
3145331Samw /*
3155331Samw  * Get information about all of the volumes in the DFS. dfs_name is
3165331Samw  * the "server" part of the UNC name used to refer to this particular
3175331Samw  * DFS.
3185331Samw  *
3195331Samw  * Valid levels are 1-3.
3205331Samw  */
3215331Samw /*ARGSUSED*/
3225331Samw static int
323*8334SJose.Borrego@Sun.COM netdfs_s_enum(void *arg, ndr_xa_t *mxa)
3245331Samw {
3255331Samw 	struct netdfs_enum *param = arg;
3265331Samw 
3275331Samw 	switch (param->level) {
3285331Samw 	case 1:
3295331Samw 	case 2:
3305331Samw 	case 3:
3315331Samw 		break;
3325331Samw 
3335331Samw 	default:
3345331Samw 		(void) bzero(param, sizeof (struct netdfs_enum));
3355331Samw 		param->status = ERROR_INVALID_LEVEL;
336*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
3375331Samw 	}
3385331Samw 
3395331Samw 	(void) bzero(param, sizeof (struct netdfs_enum));
3405331Samw 	param->status = ERROR_ACCESS_DENIED;
341*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
3425331Samw }
3435331Samw 
3445331Samw /*
3455331Samw  * Move a DFS volume and all subordinate volumes from one place in the
3465331Samw  * DFS to another place in the DFS.
3475331Samw  */
3485331Samw /*ARGSUSED*/
3495331Samw static int
350*8334SJose.Borrego@Sun.COM netdfs_s_move(void *arg, ndr_xa_t *mxa)
3515331Samw {
3525331Samw 	struct netdfs_move *param = arg;
3535331Samw 
3545331Samw 	if (param->dfs_path == NULL || param->new_path == NULL) {
3555331Samw 		bzero(param, sizeof (struct netdfs_move));
3565331Samw 		param->status = ERROR_INVALID_PARAMETER;
357*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
3585331Samw 	}
3595331Samw 
3605331Samw 	bzero(param, sizeof (struct netdfs_move));
3615331Samw 	param->status = ERROR_ACCESS_DENIED;
362*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
3635331Samw }
3645331Samw 
3655331Samw /*
3665331Samw  * Rename the current path in a DFS to a new path in the same DFS.
3675331Samw  */
3685331Samw /*ARGSUSED*/
3695331Samw static int
370*8334SJose.Borrego@Sun.COM netdfs_s_rename(void *arg, ndr_xa_t *mxa)
3715331Samw {
3725331Samw 	struct netdfs_rename *param = arg;
3735331Samw 
3745331Samw 	if (param->dfs_path == NULL || param->new_path == NULL) {
3755331Samw 		bzero(param, sizeof (struct netdfs_rename));
3765331Samw 		param->status = ERROR_INVALID_PARAMETER;
377*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
3785331Samw 	}
3795331Samw 
3805331Samw 	bzero(param, sizeof (struct netdfs_rename));
3815331Samw 	param->status = ERROR_ACCESS_DENIED;
382*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
3835331Samw }
3845331Samw 
3855331Samw /*
3865331Samw  * Add a DFS root share.
3875331Samw  */
3885331Samw /*ARGSUSED*/
3895331Samw static int
390*8334SJose.Borrego@Sun.COM netdfs_s_addstdroot(void *arg, ndr_xa_t *mxa)
3915331Samw {
3925331Samw 	struct netdfs_addstdroot *param = arg;
3935331Samw 
3945331Samw 	bzero(param, sizeof (struct netdfs_addstdroot));
3955331Samw 	param->status = ERROR_INVALID_PARAMETER;
396*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
3975331Samw }
3985331Samw 
3995331Samw /*
4005331Samw  * Remove a DFS root share.
4015331Samw  */
4025331Samw /*ARGSUSED*/
4035331Samw static int
404*8334SJose.Borrego@Sun.COM netdfs_s_remstdroot(void *arg, ndr_xa_t *mxa)
4055331Samw {
4065331Samw 	struct netdfs_remstdroot *param = arg;
4075331Samw 
4085331Samw 	bzero(param, sizeof (struct netdfs_remstdroot));
4095331Samw 	param->status = ERROR_INVALID_PARAMETER;
410*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
4115331Samw }
4125331Samw 
4135331Samw /*
4145331Samw  * Get information about all of the volumes in the DFS. dfs_path is
4155331Samw  * the "server" part of the UNC name used to refer to this particular
4165331Samw  * DFS.
4175331Samw  *
4185331Samw  * Valid levels are 1-3, 300.
4195331Samw  */
4205331Samw static int
421*8334SJose.Borrego@Sun.COM netdfs_s_enumex(void *arg, ndr_xa_t *mxa)
4225331Samw {
4235331Samw 	struct netdfs_enumex *param = arg;
4245331Samw 	netdfs_unc_t unc;
4255331Samw 	DWORD status = ERROR_SUCCESS;
4265331Samw 
4275331Samw 	if (param->dfs_path == NULL) {
4285331Samw 		bzero(param, sizeof (struct netdfs_enumex));
4295331Samw 		param->status = ERROR_INVALID_PARAMETER;
430*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
4315331Samw 	}
4325331Samw 
4335331Samw 	if (param->resume_handle == NULL)
434*8334SJose.Borrego@Sun.COM 		param->resume_handle = NDR_NEW(mxa, DWORD);
4355331Samw 
4365331Samw 	if (param->resume_handle)
4375331Samw 		*(param->resume_handle) = 0;
4385331Samw 
4395331Samw 	if (netdfs_unc_parse(mxa, (char *)param->dfs_path, &unc) != 0) {
4405331Samw 		status = ERROR_INVALID_PARAMETER;
4415331Samw 	} else {
4425331Samw 		if (unc.path == NULL)
4435331Samw 			status = ERROR_BAD_PATHNAME;
4445331Samw 
4455331Samw 		if (unc.share == NULL)
4465331Samw 			status = ERROR_INVALID_SHARENAME;
4475331Samw 	}
4485331Samw 
4495331Samw 	if (param->status != ERROR_SUCCESS) {
4505331Samw 		bzero(param, sizeof (struct netdfs_enumex));
4515331Samw 		param->status = status;
452*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
4535331Samw 	}
4545331Samw 
455*8334SJose.Borrego@Sun.COM 	param->info = NDR_NEW(mxa, struct netdfs_enum_info);
4565331Samw 	if (param->info == NULL) {
4575331Samw 		bzero(param, sizeof (struct netdfs_enumex));
4585331Samw 		param->status = ERROR_NOT_ENOUGH_MEMORY;
459*8334SJose.Borrego@Sun.COM 		return (NDR_DRC_OK);
4605331Samw 	}
4615331Samw 
4625331Samw 	bzero(param->info, sizeof (struct netdfs_enumex));
4635331Samw 	param->status = ERROR_SUCCESS;
464*8334SJose.Borrego@Sun.COM 	return (NDR_DRC_OK);
4655331Samw }
4665331Samw 
4675331Samw /*
4685331Samw  * Parse a UNC path (\\server\share\path) into components.
4695331Samw  * Path separators are converted to forward slashes.
4705331Samw  *
4715331Samw  * Returns 0 on success, otherwise -1 to indicate an error.
4725331Samw  */
4735331Samw static int
474*8334SJose.Borrego@Sun.COM netdfs_unc_parse(ndr_xa_t *mxa, const char *path, netdfs_unc_t *unc)
4755331Samw {
4765331Samw 	char *p;
4775331Samw 
4785331Samw 	if (path == NULL || unc == NULL)
4795331Samw 		return (-1);
4805331Samw 
481*8334SJose.Borrego@Sun.COM 	if ((unc->buf = NDR_STRDUP(mxa, (char *)path)) == NULL)
4825331Samw 		return (-1);
4835331Samw 
4845331Samw 	if ((p = strchr(unc->buf, '\n')) != NULL)
4855331Samw 		*p = '\0';
4865331Samw 
4875331Samw 	(void) strsubst(unc->buf, '\\', '/');
4885331Samw 	(void) strcanon(unc->buf, "/");
4895331Samw 
4905331Samw 	unc->server = unc->buf;
4915331Samw 	unc->server += strspn(unc->buf, "/");
4925331Samw 
4935331Samw 	if (unc->server) {
4945331Samw 		unc->share = strchr(unc->server, '/');
4955331Samw 		if ((p = unc->share) != NULL) {
4965331Samw 			unc->share += strspn(unc->share, "/");
4975331Samw 			*p = '\0';
4985331Samw 		}
4995331Samw 	}
5005331Samw 
5015331Samw 	if (unc->share) {
5025331Samw 		unc->path = strchr(unc->share, '/');
5035331Samw 		if ((p = unc->path) != NULL) {
5045331Samw 			unc->path += strspn(unc->path, "/");
5055331Samw 			*p = '\0';
5065331Samw 		}
5075331Samw 	}
5085331Samw 
5095331Samw 	if (unc->path) {
5105331Samw 		if ((p = strchr(unc->path, '\0')) != NULL) {
5115331Samw 			if (*(--p) == '/')
5125331Samw 				*p = '\0';
5135331Samw 		}
5145331Samw 	}
5155331Samw 
5165331Samw 	return (0);
5175331Samw }
518