smb_server.c revision 148c5f43199ca0b43fc8e3b643aab11cd66ea327
/*
* 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
*/
/*
*/
/*
* 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 / |
* | SMB_SERVER_STATE_STOPPING |
* +-----------------------------+
* |
* | 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 <netinet/in_systm.h>
#include <smbsrv/smb_kproto.h>
#include <smbsrv/smb_fsops.h>
#include <smbsrv/smb_share.h>
#include <smbsrv/smb_door.h>
#include <smbsrv/smb_kstat.h>
extern void smb_reply_notify_change_request(smb_request_t *);
static void smb_server_kstat_init(smb_server_t *);
static void smb_server_kstat_fini(smb_server_t *);
static void smb_server_timers(smb_thread_t *, void *);
in_port_t, int, int);
static void smb_server_listen_fini(smb_listener_daemon_t *);
static int smb_server_lookup(smb_server_t **);
static void smb_server_release(smb_server_t *);
static void smb_server_shutdown(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_signal_listeners(smb_server_t *);
static uint32_t smb_event_alloc_txid(void);
static void smb_server_disconnect_share(smb_session_list_t *, const char *);
static int smb_server_sesion_disconnect(smb_session_list_t *, const char *,
const char *);
static int smb_server_kstat_update(kstat_t *, int);
int smb_event_debug = 0;
static smb_llist_t smb_servers;
/*
* *****************************************************************************
* **************** Functions called from the device interface *****************
* *****************************************************************************
*
* These functions typically have to determine the relevant smb server
* to which the call applies.
*/
/*
* 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_notify_init())
continue;
if (rc = smb_net_init())
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);
}
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_tid != 0)
if (tcp_tid != 0)
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;
}
break;
}
break;
return (0);
default:
return (ENOTTY);
}
return (rc);
}
/*
* An smbd is shutting down.
*/
int
smb_server_stop(void)
{
int rc;
return (rc);
case SMB_SERVER_STATE_RUNNING:
break;
default:
break;
}
return (0);
}
smb_server_is_stopping(void)
{
if (smb_server_lookup(&sv) != 0)
return (B_TRUE);
break;
default:
break;
}
return (status);
}
int
{
int rc;
}
return (rc);
}
int
{
int rc;
}
return (rc);
}
/*
* SMB-over-NetBIOS (port 139)
*
* Traditional SMB service over NetBIOS, which requires that a NetBIOS
* session be established.
*/
int
{
int rc;
if (rc)
return (rc);
case SMB_SERVER_STATE_RUNNING:
return (EACCES);
} else {
}
break;
return (ECANCELED);
default:
return (EFAULT);
}
/*
* netbios must be ipv4
*/
return (rc);
}
/*
* SMB-over-TCP (port 445)
*/
int
{
int rc;
if (rc)
return (rc);
case SMB_SERVER_STATE_RUNNING:
return (EACCES);
} else {
}
break;
return (ECANCELED);
default:
return (EFAULT);
}
else
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);
}
int
{
int rc;
}
return (rc);
}
/*
* Enumerate objects within the server. The svcenum provides the
* enumeration context, i.e. what the caller want to get back.
*/
int
{
int rc;
case SMB_SVCENUM_TYPE_USER:
case SMB_SVCENUM_TYPE_TREE:
case SMB_SVCENUM_TYPE_FILE:
break;
default:
return (EINVAL);
}
return (rc);
return (0);
}
/*
* Look for sessions to disconnect by client and user name.
*/
int
{
int nbt_cnt;
int tcp_cnt;
int rc;
return (rc);
return (ENOENT);
return (0);
}
/*
* Close a file by uniqid.
*/
int
{
int rc;
return (rc);
}
return (rc);
}
/*
* These functions determine the relevant smb server to which the call apply.
*/
{
if (smb_server_lookup(&sv))
return (0);
return (counter);
}
/*
* Gets the vnode of the specified share path.
*
* A hold on the returned vnode pointer is taken so the caller
* must call VN_RELE.
*/
int
{
char last_comp[MAXNAMELEN];
int rc = 0;
return (rc);
case SMB_SERVER_STATE_RUNNING:
break;
default:
return (ENOTACTIVE);
}
return (ENOMEM);
}
if (rc == 0) {
}
if (rc != 0)
return (rc);
return (0);
}
/*
* 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;
case SMB_SERVER_STATE_RUNNING:
break;
default:
break;
}
}
return (rc);
}
int
smb_server_unshare(const char *sharename)
{
int rc;
return (rc);
case SMB_SERVER_STATE_RUNNING:
break;
default:
return (ENOTACTIVE);
}
return (0);
}
/*
* Disconnect the specified share.
* Typically called when a share has been removed.
*/
static void
{
while (session) {
break;
default:
break;
}
}
}
/*
* *****************************************************************************
* **************** Functions called from the internal layers ******************
* *****************************************************************************
*
* These functions are provided the relevant smb server by the caller.
*/
void
{
session);
session);
}
void
{
}
/*
*
*/
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
void
{
}
/*
* *****************************************************************************
* *************************** Static Functions ********************************
* *****************************************************************************
*/
static void
{
}
}
/*
* smb_server_kstat_init
*/
static void
{
} else {
}
}
/*
* smb_server_kstat_fini
*/
static void
{
}
}
/*
* smb_server_kstat_update
*/
static int
{
if (rw == KSTAT_READ) {
/*
* Counters
*/
/*
* Throughput
*/
/*
* Busyness
*/
&ksd->ks_utilization);
/*
* Latency & Throughput of the requests
*/
return (0);
}
if (rw == KSTAT_WRITE)
return (EACCES);
return (EIO);
}
/*
* 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 = 0;
if (pthread_create_error) {
/*
* Delete the last session created. The user space thread
* creation failed.
*/
}
/* First time listener */
} else {
}
return (ENOMEM);
}
off = 0;
on = 1;
} else {
}
if (rc != 0) {
return (rc);
}
if (rc < 0) {
return (rc);
}
}
for (;;) {
if (smb_server_is_stopping()) {
break;
}
if (rc != 0)
break;
if (smb_server_is_stopping()) {
break;
}
on = 1;
on = 1;
/*
* Create a session for this connection.
*/
if (session) {
rc = 0;
break;
} else {
}
}
if (rc != 0)
return (rc);
}
static void
{
}
}
static kt_did_t
{
}
return (tid);
}
/*
* 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
{
}
/*
* Enumerate the users associated with a session list.
*/
static void
{
int rc = 0;
if (smb_user_hold(user)) {
}
}
if (rc != 0)
break;
}
}
/*
* Disconnect sessions associated with the specified client and username.
* Empty strings are treated as wildcards.
*/
static int
{
int count = 0;
continue;
}
if (smb_user_hold(user)) {
if (!match)
if (match) {
++count;
continue;
}
}
}
}
return (count);
}
/*
* Close a file by its unique id.
*/
static int
{
if (smb_user_hold(user)) {
}
}
}
return (rc);
}
static void
{
if (ioc->maxconnections == 0)
}
static int
{
int error;
if (error != 0)
return (error);
}
static void
{
}
}
static void
{
smb_event_cancel(sv, 0);
}
}
}
smb_event_create(void)
{
if (smb_server_is_stopping())
return (NULL);
if (smb_server_lookup(&sv) != 0) {
return (NULL);
}
return (event);
}
void
{
return;
if (smb_server_lookup(&sv) != 0)
return;
}
/*
* Get the txid for the specified event.
*/
{
}
return ((uint32_t)-1);
}
/*
* Wait for event notification.
*/
int
{
int seconds = 1;
int ticks;
return (EINVAL);
while (!(event->se_notified)) {
break;
break;
}
++event->se_waittime;
}
event->se_waittime = 0;
}
/*
* If txid is non-zero, cancel the specified event.
* Otherwise, cancel all events.
*/
static void
{
while (event) {
if (txid != 0)
break;
}
}
}
/*
* If txid is non-zero, notify the specified event.
* Otherwise, notify all events.
*/
static void
{
while (event) {
if (txid != 0)
break;
}
}
}
/*
* Allocate a new transaction id (txid).
*
* 0 or -1 are not assigned because they are used to detect invalid
* conditions or to indicate all open id's.
*/
static uint32_t
smb_event_alloc_txid(void)
{
if (txid == 0)
do {
++txid;
return (txid_ret);
}