smb_share.c revision 7f667e74610492ddbce8ce60f52ece95d2401949
/*
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*/
#include <errno.h>
#include <synch.h>
#include <stdlib.h>
#include <strings.h>
#include <syslog.h>
#include <thread.h>
#include <pthread.h>
#include <assert.h>
#include <libshare.h>
#include <smbsrv/libsmbns.h>
#include <smbsrv/libmlsvc.h>
#include <smbsrv/smb_share.h>
#define SMB_SHR_ERROR_THRESHOLD 3
#define SMB_SHR_CSC_BUFSZ 64
/*
* Cache functions and vars
*/
#define SMB_SHR_HTAB_SZ 1024
/*
* Cache handle
*
* Shares cache is a hash table.
*
* sc_cache pointer to hash table handle
* sc_state cache state machine values
* sc_mtx protects handle fields
*/
typedef struct smb_shr_cache {
/*
* Cache states
*/
#define SMB_SHR_CACHE_STATE_NONE 0
#define SMB_SHR_CACHE_STATE_CREATED 1
#define SMB_SHR_CACHE_STATE_DESTROYING 2
/*
* Cache lock modes
*/
#define SMB_SHR_CACHE_RDLOCK 0
#define SMB_SHR_CACHE_WRLOCK 1
static smb_shr_cache_t smb_shr_cache;
static uint32_t smb_shr_cache_create(void);
static void smb_shr_cache_destroy(void);
static uint32_t smb_shr_cache_lock(int);
static void smb_shr_cache_unlock(void);
static int smb_shr_cache_count(void);
static smb_share_t *smb_shr_cache_findent(char *);
static void smb_shr_cache_delent(char *);
static void smb_shr_cache_freent(HT_ITEM *);
/*
* sharemgr functions
*/
static void *smb_shr_sa_loadall(void *);
static void smb_shr_sa_loadgrp(sa_group_t);
static uint32_t smb_shr_sa_loadbyname(char *);
/*
* share publishing
*/
#define SMB_SHR_PUBLISH 0
#define SMB_SHR_UNPUBLISH 1
typedef struct smb_shr_pitem {
char spi_name[MAXNAMELEN];
char spi_container[MAXPATHLEN];
char spi_op;
/*
* publish queue states
*/
#define SMB_SHR_PQS_NOQUEUE 0
#define SMB_SHR_PQS_STOPPING 3
/*
* share publishing queue
*/
typedef struct smb_shr_pqueue {
static smb_shr_pqueue_t ad_queue;
static int smb_shr_publisher_start(void);
static void smb_shr_publisher_stop(void);
static void smb_shr_publisher_queue(const char *, const char *, char);
static void *smb_shr_publisher(void *);
static void smb_shr_publisher_flush(list_t *);
static void smb_shr_publish(const char *, const char *);
static void smb_shr_unpublish(const char *, const char *);
/*
*/
static uint32_t smb_shr_addipc(void);
static void smb_shr_set_oemname(smb_share_t *);
/*
* libshare handle and synchronization
*/
typedef struct smb_sa_handle {
static smb_sa_handle_t smb_sa_handle;
/*
* Creates and initializes the cache and starts the publisher
* thread.
*/
int
smb_shr_start(void)
{
if (smb_shr_cache_create() != NERR_Success)
return (ENOMEM);
if (smb_shr_addipc() != NERR_Success)
return (ENOMEM);
return (smb_shr_publisher_start());
}
void
smb_shr_stop(void)
{
}
}
/*
* Get a handle and exclusive access to the libshare API.
*/
smb_shr_sa_enter(void)
{
if (!smb_sa_handle.sa_in_service) {
return (NULL);
}
return (NULL);
}
}
return (smb_sa_handle.sa_handle);
}
/*
* Release exclusive access to the libshare API.
*/
void
smb_shr_sa_exit(void)
{
}
/*
* Launches a thread to populate the share cache by share information
* stored in sharemgr
*/
int
smb_shr_load(void)
{
int rc;
(void) pthread_attr_init(&tattr);
(void) pthread_attr_destroy(&tattr);
return (rc);
}
/*
* Return the total number of shares
*/
int
smb_shr_count(void)
{
int n_shares = 0;
}
return (n_shares);
}
/*
* smb_shr_iterinit
*
* Initialize given iterator for traversing hash table.
*/
void
{
}
/*
* smb_shr_iterate
*
* Iterate on the shares in the hash table. The iterator must be initialized
* before the first iteration. On subsequent calls, the iterator must be
* passed unchanged.
*
* Returns NULL on failure or when all shares are visited, otherwise
* returns information of visited share.
*/
{
return (NULL);
}
}
return (share);
}
/*
* Adds the given share to cache, publishes the share in ADS
* if it has an AD container, calls kernel to take a hold on
* the shared file system. If it can't take a hold on the
* shared file system, it's either because shared directory
* does not exist or some other error has occurred, in any
* case the share is removed from the cache.
*
* If the specified share is an autohome share which already
* exists in the cache, just increments the reference count.
*/
{
int rc;
return (ERROR_INVALID_NAME);
return (NERR_InternalError);
if (cached_si) {
cached_si->shr_refcnt++;
} else {
}
return (status);
}
return (status);
}
/* don't hold the lock across door call */
/* call kernel to take a hold on the shared file system */
if (rc == 0) {
return (NERR_Success);
}
}
/*
* rc == ENOENT means the shared directory doesn't exist
*/
}
/*
* Removes the specified share from cache, removes it from AD
* if it has an AD container, and calls the kernel to release
* the hold on the shared file system.
*
* If this is an autohome share then decrement the reference
* count. If it reaches 0 then it proceeds with removing steps.
*/
smb_shr_remove(char *sharename)
{
char path[MAXPATHLEN];
char container[MAXPATHLEN];
if (!smb_shr_chkname(sharename))
return (ERROR_INVALID_NAME);
return (NERR_InternalError);
return (NERR_NetNameNotFound);
}
/* IPC$ share cannot be removed */
return (ERROR_ACCESS_DENIED);
}
if ((--si->shr_refcnt) > 0) {
return (NERR_Success);
}
}
/* call kernel to release the hold on the shared file system */
return (NERR_Success);
}
/*
* Rename a share. Check that the current name exists and the new name
* doesn't exist. The rename is performed by deleting the current share
* definition and creating a new share with the new name.
*/
{
return (ERROR_INVALID_NAME);
return (NERR_InternalError);
return (NERR_NetNameNotFound);
}
/* IPC$ share cannot be renamed */
return (ERROR_ACCESS_DENIED);
}
return (NERR_DuplicateShare);
}
return (status);
}
return (NERR_Success);
}
/*
* Load the information for the specified share into the supplied share
* info structure.
*
* First looks up the cache to see if the specified share exists, if there
* is a miss then it looks up sharemgr.
*/
{
return (NERR_NetNameNotFound);
return (status);
return (status);
}
/*
* Modifies an existing share. Properties that can be modified are:
*
* o comment
* o AD container
* o host access
*/
{
char old_container[MAXPATHLEN];
return (NERR_InternalError);
return (NERR_NetNameNotFound);
}
/* IPC$ share cannot be modified */
return (ERROR_ACCESS_DENIED);
}
if (adc_changed) {
/* save current container - needed for unpublishing */
sizeof (old_container));
sizeof (si->shr_container));
}
if (access & SMB_SHRF_ACC_NONE)
sizeof (si->shr_access_none));
if (access & SMB_SHRF_ACC_RO)
sizeof (si->shr_access_ro));
if (access & SMB_SHRF_ACC_RW)
sizeof (si->shr_access_rw));
if (adc_changed) {
}
return (NERR_Success);
}
/*
* smb_shr_exists
*
* Returns B_TRUE if the share exists. Otherwise returns B_FALSE
*/
smb_shr_exists(char *sharename)
{
return (B_FALSE);
}
return (exists);
}
/*
* If the shared directory does not begin with a /, one will be
* inserted as a prefix. If ipaddr is not zero, then also return
* information about access based on the host level access lists, if
* present. Also return access check if there is an IP address and
* shr_accflags.
*
* The value of smb_chk_hostaccess is checked for an access match.
* -1 is wildcard match
* 0 is no match
* 1 is match
*
* Precedence is none is checked first followed by ro then rw if
* needed. If x is wildcard (< 0) then check to see if the other
* values are a match. If a match, that wins.
*
* ipv6 is wide open for now, see smb_chk_hostaccess
*/
void
{
int acc = SMB_SHRF_ACC_OPEN;
/*
* Check to see if there area any share level access
* restrictions.
*/
if ((!smb_inet_iszero(ipaddr)) &&
int none = SMB_SHRF_ACC_OPEN;
int rw = SMB_SHRF_ACC_OPEN;
int ro = SMB_SHRF_ACC_OPEN;
/* make first pass to get basic value */
if (none != 0)
else if (ro != 0)
else if (rw != 0)
/* make second pass to handle '*' case */
if (none < 0) {
if (ro > 0)
else if (rw > 0)
} else if (ro < 0) {
if (none > 0)
else if (rw > 0)
} else if (rw < 0) {
if (none > 0)
else if (ro > 0)
}
}
}
/*
* smb_shr_is_special
*
* Special share reserved for interprocess communication (IPC$) or
* remote administration of the server (ADMIN$). Can also refer to
* administrative shares such as C$, D$, E$, and so forth.
*/
int
smb_shr_is_special(char *sharename)
{
int len;
return (0);
return (0);
return (STYPE_SPECIAL);
return (0);
}
/*
* smb_shr_is_restricted
*
* Check whether or not there is a restriction on a share. Restricted
* shares are generally STYPE_SPECIAL, for example, IPC$. All the
* administration share names are restricted: C$, D$ etc. Returns B_TRUE
* if the share is restricted. Otherwise B_FALSE is returned to indicate
* that there are no restrictions.
*/
smb_shr_is_restricted(char *sharename)
{
static char *restricted[] = {
"IPC$"
};
int i;
return (B_FALSE);
for (i = 0; i < sizeof (restricted)/sizeof (restricted[0]); i++) {
return (B_TRUE);
}
return (smb_shr_is_admin(sharename));
}
/*
* smb_shr_is_admin
*
* Check whether or not access to the share should be restricted to
* administrators. This is a bit of a hack because what we're doing
* is checking for the default admin shares: C$, D$ etc.. There are
* other shares that have restrictions: see smb_shr_is_restricted().
*
* Returns B_TRUE if the shares is an admin share. Otherwise B_FALSE
* is returned to indicate that there are no restrictions.
*/
smb_shr_is_admin(char *sharename)
{
return (B_FALSE);
return (B_TRUE);
}
return (B_FALSE);
}
/*
* smb_shr_chkname
*
* Check for invalid characters in a share name. The list of invalid
* characters includes control characters and the following:
*
* " / \ [ ] : | < > + ; , ? * =
*/
smb_shr_chkname(char *sharename)
{
char *invalid = "\"/\\[]:|<>+;,?*=";
char *cp;
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
}
return (B_TRUE);
}
/*
* smb_shr_get_realpath
*
* Derive the real path for a share from the path provided by a client.
* For instance, the real path of C:\ may be /cvol or the real path of
*
* clntpath - path provided by the Windows client is in the
* format of <drive letter>:\<dir>
* realpath - path that will be stored as the directory field of
* the smb_share_t structure of the share.
* maxlen - maximum length of the realpath buffer
*
* Return LAN Manager network error code.
*/
{
const char *p;
int len;
++p;
else
p = clntpath;
return (NERR_Success);
}
void
{
int n = 0;
if (--offset > 0)
continue;
if (++n == LMSHARES_PER_REQUEST)
break;
}
}
}
/*
* ============================================
* ============================================
*/
/*
* Looks up the given share in the cache and return
* the info in 'si'
*/
static uint32_t
{
return (NERR_NetNameNotFound);
}
}
return (status);
}
/*
* Add IPC$ to the cache upon startup.
*/
static uint32_t
smb_shr_addipc(void)
{
}
return (status);
}
/*
* smb_shr_set_oemname
*
* Generate the OEM name for the specified share. If the name is
* shorter than 13 bytes the oemname will be saved in si->shr_oemname.
* Otherwise si->shr_oemname will be empty and SMB_SHRF_LONGNAME will
* be set in si->shr_flags.
*/
static void
{
unsigned int cpid = oem_get_smb_cpid();
char *oem_name;
int length;
return;
}
} else {
}
}
/*
* ============================================
* Cache management functions
*
* All cache functions are private
* ============================================
*/
/*
* Create the share cache (hash table).
*/
static uint32_t
smb_shr_cache_create(void)
{
switch (smb_shr_cache.sc_state) {
case SMB_SHR_CACHE_STATE_NONE:
MAXNAMELEN, 0);
break;
}
smb_shr_cache.sc_nops = 0;
break;
default:
assert(0);
break;
}
return (status);
}
/*
* Destroy the share cache (hash table).
* destroying the cache.
*/
static void
smb_shr_cache_destroy(void)
{
while (smb_shr_cache.sc_nops > 0)
}
}
/*
* If the cache is in "created" state, lock the cache for read
*
* Whenever a lock is granted, the number of inflight cache
* operations is incremented.
*/
static uint32_t
smb_shr_cache_lock(int mode)
{
return (NERR_InternalError);
}
/*
* Lock has to be taken outside the mutex otherwise
* there could be a deadlock
*/
if (mode == SMB_SHR_CACHE_RDLOCK)
else
return (NERR_Success);
}
/*
* Decrement the number of inflight operations and then unlock.
*/
static void
smb_shr_cache_unlock(void)
{
}
/*
* Return the total number of shares
*/
static int
smb_shr_cache_count(void)
{
}
/*
* looks up the given share name in the cache and if it
* finds a match returns a pointer to the cached entry.
* Note that since a pointer is returned this function
* MUST be protected by smb_shr_cache_lock/unlock pair
*/
static smb_share_t *
smb_shr_cache_findent(char *sharename)
{
(void) utf8_strlwr(sharename);
return (NULL);
}
/*
* the cache based on the given iterator.
*
* Calls to this function MUST be protected by
*/
static smb_share_t *
{
} else {
}
return (NULL);
}
/*
* Add the specified share to the cache. Memory needs to be allocated
* for the cache entry and the passed information is copied to the
* allocated space.
*/
static uint32_t
{
return (ERROR_NOT_ENOUGH_MEMORY);
== NULL) {
}
return (status);
}
/*
* Delete the specified share from the cache.
*/
static void
smb_shr_cache_delent(char *sharename)
{
(void) utf8_strlwr(sharename);
}
/*
* Call back to free the given cache entry.
*/
static void
{
}
/*
* ============================================
* Interfaces to sharemgr
*
* All functions in this section are private
* ============================================
*/
/*
* Load shares from sharemgr
*/
/*ARGSUSED*/
static void *
smb_shr_sa_loadall(void *args)
{
char *gstate;
return (NULL);
continue;
if (gdisabled)
continue;
}
}
return (NULL);
}
/*
* Load the shares contained in the specified group.
*
* Don't process groups on which the smb protocol is disabled.
* The top level ZFS group won't have the smb protocol enabled
* but sub-groups will.
*
* We will tolerate a limited number of errors and then give
* up on the current group. A typical error might be that the
* shared directory no longer exists.
*/
static void
{
int error_count = 0;
return;
++error_count;
break;
}
break;
}
}
/*
* Load a share definition from sharemgr and add it to the cache.
* If the share is already in the cache then it doesn't do anything.
*
* This function does not report duplicate shares as error since
* a share might have been added by smb_shr_get() while load is
* in progress.
*/
static uint32_t
{
char *sharename;
return (NERR_InternalError);
if (loaded)
return (NERR_Success);
return (status);
}
return (status);
}
return (NERR_Success);
}
/*
* Read the specified share information from sharemgr and return
* it in the given smb_share_t structure.
*
* Shares read from sharemgr are marked as permanent/persistent.
*/
static uint32_t
{
char *path;
char *rname;
return (NERR_InternalError);
return (NERR_InternalError);
}
}
return (NERR_Success);
sizeof (si->shr_container));
}
}
}
}
sizeof (si->shr_access_none));
}
}
sizeof (si->shr_access_ro));
}
}
sizeof (si->shr_access_rw));
}
}
return (NERR_Success);
}
/*
* Map a client-side caching (CSC) option to the appropriate share
* flag. Only one option is allowed; an error will be logged if
* multiple options have been specified. We don't need to do anything
* about multiple values here because the SRVSVC will not recognize
* a value containing multiple flags and will return the default value.
*
* If the option value is not recognized, it will be ignored: invalid
* values will typically be caught and rejected by sharemgr.
*/
void
{
struct {
char *value;
} cscopt[] = {
{ "disabled", SMB_SHRF_CSC_DISABLED },
{ "manual", SMB_SHRF_CSC_MANUAL },
{ "auto", SMB_SHRF_CSC_AUTO },
{ "vdo", SMB_SHRF_CSC_VDO }
};
int i;
break;
}
}
case 0:
case SMB_SHRF_CSC_DISABLED:
case SMB_SHRF_CSC_MANUAL:
case SMB_SHRF_CSC_AUTO:
case SMB_SHRF_CSC_VDO:
break;
default:
break;
}
}
/*
* looks up sharemgr for the given share (resource) and loads
* the definition into cache if lookup is successful
*/
static uint32_t
smb_shr_sa_loadbyname(char *sharename)
{
return (NERR_InternalError);
return (NERR_NetNameNotFound);
}
return (NERR_InternalError);
}
return (status);
}
/*
* ============================================
* Share publishing functions
*
* All the functions are private
* ============================================
*/
static void
{
}
static void
{
}
/*
* In domain mode, put a share on the publisher queue.
* This is a no-op if the smb service is in Workgroup mode.
*/
static void
{
return;
if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
return;
case SMB_SHR_PQS_READY:
case SMB_SHR_PQS_PUBLISHING:
break;
default:
return;
}
return;
sizeof (item->spi_container));
}
/*
* Publishing won't be activated if the smb service is running in
* Workgroup mode.
*/
static int
smb_shr_publisher_start(void)
{
int rc;
if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
return (0);
return (-1);
}
(void) pthread_attr_init(&tattr);
(void) pthread_attr_destroy(&tattr);
return (rc);
}
static void
smb_shr_publisher_stop(void)
{
if (smb_config_get_secmode() != SMB_SECMODE_DOMAIN)
return;
case SMB_SHR_PQS_READY:
case SMB_SHR_PQS_PUBLISHING:
break;
default:
break;
}
}
/*
* This is the publisher daemon thread. While running, the thread waits
* on a conditional variable until notified that a share needs to be
* [un]published or that the thread should be terminated.
*
* Entries may remain in the outgoing queue if the Active Directory
* service is inaccessible, in which case the thread wakes up every 60
* seconds to retry.
*/
/*ARGSUSED*/
static void *
smb_shr_publisher(void *arg)
{
char hostname[MAXHOSTNAMELEN];
return (NULL);
}
for (;;) {
if (list_is_empty(&publist)) {
} else {
break;
}
}
break;
}
/*
* Transfer queued items to the local list so that
* the mutex can be released.
*/
}
}
}
return (NULL);
}
/*
* Remove items from the specified queue and [un]publish them.
*/
static void
{
return;
}
else
}
}
/*
*/
static void
{
}
}