smb_server.c revision 4846df9b53c813b173160a71c267f6678e9bf59b
/*
* 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 2012 Nexenta Systems, Inc. All rights reserved.
*/
/*
* 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 are started.
*
* When a client establishes a connection the thread listening dispatches
* a task with the new session as an argument. If the dispatch fails the new
* session context is destroyed.
*
* 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 *);
typedef struct {
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 *);
int smb_server_lookup(smb_server_t **);
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 uint32_t smb_event_alloc_txid(void);
static void smb_server_disconnect_share(smb_llist_t *, const char *);
static int smb_server_session_disconnect(smb_llist_t *, const char *,
const char *);
static int smb_server_kstat_update(kstat_t *, int);
static int smb_server_legacy_kstat_update(kstat_t *, int);
char *, in_port_t, int);
static void smb_server_listener_destroy(smb_listener_daemon_t *);
static int smb_server_listener_start(smb_listener_daemon_t *);
static void smb_server_listener_stop(smb_listener_daemon_t *);
static void smb_server_listener(smb_thread_t *, void *);
static void smb_server_receiver(void *);
static void smb_server_destroy_session(smb_listener_daemon_t *,
smb_session_t *);
smb_kspooldoc_t *);
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_oplock_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:
break;
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;
int family;
if (rc)
return (rc);
break;
}
break;
break;
break;
}
break;
}
break;
if (rc != 0)
break;
if (rc != 0)
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_server_spooldoc
*
* Waits for print file close broadcast.
* Gets the head of the fid list,
* then searches the spooldoc list and returns
* this info via the ioctl to user land.
*
* rc - 0 success
*/
int
{
int rc;
return (rc);
goto out;
}
for (;;) {
break;
}
rc = 0;
break;
}
break;
}
}
if (rc != 0)
goto out;
} else {
/* Did not find that print job. */
}
out:
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
{
}
/*
*
*/
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
{
char name[KSTAT_STRLEN];
} else {
}
sizeof (smb_server_legacy_kstat_t) / sizeof (kstat_named_t), 0);
}
}
/*
* 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);
}
static int
{
int rc;
switch (rw) {
case KSTAT_WRITE:
break;
case KSTAT_READ:
if (!smb_server_lookup(&sv)) {
rc = 0;
break;
}
default:
break;
}
return (rc);
}
/*
* smb_server_shutdown
*/
static void
{
/*
* smb_kshare_export may have a request on here.
* Normal sessions do this in smb_session_cancel()
* but this is a "fake" session used only for the
* requests used by the kshare thread(s).
*/
}
}
}
}
/*
* smb_server_listener_init
*
* Initializes listener contexts.
*/
static void
char *name,
int family)
{
} else {
}
}
/*
* smb_server_listener_destroy
*
* Destroyes listener contexts.
*/
static void
{
}
/*
* smb_server_listener_start
*
* Starts the listener associated with the context passed in.
*
* Return: 0 Success
* not 0 Failure
*/
static int
{
int rc;
return (EINVAL);
return (ENOMEM);
}
off = 0;
on = 1;
} else {
}
if (rc != 0) {
return (rc);
}
if (rc < 0) {
return (rc);
}
if (rc != 0) {
return (rc);
}
return (0);
}
/*
* smb_server_listener_stop
*
* Stops the listener associated with the context passed in.
*/
static void
{
}
}
/*
* smb_server_listener
*
* Entry point of the listeners.
*/
static void
{
int on;
int txbuf_size;
== 0) {
on = 1;
on = 1;
/*
* Create a session for this connection.
*/
}
/* Disconnect all the sessions this listener created. */
}
}
/*
* smb_server_receiver
*
* Entry point of the receiver threads.
*/
static void
smb_server_receiver(void *arg)
{
}
/*
* smb_server_lookup
*
* This function tries to find the server associated with the zone of the
* caller.
*/
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.
*/
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)
/*
* Note that these two optional protocol features
* (oplocks, raw_mode) have unfortunate interactions.
* Since raw_mode is only wanted by ancient clients,
* we just turn it off (that's what MS recommends).
* Leave some evidence in the log if someone has
* patched smb_raw_mode to enable it.
*/
"Raw mode enabled: Disabling opportunistic locks");
}
}
static int
{
int error;
if (error != 0)
return (error);
}
static void
{
}
}
smb_event_create(int timeout)
{
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.
*/
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);
}
/*
* Called by the ioctl to find the corresponding
* spooldoc node. removes node on success
*
* Return values
* rc
* B_FALSE - not found
* B_TRUE - found
*
*/
static boolean_t
{
/*
* check for a matching fid
*/
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Adds the spool fid to a linked list to be used
* as a search key in the spooldoc queue
*
* Return values
* rc non-zero error
* rc zero success
*
*/
void
{
return;
}
/*
* Called by the ioctl to get and remove the head of the fid list
*
* Return values
* int fd
* greater than 0 success
* 0 - error
*
*/
static uint16_t
{
} else {
fid = 0;
}
return (fid);
}
/*
* Adds the spooldoc to the tail of the spooldoc list
*
* Return values
* rc non-zero error
* rc zero success
*/
int
{
int rc = 0;
if (rc)
return (rc);
return (rc);
}
/*
* smb_server_create_session
*/
static void
{
sizeof (smb_receiver_arg_t));
return;
} else {
}
}
static void
{
}