smb_server.c revision 08f0d8da054d72c87f9a35f2ea891d2c3541ceb5
/*
* 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.
*/
/*
* General Structures Layout
* -------------------------
*
* This is a simplified diagram showing the relationship between most of the
* main structures.
*
* +-------------------+
* | SMB_SERVER |
* +-------------------+
* |
* |
* v
* +-------------------+ +-------------------+ +-------------------+
* | SESSION |<----->| SESSION |......| SESSION |
* +-------------------+ +-------------------+ +-------------------+
* |
* |
* v
* +-------------------+ +-------------------+ +-------------------+
* | USER |<----->| USER |......| USER |
* +-------------------+ +-------------------+ +-------------------+
* |
* |
* v
* +-------------------+ +-------------------+ +-------------------+
* | TREE |<----->| TREE |......| TREE |
* +-------------------+ +-------------------+ +-------------------+
* | |
* | |
* | v
* | +-------+ +-------+ +-------+
* | | OFILE |<----->| OFILE |......| OFILE |
* | +-------+ +-------+ +-------+
* |
* |
* v
* +-------+ +------+ +------+
* | ODIR |<----->| ODIR |......| ODIR |
* +-------+ +------+ +------+
*
*
* Module Interface Overview
* -------------------------
*
*
* +===================================+
* | smbd daemon |
* +===================================+
* | | ^
* | | |
* User | | |
* -----------|--------------|----------------|--------------------------------
* Kernel | | |
* | | |
* | | |
* +=========|==============|================|=================+
* | v v | |
* | +-----------+ +--------------------+ +------------------+ |
* | | IO | | Kernel Door Server | | User Door Servers| |
* | | Interface | | Interface | | Interface | |
* | +-----------+ +--------------------+ +------------------+ |
* | | | ^ ^ |
* | v v | | | +=========+
* | +-----------------------------------+ | | | |
* | + SMB Server Management (this file) |<------------------| ZFS |
* | +-----------------------------------+ | | | |
* | | | | Module |
* | +-----------------------------------+ | | | |
* | + SMB Server Internal Layers |------+ | +=========+
* | +-----------------------------------+ |
* | |
* | |
* +===========================================================+
*
*
* Server State Machine
* --------------------
* |
* | T0
* |
* v
* +-----------------------------+
* | SMB_SERVER_STATE_CREATED |
* +-----------------------------+
* |
* | T1
* |
* v
* +-----------------------------+
* | SMB_SERVER_STATE_CONFIGURED |
* +-----------------------------+
* |
* | T2
* |
* v
* +-----------------------------+
* | SMB_SERVER_STATE_RUNNING |
* +-----------------------------+
* |
* | T3
* |
* v
* +-----------------------------+
* | SMB_SERVER_STATE_DELETING |
* +-----------------------------+
* |
* |
* |
* v
*
* States
* ------
*
* SMB_SERVER_STATE_CREATED
*
* This is the state of the server just after creation.
*
* SMB_SERVER_STATE_CONFIGURED
*
* The server has been configured.
*
* SMB_SERVER_STATE_RUNNING
*
* The server has been started. While in this state the threads listening on
* the sockets car be started. The smbd daemon does so through an Ioctl:
*
* smb_drv_ioctl(SMB_IOC_NBT_LISTEN) --> smb_server_nbt_listen()
* smb_drv_ioctl(SMB_IOC_TCP_LISTEN) --> smb_server_nbt_listen()
*
* When a client establishes a connection the thread listening leaves
* temporarily the kernel. While in user space it creates a thread for the
* new session. It then returns to kernel with the result of the thread
* creation. If the creation failed the new session context is destroyed
* before returning listening.
*
* The new created thread enters the kernel though an Ioctl:
*
* smb_drv_ioctl(SMB_IOC_NBT_RECEIVE) --> smb_server_nbt_receive()
* smb_drv_ioctl(SMB_IOC_TCP_RECEIVE) --> smb_server_tcp_receive()
*
* SMB_SERVER_STATE_STOPPING
*
* The threads listening on the NBT and TCP sockets are being terminated.
*
*
* Transitions
* -----------
*
* Transition T0
*
* The daemon smbd triggers its creation by opening the smbsrv device. If
* the zone where the daemon lives doesn't have an smb server yet it is
* created.
*
* smb_drv_open() --> smb_server_create()
*
* Transition T1
*
* This transition occurs in smb_server_configure(). It is triggered by the
* daemon through an Ioctl.
*
* smb_drv_ioctl(SMB_IOC_CONFIG) --> smb_server_configure()
*
* Transition T2
*
* This transition occurs in smb_server_start(). It is triggered by the
* daemon through an Ioctl.
*
* smb_drv_ioctl(SMB_IOC_START) --> smb_server_start()
*
* Transition T3
*
* This transition occurs in smb_server_delete(). It is triggered by the
* daemon when closing the smbsrv device
*
* smb_drv_close() --> smb_server_delete()
*
* Comments
* --------
*
* This files assumes that there will one SMB server per zone. For now the
* smb server works only in global zone. There's nothing in this file preventing
* an smb server from being created in a non global zone. That limitation is
* enforced in user space.
*/
#include <sys/socketvar.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_incl.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_share.h>
#include <smbsrv/smb_door_svc.h>
#include <smbsrv/smb_kstat.h>
extern void smb_dispatch_kstat_init(void);
extern void smb_dispatch_kstat_fini(void);
extern void smb_reply_notify_change_request(smb_request_t *);
static int smb_server_kstat_init(smb_server_t *);
static void smb_server_kstat_fini(smb_server_t *);
static int smb_server_kstat_update_info(kstat_t *, int);
static void smb_server_timers(smb_thread_t *, void *);
in_port_t, int, int);
static int smb_server_lookup(smb_server_t **);
static void smb_server_release(smb_server_t *);
static int smb_server_ulist_geti(smb_session_list_t *, int,
smb_opipe_context_t *, int);
static void smb_server_stop(smb_server_t *);
static int smb_server_fsop_start(smb_server_t *);
static void smb_server_fsop_stop(smb_server_t *);
static void smb_server_disconnect_share(char *, smb_server_t *);
static void smb_server_thread_unexport(smb_thread_t *, void *);
static smb_llist_t smb_servers;
/*
* *****************************************************************************
* **************** Functions called from the device interface *****************
* *****************************************************************************
*
* These functions determine the relevant smb server to which the call apply.
*/
/*
* smb_server_svc_init
*
* This function must be called from smb_drv_attach().
*/
int
smb_server_svc_init(void)
{
int rc = 0;
while (rc == 0) {
if (rc = smb_mbc_init())
continue;
if (rc = smb_vop_init())
continue;
if (rc = smb_node_init())
continue;
if (rc = smb_fem_init())
continue;
if (rc = smb_user_init())
continue;
if (rc = smb_notify_init())
continue;
if (rc = smb_net_init())
continue;
if (rc = smb_kdoor_srv_start())
continue;
return (0);
}
smb_net_fini();
smb_fem_fini();
smb_vop_fini();
smb_mbc_fini();
return (rc);
}
/*
* smb_server_svc_fini
*
* This function must called from smb_drv_detach(). It will fail if servers
* still exist.
*/
int
smb_server_svc_fini(void)
{
if (smb_llist_get_count(&smb_servers) == 0) {
smb_net_fini();
smb_fem_fini();
smb_vop_fini();
smb_mbc_fini();
rc = 0;
}
return (rc);
}
/*
* smb_server_create
*
* This function will fail if there's already a server associated with the
* caller's zone.
*/
int
smb_server_create(void)
{
while (sv) {
return (EPERM);
}
}
return (ENOMEM);
}
(void) smb_server_kstat_init(sv);
return (0);
}
/*
* smb_server_delete
*
* This function will delete the server passed in. It will make sure that all
* activity associated that server has ceased before destroying it.
*/
int
smb_server_delete(void)
{
int rc;
if (rc != 0)
return (rc);
case SMB_SERVER_STATE_RUNNING:
{
}
}
if (nbt)
if (tcp)
break;
}
case SMB_SERVER_STATE_CREATED:
break;
default:
return (ENOTTY);
}
}
return (0);
}
/*
* smb_server_configure
*/
int
{
int rc = 0;
if (rc)
return (rc);
case SMB_SERVER_STATE_CREATED:
break;
break;
case SMB_SERVER_STATE_RUNNING:
break;
default:
break;
}
return (rc);
}
/*
* smb_server_start
*/
int
{
int rc = 0;
if (rc)
return (rc);
break;
}
break;
break;
break;
if (rc = smb_kdoor_srv_set_downcall()) {
break;
}
break;
break;
break;
}
return (0);
default:
return (ENOTTY);
}
return (rc);
}
/*
* smb_server_nbt_listen: SMB-over-NetBIOS service
*
* Traditional SMB service over NetBIOS (port 139), which requires
* that a NetBIOS session be established.
*/
int
{
int rc;
if (rc)
return (rc);
case SMB_SERVER_STATE_RUNNING:
return (EACCES);
} else {
}
break;
default:
return (EFAULT);
}
/*
* netbios must be ipv4
*/
if (rc) {
}
return (rc);
}
int
{
int rc;
if (rc)
return (rc);
case SMB_SERVER_STATE_RUNNING:
return (EACCES);
} else {
}
break;
default:
return (EFAULT);
}
else
if (rc) {
}
return (rc);
}
/*
* smb_server_nbt_receive
*/
int
smb_server_nbt_receive(void)
{
int rc;
}
return (rc);
}
/*
* smb_server_tcp_receive
*/
int
smb_server_tcp_receive(void)
{
int rc;
}
return (rc);
}
int
{
int rc;
}
return (rc);
}
/*
* *****************************************************************************
* ****************** Functions called from the door interface *****************
* *****************************************************************************
*
* These functions determine the relevant smb server to which the call apply.
*/
{
if (smb_server_lookup(&sv) == 0) {
}
return (counter);
}
{
if (smb_server_lookup(&sv))
return (0);
return (counter);
}
/*
* smb_server_disconnect_share
*
* Disconnects the specified share. This function should be called after the
* share passed in has been made unavailable by the "share manager".
*/
static void
{
}
int
{
if (!dr_ulist)
return (-1);
if (smb_server_lookup(&sv))
return (-1);
max_cnt);
}
/*
* smb_server_share_export()
*
* This function handles kernel processing at share enable time.
*
* At share-enable time (LMSHRD_ADD), the file system corresponding to
* the share is checked for characteristics that are required for SMB
* sharing. If this check passes, then a hold is taken on the root vnode
* of the file system (or a reference count on the corresponding smb_vfs_t
* is bumped), preventing an unmount. (See smb_vfs_hold()).
*/
int
smb_server_share_export(char *path)
{
int error = 0;
char last_comp[MAXNAMELEN];
if (smb_server_lookup(&sv))
return (EINVAL);
return (ENOMEM);
}
if (error) {
return (error);
}
if (error) {
return (error);
}
#ifdef SMB_ENFORCE_NODEV
return (EINVAL);
}
#endif /* SMB_ENFORCE_NODEV */
/*
* The refcount on the smb_vfs has been incremented.
* If it wasn't already, a hold has also been taken
* on the root vnode of the file system.
*/
return (error);
}
/*
* smb_server_share_unexport()
*
* This function is invoked when a share is disabled to disconnect trees
* file system. Queueing the request for asynchronous processing allows the
* call to return immediately so that, if the unshare is being done in the
* context of a forced unmount, the forced unmount will always be able to
* proceed (unblocking stuck I/O and eventually allowing all blocked unshare
* processes to complete).
*
* The path lookup to find the root vnode of the VFS in question and the
* release of this vnode are done synchronously prior to any associated
* unmount. Doing these asynchronous to an associated unmount could run
* the risk of a spurious EBUSY for a standard unmount or an EIO during
* the path lookup due to a forced unmount finishing first.
*/
int
{
char last_comp[MAXNAMELEN];
int rc;
return (rc);
return (ENOMEM);
}
if (rc) {
return (rc);
}
if (rc) {
return (rc);
}
return (0);
}
/*
* smb_server_thread_unexport
*
* This function processes the unexport event list and disconnects shares
* asynchronously. The function executes as a zone-specific thread.
*
* The server arg passed in is safe to use without a reference count, because
* the server cannot be deleted until smb_thread_stop()/destroy() return,
* which is also when the thread exits.
*/
static void
{
while (smb_thread_continue(thread)) {
!= NULL) {
}
}
}
/*
* This is a special interface that will be utilized by ZFS to cause a share to
*
* arg is either a lmshare_info_t or share_name from userspace.
* It will need to be copied into the kernel. It is lmshare_info_t
* for add operations and share_name for delete operations.
*/
int
{
int rc;
if (rc == 0) {
} else {
}
}
return (rc);
}
/*
* *****************************************************************************
* **************** Functions called from the internal layers ******************
* *****************************************************************************
*
* These functions are provided the relevant smb server by the caller.
*/
void
{
session);
session);
}
void
{
}
/*
* *****************************************************************************
* *************************** Static Functions ********************************
* *****************************************************************************
*/
static void
{
}
}
/*
* smb_server_kstat_init
*/
static int
{
}
/* create and initialize smb kstats - smb_dispatch stats */
return (0);
}
/*
* smb_server_kstat_fini
*/
static void
{
}
}
/* ARGSUSED */
static int
{
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
}
return (0);
}
/*
* smb_server_stop
*
* The mutex of the server must have been entered before calling this function.
*/
static void
{
if (sv->sv_session) {
}
if (sv->sv_thread_pool) {
}
}
static int
int family,
int pthread_create_error)
{
int rc;
if (pthread_create_error) {
/*
* Delete the last session created. The user space thread
* creation failed.
*/
}
/* First time listener */
} else {
}
} else {
}
if (rc == 0) {
if (rc < 0) {
"Port %d: listen failed", port);
return (rc);
}
} else {
"Port %d: bind failed", port);
return (rc);
}
} else {
"Port %d: socket create failed", port);
return (ENOMEM);
}
}
for (;;) {
if (rc == 0) {
(const void *)&txbuf_size, sizeof (txbuf_size),
CRED());
/*
* Create a session for this connection.
*/
if (session) {
session);
break;
} else {
}
continue;
}
break;
}
return (rc);
}
/*
* smb_server_lookup
*
* This function tries to find the server associated with the zone of the
* caller.
*/
static int
{
while (sv) {
return (0);
}
break;
}
}
return (EPERM);
}
/*
* smb_server_release
*
* This function decrements the reference count of the server and signals its
* condition variable if the state of the server is SMB_SERVER_STATE_DELETING.
*/
static void
{
}
static int
int offset,
int max_cnt)
{
continue;
}
ctx++;
cnt++;
}
}
}
return (cnt);
}
static void
{
if (cfg->skc_maxconnections == 0)
}
static int
{
int error;
if (error != 0)
return (error);
}
static void
{
}
}