/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <strings.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <dlfcn.h>
#include <pthread.h>
#include <syslog.h>
#include <assert.h>
#include <libgen.h>
#include <note.h>
#include <sys/fs_reparse.h>
#include <smbsrv/libntsvcs.h>
#include <smbsrv/smb_share.h>
#include "smb_reparse.h"
#include "dfs.h"
static struct {
} dfs_intr_ops;
/*
* DFS Namespace
*
* Currently, only ONE standalone namespace is supported.
*
* ns_name The name of the exported namespace. This will be the only
* exported namespace until hosting multiple namespaces
* is supported.
*
* ns_path The filesystem path of the root share.
*
* ns_exported B_TRUE if a standalone namespace is exported
*
* ns_cache Caches links' UNC and filesystem path where
* the key is the UNC path.
*/
typedef struct dfs_ns {
} dfs_ns_t;
/*
* Namespace cache node operations
*/
static int dfs_node_cmp(const void *, const void *);
static void dfs_node_destroy(void *);
dfs_node_cmp, /* compare */
NULL, /* add */
dfs_node_destroy, /* remove */
NULL, /* hold */
NULL, /* rele */
dfs_node_destroy /* flush */
};
/*
* System's NetBIOS name
*/
/*
* Lock for accessing root information (extended attribute)
*/
static struct {
const char *svc_name;
} dfs_ns_info[] = {
};
/*
* Namespace functions
*/
static void dfs_ns_load(const char *, const char *);
static void dfs_ns_unload(const char *);
static void dfs_ns_cleanup(const char *, uint32_t);
static void dfs_ns_populate_cache(const char *, const char *);
/*
* Root functions
*/
static int dfs_root_add(const char *, dfs_info_t *);
static uint32_t dfs_root_remove(const char *);
static int dfs_root_xopen(const char *, int);
static void dfs_root_xclose(int);
/*
* Link functions
*/
/*
* Target functions
*/
static void dfs_target_init(dfs_target_t *, const char *, const char *,
uint32_t);
const char *);
/*
* Utility functions
*/
static boolean_t dfs_path_isdir(const char *);
static void dfs_path_create(const char *);
static const char *dfs_svc_name(uint32_t);
static uint32_t dfs_ns_type(const char *);
/*
* DFS module initialization:
*
* - gets system's NetBIOS name
* - installs interposition ops
*/
void
dfs_init(void)
{
return;
return;
if ((dfs_intr_ops.dfsops_remote_count =
dfs_intr_hdl = NULL;
}
}
/*
* DFS module cleanup:
*
* - destroys the namespace cache
*/
void
dfs_fini(void)
{
}
/*
* file system operations need to be performed. These operations
* should take place on behalf of the connected user (typically
* Administrator) and to do so we need to have an infrastructure
* in place so that smbd can act as a client and sends request to
* the kernel. Right now, we lack this infrastructure, so we make
* a compromise by temporarily enabling some privileges for smbd
*/
void
{
}
/*
* ========================
* Namespace API (public)
* ========================
*/
/*
* Sets up a dfs_ns_t structure for the specified namespace
* (root share) if a namespace hasn't already been exported.
*/
void *
{
if (dfsns.ns_exported) {
" Only one standalone namespace is supported."
" A namespace is already exported for %s:%s.",
}
return (NULL);
}
return (NULL);
}
smb_share_free(&si);
return (NULL);
}
smb_share_free(&si);
return (NULL);
}
smb_share_free(&si);
return (NULL);
}
/*
* If the specified namespace is exported, the cache
* will be destroyed and the namespace is marked as
* not exported.
*
* If no name is specified then the active namespace
* will be unexported if there is one.
*/
void
{
if (DFS_NS_EXPORTED(name)) {
}
}
/*
* Returns the file system path for the given namespace.
*/
static uint32_t
{
return (ERROR_INVALID_PARAMETER);
} else {
}
return (status);
}
/*
* Returns the number of DFS root shares i.e. the number
* of standalone namespaces.
*
* The caller must be holding the namespace lock (dfsns.ns_lock)
* for at least reading.
*/
dfs_ns_count(void)
{
int rc;
/*
* If this call fails, let's assume there's at least one root
* namespace already configured. The interposer library cannot
* confirm or deny the presence of a namespace, so let's take
* the safe approach and assume one exists.
*/
nroot = 1;
"assuming one namespace exists", rc);
}
if (dfsns.ns_exported)
nroot++;
return (nroot);
}
/*
* Creates a DFS root with the given name and comment.
*
* This function does not create the root share, it
* should already exist.
*/
{
dfs_target_t t;
if (*rootshr == '\\') {
/* Windows has a special case here! */
return (ERROR_BAD_PATHNAME);
}
if (!dfs_isvalid_nstype(ns_type))
return (ERROR_NOT_SUPPORTED);
/* For now only allow a single standalone namespace */
if (dfs_ns_count() > 0) {
return (ERROR_NOT_SUPPORTED);
}
if (dfsns.ns_exported) {
"dfs: trying to create %s:%s namespace."
" Only one standalone namespace is supported."
" A namespace is already exported for %s:%s",
return (ERROR_NOT_SUPPORTED);
}
/* This DFS root is already exported */
return (ERROR_FILE_EXISTS);
}
" Only one standalone namespace is supported."
" A namespace is already exported for %s:%s.",
return (ERROR_NOT_SUPPORTED);
}
return (NERR_NetNameNotFound);
}
if (cmnt)
info.i_propflags = 0;
smb_share_free(&si);
return (status);
}
if (status == ERROR_SUCCESS) {
}
smb_share_free(&si);
return (status);
}
/*
* Removes the namespace and all the links in it.
*/
{
return (ERROR_NOT_FOUND);
smb_share_free(&si);
return (ERROR_NOT_FOUND);
}
smb_share_free(&si);
return (ERROR_NOT_FOUND);
}
smb_share_free(&si);
return (ERROR_NOT_FOUND);
}
if (status != ERROR_SUCCESS) {
smb_share_free(&si);
return (status);
}
smb_share_free(&si);
return (status);
}
/*
* do not remove links for fedfs namespaces
* When fedfs namespaces are destroyed, the
* entire dataset will be destroyed.
*/
if (ns_type != SMB_NS_FEDFS)
smb_share_free(&si);
return (ERROR_SUCCESS);
}
/*
* Determines the DFS namespace flavor.
*/
{
return (0);
/* get flavor info from state info (info level 2) */
return (0);
}
/*
* Adds the given target to the link in the specified namespace.
* It will update the cache if this is a new link
*/
{
return (ERROR_INVALID_PARAMETER);
ns_type);
if (status != ERROR_SUCCESS) {
return (status);
}
if (!newlink)
return (status);
}
}
return (status);
}
/*
* Removes the given target from the link in the specified namespace.
*
* If the link is removed as a result the cache will be updated.
*/
{
return (ERROR_INVALID_PARAMETER);
if (status != ERROR_SUCCESS)
return (status);
return (ERROR_SUCCESS);
if (!DFS_STAT_ISSMB(stat)) {
/* relpath may contain '/' */
}
}
/*
* if link is removed then try to remove its
* empty parent directories if any
*/
if (stat == DFS_STAT_NOTFOUND)
return (status);
}
/*
* Returns the number of links + 1 (for root) in the
* specified namespace if this is the exported one
*
* The caller must be holding the namespace lock
* (dfsns.ns_lock) for writing.
*/
{
if (DFS_NS_EXPORTED(name)) {
}
return (num);
}
/*
* Returns B_TRUE if a namespace of the specified
* type exists in namespace cache.
*
* The caller must be holding the namespace lock
* (dfsns.ns_lock) for at least reading.
*/
{
return (B_TRUE);
else
return (B_FALSE);
}
/*
* Locks the namespace for writing.
* Used during iteration.
*/
void
{
}
/*
* Unlocks the namespace
*/
void
{
}
/*
* Returns the first node in the namespace cache.
* The first node in the cache is the root of the
* namespace.
*
* The caller must be holding the namespace lock
* (dfsns.ns_lock) for at least reading.
*/
{
}
/*
* Returns the next node in the namespace cache after
* the passed 'node'
*
* The caller must be holding the namespace lock
* (dfsns.ns_lock) for at least reading.
*/
{
}
/*
* ==================
* Root API (public)
* ==================
*/
/*
* Retrieves the information of the root specified by its path.
*
* Info level (1) only needs the UNC path which is not stored,
* it is constructed so the function will return without
* accessing the backend storage.
*
* The dfs root in backend storage must be of the same namespace
* type as requested.
*/
{
int xfd;
if (infolvl == 1)
return (ERROR_SUCCESS);
(void) rw_rdlock(&dfs_root_rwl);
}
(void) rw_unlock(&dfs_root_rwl);
if (status == ERROR_SUCCESS) {
}
}
return (status);
}
/*
* Sets the provided information for the specified root or root target.
* Root is specified by 'rootdir' and the target is specified by
* (t_server, t_share) pair. Only information items needed for given
* information level (infolvl) is valid in the passed DFS info structure
* 'info'.
*/
{
int xfd;
(void) rw_wrlock(&dfs_root_rwl);
(void) rw_unlock(&dfs_root_rwl);
return (ERROR_INTERNAL_ERROR);
}
if (status != ERROR_SUCCESS) {
(void) rw_unlock(&dfs_root_rwl);
return (status);
}
(void) rw_unlock(&dfs_root_rwl);
return (ERROR_NOT_FOUND);
}
if (status == ERROR_SUCCESS)
(void) rw_unlock(&dfs_root_rwl);
return (status);
}
/*
* ==================
* Link API (public)
* ==================
*/
/*
* Gets the status of the given path as a link
*/
{
int svctype;
return (ERROR_INTERNAL_ERROR);
switch (*stat) {
case SMB_REPARSE_NOTFOUND:
break;
case SMB_REPARSE_NOTREPARSE:
*stat = DFS_STAT_NOTLINK;
break;
case SMB_REPARSE_ISREPARSE:
switch (svctype) {
case SMB_SVCTYPE_DFS:
*stat = DFS_STAT_ISDFS;
break;
case SMB_SVCTYPE_FEDFS:
*stat = DFS_STAT_ISFEDFS;
break;
default:
break;
}
}
break;
default:
*stat = DFS_STAT_UNKNOWN;
break;
}
return (ERROR_SUCCESS);
}
/*
* Creates a new DFS link or adds a new target to an existing link
*
* Optional verification of link target existence is not implemented.
*
* From the spec:
* If DFS_RESTORE_VOLUME is not specified on the Flags parameter,
* the server MAY choose to verify whether the link target exists.
* If DFS_RESTORE_VOLUME is specified, the server MUST NOT perform
* this test. If it performs the test and the link target does not
* exist, the server MUST fail the call with NERR_NetNameNotFound.
*
*/
{
dfs_target_t *t;
int ntargets;
return (status);
switch (stat) {
case DFS_STAT_NOTFOUND:
case DFS_STAT_ISREPARSE:
/* Create a new DFS link */
ns_type);
if (status != ERROR_SUCCESS)
return (status);
break;
case DFS_STAT_ISDFS:
case DFS_STAT_ISFEDFS:
/*
* A reparse point can not contain both DFS and
* FEDFS referrals
*/
return (ERROR_FILE_EXISTS);
/*
* Add a target to an existing link only
* if DFS_ADD_VOLUME flag is not specified.
* The comment MUST be ignored when
* adding a target to an existing link.
*/
if (flags & DFS_ADD_VOLUME)
return (ERROR_FILE_EXISTS);
ns_type);
if (status != ERROR_SUCCESS)
return (status);
break;
case DFS_STAT_NOTLINK:
/* specified path points to a non-reparse object */
return (ERROR_FILE_EXISTS);
default:
return (ERROR_INTERNAL_ERROR);
}
/* checks to see if the target already exists */
return (ERROR_FILE_EXISTS);
}
/* add the new target */
if (t == NULL) {
return (ERROR_NOT_ENOUGH_MEMORY);
}
info.i_ntargets++;
return (status);
}
/*
* Removes a link or a link target from a DFS namespace. A link can be
* removed regardless of the number of targets associated with it.
*
* 'server' and 'share' parameters specify a target, so if they are NULL
* it means the link should be removed, otherwise the specified target
* is removed if found.
*/
{
return (status);
return (ERROR_NOT_FOUND);
/* remove the link */
return (ERROR_INTERNAL_ERROR);
return (ERROR_SUCCESS);
}
/* remove the specified target in the link */
if (status != ERROR_SUCCESS)
return (status);
/* checks to see if the target exists */
if (idx != -1) {
info.i_ntargets--;
} else {
return (ERROR_FILE_NOT_FOUND);
}
if (info.i_ntargets == 0) {
/* if last target, then remove the link */
} else {
}
return (status);
}
/*
* Sets the provided information for the specified link or link target.
* Link is specified by 'path' and the target is specified by
* (t_server, t_share) pair. Only information items needed for given
* information level (infolvl) is valid in the passed DFS info structure
* 'info'.
*/
{
if (status != ERROR_SUCCESS)
return (status);
if (status == ERROR_SUCCESS)
return (status);
}
/*
* Gets the DFS link info.
*
* If path is NULL, it just does some initialization.
*
* Info level (1) only needs the UNC path which is not
* stored, it is constructed so the function will return
* without accessing the backend storage.
*/
{
int rc;
info->i_propflags = 0;
return (ERROR_SUCCESS);
}
if (infolvl == 1)
return (ERROR_SUCCESS);
switch (rc) {
case 0:
break;
case ENOENT:
case ENOTSUP:
break;
default:
break;
}
return (status);
}
/*
*/
{
ns_type);
} else {
ns_type);
}
if (status == ERROR_SUCCESS)
return (status);
}
/*
* ==================
* Misc API (public)
* ==================
*/
/*
* Takes a DFS path in UNC format (dfs_path) and parse it into a dfs_path_t
* structure.
*
* dfs_path_free() MUST be called to free the allocated memory in this
* function.
*
* Returns:
*
* ERROR_INVALID_PARAMETER path is not a valid UNC or not valid for the
* specified object type
* ERROR_NOT_ENOUGH_MEMORY not enough memory to peform the parse
* ERROR_NOT_FOUND namespace specified does not exist
*/
{
int rc;
switch (rc) {
case EINVAL:
return (ERROR_INVALID_PARAMETER);
case ENOMEM:
return (ERROR_NOT_ENOUGH_MEMORY);
default:
break;
}
!= ERROR_SUCCESS) {
return (ERROR_NOT_FOUND);
}
if (path_type == DFS_OBJECT_ANY)
else
case DFS_OBJECT_LINK:
else
break;
case DFS_OBJECT_ROOT:
else
break;
default:
}
if (status != ERROR_SUCCESS)
return (status);
}
/*
* Frees the allocated memory for p_unc field of the passed path
*/
void
{
}
/*
* Free the allocated memory for targets in the given info
* structure
*/
void
{
if (info)
}
{
int i;
if (src_info->i_ntargets == 0) {
dst_info->i_ntargets = 0;
return (ERROR_SUCCESS);
}
sizeof (dfs_target_t));
return (ERROR_NOT_ENOUGH_MEMORY);
for (i = 0; i < src_info->i_ntargets; i++)
return (ERROR_SUCCESS);
}
/*
* Trace the given DFS info structure
*/
void
{
dfs_target_t *t;
int i;
return;
return;
t->t_priority.p_rank);
}
}
/*
* Removes DFS links and empty directories.
*
*/
static void
{
char *fname;
return;
continue;
}
if (dfs_path_isdir(fspath)) {
continue;
}
continue;
}
}
static int
{
int xfd;
(void) rw_wrlock(&dfs_root_rwl);
}
(void) rw_unlock(&dfs_root_rwl);
return (status);
}
/*
* Deletes the specified root information
*/
static uint32_t
{
int attrdirfd;
int err = 0;
(void) rw_wrlock(&dfs_root_rwl);
}
} else {
}
(void) rw_unlock(&dfs_root_rwl);
if (err != 0) {
return (ERROR_INTERNAL_ERROR);
}
return (ERROR_SUCCESS);
}
/*
* Opens DFS root directory's extended attribute with the given mode.
*/
static int
{
int dfd;
int err = 0;
if (xfd == -1)
} else {
}
if (err != 0) {
}
return (xfd);
}
/*
* Closes given extended attribute file descriptor
*/
static void
{
}
/*
* Writes the given DFS data in the DFS root directory's
* extended attribute specified with xfd file descriptor.
*/
static uint32_t
{
return (status);
}
/*
* Reads DFS root information from its directory extended attribute
* and parse it into given dfs_info_t structure
*/
static uint32_t
{
char *buf;
return (ERROR_INTERNAL_ERROR);
return (ERROR_NOT_ENOUGH_MEMORY);
else
return (status);
}
/*
* Encodes (packs) DFS information in 'info' into a flat
* buffer in a name-value format. This function allocates a
* buffer with appropriate size to contain all the information
* so the caller MUST free the allocated memory by calling free().
*/
static uint32_t
{
dfs_target_t *t;
int rc;
return (ERROR_NOT_ENOUGH_MEMORY);
t->t_priority.p_class);
t->t_priority.p_rank);
if (rc == 0)
}
/*
* Decodes (unpack) provided buffer which contains a list of name-value
* pairs into given dfs_info_t structure
*/
static uint32_t
{
int rc;
return (ERROR_INTERNAL_ERROR);
/*
* If not defined, use SMB-DFS
*/
rc = 0;
}
&info->i_propflags);
if (rc != 0) {
return (ERROR_INTERNAL_ERROR);
}
switch (infolvl) {
case DFS_INFO_LEVEL_ALL:
case DFS_INFO_LEVEL_3:
case DFS_INFO_LEVEL_4:
/* need target information */
break;
case DFS_INFO_LEVEL_6:
case DFS_INFO_LEVEL_9:
/* need target and priority information */
break;
default:
return (ERROR_SUCCESS);
}
return (ERROR_NOT_ENOUGH_MEMORY);
}
if (rc != 0) {
return (ERROR_INTERNAL_ERROR);
}
if (decode_priority) {
if (rc == 0)
return (ERROR_INTERNAL_ERROR);
} else if (rc == 0) {
}
}
return (ERROR_SUCCESS);
}
/*
* Determines if the passed state is valid for a DFS root
*
* This is based on test results against Win2003 and in some cases
* does not match [MS-DFSNM] spec.
*/
static uint32_t
{
switch (state) {
case DFS_VOLUME_STATE_OK:
return (ERROR_SUCCESS);
return (ERROR_INVALID_PARAMETER);
case DFS_VOLUME_STATE_OFFLINE:
case DFS_VOLUME_STATE_ONLINE:
case DFS_VOLUME_STATE_STANDBY:
return (ERROR_NOT_SUPPORTED);
default:
break;
}
return (ERROR_INVALID_PARAMETER);
}
/*
* Stores given information for the specified link
*
*/
static uint32_t
{
int rc;
if (rc != 0) {
switch (rc) {
case ENOTSUP:
break;
case ENOMEM:
break;
case EINVAL:
break;
default:
break;
}
} else {
}
return (status);
}
/*
* Determines if the passed state is valid for a link
*/
static boolean_t
{
return (state == DFS_VOLUME_STATE_OK ||
}
/*
* Initializes the given target structure (t) with provided information.
*/
static void
{
t->t_priority.p_rank = 0;
}
/*
* Lookup the specified target (server, share) in the given
* target list (targets). If there is a match its index is
* returned, otherwise -1 will be returned.
*/
static int
{
dfs_target_t *t;
int i;
return (i);
}
return (-1);
}
/*
*/
static boolean_t
{
return (state == DFS_STORAGE_STATE_ONLINE ||
}
/*
* Compare function used by smb_avl_t
*/
static int
{
int rc;
if (rc < 0)
return (-1);
if (rc > 0)
return (1);
return (0);
}
static dfs_node_t *
{
return (NULL);
return (dn);
}
static void
dfs_node_destroy(void *p)
{
free(p);
}
/*
* starting from DFS root directory, scans the tree for DFS links
* and adds them to the cache.
*
* The caller must be holding the namespace lock (dfsns.ns_lock) for writing.
*/
static void
{
char *fname;
return;
return;
continue;
}
fname);
if (dfs_path_isdir(fspath)) {
if (DFS_STAT_ISSMB(stat)) {
!= 0)
}
}
}
}
}
/*
* Creates a cache for the given namespace, traverse
* the file system starting from the given path looking
* for all the links and load their information into the
* cache.
*
* The caller must be holding the namespace lock (dfsns.ns_lock) for writing.
*/
static void
{
/*
* only cache referrals for DFS namespaces
*/
return;
name);
}
}
}
/*
* If this namespace hasn't been cached then return
* without flushing the cache; otherwise flush and
* destroy the cache.
*
* The caller must be holding the namespace lock
* (dfsns.ns_lock) for writing.
*/
static void
{
(void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 0);
}
}
/*
* Determines whether the given path is a directory.
*/
static boolean_t
{
return (B_FALSE);
}
/*
* Creates intermediate directories of a link from the root share path.
*
* TODO: directories should be created by smbsrv to get Windows compatible
* ACL inheritance.
*/
static void
{
char *p;
/* drop the link itself from the path */
*p = '\0';
}
}
/*
* Removes empty directories
*/
static void
{
char *p;
/* drop the link itself from the path */
*p = '\0';
}
}
}
/*
* level, and whether it is the object's state or its target's state
*/
static uint32_t
{
switch (infolvl) {
case DFS_INFO_LEVEL_101:
if (type == DFS_OBJECT_ROOT) {
if (!target)
return (dfs_root_isvalidstate(state));
if (!dfs_target_isvalidstate(state))
else if (state == DFS_STORAGE_STATE_OFFLINE)
} else {
if (!target) {
if (!dfs_link_isvalidstate(state))
} else {
if (!dfs_target_isvalidstate(state))
}
}
break;
case DFS_INFO_LEVEL_105:
if (state == 0)
return (ERROR_SUCCESS);
if (type == DFS_OBJECT_ROOT) {
switch (state) {
case DFS_VOLUME_STATE_OK:
case DFS_VOLUME_STATE_OFFLINE:
case DFS_VOLUME_STATE_ONLINE:
case DFS_VOLUME_STATE_STANDBY:
break;
default:
}
} else {
switch (state) {
case DFS_VOLUME_STATE_OK:
case DFS_VOLUME_STATE_OFFLINE:
case DFS_VOLUME_STATE_ONLINE:
break;
case DFS_VOLUME_STATE_STANDBY:
break;
default:
}
}
break;
default:
}
return (status);
}
/*
* Validates the given property flag mask based on the object
*/
static uint32_t
{
if (flavor == DFS_VOLUME_FLAVOR_STANDALONE) {
if (type == DFS_OBJECT_LINK)
if (propflag_mask & flgs_not_supported)
return (ERROR_NOT_SUPPORTED);
}
return (ERROR_SUCCESS);
}
/*
* Based on the specified information level (infolvl) copy parts of the
* information provided through newinfo into the existing information
* (info) for the given object.
*/
static uint32_t
{
int target_idx;
if (target_idx == -1)
return (ERROR_FILE_NOT_FOUND);
}
switch (infolvl) {
case DFS_INFO_LEVEL_100:
break;
case DFS_INFO_LEVEL_101:
if (status != ERROR_SUCCESS)
return (status);
if (!target_op) {
/*
* states specified by this mask should not be stored
*/
if (state & DFS_VOLUME_STATES_SRV_OPS)
return (ERROR_SUCCESS);
} else {
}
break;
case DFS_INFO_LEVEL_102:
break;
case DFS_INFO_LEVEL_103:
break;
case DFS_INFO_LEVEL_104:
break;
case DFS_INFO_LEVEL_105:
if (status != ERROR_SUCCESS)
return (status);
if (status != ERROR_SUCCESS)
return (status);
break;
default:
}
return (status);
}
static boolean_t
{
int i;
for (i = 0; i < DFS_NS_NUM; ++i) {
return (B_TRUE);
}
return (B_FALSE);
}
/*
* return service type string from namespace type
*/
static const char *
{
int i;
for (i = 0; i < DFS_NS_NUM; ++i) {
return (dfs_ns_info[i].svc_name);
}
return (DFS_UNKNOWN_SVCTYPE);
}
/*
* return namespace type from service type string
*/
static uint32_t
{
int i;
for (i = 0; i < DFS_NS_NUM; ++i) {
return (dfs_ns_info[i].ns_type);
}
/* default to SMB-DFS */
return (SMB_NS_DFS);
}