From faa8edccef556fb281d24b76b9739c994128f6ef Mon Sep 17 00:00:00 2001 From: Stefan Metzmacher Date: Wed, 28 Mar 2012 16:14:09 +0200 Subject: [PATCH] s3:smbd: make use of smbXsrv_tcon for smb1 Pair-Programmed-With: Michael Adam metze --- source3/include/smb.h | 1 - source3/include/vfs.h | 2 +- source3/smbd/conn.c | 118 ----------------------------------------------- source3/smbd/conn_idle.c | 72 ++++++++++++++++------------- source3/smbd/globals.h | 4 -- source3/smbd/ipc.c | 24 +++++++++- source3/smbd/process.c | 27 +++++++++-- source3/smbd/proto.h | 3 -- source3/smbd/reply.c | 58 +++++++++++++++++++++-- source3/smbd/service.c | 44 +++++++++++++++++- 10 files changed, 183 insertions(+), 170 deletions(-) diff --git a/source3/include/smb.h b/source3/include/smb.h index 1c05459cf5f..f58095ec418 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -673,7 +673,6 @@ enum acl_compatibility {ACL_COMPAT_AUTO, ACL_COMPAT_WINNT, ACL_COMPAT_WIN2K}; #define VUID_OFFSET 100 /* Amount to bias returned vuid numbers */ #define TID_FIELD_INVALID 0 -#define CNUM_OFFSET 1 /* shift for bitmap index */ #define FNUM_FIELD_INVALID 0 diff --git a/source3/include/vfs.h b/source3/include/vfs.h index 1496164fafe..bd66d34043d 100644 --- a/source3/include/vfs.h +++ b/source3/include/vfs.h @@ -280,7 +280,7 @@ struct share_params { typedef struct connection_struct { struct connection_struct *next, *prev; struct smbd_server_connection *sconn; /* can be NULL */ - struct smbXsrv_tcon0 *tcon; /* for now NULL for SMB1 */ + struct smbXsrv_tcon0 *tcon; /* can be NULL */ uint32_t cnum; /* an index passed over the wire */ struct share_params *params; bool force_user; diff --git a/source3/smbd/conn.c b/source3/smbd/conn.c index 399935a9645..28e1850b450 100644 --- a/source3/smbd/conn.c +++ b/source3/smbd/conn.c @@ -24,22 +24,6 @@ #include "smbd/globals.h" #include "lib/util/bitmap.h" -/* The connections bitmap is expanded in increments of BITMAP_BLOCK_SZ. The - * maximum size of the bitmap is the largest positive integer, but you will hit - * the "max connections" limit, looong before that. - */ - -#define BITMAP_BLOCK_SZ 128 - -/**************************************************************************** - Init the conn structures. -****************************************************************************/ - -void conn_init(struct smbd_server_connection *sconn) -{ - sconn->smb1.tcons.bmap = bitmap_talloc(sconn, BITMAP_BLOCK_SZ); -} - /**************************************************************************** Return the number of open connections. ****************************************************************************/ @@ -68,27 +52,6 @@ bool conn_snum_used(struct smbd_server_connection *sconn, } /**************************************************************************** - Find a conn given a cnum. -****************************************************************************/ - -connection_struct *conn_find(struct smbd_server_connection *sconn, uint32_t cnum) -{ - size_t count=0; - struct connection_struct *conn; - - for (conn=sconn->connections; conn; conn=conn->next,count++) { - if (conn->cnum == cnum) { - if (count > 10) { - DLIST_PROMOTE(sconn->connections, conn); - } - return conn; - } - } - - return NULL; -} - -/**************************************************************************** Find first available connection slot, starting from a random position. The randomisation stops problems with the server dieing and clients thinking the server is still available. @@ -97,71 +60,6 @@ connection_struct *conn_find(struct smbd_server_connection *sconn, uint32_t cnum connection_struct *conn_new(struct smbd_server_connection *sconn) { connection_struct *conn; - int i; - uint32_t cnum; - int find_offset = 1; - - if (sconn->using_smb2) { - /* SMB2 */ - if (!(conn=talloc_zero(NULL, connection_struct)) || - !(conn->params = talloc(conn, struct share_params))) { - DEBUG(0,("TALLOC_ZERO() failed!\n")); - TALLOC_FREE(conn); - return NULL; - } - conn->sconn = sconn; - - DLIST_ADD(sconn->connections, conn); - sconn->num_connections++; - - return conn; - } - - /* SMB1 */ -find_again: - i = bitmap_find(sconn->smb1.tcons.bmap, find_offset); - - if (i == -1) { - /* Expand the connections bitmap. */ - int oldsz = sconn->smb1.tcons.bmap->n; - int newsz = sconn->smb1.tcons.bmap->n + - BITMAP_BLOCK_SZ; - struct bitmap * nbmap; - - if (newsz <= oldsz) { - /* Integer wrap. */ - DEBUG(0,("ERROR! Out of connection structures\n")); - return NULL; - } - - DEBUG(4,("resizing connections bitmap from %d to %d\n", - oldsz, newsz)); - - nbmap = bitmap_talloc(sconn, newsz); - if (!nbmap) { - DEBUG(0,("ERROR! malloc fail.\n")); - return NULL; - } - - bitmap_copy(nbmap, sconn->smb1.tcons.bmap); - TALLOC_FREE(sconn->smb1.tcons.bmap); - - sconn->smb1.tcons.bmap = nbmap; - find_offset = oldsz; /* Start next search in the new portion. */ - - goto find_again; - } - - /* The bitmap position is used below as the connection number - * conn->cnum). This ends up as the TID field in the SMB header, - * which is limited to 16 bits (we skip 0xffff which is the - * NULL TID). - */ - cnum = i + CNUM_OFFSET; - if (cnum >= 0xFFFF) { - DEBUG(0, ("Maximum connection limit reached\n")); - return NULL; - } if (!(conn=talloc_zero(NULL, connection_struct)) || !(conn->params = talloc(conn, struct share_params))) { @@ -170,11 +68,8 @@ find_again: return NULL; } conn->sconn = sconn; - conn->cnum = cnum; conn->force_group_gid = (gid_t)-1; - bitmap_set(sconn->smb1.tcons.bmap, i); - string_set(&conn->connectpath,""); string_set(&conn->origpath,""); @@ -294,19 +189,6 @@ void conn_free(connection_struct *conn) return; } - if (!conn->sconn->using_smb2 && - conn->sconn->smb1.tcons.bmap != NULL && - conn->cnum >= CNUM_OFFSET && - conn->cnum < 0xFFFF) - { - int i = conn->cnum - CNUM_OFFSET; - /* - * Can be NULL for fake connections created by - * create_conn_struct() - */ - bitmap_clear(conn->sconn->smb1.tcons.bmap, i); - } - DLIST_REMOVE(conn->sconn->connections, conn); SMB_ASSERT(conn->sconn->num_connections > 0); conn->sconn->num_connections--; diff --git a/source3/smbd/conn_idle.c b/source3/smbd/conn_idle.c index 22bc20ef1f5..fd3bf9d9d6b 100644 --- a/source3/smbd/conn_idle.c +++ b/source3/smbd/conn_idle.c @@ -90,14 +90,7 @@ void conn_close_all(struct smbd_server_connection *sconn) if (sconn->using_smb2) { smbXsrv_session_logoff_all(sconn->conn); } else { - /* SMB1 */ - connection_struct *conn, *next; - - for (conn=sconn->connections;conn;conn=next) { - next=conn->next; - set_current_service(conn, 0, True); - close_cnum(conn, conn->vuid); - } + smb1srv_tcon_disconnect_all(sconn->conn); } } @@ -111,38 +104,51 @@ void conn_close_all(struct smbd_server_connection *sconn) void conn_force_tdis(struct smbd_server_connection *sconn, const char *sharename) { connection_struct *conn, *next; + bool close_all = false; if (strcmp(sharename, "*") == 0) { - DEBUG(1,("Forcing close of all shares\n")); - conn_close_all(sconn); - return; + close_all = true; + DEBUG(1, ("conn_force_tdis: Forcing close of all shares\n")); } - if (sconn->using_smb2) { - for (conn=sconn->connections;conn;conn=next) { - struct smbXsrv_tcon *tcon; - - next = conn->next; - tcon = conn->tcon; + /* SMB1 and SMB 2*/ + for (conn = sconn->connections; conn; conn = next) { + struct smbXsrv_tcon *tcon; + bool do_close = false; + NTSTATUS status; + uint64_t vuid = UID_FIELD_INVALID; + + next = conn->next; + + if (close_all) { + do_close = true; + } else if (strequal(lp_servicename(SNUM(conn)), sharename)) { + DEBUG(1, ("conn_force_tdis: Forcing close of " + "share '%s' (wire_id=0x%08x)\n", + tcon->global->share_name, + tcon->global->tcon_wire_id)); + do_close = true; + } - if (!strequal(lp_servicename(SNUM(conn)), sharename)) { - continue; - } + if (!do_close) { + continue; + } - DEBUG(1,("Forcing close of share %s cnum=%d\n", - sharename, conn->cnum)); - smbXsrv_tcon_disconnect(tcon, conn->vuid); - TALLOC_FREE(tcon); + if (sconn->using_smb2) { + vuid = conn->vuid; } - } else { - /* SMB1 */ - for (conn=sconn->connections;conn;conn=next) { - next=conn->next; - if (strequal(lp_servicename(SNUM(conn)), sharename)) { - DEBUG(1,("Forcing close of share %s cnum=%d\n", - sharename, conn->cnum)); - close_cnum(conn, UID_FIELD_INVALID); - } + + conn = NULL; + status = smbXsrv_tcon_disconnect(tcon, vuid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("conn_force_tdis: " + "smbXsrv_tcon_disconnect() of share '%s' " + "(wire_id=0x%08x) failed: %s\n", + tcon->global->share_name, + tcon->global->tcon_wire_id, + nt_errstr(status))); } + + TALLOC_FREE(tcon); } } diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index a68c2034c8a..feda48b2e76 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -614,10 +614,6 @@ struct smbd_server_connection { */ uint16_t next_vuid; } sessions; - struct { - /* number of open connections */ - struct bitmap *bmap; - } tcons; struct smb_signing_state *signing_state; struct notify_mid_map *notify_mid_maps; diff --git a/source3/smbd/ipc.c b/source3/smbd/ipc.c index 77b9249e6f4..91d5047c4c6 100644 --- a/source3/smbd/ipc.c +++ b/source3/smbd/ipc.c @@ -631,8 +631,30 @@ static void handle_trans(connection_struct *conn, struct smb_request *req, state->max_param_return); if (state->close_on_completion) { - close_cnum(conn,state->vuid); + struct smbXsrv_tcon *tcon; + NTSTATUS status; + + tcon = conn->tcon; req->conn = NULL; + conn = NULL; + + /* + * TODO: cancel all outstanding requests on the tcon + */ + status = smbXsrv_tcon_disconnect(tcon, state->vuid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("handle_trans: " + "smbXsrv_tcon_disconnect() failed: %s\n", + nt_errstr(status))); + /* + * If we hit this case, there is something completely + * wrong, so we better disconnect the transport connection. + */ + exit_server(__location__ ": smbXsrv_tcon_disconnect failed"); + return; + } + + TALLOC_FREE(tcon); } return; diff --git a/source3/smbd/process.c b/source3/smbd/process.c index e25fbc77c16..311ef9dd2d0 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -516,6 +516,9 @@ static bool init_smb_request(struct smb_request *req, size_t unread_bytes, bool encrypted, uint32_t seqnum) { + struct smbXsrv_tcon *tcon; + NTSTATUS status; + NTTIME now; size_t req_size = smb_len(inbuf) + 4; /* Ensure we have at least smb_size bytes. */ @@ -526,6 +529,7 @@ static bool init_smb_request(struct smb_request *req, } req->request_time = timeval_current(); + now = timeval_to_nttime(&req->request_time); req->cmd = CVAL(inbuf, smb_com); req->flags2 = SVAL(inbuf, smb_flg2); @@ -541,7 +545,12 @@ static bool init_smb_request(struct smb_request *req, req->unread_bytes = unread_bytes; req->encrypted = encrypted; req->sconn = sconn; - req->conn = conn_find(sconn,req->tid); + status = smb1srv_tcon_lookup(sconn->conn, req->tid, now, &tcon); + if (NT_STATUS_IS_OK(status)) { + req->conn = tcon->compat; + } else { + req->conn = NULL; + } req->chain_fsp = NULL; req->smb2req = NULL; req->priv_paths = NULL; @@ -1643,10 +1652,18 @@ void smb_request_done(struct smb_request *req) while ((next_index < num_reqs) && (IVAL(req->outbuf, smb_rcls) == 0)) { struct smb_request *next = reqs[next_index]; + struct smbXsrv_tcon *tcon; + NTTIME now = timeval_to_nttime(&req->request_time); next->vuid = SVAL(req->outbuf, smb_uid); next->tid = SVAL(req->outbuf, smb_tid); - next->conn = conn_find(req->sconn, req->tid); + status = smb1srv_tcon_lookup(req->sconn->conn, req->tid, + now, &tcon); + if (NT_STATUS_IS_OK(status)) { + req->conn = tcon->compat; + } else { + req->conn = NULL; + } next->chain_fsp = req->chain_fsp; next->inbuf = (uint8_t *)req->inbuf; @@ -3167,6 +3184,11 @@ NTSTATUS smbXsrv_connection_init_tables(struct smbXsrv_connection *conn, if (!NT_STATUS_IS_OK(status)) { return status; } + } else { + status = smb1srv_tcon_table_init(conn); + if (!NT_STATUS_IS_OK(status)) { + return status; + } } return NT_STATUS_OK; @@ -3509,7 +3531,6 @@ void smbd_process(struct tevent_context *ev_ctx, /* this holds info on user ids that are already validated for this VC */ sconn->smb1.sessions.next_vuid = VUID_OFFSET; - conn_init(sconn); if (!init_dptrs(sconn)) { exit_server("init_dptrs() failed"); } diff --git a/source3/smbd/proto.h b/source3/smbd/proto.h index f774a3bf8cc..e8dbdfb928d 100644 --- a/source3/smbd/proto.h +++ b/source3/smbd/proto.h @@ -143,11 +143,8 @@ NTSTATUS delete_all_streams(connection_struct *conn, const char *fname); /* The following definitions come from smbd/conn.c */ -void conn_init(struct smbd_server_connection *sconn); int conn_num_open(struct smbd_server_connection *sconn); bool conn_snum_used(struct smbd_server_connection *sconn, int snum); -connection_struct *conn_find(struct smbd_server_connection *sconn, - uint32_t cnum); connection_struct *conn_new(struct smbd_server_connection *sconn); void conn_close_all(struct smbd_server_connection *sconn); bool conn_idle_all(struct smbd_server_connection *sconn, time_t t); diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index ca735b1f7c4..50c974f22b8 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -736,9 +736,31 @@ void reply_tcon_and_X(struct smb_request *req) /* we might have to close an old one */ if ((tcon_flags & 0x1) && conn) { - close_cnum(conn,req->vuid); + struct smbXsrv_tcon *tcon; + NTSTATUS status; + + tcon = conn->tcon; req->conn = NULL; conn = NULL; + + /* + * TODO: cancel all outstanding requests on the tcon + */ + status = smbXsrv_tcon_disconnect(tcon, req->vuid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("reply_tcon_and_X: " + "smbXsrv_tcon_disconnect() failed: %s\n", + nt_errstr(status))); + /* + * If we hit this case, there is something completely + * wrong, so we better disconnect the transport connection. + */ + END_PROFILE(SMBtconX); + exit_server(__location__ ": smbXsrv_tcon_disconnect failed"); + return; + } + + TALLOC_FREE(tcon); } if ((passlen > MAX_PASS_LEN) || (passlen >= req->buflen)) { @@ -4371,6 +4393,9 @@ bool is_valid_writeX_buffer(struct smbd_server_connection *sconn, connection_struct *conn = NULL; unsigned int doff = 0; size_t len = smb_len_large(inbuf); + struct smbXsrv_tcon *tcon; + NTSTATUS status; + NTTIME now = 0; if (is_encrypted_packet(sconn, inbuf)) { /* Can't do this on encrypted @@ -4389,11 +4414,14 @@ bool is_valid_writeX_buffer(struct smbd_server_connection *sconn, return false; } - conn = conn_find(sconn, SVAL(inbuf, smb_tid)); - if (conn == NULL) { + status = smb1srv_tcon_lookup(sconn->conn, SVAL(inbuf, smb_tid), + now, &tcon); + if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("is_valid_writeX_buffer: bad tid\n")); return false; } + conn = tcon->compat; + if (IS_IPC(conn)) { DEBUG(10,("is_valid_writeX_buffer: IPC$ tid\n")); return false; @@ -5056,7 +5084,10 @@ void reply_unlock(struct smb_request *req) void reply_tdis(struct smb_request *req) { + NTSTATUS status; connection_struct *conn = req->conn; + struct smbXsrv_tcon *tcon; + START_PROFILE(SMBtdis); if (!conn) { @@ -5066,9 +5097,28 @@ void reply_tdis(struct smb_request *req) return; } - close_cnum(conn,req->vuid); + tcon = conn->tcon; req->conn = NULL; + /* + * TODO: cancel all outstanding requests on the tcon + */ + status = smbXsrv_tcon_disconnect(tcon, req->vuid); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("reply_tdis: " + "smbXsrv_tcon_disconnect() failed: %s\n", + nt_errstr(status))); + /* + * If we hit this case, there is something completely + * wrong, so we better disconnect the transport connection. + */ + END_PROFILE(SMBtdis); + exit_server(__location__ ": smbXsrv_tcon_disconnect failed"); + return; + } + + TALLOC_FREE(tcon); + reply_outbuf(req, 0, 0); END_PROFILE(SMBtdis); return; diff --git a/source3/smbd/service.c b/source3/smbd/service.c index fe5838225d9..da5d763ad8f 100644 --- a/source3/smbd/service.c +++ b/source3/smbd/service.c @@ -917,12 +917,32 @@ static connection_struct *make_connection_smb1(struct smbd_server_connection *sc const char *pdev, NTSTATUS *pstatus) { - connection_struct *conn = conn_new(sconn); + struct smbXsrv_tcon *tcon; + NTSTATUS status; + NTTIME now = 0; + struct connection_struct *conn; + const char *share_name; + + status = smb1srv_tcon_create(sconn->conn, now, &tcon); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("make_connection_smb1: Couldn't find free tcon %s.\n", + nt_errstr(status))); + *pstatus = status; + return NULL; + } + + conn = conn_new(sconn); if (!conn) { + TALLOC_FREE(tcon); + DEBUG(0,("make_connection_smb1: Couldn't find free connection.\n")); *pstatus = NT_STATUS_INSUFFICIENT_RESOURCES; return NULL; } + + conn->cnum = tcon->global->tcon_wire_id; + conn->tcon = tcon; + *pstatus = make_connection_snum(sconn, conn, snum, @@ -930,9 +950,29 @@ static connection_struct *make_connection_smb1(struct smbd_server_connection *sc pdev); if (!NT_STATUS_IS_OK(*pstatus)) { conn_free(conn); + TALLOC_FREE(tcon); return NULL; } - return conn; + + share_name = lp_servicename(SNUM(conn)); + tcon->global->share_name = talloc_strdup(tcon->global, share_name); + if (tcon->global->share_name == NULL) { + conn_free(conn); + TALLOC_FREE(tcon); + *pstatus = NT_STATUS_NO_MEMORY; + return NULL; + } + + tcon->compat = talloc_move(tcon, &conn); + tcon->status = NT_STATUS_OK; + + *pstatus = smbXsrv_tcon_update(tcon); + if (!NT_STATUS_IS_OK(*pstatus)) { + TALLOC_FREE(tcon); + return NULL; + } + + return tcon->compat; } /**************************************************************************** -- 2.11.4.GIT