2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <strings.h>
2N/A#include <errno.h>
2N/A#include <unistd.h>
2N/A#include <fcntl.h>
2N/A#include <dirent.h>
2N/A#include <dlfcn.h>
2N/A#include <pthread.h>
2N/A#include <syslog.h>
2N/A#include <assert.h>
2N/A#include <libgen.h>
2N/A#include <note.h>
2N/A#include <sys/fs_reparse.h>
2N/A#include <uuid/uuid.h>
2N/A
2N/A#include <smbsrv/libsmb.h>
2N/A#include <smbsrv/libntsvcs.h>
2N/A#include <smbsrv/smb_dfs.h>
2N/A#include <smbsrv/smb_share.h>
2N/A
2N/A#include "smb_reparse.h"
2N/A#include "dfs.h"
2N/A
2N/Astatic void *dfs_intr_hdl = NULL;
2N/A
2N/Astatic struct {
2N/A int (*dfsops_remote_count)(uint32_t *);
2N/A} dfs_intr_ops;
2N/A
2N/A/*
2N/A * DFS Namespace
2N/A *
2N/A * Currently, only ONE standalone namespace is supported.
2N/A *
2N/A * ns_name The name of the exported namespace. This will be the only
2N/A * exported namespace until hosting multiple namespaces
2N/A * is supported.
2N/A *
2N/A * ns_path The filesystem path of the root share.
2N/A *
2N/A * ns_exported B_TRUE if a standalone namespace is exported
2N/A *
2N/A * ns_cache Caches links' UNC and filesystem path where
2N/A * the key is the UNC path.
2N/A */
2N/Atypedef struct dfs_ns {
2N/A rwlock_t ns_lock;
2N/A char ns_name[MAXNAMELEN];
2N/A char ns_path[MAXPATHLEN];
2N/A uint32_t ns_type;
2N/A boolean_t ns_exported;
2N/A smb_avl_t *ns_cache;
2N/A} dfs_ns_t;
2N/A
2N/Adfs_ns_t dfsns;
2N/A
2N/A/*
2N/A * Namespace cache node operations
2N/A */
2N/Astatic int dfs_node_cmp(const void *, const void *);
2N/Astatic dfs_node_t *dfs_node_create(const char *, const char *, uint32_t);
2N/Astatic void dfs_node_destroy(void *);
2N/A
2N/Astatic smb_avl_nops_t dfs_node_ops = {
2N/A dfs_node_cmp, /* compare */
2N/A NULL, /* add */
2N/A dfs_node_destroy, /* remove */
2N/A NULL, /* hold */
2N/A NULL, /* rele */
2N/A dfs_node_destroy /* flush */
2N/A};
2N/A
2N/A/*
2N/A * System's NetBIOS name
2N/A */
2N/Astatic char dfs_nbname[NETBIOS_NAME_SZ];
2N/A
2N/A/*
2N/A * Lock for accessing root information (extended attribute)
2N/A */
2N/Astatic rwlock_t dfs_root_rwl;
2N/A
2N/Astatic struct {
2N/A uint32_t ns_type;
2N/A const char *svc_name;
2N/A} dfs_ns_info[] = {
2N/A { SMB_NS_DFS, DFS_REPARSE_SVCTYPE },
2N/A { SMB_NS_FEDFS, DFS_FEDFS_SVCTYPE }
2N/A};
2N/A
2N/A#define DFS_NS_NUM (sizeof (dfs_ns_info)/sizeof (dfs_ns_info[0]))
2N/A
2N/A#define DFS_NS_EXPORTED(n) (dfsns.ns_exported && \
2N/A (((n) == NULL) || (smb_strcasecmp((n), dfsns.ns_name, 0) == 0)))
2N/A
2N/Aextern uint32_t srvsvc_shr_setdfsroot(smb_share_t *, boolean_t);
2N/A
2N/A/*
2N/A * Namespace functions
2N/A */
2N/Astatic void dfs_ns_load(const char *, const char *);
2N/Astatic void dfs_ns_unload(const char *);
2N/Astatic void dfs_ns_cleanup(const char *, uint32_t);
2N/Astatic uint32_t dfs_ns_path(const char *, char *, size_t, uint32_t);
2N/Astatic void dfs_ns_populate_cache(const char *, const char *);
2N/A
2N/A/*
2N/A * Root functions
2N/A */
2N/Astatic int dfs_root_add(const char *, dfs_info_t *);
2N/Astatic uint32_t dfs_root_remove(const char *);
2N/Astatic uint32_t dfs_root_encode(dfs_info_t *, char **, size_t *);
2N/Astatic uint32_t dfs_root_decode(dfs_info_t *, char *, size_t, uint32_t);
2N/Astatic uint32_t dfs_root_isvalidstate(uint32_t);
2N/A
2N/Astatic int dfs_root_xopen(const char *, int);
2N/Astatic void dfs_root_xclose(int);
2N/Astatic uint32_t dfs_root_xwrite(int, dfs_info_t *);
2N/Astatic uint32_t dfs_root_xread(int, dfs_info_t *, uint32_t);
2N/A
2N/A/*
2N/A * Link functions
2N/A */
2N/Astatic uint32_t dfs_link_commit(const char *, dfs_info_t *, uint32_t);
2N/Astatic boolean_t dfs_link_isvalidstate(uint32_t);
2N/A
2N/A/*
2N/A * Target functions
2N/A */
2N/Astatic void dfs_target_init(dfs_target_t *, const char *, const char *,
2N/A uint32_t);
2N/Astatic int dfs_target_find(dfs_target_t *, uint32_t, const char *,
2N/A const char *);
2N/Astatic boolean_t dfs_target_isvalidstate(uint32_t);
2N/A
2N/A/*
2N/A * Utility functions
2N/A */
2N/Astatic boolean_t dfs_path_isdir(const char *);
2N/Astatic void dfs_path_create(const char *);
2N/Astatic void dfs_path_remove(smb_unc_t *, uint32_t);
2N/Astatic uint32_t dfs_modinfo(uint32_t, dfs_info_t *, dfs_info_t *, uint32_t);
2N/Astatic boolean_t dfs_isvalid_nstype(uint32_t);
2N/Astatic const char *dfs_svc_name(uint32_t);
2N/Astatic uint32_t dfs_ns_type(const char *);
2N/A
2N/A/*
2N/A * DFS module initialization:
2N/A *
2N/A * - gets system's NetBIOS name
2N/A * - installs interposition ops
2N/A */
2N/Avoid
2N/Adfs_init(void)
2N/A{
2N/A smb_domain_t di;
2N/A
2N/A if (!smb_domain_lookup_type(SMB_DOMAIN_LOCAL, &di))
2N/A return;
2N/A
2N/A (void) strlcpy(dfs_nbname, di.di_nbname, NETBIOS_NAME_SZ);
2N/A
2N/A bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops));
2N/A
2N/A if ((dfs_intr_hdl = smb_dlopen()) == NULL)
2N/A return;
2N/A
2N/A if ((dfs_intr_ops.dfsops_remote_count =
2N/A (int (*)())dlsym(dfs_intr_hdl, "smb_dfs_remote_count")) == NULL) {
2N/A smb_dlclose(dfs_intr_hdl);
2N/A dfs_intr_hdl = NULL;
2N/A bzero((void *)&dfs_intr_ops, sizeof (dfs_intr_ops));
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * DFS module cleanup:
2N/A *
2N/A * - destroys the namespace cache
2N/A */
2N/Avoid
2N/Adfs_fini(void)
2N/A{
2N/A smb_dlclose(dfs_intr_hdl);
2N/A dfs_ns_unexport(NULL);
2N/A}
2N/A
2N/A/*
2N/A * To successfully handle some of link/root requests, some
2N/A * file system operations need to be performed. These operations
2N/A * should take place on behalf of the connected user (typically
2N/A * Administrator) and to do so we need to have an infrastructure
2N/A * in place so that smbd can act as a client and sends request to
2N/A * the kernel. Right now, we lack this infrastructure, so we make
2N/A * a compromise by temporarily enabling some privileges for smbd
2N/A * to be able to fulfill various link/root requests.
2N/A */
2N/Avoid
2N/Adfs_setpriv(priv_op_t op)
2N/A{
2N/A (void) priv_set(op, PRIV_EFFECTIVE,
2N/A PRIV_FILE_DAC_READ,
2N/A PRIV_FILE_DAC_WRITE,
2N/A PRIV_FILE_DAC_EXECUTE,
2N/A PRIV_FILE_DAC_SEARCH, NULL);
2N/A}
2N/A
2N/A/*
2N/A * ========================
2N/A * Namespace API (public)
2N/A * ========================
2N/A */
2N/A
2N/A/*
2N/A * Sets up a dfs_ns_t structure for the specified namespace
2N/A * (root share) if a namespace hasn't already been exported.
2N/A */
2N/Avoid *
2N/Adfs_ns_export(void *arg)
2N/A{
2N/A char *share = arg;
2N/A smb_share_t si;
2N/A dfs_info_t info;
2N/A
2N/A (void) rw_wrlock(&dfsns.ns_lock);
2N/A
2N/A if (dfsns.ns_exported) {
2N/A if (smb_strcasecmp(dfsns.ns_name, share, 0) != 0) {
2N/A syslog(LOG_WARNING, "dfs: trying to export %s."
2N/A " Only one standalone namespace is supported."
2N/A " A namespace is already exported for %s:%s.",
2N/A share, dfs_svc_name(dfsns.ns_type), dfsns.ns_name);
2N/A }
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A free(share);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (smb_share_lookup(share, &si) != NERR_Success) {
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A free(share);
2N/A return (NULL);
2N/A }
2N/A
2N/A if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0) {
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A smb_share_free(&si);
2N/A free(share);
2N/A return (NULL);
2N/A }
2N/A
2N/A if (dfs_root_getinfo(si.shr_path, &info,
2N/A DFS_INFO_LEVEL_ALL, SMB_NS_ANY) != ERROR_SUCCESS) {
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A smb_share_free(&si);
2N/A free(share);
2N/A return (NULL);
2N/A }
2N/A
2N/A (void) strlcpy(dfsns.ns_name, share, sizeof (dfsns.ns_name));
2N/A (void) strlcpy(dfsns.ns_path, si.shr_path, sizeof (dfsns.ns_path));
2N/A dfsns.ns_type = info.i_ns_type;
2N/A dfs_ns_load(share, si.shr_path);
2N/A dfsns.ns_exported = B_TRUE;
2N/A
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A dfs_info_free(&info);
2N/A smb_share_free(&si);
2N/A free(share);
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * If the specified namespace is exported, the cache
2N/A * will be destroyed and the namespace is marked as
2N/A * not exported.
2N/A *
2N/A * If no name is specified then the active namespace
2N/A * will be unexported if there is one.
2N/A */
2N/Avoid
2N/Adfs_ns_unexport(const char *name)
2N/A{
2N/A (void) rw_wrlock(&dfsns.ns_lock);
2N/A
2N/A if (DFS_NS_EXPORTED(name)) {
2N/A dfs_ns_unload(name);
2N/A *dfsns.ns_name = '\0';
2N/A *dfsns.ns_path = '\0';
2N/A dfsns.ns_type = 0;
2N/A dfsns.ns_exported = B_FALSE;
2N/A }
2N/A
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A}
2N/A
2N/A/*
2N/A * Returns the file system path for the given namespace.
2N/A */
2N/Astatic uint32_t
2N/Adfs_ns_path(const char *name, char *path, size_t pathsz, uint32_t ns_type)
2N/A{
2N/A uint32_t status;
2N/A
2N/A if (name == NULL)
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A (void) rw_rdlock(&dfsns.ns_lock);
2N/A
2N/A if (DFS_NS_EXPORTED(name) && dfsns.ns_type == ns_type) {
2N/A (void) strlcpy(path, dfsns.ns_path, pathsz);
2N/A status = ERROR_SUCCESS;
2N/A } else {
2N/A status = ERROR_NOT_FOUND;
2N/A }
2N/A
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Returns the number of DFS root shares i.e. the number
2N/A * of standalone namespaces.
2N/A *
2N/A * The caller must be holding the namespace lock (dfsns.ns_lock)
2N/A * for at least reading.
2N/A */
2N/Auint32_t
2N/Adfs_ns_count(void)
2N/A{
2N/A uint32_t nroot = 0;
2N/A int rc;
2N/A
2N/A if (dfs_intr_ops.dfsops_remote_count != NULL &&
2N/A (rc = dfs_intr_ops.dfsops_remote_count(&nroot)) != 0) {
2N/A /*
2N/A * If this call fails, let's assume there's at least one root
2N/A * namespace already configured. The interposer library cannot
2N/A * confirm or deny the presence of a namespace, so let's take
2N/A * the safe approach and assume one exists.
2N/A */
2N/A nroot = 1;
2N/A syslog(LOG_WARNING, "dfs: dfsops_remote_count() failed: %d, "
2N/A "assuming one namespace exists", rc);
2N/A }
2N/A
2N/A if (dfsns.ns_exported)
2N/A nroot++;
2N/A
2N/A return (nroot);
2N/A}
2N/A
2N/A/*
2N/A * Creates a DFS root with the given name and comment.
2N/A *
2N/A * This function does not create the root share, it
2N/A * should already exist.
2N/A */
2N/Auint32_t
2N/Adfs_ns_create(const char *rootshr, const char *cmnt, uint32_t ns_type)
2N/A{
2N/A dfs_info_t info;
2N/A dfs_target_t t;
2N/A smb_share_t si;
2N/A uuid_t uuid;
2N/A uint32_t status;
2N/A
2N/A if (*rootshr == '\\') {
2N/A /* Windows has a special case here! */
2N/A return (ERROR_BAD_PATHNAME);
2N/A }
2N/A
2N/A if (!dfs_isvalid_nstype(ns_type))
2N/A return (ERROR_NOT_SUPPORTED);
2N/A
2N/A (void) rw_wrlock(&dfsns.ns_lock);
2N/A
2N/A /* For now only allow a single standalone namespace */
2N/A if (dfs_ns_count() > 0) {
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A return (ERROR_NOT_SUPPORTED);
2N/A }
2N/A
2N/A if (dfsns.ns_exported) {
2N/A if (dfsns.ns_type != ns_type) {
2N/A syslog(LOG_WARNING,
2N/A "dfs: trying to create %s:%s namespace."
2N/A " Only one standalone namespace is supported."
2N/A " A namespace is already exported for %s:%s",
2N/A dfs_svc_name(ns_type), rootshr,
2N/A dfs_svc_name(dfsns.ns_type), dfsns.ns_name);
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A return (ERROR_NOT_SUPPORTED);
2N/A }
2N/A
2N/A if (smb_strcasecmp(dfsns.ns_name, rootshr, 0) == 0) {
2N/A /* This DFS root is already exported */
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A return (ERROR_FILE_EXISTS);
2N/A }
2N/A
2N/A syslog(LOG_WARNING, "dfs: trying to create %s namespace."
2N/A " Only one standalone namespace is supported."
2N/A " A namespace is already exported for %s:%s.",
2N/A rootshr, dfs_svc_name(dfsns.ns_type), dfsns.ns_name);
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A return (ERROR_NOT_SUPPORTED);
2N/A }
2N/A
2N/A if (smb_share_lookup(rootshr, &si) != NERR_Success) {
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A return (NERR_NetNameNotFound);
2N/A }
2N/A
2N/A bzero(&info, sizeof (info));
2N/A if (cmnt)
2N/A (void) strlcpy(info.i_comment, cmnt, sizeof (info.i_comment));
2N/A info.i_ns_type = ns_type;
2N/A info.i_state = DFS_VOLUME_STATE_OK | DFS_VOLUME_FLAVOR_STANDALONE;
2N/A info.i_timeout = DFS_ROOT_TIMEOUT;
2N/A info.i_propflags = 0;
2N/A
2N/A uuid_generate_random(uuid);
2N/A uuid_unparse(uuid, info.i_guid);
2N/A
2N/A dfs_target_init(&t, dfs_nbname, rootshr, DFS_STORAGE_STATE_ONLINE);
2N/A
2N/A info.i_ntargets = 1;
2N/A info.i_targets = &t;
2N/A
2N/A if ((status = dfs_root_add(si.shr_path, &info)) != ERROR_SUCCESS) {
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A smb_share_free(&si);
2N/A return (status);
2N/A }
2N/A
2N/A status = srvsvc_shr_setdfsroot(&si, B_TRUE);
2N/A if (status == ERROR_SUCCESS) {
2N/A (void) strlcpy(dfsns.ns_name, rootshr, sizeof (dfsns.ns_name));
2N/A (void) strlcpy(dfsns.ns_path, si.shr_path,
2N/A sizeof (dfsns.ns_path));
2N/A dfsns.ns_type = ns_type;
2N/A dfs_ns_load(rootshr, si.shr_path);
2N/A dfsns.ns_exported = B_TRUE;
2N/A }
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A
2N/A smb_share_free(&si);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Removes the namespace and all the links in it.
2N/A */
2N/Auint32_t
2N/Adfs_ns_destroy(const char *name, uint32_t ns_type)
2N/A{
2N/A smb_share_t si;
2N/A dfs_info_t info;
2N/A uint32_t status;
2N/A
2N/A if (smb_share_lookup(name, &si) != NERR_Success)
2N/A return (ERROR_NOT_FOUND);
2N/A
2N/A if ((si.shr_flags & SMB_SHRF_DFSROOT) == 0) {
2N/A smb_share_free(&si);
2N/A return (ERROR_NOT_FOUND);
2N/A }
2N/A
2N/A if (dfs_root_getinfo(si.shr_path, &info,
2N/A DFS_INFO_LEVEL_ALL, SMB_NS_ANY) != ERROR_SUCCESS) {
2N/A smb_share_free(&si);
2N/A return (ERROR_NOT_FOUND);
2N/A }
2N/A
2N/A if (info.i_ns_type != ns_type) {
2N/A dfs_info_free(&info);
2N/A smb_share_free(&si);
2N/A return (ERROR_NOT_FOUND);
2N/A }
2N/A
2N/A dfs_info_free(&info);
2N/A
2N/A status = srvsvc_shr_setdfsroot(&si, B_FALSE);
2N/A if (status != ERROR_SUCCESS) {
2N/A syslog(LOG_WARNING, "dfs: failed to disable root share %s (%d)",
2N/A name, status);
2N/A smb_share_free(&si);
2N/A return (status);
2N/A }
2N/A
2N/A if ((status = dfs_root_remove(si.shr_path)) != ERROR_SUCCESS) {
2N/A smb_share_free(&si);
2N/A return (status);
2N/A }
2N/A
2N/A /*
2N/A * do not remove links for fedfs namespaces
2N/A * When fedfs namespaces are destroyed, the
2N/A * entire dataset will be destroyed.
2N/A */
2N/A if (ns_type != SMB_NS_FEDFS)
2N/A dfs_ns_cleanup(si.shr_path, ns_type);
2N/A dfs_ns_unexport(name);
2N/A smb_share_free(&si);
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Determines the DFS namespace flavor.
2N/A */
2N/Auint32_t
2N/Adfs_ns_getflavor(const char *name, uint32_t ns_type)
2N/A{
2N/A char rootdir[DFS_PATH_MAX];
2N/A dfs_info_t info;
2N/A
2N/A if (dfs_ns_path(name, rootdir, DFS_PATH_MAX, ns_type) != ERROR_SUCCESS)
2N/A return (0);
2N/A
2N/A /* get flavor info from state info (info level 2) */
2N/A if (dfs_root_getinfo(rootdir, &info, 2, ns_type) != ERROR_SUCCESS)
2N/A return (0);
2N/A
2N/A return (info.i_state & DFS_VOLUME_FLAVORS);
2N/A}
2N/A
2N/A/*
2N/A * Adds the given target to the link in the specified namespace.
2N/A * It will update the cache if this is a new link
2N/A */
2N/Auint32_t
2N/Adfs_ns_addlink(const char *name, dfs_path_t *dfspath, const char *server,
2N/A const char *share, const char *cmnt, uint32_t flags, uint32_t ns_type)
2N/A{
2N/A dfs_node_t *dn;
2N/A char uncpath[DFS_PATH_MAX];
2N/A char *fspath = dfspath->p_fspath;
2N/A uint32_t status;
2N/A boolean_t newlink;
2N/A
2N/A if (name == NULL)
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A dfs_path_create(fspath);
2N/A
2N/A status = dfs_link_add(fspath, server, share, cmnt, flags, &newlink,
2N/A ns_type);
2N/A if (status != ERROR_SUCCESS) {
2N/A dfs_path_remove(&dfspath->p_unc, ns_type);
2N/A return (status);
2N/A }
2N/A
2N/A if (!newlink)
2N/A return (status);
2N/A
2N/A (void) rw_wrlock(&dfsns.ns_lock);
2N/A
2N/A if (DFS_NS_EXPORTED(name) && (dfsns.ns_cache != NULL)) {
2N/A (void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s\\%s",
2N/A dfs_nbname, dfspath->p_unc.unc_share,
2N/A dfspath->p_unc.unc_path);
2N/A
2N/A (void) strsubst(uncpath, '/', '\\');
2N/A
2N/A dn = dfs_node_create(uncpath, fspath, DFS_OBJECT_LINK);
2N/A if (dn != NULL) {
2N/A if (smb_avl_add(dfsns.ns_cache, dn) != 0)
2N/A dfs_node_destroy(dn);
2N/A }
2N/A }
2N/A
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Removes the given target from the link in the specified namespace.
2N/A *
2N/A * If the link is removed as a result the cache will be updated.
2N/A */
2N/Auint32_t
2N/Adfs_ns_removelink(const char *name, dfs_path_t *dfspath,
2N/A const char *server, const char *share, uint32_t ns_type)
2N/A{
2N/A dfs_node_t dn;
2N/A uint32_t status, stat;
2N/A
2N/A if (name == NULL)
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A status = dfs_link_remove(dfspath->p_fspath, server, share, ns_type);
2N/A
2N/A if (status != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A if (dfs_link_stat(dfspath->p_fspath, &stat) != ERROR_SUCCESS)
2N/A return (ERROR_SUCCESS);
2N/A
2N/A if (!DFS_STAT_ISSMB(stat)) {
2N/A (void) rw_wrlock(&dfsns.ns_lock);
2N/A
2N/A if (DFS_NS_EXPORTED(name) && (dfsns.ns_cache != NULL)) {
2N/A (void) snprintf(dn.dn_uncpath, sizeof (dn.dn_uncpath),
2N/A "\\\\%s\\%s\\%s", dfs_nbname, name,
2N/A dfspath->p_unc.unc_path);
2N/A
2N/A /* relpath may contain '/' */
2N/A (void) strsubst(dn.dn_uncpath, '/', '\\');
2N/A smb_avl_remove(dfsns.ns_cache, &dn);
2N/A }
2N/A
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A }
2N/A
2N/A /*
2N/A * if link is removed then try to remove its
2N/A * empty parent directories if any
2N/A */
2N/A if (stat == DFS_STAT_NOTFOUND)
2N/A dfs_path_remove(&dfspath->p_unc, ns_type);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Returns the number of links + 1 (for root) in the
2N/A * specified namespace if this is the exported one
2N/A *
2N/A * The caller must be holding the namespace lock
2N/A * (dfsns.ns_lock) for writing.
2N/A */
2N/Auint32_t
2N/Adfs_ns_numlink(const char *name)
2N/A{
2N/A uint32_t num = 0;
2N/A
2N/A if (DFS_NS_EXPORTED(name)) {
2N/A if (dfsns.ns_cache == NULL)
2N/A dfs_ns_load(dfsns.ns_name, dfsns.ns_path);
2N/A
2N/A num = smb_avl_numnodes(dfsns.ns_cache);
2N/A }
2N/A
2N/A return (num);
2N/A}
2N/A
2N/A/*
2N/A * Returns B_TRUE if a namespace of the specified
2N/A * type exists in namespace cache.
2N/A *
2N/A * The caller must be holding the namespace lock
2N/A * (dfsns.ns_lock) for at least reading.
2N/A */
2N/Aboolean_t
2N/Adfs_ns_exists(uint32_t ns_type)
2N/A{
2N/A if (dfsns.ns_exported && dfsns.ns_type == ns_type)
2N/A return (B_TRUE);
2N/A else
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * Locks the namespace for writing.
2N/A * Used during iteration.
2N/A */
2N/Avoid
2N/Adfs_ns_hold(const char *name)
2N/A{
2N/A NOTE(ARGUNUSED(name))
2N/A
2N/A (void) rw_wrlock(&dfsns.ns_lock);
2N/A}
2N/A
2N/A/*
2N/A * Unlocks the namespace
2N/A */
2N/Avoid
2N/Adfs_ns_rele(const char *name)
2N/A{
2N/A NOTE(ARGUNUSED(name))
2N/A
2N/A (void) rw_unlock(&dfsns.ns_lock);
2N/A}
2N/A
2N/A/*
2N/A * Returns the first node in the namespace cache.
2N/A * The first node in the cache is the root of the
2N/A * namespace.
2N/A *
2N/A * The caller must be holding the namespace lock
2N/A * (dfsns.ns_lock) for at least reading.
2N/A */
2N/Adfs_node_t *
2N/Adfs_ns_firstlink(const char *name)
2N/A{
2N/A NOTE(ARGUNUSED(name))
2N/A
2N/A return (smb_avl_first(dfsns.ns_cache));
2N/A}
2N/A
2N/A/*
2N/A * Returns the next node in the namespace cache after
2N/A * the passed 'node'
2N/A *
2N/A * The caller must be holding the namespace lock
2N/A * (dfsns.ns_lock) for at least reading.
2N/A */
2N/Adfs_node_t *
2N/Adfs_ns_nextlink(const char *name, dfs_node_t *node)
2N/A{
2N/A NOTE(ARGUNUSED(name))
2N/A
2N/A return (smb_avl_next(dfsns.ns_cache, node));
2N/A}
2N/A
2N/A/*
2N/A * ==================
2N/A * Root API (public)
2N/A * ==================
2N/A */
2N/A
2N/A/*
2N/A * Retrieves the information of the root specified by its path.
2N/A *
2N/A * Info level (1) only needs the UNC path which is not stored,
2N/A * it is constructed so the function will return without
2N/A * accessing the backend storage.
2N/A *
2N/A * The dfs root in backend storage must be of the same namespace
2N/A * type as requested.
2N/A */
2N/Auint32_t
2N/Adfs_root_getinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl,
2N/A uint32_t ns_type)
2N/A{
2N/A uint32_t status = ERROR_INTERNAL_ERROR;
2N/A int xfd;
2N/A
2N/A bzero(info, sizeof (dfs_info_t));
2N/A info->i_type = DFS_OBJECT_ROOT;
2N/A
2N/A if (infolvl == 1)
2N/A return (ERROR_SUCCESS);
2N/A
2N/A (void) rw_rdlock(&dfs_root_rwl);
2N/A if ((xfd = dfs_root_xopen(rootdir, O_RDONLY)) > 0) {
2N/A status = dfs_root_xread(xfd, info, infolvl);
2N/A dfs_root_xclose(xfd);
2N/A }
2N/A (void) rw_unlock(&dfs_root_rwl);
2N/A
2N/A if (status == ERROR_SUCCESS) {
2N/A if (ns_type != SMB_NS_ANY && info->i_ns_type != ns_type) {
2N/A dfs_info_free(info);
2N/A status = ERROR_NOT_FOUND;
2N/A }
2N/A }
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Sets the provided information for the specified root or root target.
2N/A * Root is specified by 'rootdir' and the target is specified by
2N/A * (t_server, t_share) pair. Only information items needed for given
2N/A * information level (infolvl) is valid in the passed DFS info structure
2N/A * 'info'.
2N/A */
2N/Auint32_t
2N/Adfs_root_setinfo(const char *rootdir, dfs_info_t *info, uint32_t infolvl,
2N/A uint32_t ns_type)
2N/A{
2N/A dfs_info_t curinfo;
2N/A uint32_t status = ERROR_SUCCESS;
2N/A int xfd;
2N/A
2N/A (void) rw_wrlock(&dfs_root_rwl);
2N/A if ((xfd = dfs_root_xopen(rootdir, O_RDWR)) < 0) {
2N/A (void) rw_unlock(&dfs_root_rwl);
2N/A return (ERROR_INTERNAL_ERROR);
2N/A }
2N/A
2N/A status = dfs_root_xread(xfd, &curinfo, DFS_INFO_LEVEL_ALL);
2N/A if (status != ERROR_SUCCESS) {
2N/A dfs_root_xclose(xfd);
2N/A (void) rw_unlock(&dfs_root_rwl);
2N/A return (status);
2N/A }
2N/A
2N/A if (ns_type != SMB_NS_ANY && curinfo.i_ns_type != ns_type) {
2N/A dfs_root_xclose(xfd);
2N/A (void) rw_unlock(&dfs_root_rwl);
2N/A dfs_info_free(&curinfo);
2N/A return (ERROR_NOT_FOUND);
2N/A }
2N/A
2N/A status = dfs_modinfo(DFS_OBJECT_ROOT, &curinfo, info, infolvl);
2N/A if (status == ERROR_SUCCESS)
2N/A status = dfs_root_xwrite(xfd, &curinfo);
2N/A
2N/A dfs_root_xclose(xfd);
2N/A (void) rw_unlock(&dfs_root_rwl);
2N/A
2N/A dfs_info_free(&curinfo);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * ==================
2N/A * Link API (public)
2N/A * ==================
2N/A */
2N/A
2N/A/*
2N/A * Gets the status of the given path as a link
2N/A */
2N/Auint32_t
2N/Adfs_link_stat(const char *path, uint32_t *stat)
2N/A{
2N/A int svctype;
2N/A
2N/A if (smb_reparse_stat(path, stat) != 0)
2N/A return (ERROR_INTERNAL_ERROR);
2N/A
2N/A switch (*stat) {
2N/A case SMB_REPARSE_NOTFOUND:
2N/A *stat = DFS_STAT_NOTFOUND;
2N/A break;
2N/A case SMB_REPARSE_NOTREPARSE:
2N/A *stat = DFS_STAT_NOTLINK;
2N/A break;
2N/A case SMB_REPARSE_ISREPARSE:
2N/A *stat = DFS_STAT_ISREPARSE;
2N/A if (smb_reparse_svctype(path, &svctype) == 0) {
2N/A switch (svctype) {
2N/A case SMB_SVCTYPE_DFS:
2N/A *stat = DFS_STAT_ISDFS;
2N/A break;
2N/A case SMB_SVCTYPE_FEDFS:
2N/A *stat = DFS_STAT_ISFEDFS;
2N/A break;
2N/A default:
2N/A break;
2N/A }
2N/A }
2N/A break;
2N/A default:
2N/A *stat = DFS_STAT_UNKNOWN;
2N/A break;
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Creates a new DFS link or adds a new target to an existing link
2N/A *
2N/A * Optional verification of link target existence is not implemented.
2N/A *
2N/A * From the spec:
2N/A * If DFS_RESTORE_VOLUME is not specified on the Flags parameter,
2N/A * the server MAY choose to verify whether the link target exists.
2N/A * If DFS_RESTORE_VOLUME is specified, the server MUST NOT perform
2N/A * this test. If it performs the test and the link target does not
2N/A * exist, the server MUST fail the call with NERR_NetNameNotFound.
2N/A *
2N/A */
2N/Auint32_t
2N/Adfs_link_add(const char *path, const char *server, const char *share,
2N/A const char *cmnt, uint32_t flags, boolean_t *newlink, uint32_t ns_type)
2N/A{
2N/A dfs_info_t info;
2N/A dfs_target_t *t;
2N/A int ntargets;
2N/A uint32_t status;
2N/A uint32_t stat;
2N/A
2N/A *newlink = B_FALSE;
2N/A
2N/A if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A switch (stat) {
2N/A case DFS_STAT_NOTFOUND:
2N/A case DFS_STAT_ISREPARSE:
2N/A /* Create a new DFS link */
2N/A
2N/A status = dfs_link_getinfo(NULL, &info, DFS_INFO_LEVEL_ALL,
2N/A ns_type);
2N/A if (status != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A (void) strlcpy(info.i_comment, (cmnt) ? cmnt : "",
2N/A sizeof (info.i_comment));
2N/A *newlink = B_TRUE;
2N/A break;
2N/A
2N/A case DFS_STAT_ISDFS:
2N/A case DFS_STAT_ISFEDFS:
2N/A /*
2N/A * A reparse point can not contain both DFS and
2N/A * FEDFS referrals
2N/A */
2N/A if ((stat == DFS_STAT_ISDFS && ns_type == SMB_NS_FEDFS) ||
2N/A (stat == DFS_STAT_ISFEDFS && ns_type == SMB_NS_DFS))
2N/A return (ERROR_FILE_EXISTS);
2N/A
2N/A /*
2N/A * Add a target to an existing link only
2N/A * if DFS_ADD_VOLUME flag is not specified.
2N/A * The comment MUST be ignored when
2N/A * adding a target to an existing link.
2N/A */
2N/A
2N/A if (flags & DFS_ADD_VOLUME)
2N/A return (ERROR_FILE_EXISTS);
2N/A
2N/A status = dfs_link_getinfo(path, &info, DFS_INFO_LEVEL_ALL,
2N/A ns_type);
2N/A if (status != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A break;
2N/A
2N/A case DFS_STAT_NOTLINK:
2N/A /* specified path points to a non-reparse object */
2N/A return (ERROR_FILE_EXISTS);
2N/A
2N/A default:
2N/A return (ERROR_INTERNAL_ERROR);
2N/A }
2N/A
2N/A /* checks to see if the target already exists */
2N/A ntargets = info.i_ntargets;
2N/A if (dfs_target_find(info.i_targets, ntargets, server, share) != -1) {
2N/A dfs_info_free(&info);
2N/A return (ERROR_FILE_EXISTS);
2N/A }
2N/A
2N/A /* add the new target */
2N/A t = realloc(info.i_targets, (ntargets + 1) * sizeof (dfs_target_t));
2N/A if (t == NULL) {
2N/A dfs_info_free(&info);
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A }
2N/A
2N/A info.i_targets = t;
2N/A dfs_target_init(&info.i_targets[ntargets], server, share,
2N/A DFS_STORAGE_STATE_ONLINE);
2N/A info.i_ntargets++;
2N/A
2N/A status = dfs_link_commit(path, &info, ns_type);
2N/A
2N/A dfs_info_free(&info);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Removes a link or a link target from a DFS namespace. A link can be
2N/A * removed regardless of the number of targets associated with it.
2N/A *
2N/A * 'server' and 'share' parameters specify a target, so if they are NULL
2N/A * it means the link should be removed, otherwise the specified target
2N/A * is removed if found.
2N/A */
2N/Auint32_t
2N/Adfs_link_remove(const char *path, const char *server, const char *share,
2N/A uint32_t ns_type)
2N/A{
2N/A dfs_info_t info;
2N/A uint32_t status, stat;
2N/A int rc, idx;
2N/A
2N/A assert(dfs_isvalid_nstype(ns_type));
2N/A
2N/A if ((status = dfs_link_stat(path, &stat)) != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A if ((ns_type == SMB_NS_DFS && stat != DFS_STAT_ISDFS) ||
2N/A (ns_type == SMB_NS_FEDFS && stat != DFS_STAT_ISFEDFS))
2N/A return (ERROR_NOT_FOUND);
2N/A
2N/A if (server == NULL && share == NULL) {
2N/A /* remove the link */
2N/A if (smb_reparse_svcdel(path, dfs_svc_name(ns_type)) != 0)
2N/A return (ERROR_INTERNAL_ERROR);
2N/A
2N/A return (ERROR_SUCCESS);
2N/A }
2N/A
2N/A /* remove the specified target in the link */
2N/A
2N/A status = dfs_link_getinfo(path, &info, DFS_INFO_LEVEL_ALL, ns_type);
2N/A if (status != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A /* checks to see if the target exists */
2N/A idx = dfs_target_find(info.i_targets, info.i_ntargets, server, share);
2N/A if (idx != -1) {
2N/A bcopy(&info.i_targets[idx + 1], &info.i_targets[idx],
2N/A (info.i_ntargets - idx - 1) * sizeof (dfs_target_t));
2N/A info.i_ntargets--;
2N/A } else {
2N/A dfs_info_free(&info);
2N/A return (ERROR_FILE_NOT_FOUND);
2N/A }
2N/A
2N/A if (info.i_ntargets == 0) {
2N/A /* if last target, then remove the link */
2N/A rc = smb_reparse_svcdel(path, dfs_svc_name(ns_type));
2N/A status = (rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR;
2N/A } else {
2N/A status = dfs_link_commit(path, &info, ns_type);
2N/A }
2N/A
2N/A dfs_info_free(&info);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Sets the provided information for the specified link or link target.
2N/A * Link is specified by 'path' and the target is specified by
2N/A * (t_server, t_share) pair. Only information items needed for given
2N/A * information level (infolvl) is valid in the passed DFS info structure
2N/A * 'info'.
2N/A */
2N/Auint32_t
2N/Adfs_link_setinfo(const char *path, dfs_info_t *info, uint32_t infolvl,
2N/A uint32_t ns_type)
2N/A{
2N/A dfs_info_t curinfo;
2N/A uint32_t status;
2N/A
2N/A status = dfs_link_getinfo(path, &curinfo, DFS_INFO_LEVEL_ALL, ns_type);
2N/A if (status != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A status = dfs_modinfo(DFS_OBJECT_LINK, &curinfo, info, infolvl);
2N/A if (status == ERROR_SUCCESS)
2N/A status = dfs_link_commit(path, &curinfo, ns_type);
2N/A
2N/A dfs_info_free(&curinfo);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Gets the DFS link info.
2N/A *
2N/A * If path is NULL, it just does some initialization.
2N/A *
2N/A * Info level (1) only needs the UNC path which is not
2N/A * stored, it is constructed so the function will return
2N/A * without accessing the backend storage.
2N/A */
2N/Auint32_t
2N/Adfs_link_getinfo(const char *path, dfs_info_t *info, uint32_t infolvl,
2N/A uint32_t ns_type)
2N/A{
2N/A uint32_t status;
2N/A uuid_t uuid;
2N/A int rc;
2N/A
2N/A bzero(info, sizeof (dfs_info_t));
2N/A info->i_type = DFS_OBJECT_LINK;
2N/A
2N/A if (path == NULL) {
2N/A info->i_state = DFS_VOLUME_STATE_OK;
2N/A info->i_timeout = DFS_LINK_TIMEOUT;
2N/A info->i_propflags = 0;
2N/A uuid_generate_random(uuid);
2N/A uuid_unparse(uuid, info->i_guid);
2N/A return (ERROR_SUCCESS);
2N/A }
2N/A
2N/A if (infolvl == 1)
2N/A return (ERROR_SUCCESS);
2N/A
2N/A rc = smb_reparse_svcget(path, dfs_svc_name(ns_type), info);
2N/A switch (rc) {
2N/A case 0:
2N/A status = ERROR_SUCCESS;
2N/A break;
2N/A case ENOENT:
2N/A case ENOTSUP:
2N/A status = ERROR_NOT_FOUND;
2N/A break;
2N/A default:
2N/A status = ERROR_INTERNAL_ERROR;
2N/A break;
2N/A }
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Get the DFS data for the given root/link
2N/A */
2N/Auint32_t
2N/Adfs_getinfo(dfs_node_t *dn, dfs_info_t *info, uint32_t infolvl,
2N/A uint32_t ns_type)
2N/A{
2N/A uint32_t status;
2N/A
2N/A if (dn->dn_type == DFS_OBJECT_LINK) {
2N/A status = dfs_link_getinfo(dn->dn_fspath, info, infolvl,
2N/A ns_type);
2N/A } else {
2N/A status = dfs_root_getinfo(dn->dn_fspath, info, infolvl,
2N/A ns_type);
2N/A }
2N/A (void) strlcpy(info->i_uncpath, dn->dn_uncpath,
2N/A sizeof (info->i_uncpath));
2N/A
2N/A if (status == ERROR_SUCCESS)
2N/A dfs_info_trace("dfs_getinfo", info);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * ==================
2N/A * Misc API (public)
2N/A * ==================
2N/A */
2N/A
2N/A/*
2N/A * Takes a DFS path in UNC format (dfs_path) and parse it into a dfs_path_t
2N/A * structure.
2N/A *
2N/A * dfs_path_free() MUST be called to free the allocated memory in this
2N/A * function.
2N/A *
2N/A * Returns:
2N/A *
2N/A * ERROR_INVALID_PARAMETER path is not a valid UNC or not valid for the
2N/A * specified object type
2N/A * ERROR_NOT_ENOUGH_MEMORY not enough memory to peform the parse
2N/A * ERROR_NOT_FOUND namespace specified does not exist
2N/A */
2N/Auint32_t
2N/Adfs_path_parse(dfs_path_t *path, const char *dfs_path, uint32_t path_type,
2N/A uint32_t ns_type)
2N/A{
2N/A char rootdir[DFS_PATH_MAX];
2N/A smb_unc_t *unc;
2N/A uint32_t status = ERROR_SUCCESS;
2N/A int rc;
2N/A
2N/A bzero(path, sizeof (dfs_path_t));
2N/A unc = &path->p_unc;
2N/A
2N/A rc = smb_unc_init(dfs_path, unc);
2N/A switch (rc) {
2N/A case EINVAL:
2N/A return (ERROR_INVALID_PARAMETER);
2N/A case ENOMEM:
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A if (dfs_ns_path(unc->unc_share, rootdir, DFS_PATH_MAX, ns_type)
2N/A != ERROR_SUCCESS) {
2N/A smb_unc_free(unc);
2N/A return (ERROR_NOT_FOUND);
2N/A }
2N/A
2N/A if (path_type == DFS_OBJECT_ANY)
2N/A path->p_type = (unc->unc_path != NULL)
2N/A ? DFS_OBJECT_LINK : DFS_OBJECT_ROOT;
2N/A else
2N/A path->p_type = path_type;
2N/A
2N/A switch (path->p_type) {
2N/A case DFS_OBJECT_LINK:
2N/A if ((unc->unc_path == NULL) || (*unc->unc_path == '\0'))
2N/A status = ERROR_NOT_FOUND;
2N/A else
2N/A (void) snprintf(path->p_fspath, sizeof (path->p_fspath),
2N/A "%s/%s", rootdir, unc->unc_path);
2N/A break;
2N/A
2N/A case DFS_OBJECT_ROOT:
2N/A if (unc->unc_path == NULL)
2N/A (void) strlcpy(path->p_fspath, rootdir,
2N/A sizeof (path->p_fspath));
2N/A else
2N/A status = ERROR_INVALID_PARAMETER;
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_INVALID_PARAMETER;
2N/A }
2N/A
2N/A if (status != ERROR_SUCCESS)
2N/A smb_unc_free(unc);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Frees the allocated memory for p_unc field of the passed path
2N/A */
2N/Avoid
2N/Adfs_path_free(dfs_path_t *path)
2N/A{
2N/A if (path != NULL)
2N/A smb_unc_free(&path->p_unc);
2N/A}
2N/A
2N/A/*
2N/A * Free the allocated memory for targets in the given info
2N/A * structure
2N/A */
2N/Avoid
2N/Adfs_info_free(dfs_info_t *info)
2N/A{
2N/A if (info)
2N/A free(info->i_targets);
2N/A}
2N/A
2N/Auint32_t
2N/Adfs_info_copy(dfs_info_t *src_info, dfs_info_t *dst_info)
2N/A{
2N/A dfs_target_t *src_tgt, *dst_tgt;
2N/A int i;
2N/A
2N/A *dst_info = *src_info;
2N/A
2N/A if (src_info->i_ntargets == 0) {
2N/A dst_info->i_ntargets = 0;
2N/A dst_info->i_targets = NULL;
2N/A return (ERROR_SUCCESS);
2N/A }
2N/A
2N/A dst_info->i_targets = calloc(src_info->i_ntargets,
2N/A sizeof (dfs_target_t));
2N/A if (dst_info->i_targets == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A dst_tgt = dst_info->i_targets;
2N/A src_tgt = src_info->i_targets;
2N/A for (i = 0; i < src_info->i_ntargets; i++)
2N/A *dst_tgt++ = *src_tgt++;
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Trace the given DFS info structure
2N/A */
2N/Avoid
2N/Adfs_info_trace(const char *msg, dfs_info_t *info)
2N/A{
2N/A dfs_target_t *t;
2N/A int i;
2N/A
2N/A smb_tracef("%s", msg);
2N/A if (info == NULL)
2N/A return;
2N/A
2N/A smb_tracef("UNC\t%s", info->i_uncpath);
2N/A smb_tracef("comment\t%s", info->i_comment);
2N/A smb_tracef("GUID\t%s", info->i_guid);
2N/A smb_tracef("state\t%X", info->i_state);
2N/A smb_tracef("timeout\t%d", info->i_timeout);
2N/A smb_tracef("props\t%X", info->i_propflags);
2N/A smb_tracef("# targets\t%X", info->i_ntargets);
2N/A
2N/A if (info->i_targets == NULL)
2N/A return;
2N/A
2N/A for (i = 0, t = info->i_targets; i < info->i_ntargets; i++, t++) {
2N/A smb_tracef("[%d] \\\\%s\\%s", i, t->t_server, t->t_share);
2N/A smb_tracef("[%d] state\t%X", i, t->t_state);
2N/A smb_tracef("[%d] priority\t%d:%d", i, t->t_priority.p_class,
2N/A t->t_priority.p_rank);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Removes DFS links and empty directories.
2N/A *
2N/A */
2N/Astatic void
2N/Adfs_ns_cleanup(const char *dir, uint32_t ns_type)
2N/A{
2N/A char fspath[DFS_PATH_MAX];
2N/A char *fname;
2N/A DIR *dirp;
2N/A struct dirent *dp;
2N/A uint32_t stat;
2N/A
2N/A if ((dirp = opendir(dir)) == NULL)
2N/A return;
2N/A
2N/A while ((dp = readdir(dirp)) != NULL) {
2N/A fname = dp->d_name;
2N/A
2N/A if (strcmp(fname, ".") == 0 ||
2N/A strcmp(fname, "..") == 0) {
2N/A continue;
2N/A }
2N/A
2N/A (void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", dir, fname);
2N/A
2N/A if (dfs_path_isdir(fspath)) {
2N/A dfs_ns_cleanup(fspath, ns_type);
2N/A (void) rmdir(fspath);
2N/A continue;
2N/A }
2N/A
2N/A if (dfs_link_stat(fspath, &stat) != ERROR_SUCCESS)
2N/A continue;
2N/A
2N/A if ((stat == DFS_STAT_ISDFS && ns_type == SMB_NS_DFS) ||
2N/A (stat == DFS_STAT_ISFEDFS && ns_type == SMB_NS_FEDFS))
2N/A (void) dfs_link_remove(fspath, NULL, NULL, ns_type);
2N/A }
2N/A
2N/A (void) closedir(dirp);
2N/A}
2N/A
2N/Astatic int
2N/Adfs_root_add(const char *rootdir, dfs_info_t *info)
2N/A{
2N/A uint32_t status = ERROR_INTERNAL_ERROR;
2N/A int xfd;
2N/A
2N/A (void) rw_wrlock(&dfs_root_rwl);
2N/A if ((xfd = dfs_root_xopen(rootdir, O_CREAT | O_TRUNC | O_RDWR)) > 0) {
2N/A status = dfs_root_xwrite(xfd, info);
2N/A dfs_root_xclose(xfd);
2N/A }
2N/A (void) rw_unlock(&dfs_root_rwl);
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Deletes the specified root information
2N/A */
2N/Astatic uint32_t
2N/Adfs_root_remove(const char *rootdir)
2N/A{
2N/A int attrdirfd;
2N/A int err = 0;
2N/A
2N/A (void) rw_wrlock(&dfs_root_rwl);
2N/A
2N/A if ((attrdirfd = attropen(rootdir, ".", O_RDONLY)) > 0) {
2N/A if (unlinkat(attrdirfd, DFS_ROOT_XATTR, 0) == -1) {
2N/A if (errno != ENOENT)
2N/A err = errno;
2N/A }
2N/A (void) close(attrdirfd);
2N/A } else {
2N/A err = errno;
2N/A }
2N/A
2N/A (void) rw_unlock(&dfs_root_rwl);
2N/A
2N/A if (err != 0) {
2N/A syslog(LOG_DEBUG, "dfs: failed to remove root info %s (%d)",
2N/A rootdir, err);
2N/A return (ERROR_INTERNAL_ERROR);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Opens DFS root directory's extended attribute with the given mode.
2N/A */
2N/Astatic int
2N/Adfs_root_xopen(const char *rootdir, int oflag)
2N/A{
2N/A int dfd;
2N/A int xfd = -1;
2N/A int err = 0;
2N/A
2N/A if ((dfd = open(rootdir, O_RDONLY)) > 0) {
2N/A xfd = openat(dfd, DFS_ROOT_XATTR, oflag | O_XATTR, 0600);
2N/A if (xfd == -1)
2N/A err = errno;
2N/A (void) close(dfd);
2N/A } else {
2N/A err = errno;
2N/A }
2N/A
2N/A if (err != 0) {
2N/A syslog(LOG_DEBUG, "dfs: failed to open root directory %s (%d)",
2N/A rootdir, err);
2N/A }
2N/A
2N/A return (xfd);
2N/A}
2N/A
2N/A/*
2N/A * Closes given extended attribute file descriptor
2N/A */
2N/Astatic void
2N/Adfs_root_xclose(int xfd)
2N/A{
2N/A (void) close(xfd);
2N/A}
2N/A
2N/A/*
2N/A * Writes the given DFS data in the DFS root directory's
2N/A * extended attribute specified with xfd file descriptor.
2N/A */
2N/Astatic uint32_t
2N/Adfs_root_xwrite(int xfd, dfs_info_t *info)
2N/A{
2N/A size_t nbytes;
2N/A char *buf = NULL;
2N/A size_t buflen;
2N/A uint32_t status;
2N/A
2N/A if ((status = dfs_root_encode(info, &buf, &buflen)) != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A (void) lseek(xfd, 0, SEEK_SET);
2N/A nbytes = write(xfd, buf, buflen);
2N/A free(buf);
2N/A
2N/A return ((nbytes == buflen) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR);
2N/A}
2N/A
2N/A/*
2N/A * Reads DFS root information from its directory extended attribute
2N/A * and parse it into given dfs_info_t structure
2N/A */
2N/Astatic uint32_t
2N/Adfs_root_xread(int xfd, dfs_info_t *info, uint32_t infolvl)
2N/A{
2N/A struct stat statbuf;
2N/A uint32_t status;
2N/A char *buf;
2N/A
2N/A if (fstat(xfd, &statbuf) != 0)
2N/A return (ERROR_INTERNAL_ERROR);
2N/A
2N/A if ((buf = malloc(statbuf.st_size)) == NULL)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A if (read(xfd, buf, statbuf.st_size) == statbuf.st_size)
2N/A status = dfs_root_decode(info, buf, statbuf.st_size, infolvl);
2N/A else
2N/A status = ERROR_INTERNAL_ERROR;
2N/A
2N/A free(buf);
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Encodes (packs) DFS information in 'info' into a flat
2N/A * buffer in a name-value format. This function allocates a
2N/A * buffer with appropriate size to contain all the information
2N/A * so the caller MUST free the allocated memory by calling free().
2N/A */
2N/Astatic uint32_t
2N/Adfs_root_encode(dfs_info_t *info, char **buf, size_t *bufsz)
2N/A{
2N/A dfs_target_t *t;
2N/A nvlist_t *nvl;
2N/A int rc;
2N/A
2N/A if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A
2N/A rc = nvlist_add_string(nvl, DFS_ROOT_PROP_SVCTYPE,
2N/A dfs_svc_name(info->i_ns_type));
2N/A rc |= nvlist_add_string(nvl, DFS_ROOT_PROP_CMNT, info->i_comment);
2N/A rc |= nvlist_add_string(nvl, DFS_ROOT_PROP_GUID, info->i_guid);
2N/A rc |= nvlist_add_uint32(nvl, DFS_ROOT_PROP_STATE, info->i_state);
2N/A rc |= nvlist_add_uint32(nvl, DFS_ROOT_PROP_TTL, info->i_timeout);
2N/A rc |= nvlist_add_uint32(nvl, DFS_ROOT_PROP_FLAGS, info->i_propflags);
2N/A t = info->i_targets;
2N/A rc |= nvlist_add_string(nvl, DFS_ROOT_PROP_SERVER, t->t_server);
2N/A rc |= nvlist_add_string(nvl, DFS_ROOT_PROP_SHARE, t->t_share);
2N/A rc |= nvlist_add_uint32(nvl, DFS_ROOT_PROP_TSTATE, t->t_state);
2N/A rc |= nvlist_add_uint32(nvl, DFS_ROOT_PROP_PRI_CLASS,
2N/A t->t_priority.p_class);
2N/A rc |= nvlist_add_uint16(nvl, DFS_ROOT_PROP_PRI_RANK,
2N/A t->t_priority.p_rank);
2N/A
2N/A if (rc == 0)
2N/A rc = nvlist_pack(nvl, buf, bufsz, NV_ENCODE_NATIVE, 0);
2N/A
2N/A nvlist_free(nvl);
2N/A
2N/A return ((rc == 0) ? ERROR_SUCCESS : ERROR_INTERNAL_ERROR);
2N/A}
2N/A
2N/A/*
2N/A * Decodes (unpack) provided buffer which contains a list of name-value
2N/A * pairs into given dfs_info_t structure
2N/A */
2N/Astatic uint32_t
2N/Adfs_root_decode(dfs_info_t *info, char *buf, size_t bufsz, uint32_t infolvl)
2N/A{
2N/A nvlist_t *nvl;
2N/A char *cmnt, *svctype, *guid;
2N/A char *t_server, *t_share;
2N/A uint32_t t_state;
2N/A uint32_t t_priority_class;
2N/A uint16_t t_priority_rank;
2N/A boolean_t decode_priority = B_FALSE;
2N/A int rc;
2N/A
2N/A if (nvlist_unpack(buf, bufsz, &nvl, 0) != 0)
2N/A return (ERROR_INTERNAL_ERROR);
2N/A
2N/A rc = nvlist_lookup_string(nvl, DFS_ROOT_PROP_SVCTYPE, &svctype);
2N/A if (rc == ENOENT) {
2N/A /*
2N/A * If not defined, use SMB-DFS
2N/A */
2N/A svctype = DFS_REPARSE_SVCTYPE;
2N/A rc = 0;
2N/A }
2N/A rc |= nvlist_lookup_string(nvl, DFS_ROOT_PROP_CMNT, &cmnt);
2N/A rc |= nvlist_lookup_string(nvl, DFS_ROOT_PROP_GUID, &guid);
2N/A rc |= nvlist_lookup_uint32(nvl, DFS_ROOT_PROP_STATE, &info->i_state);
2N/A rc |= nvlist_lookup_uint32(nvl, DFS_ROOT_PROP_TTL, &info->i_timeout);
2N/A rc |= nvlist_lookup_uint32(nvl, DFS_ROOT_PROP_FLAGS,
2N/A &info->i_propflags);
2N/A
2N/A if (rc != 0) {
2N/A nvlist_free(nvl);
2N/A return (ERROR_INTERNAL_ERROR);
2N/A }
2N/A
2N/A info->i_ns_type = dfs_ns_type(svctype);
2N/A (void) strlcpy(info->i_comment, (cmnt) ? cmnt : "",
2N/A sizeof (info->i_comment));
2N/A (void) strlcpy(info->i_guid, (guid) ? guid : "", sizeof (info->i_guid));
2N/A
2N/A info->i_targets = NULL;
2N/A info->i_ntargets = 1;
2N/A
2N/A switch (infolvl) {
2N/A case DFS_INFO_LEVEL_ALL:
2N/A case DFS_INFO_LEVEL_3:
2N/A case DFS_INFO_LEVEL_4:
2N/A /* need target information */
2N/A break;
2N/A case DFS_INFO_LEVEL_6:
2N/A case DFS_INFO_LEVEL_9:
2N/A /* need target and priority information */
2N/A decode_priority = B_TRUE;
2N/A break;
2N/A default:
2N/A nvlist_free(nvl);
2N/A return (ERROR_SUCCESS);
2N/A }
2N/A
2N/A info->i_targets = malloc(sizeof (dfs_target_t));
2N/A if (info->i_targets == NULL) {
2N/A nvlist_free(nvl);
2N/A return (ERROR_NOT_ENOUGH_MEMORY);
2N/A }
2N/A
2N/A rc = nvlist_lookup_string(nvl, DFS_ROOT_PROP_SERVER, &t_server);
2N/A rc |= nvlist_lookup_string(nvl, DFS_ROOT_PROP_SHARE, &t_share);
2N/A rc |= nvlist_lookup_uint32(nvl, DFS_ROOT_PROP_TSTATE, &t_state);
2N/A if (rc != 0) {
2N/A nvlist_free(nvl);
2N/A free(info->i_targets);
2N/A return (ERROR_INTERNAL_ERROR);
2N/A }
2N/A dfs_target_init(info->i_targets, t_server, t_share, t_state);
2N/A
2N/A if (decode_priority) {
2N/A rc = nvlist_lookup_uint32(nvl, DFS_ROOT_PROP_PRI_CLASS,
2N/A &t_priority_class);
2N/A if (rc == 0)
2N/A rc = nvlist_lookup_uint16(nvl, DFS_ROOT_PROP_PRI_RANK,
2N/A &t_priority_rank);
2N/A
2N/A if (rc != 0 && rc != ENOENT) {
2N/A nvlist_free(nvl);
2N/A free(info->i_targets);
2N/A return (ERROR_INTERNAL_ERROR);
2N/A } else if (rc == 0) {
2N/A info->i_targets->t_priority.p_class = t_priority_class;
2N/A info->i_targets->t_priority.p_rank = t_priority_rank;
2N/A }
2N/A }
2N/A
2N/A nvlist_free(nvl);
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Determines if the passed state is valid for a DFS root
2N/A *
2N/A * This is based on test results against Win2003 and in some cases
2N/A * does not match [MS-DFSNM] spec.
2N/A */
2N/Astatic uint32_t
2N/Adfs_root_isvalidstate(uint32_t state)
2N/A{
2N/A switch (state) {
2N/A case DFS_VOLUME_STATE_OK:
2N/A case DFS_VOLUME_STATE_RESYNCHRONIZE:
2N/A return (ERROR_SUCCESS);
2N/A
2N/A case DFS_VOLUME_STATE_INCONSISTENT:
2N/A case DFS_VOLUME_STATE_FORCE_SYNC:
2N/A return (ERROR_INVALID_PARAMETER);
2N/A
2N/A case DFS_VOLUME_STATE_OFFLINE:
2N/A case DFS_VOLUME_STATE_ONLINE:
2N/A case DFS_VOLUME_STATE_STANDBY:
2N/A return (ERROR_NOT_SUPPORTED);
2N/A default:
2N/A break;
2N/A }
2N/A
2N/A return (ERROR_INVALID_PARAMETER);
2N/A}
2N/A
2N/A/*
2N/A * Stores given information for the specified link
2N/A *
2N/A */
2N/Astatic uint32_t
2N/Adfs_link_commit(const char *path, dfs_info_t *info, uint32_t ns_type)
2N/A{
2N/A uint32_t status;
2N/A int rc;
2N/A
2N/A rc = smb_reparse_svcadd(path, dfs_svc_name(ns_type), info);
2N/A if (rc != 0) {
2N/A switch (rc) {
2N/A case ENOTSUP:
2N/A status = ERROR_NOT_FOUND;
2N/A break;
2N/A case ENOMEM:
2N/A status = ERROR_NOT_ENOUGH_MEMORY;
2N/A break;
2N/A case EINVAL:
2N/A status = ERROR_INVALID_NAME;
2N/A break;
2N/A default:
2N/A status = ERROR_INTERNAL_ERROR;
2N/A break;
2N/A }
2N/A } else {
2N/A status = ERROR_SUCCESS;
2N/A }
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Determines if the passed state is valid for a link
2N/A */
2N/Astatic boolean_t
2N/Adfs_link_isvalidstate(uint32_t state)
2N/A{
2N/A return (state == DFS_VOLUME_STATE_OK ||
2N/A state == DFS_VOLUME_STATE_OFFLINE ||
2N/A state == DFS_VOLUME_STATE_ONLINE);
2N/A}
2N/A
2N/A/*
2N/A * Initializes the given target structure (t) with provided information.
2N/A */
2N/Astatic void
2N/Adfs_target_init(dfs_target_t *t, const char *srv, const char *share,
2N/A uint32_t state)
2N/A{
2N/A (void) strlcpy(t->t_server, (srv) ? srv : "", sizeof (t->t_server));
2N/A (void) strlcpy(t->t_share, (share) ? share : "", sizeof (t->t_share));
2N/A t->t_state = state;
2N/A t->t_priority.p_class = DfsSiteCostNormalPriorityClass;
2N/A t->t_priority.p_rank = 0;
2N/A}
2N/A
2N/A/*
2N/A * Lookup the specified target (server, share) in the given
2N/A * target list (targets). If there is a match its index is
2N/A * returned, otherwise -1 will be returned.
2N/A */
2N/Astatic int
2N/Adfs_target_find(dfs_target_t *targets, uint32_t ntargets,
2N/A const char *server, const char *share)
2N/A{
2N/A dfs_target_t *t;
2N/A int i;
2N/A
2N/A for (i = 0, t = targets; i < ntargets; i++, t++) {
2N/A if ((smb_strcasecmp(t->t_server, server, 0) == 0) &&
2N/A (smb_strcasecmp(t->t_share, share, 0) == 0))
2N/A return (i);
2N/A }
2N/A
2N/A return (-1);
2N/A}
2N/A
2N/A/*
2N/A * Determines if the passed state is valid for a link/root target
2N/A */
2N/Astatic boolean_t
2N/Adfs_target_isvalidstate(uint32_t state)
2N/A{
2N/A return (state == DFS_STORAGE_STATE_ONLINE ||
2N/A state == DFS_STORAGE_STATE_OFFLINE);
2N/A}
2N/A
2N/A/*
2N/A * Compare function used by smb_avl_t
2N/A */
2N/Astatic int
2N/Adfs_node_cmp(const void *p1, const void *p2)
2N/A{
2N/A dfs_node_t *dn1 = (dfs_node_t *)p1;
2N/A dfs_node_t *dn2 = (dfs_node_t *)p2;
2N/A int rc;
2N/A
2N/A assert(dn1);
2N/A assert(dn2);
2N/A
2N/A rc = smb_strcasecmp(dn1->dn_uncpath, dn2->dn_uncpath, 0);
2N/A
2N/A if (rc < 0)
2N/A return (-1);
2N/A
2N/A if (rc > 0)
2N/A return (1);
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic dfs_node_t *
2N/Adfs_node_create(const char *uncpath, const char *fspath, uint32_t type)
2N/A{
2N/A dfs_node_t *dn;
2N/A
2N/A if ((dn = calloc(1, sizeof (dfs_node_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A (void) strlcpy(dn->dn_uncpath, uncpath, sizeof (dn->dn_uncpath));
2N/A (void) strlcpy(dn->dn_fspath, fspath, sizeof (dn->dn_fspath));
2N/A dn->dn_type = type;
2N/A
2N/A return (dn);
2N/A}
2N/A
2N/Astatic void
2N/Adfs_node_destroy(void *p)
2N/A{
2N/A free(p);
2N/A}
2N/A
2N/A/*
2N/A * starting from DFS root directory, scans the tree for DFS links
2N/A * and adds them to the cache.
2N/A *
2N/A * The caller must be holding the namespace lock (dfsns.ns_lock) for writing.
2N/A */
2N/Astatic void
2N/Adfs_ns_populate_cache(const char *unc_prefix, const char *dir)
2N/A{
2N/A dfs_node_t *dn;
2N/A char fspath[DFS_PATH_MAX];
2N/A char uncpath[DFS_PATH_MAX];
2N/A char *fname;
2N/A DIR *dirp;
2N/A struct dirent *dp;
2N/A uint32_t stat;
2N/A
2N/A if (dfsns.ns_cache == NULL)
2N/A return;
2N/A
2N/A if ((dirp = opendir(dir)) == NULL)
2N/A return;
2N/A
2N/A while ((dp = readdir(dirp)) != NULL) {
2N/A fname = dp->d_name;
2N/A
2N/A if (strcmp(fname, ".") == 0 ||
2N/A strcmp(fname, "..") == 0) {
2N/A continue;
2N/A }
2N/A
2N/A (void) snprintf(fspath, DFS_PATH_MAX, "%s/%s", dir, fname);
2N/A (void) snprintf(uncpath, DFS_PATH_MAX, "%s\\%s", unc_prefix,
2N/A fname);
2N/A
2N/A if (dfs_path_isdir(fspath)) {
2N/A dfs_ns_populate_cache(uncpath, fspath);
2N/A } else if (dfs_link_stat(fspath, &stat) == ERROR_SUCCESS) {
2N/A if (DFS_STAT_ISSMB(stat)) {
2N/A dn = dfs_node_create(uncpath, fspath,
2N/A DFS_OBJECT_LINK);
2N/A if (dn != NULL) {
2N/A if (smb_avl_add(dfsns.ns_cache, dn)
2N/A != 0)
2N/A dfs_node_destroy(dn);
2N/A }
2N/A }
2N/A }
2N/A }
2N/A
2N/A (void) closedir(dirp);
2N/A}
2N/A
2N/A/*
2N/A * Creates a cache for the given namespace, traverse
2N/A * the file system starting from the given path looking
2N/A * for all the links and load their information into the
2N/A * cache.
2N/A *
2N/A * The caller must be holding the namespace lock (dfsns.ns_lock) for writing.
2N/A */
2N/Astatic void
2N/Adfs_ns_load(const char *name, const char *path)
2N/A{
2N/A dfs_node_t *dn;
2N/A char uncpath[DFS_PATH_MAX];
2N/A
2N/A (void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 1);
2N/A
2N/A /*
2N/A * only cache referrals for DFS namespaces
2N/A */
2N/A if (dfsns.ns_type != SMB_NS_DFS)
2N/A return;
2N/A
2N/A dfsns.ns_cache = smb_avl_create(sizeof (dfs_node_t),
2N/A offsetof(dfs_node_t, dn_hook), &dfs_node_ops);
2N/A
2N/A if (dfsns.ns_cache != NULL) {
2N/A (void) snprintf(uncpath, DFS_PATH_MAX, "\\\\%s\\%s", dfs_nbname,
2N/A name);
2N/A dn = dfs_node_create(uncpath, path, DFS_OBJECT_ROOT);
2N/A if (dn != NULL) {
2N/A if (smb_avl_add(dfsns.ns_cache, dn) != 0)
2N/A dfs_node_destroy(dn);
2N/A }
2N/A dfs_ns_populate_cache(uncpath, path);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * If this namespace hasn't been cached then return
2N/A * without flushing the cache; otherwise flush and
2N/A * destroy the cache.
2N/A *
2N/A * The caller must be holding the namespace lock
2N/A * (dfsns.ns_lock) for writing.
2N/A */
2N/Astatic void
2N/Adfs_ns_unload(const char *name)
2N/A{
2N/A NOTE(ARGUNUSED(name))
2N/A
2N/A (void) smb_config_setnum(SMB_CI_DFS_STDROOT_NUM, 0);
2N/A
2N/A if (dfsns.ns_cache != NULL) {
2N/A smb_avl_flush(dfsns.ns_cache);
2N/A smb_avl_destroy(dfsns.ns_cache);
2N/A dfsns.ns_cache = NULL;
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Determines whether the given path is a directory.
2N/A */
2N/Astatic boolean_t
2N/Adfs_path_isdir(const char *path)
2N/A{
2N/A struct stat statbuf;
2N/A
2N/A if (lstat(path, &statbuf) != 0)
2N/A return (B_FALSE);
2N/A
2N/A return ((statbuf.st_mode & S_IFMT) == S_IFDIR);
2N/A}
2N/A
2N/A/*
2N/A * Creates intermediate directories of a link from the root share path.
2N/A *
2N/A * TODO: directories should be created by smbsrv to get Windows compatible
2N/A * ACL inheritance.
2N/A */
2N/Astatic void
2N/Adfs_path_create(const char *path)
2N/A{
2N/A char dirpath[DFS_PATH_MAX];
2N/A mode_t mode;
2N/A char *p;
2N/A
2N/A (void) strlcpy(dirpath, path, DFS_PATH_MAX);
2N/A
2N/A /* drop the link itself from the path */
2N/A if ((p = strrchr(dirpath, '/')) != NULL) {
2N/A *p = '\0';
2N/A mode = umask(0);
2N/A (void) mkdirp(dirpath, 0777);
2N/A (void) umask(mode);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Removes empty directories
2N/A */
2N/Astatic void
2N/Adfs_path_remove(smb_unc_t *unc, uint32_t ns_type)
2N/A{
2N/A char rootdir[DFS_PATH_MAX];
2N/A char relpath[DFS_PATH_MAX];
2N/A char dir[DFS_PATH_MAX];
2N/A uint32_t status;
2N/A char *p;
2N/A
2N/A status = dfs_ns_path(unc->unc_share, rootdir, DFS_PATH_MAX, ns_type);
2N/A if ((status == ERROR_SUCCESS) && (chdir(rootdir) == 0)) {
2N/A (void) strlcpy(relpath, unc->unc_path, DFS_PATH_MAX);
2N/A /* drop the link itself from the path */
2N/A if ((p = strrchr(relpath, '/')) != NULL) {
2N/A *p = '\0';
2N/A (void) rmdirp(relpath, dir);
2N/A }
2N/A }
2N/A}
2N/A
2N/A
2N/A/*
2N/A * Validates the given state based on the object type (root/link), info
2N/A * level, and whether it is the object's state or its target's state
2N/A */
2N/Astatic uint32_t
2N/Adfs_isvalidstate(uint32_t state, uint32_t type, boolean_t target,
2N/A uint32_t infolvl)
2N/A{
2N/A uint32_t status = ERROR_SUCCESS;
2N/A
2N/A switch (infolvl) {
2N/A case DFS_INFO_LEVEL_101:
2N/A if (type == DFS_OBJECT_ROOT) {
2N/A if (!target)
2N/A return (dfs_root_isvalidstate(state));
2N/A
2N/A if (!dfs_target_isvalidstate(state))
2N/A status = ERROR_INVALID_PARAMETER;
2N/A else if (state == DFS_STORAGE_STATE_OFFLINE)
2N/A status = ERROR_NOT_SUPPORTED;
2N/A } else {
2N/A if (!target) {
2N/A if (!dfs_link_isvalidstate(state))
2N/A status = ERROR_INVALID_PARAMETER;
2N/A } else {
2N/A if (!dfs_target_isvalidstate(state))
2N/A status = ERROR_INVALID_PARAMETER;
2N/A }
2N/A }
2N/A break;
2N/A
2N/A case DFS_INFO_LEVEL_105:
2N/A if (state == 0)
2N/A return (ERROR_SUCCESS);
2N/A
2N/A if (type == DFS_OBJECT_ROOT) {
2N/A switch (state) {
2N/A case DFS_VOLUME_STATE_OK:
2N/A case DFS_VOLUME_STATE_OFFLINE:
2N/A case DFS_VOLUME_STATE_ONLINE:
2N/A case DFS_VOLUME_STATE_RESYNCHRONIZE:
2N/A case DFS_VOLUME_STATE_STANDBY:
2N/A status = ERROR_NOT_SUPPORTED;
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_INVALID_PARAMETER;
2N/A }
2N/A } else {
2N/A switch (state) {
2N/A case DFS_VOLUME_STATE_OK:
2N/A case DFS_VOLUME_STATE_OFFLINE:
2N/A case DFS_VOLUME_STATE_ONLINE:
2N/A break;
2N/A
2N/A case DFS_VOLUME_STATE_RESYNCHRONIZE:
2N/A case DFS_VOLUME_STATE_STANDBY:
2N/A status = ERROR_NOT_SUPPORTED;
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_INVALID_PARAMETER;
2N/A }
2N/A }
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_INVALID_LEVEL;
2N/A }
2N/A
2N/A return (status);
2N/A}
2N/A
2N/A/*
2N/A * Validates the given property flag mask based on the object
2N/A * type (root/link) and namespace flavor.
2N/A */
2N/Astatic uint32_t
2N/Adfs_isvalidpropflagmask(uint32_t propflag_mask, uint32_t type,
2N/A uint32_t flavor)
2N/A{
2N/A uint32_t flgs_not_supported;
2N/A
2N/A flgs_not_supported = DFS_PROPERTY_FLAG_ROOT_SCALABILITY
2N/A | DFS_PROPERTY_FLAG_CLUSTER_ENABLED
2N/A | DFS_PROPERTY_FLAG_ABDE;
2N/A
2N/A if (flavor == DFS_VOLUME_FLAVOR_STANDALONE) {
2N/A if (type == DFS_OBJECT_LINK)
2N/A flgs_not_supported |= DFS_PROPERTY_FLAG_SITE_COSTING;
2N/A if (propflag_mask & flgs_not_supported)
2N/A return (ERROR_NOT_SUPPORTED);
2N/A }
2N/A
2N/A return (ERROR_SUCCESS);
2N/A}
2N/A
2N/A/*
2N/A * Based on the specified information level (infolvl) copy parts of the
2N/A * information provided through newinfo into the existing information
2N/A * (info) for the given object.
2N/A */
2N/Astatic uint32_t
2N/Adfs_modinfo(uint32_t type, dfs_info_t *info, dfs_info_t *newinfo,
2N/A uint32_t infolvl)
2N/A{
2N/A boolean_t target_op = B_FALSE;
2N/A uint32_t status = ERROR_SUCCESS;
2N/A uint32_t state;
2N/A int target_idx;
2N/A
2N/A if (newinfo->i_targets != NULL) {
2N/A target_idx = dfs_target_find(info->i_targets, info->i_ntargets,
2N/A newinfo->i_targets->t_server, newinfo->i_targets->t_share);
2N/A if (target_idx == -1)
2N/A return (ERROR_FILE_NOT_FOUND);
2N/A target_op = B_TRUE;
2N/A }
2N/A
2N/A switch (infolvl) {
2N/A case DFS_INFO_LEVEL_100:
2N/A (void) strlcpy(info->i_comment, newinfo->i_comment,
2N/A sizeof (newinfo->i_comment));
2N/A break;
2N/A
2N/A case DFS_INFO_LEVEL_101:
2N/A state = (target_op)
2N/A ? newinfo->i_targets->t_state : newinfo->i_state;
2N/A status = dfs_isvalidstate(state, type, target_op, 101);
2N/A if (status != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A if (!target_op) {
2N/A /*
2N/A * states specified by this mask should not be stored
2N/A */
2N/A if (state & DFS_VOLUME_STATES_SRV_OPS)
2N/A return (ERROR_SUCCESS);
2N/A
2N/A info->i_state = state;
2N/A } else {
2N/A info->i_targets[target_idx].t_state = state;
2N/A }
2N/A break;
2N/A
2N/A case DFS_INFO_LEVEL_102:
2N/A info->i_timeout = newinfo->i_timeout;
2N/A break;
2N/A
2N/A case DFS_INFO_LEVEL_103:
2N/A info->i_propflags = newinfo->i_propflags;
2N/A break;
2N/A
2N/A case DFS_INFO_LEVEL_104:
2N/A info->i_targets[target_idx].t_priority =
2N/A newinfo->i_targets->t_priority;
2N/A break;
2N/A
2N/A case DFS_INFO_LEVEL_105:
2N/A status = dfs_isvalidstate(newinfo->i_state, type, B_FALSE, 105);
2N/A if (status != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A status = dfs_isvalidpropflagmask(newinfo->i_propflag_mask, type,
2N/A newinfo->i_flavor);
2N/A if (status != ERROR_SUCCESS)
2N/A return (status);
2N/A
2N/A (void) strlcpy(info->i_comment, newinfo->i_comment,
2N/A sizeof (newinfo->i_comment));
2N/A if (newinfo->i_state != 0)
2N/A info->i_state = newinfo->i_state;
2N/A info->i_timeout = newinfo->i_timeout;
2N/A info->i_propflags = newinfo->i_propflags;
2N/A break;
2N/A
2N/A default:
2N/A status = ERROR_INVALID_LEVEL;
2N/A }
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Astatic boolean_t
2N/Adfs_isvalid_nstype(uint32_t ns_type)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < DFS_NS_NUM; ++i) {
2N/A if (dfs_ns_info[i].ns_type == ns_type)
2N/A return (B_TRUE);
2N/A }
2N/A
2N/A return (B_FALSE);
2N/A}
2N/A
2N/A/*
2N/A * return service type string from namespace type
2N/A */
2N/Astatic const char *
2N/Adfs_svc_name(uint32_t ns_type)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < DFS_NS_NUM; ++i) {
2N/A if (dfs_ns_info[i].ns_type == ns_type)
2N/A return (dfs_ns_info[i].svc_name);
2N/A }
2N/A
2N/A return (DFS_UNKNOWN_SVCTYPE);
2N/A}
2N/A
2N/A/*
2N/A * return namespace type from service type string
2N/A */
2N/Astatic uint32_t
2N/Adfs_ns_type(const char *svc_name)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < DFS_NS_NUM; ++i) {
2N/A if (strcasecmp(svc_name, dfs_ns_info[i].svc_name) == 0)
2N/A return (dfs_ns_info[i].ns_type);
2N/A }
2N/A
2N/A /* default to SMB-DFS */
2N/A return (SMB_NS_DFS);
2N/A}