1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00
samba-mirror/source3/smbd/conn.c
Jeremy Allison e4f5bfb34b Fix use of DLIST_REMOVE as spotted by Constantine Vetoshev <gepardcv@gmail.com>.
This API is unusual in that if used to remove a non-list head it nulls out
the next and prev pointers. This is what you want for debugging (don't want
an entry removed from the list to be still virtually linked into it) but
means there is no consistent idiom for use as the next and prev pointers
get trashed on removal from the list, meaning you must save them yourself.
You can use it one way when deleting everything via the head pointer, as
this preserves the next pointer, but you *must* use it another way when not
deleting everything via the head pointer. Fix all known uses of this (the main
one is in conn_free_internal() and would not free all the private data entries
for vfs modules. The other changes in web/statuspage.c and winbindd_util.c
are not strictly neccessary, as the head pointer is being used, but I've done
them for consistency. Long term we must revisit this as this API is too hard
to use correctly.
Jeremy.
2008-10-01 13:15:54 -07:00

330 lines
8.8 KiB
C

/*
Unix SMB/CIFS implementation.
Manage connections_struct structures
Copyright (C) Andrew Tridgell 1998
Copyright (C) Alexander Bokovoy 2002
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.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
static connection_struct *Connections;
/* number of open connections */
static struct bitmap *bmap;
static int num_open;
/****************************************************************************
init the conn structures
****************************************************************************/
void conn_init(void)
{
bmap = bitmap_allocate(BITMAP_BLOCK_SZ);
}
/****************************************************************************
return the number of open connections
****************************************************************************/
int conn_num_open(void)
{
return num_open;
}
/****************************************************************************
check if a snum is in use
****************************************************************************/
bool conn_snum_used(int snum)
{
connection_struct *conn;
for (conn=Connections;conn;conn=conn->next) {
if (conn->params->service == snum) {
return(True);
}
}
return(False);
}
/****************************************************************************
Find a conn given a cnum.
****************************************************************************/
connection_struct *conn_find(unsigned cnum)
{
int count=0;
connection_struct *conn;
for (conn=Connections;conn;conn=conn->next,count++) {
if (conn->cnum == cnum) {
if (count > 10) {
DLIST_PROMOTE(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.
****************************************************************************/
connection_struct *conn_new(void)
{
connection_struct *conn;
int i;
int find_offset = 1;
find_again:
i = bitmap_find(bmap, find_offset);
if (i == -1) {
/* Expand the connections bitmap. */
int oldsz = bmap->n;
int newsz = 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_allocate(newsz);
if (!nbmap) {
DEBUG(0,("ERROR! malloc fail.\n"));
return NULL;
}
bitmap_copy(nbmap, bmap);
bitmap_free(bmap);
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).
*/
if (i > 65534) {
DEBUG(0, ("Maximum connection limit reached\n"));
return NULL;
}
if (!(conn=TALLOC_ZERO_P(NULL, connection_struct)) ||
!(conn->params = TALLOC_P(conn, struct share_params))) {
DEBUG(0,("TALLOC_ZERO() failed!\n"));
TALLOC_FREE(conn);
return NULL;
}
conn->cnum = i;
bitmap_set(bmap, i);
num_open++;
string_set(&conn->dirpath,"");
string_set(&conn->connectpath,"");
string_set(&conn->origpath,"");
DLIST_ADD(Connections, conn);
return conn;
}
/****************************************************************************
Close all conn structures.
return true if any were closed
****************************************************************************/
bool conn_close_all(void)
{
connection_struct *conn, *next;
bool ret = false;
for (conn=Connections;conn;conn=next) {
next=conn->next;
set_current_service(conn, 0, True);
close_cnum(conn, conn->vuid);
ret = true;
}
return ret;
}
/****************************************************************************
Idle inactive connections.
****************************************************************************/
bool conn_idle_all(time_t t)
{
int deadtime = lp_deadtime()*60;
pipes_struct *plist = NULL;
connection_struct *conn;
if (deadtime <= 0)
deadtime = DEFAULT_SMBD_TIMEOUT;
for (conn=Connections;conn;conn=conn->next) {
time_t age = t - conn->lastused;
/* Update if connection wasn't idle. */
if (conn->lastused != conn->lastused_count) {
conn->lastused = t;
conn->lastused_count = t;
}
/* close dirptrs on connections that are idle */
if (age > DPTR_IDLE_TIMEOUT) {
dptr_idlecnum(conn);
}
if (conn->num_files_open > 0 || age < deadtime) {
return False;
}
}
/*
* Check all pipes for any open handles. We cannot
* idle with a handle open.
*/
for (plist = get_first_internal_pipe(); plist;
plist = get_next_internal_pipe(plist)) {
if (plist->pipe_handles && plist->pipe_handles->count) {
return False;
}
}
return True;
}
/****************************************************************************
Clear a vuid out of the validity cache, and as the 'owner' of a connection.
****************************************************************************/
void conn_clear_vuid_caches(uint16_t vuid)
{
connection_struct *conn;
for (conn=Connections;conn;conn=conn->next) {
if (conn->vuid == vuid) {
conn->vuid = UID_FIELD_INVALID;
}
conn_clear_vuid_cache(conn, vuid);
}
}
/****************************************************************************
Free a conn structure - internal part.
****************************************************************************/
void conn_free_internal(connection_struct *conn)
{
vfs_handle_struct *handle = NULL, *thandle = NULL;
struct trans_state *state = NULL;
/* Free vfs_connection_struct */
handle = conn->vfs_handles;
while(handle) {
thandle = handle->next;
DLIST_REMOVE(conn->vfs_handles, handle);
if (handle->free_data)
handle->free_data(&handle->data);
handle = thandle;
}
/* Free any pending transactions stored on this conn. */
for (state = conn->pending_trans; state; state = state->next) {
/* state->setup is a talloc child of state. */
SAFE_FREE(state->param);
SAFE_FREE(state->data);
}
free_namearray(conn->veto_list);
free_namearray(conn->hide_list);
free_namearray(conn->veto_oplock_list);
free_namearray(conn->aio_write_behind_list);
string_free(&conn->dirpath);
string_free(&conn->connectpath);
string_free(&conn->origpath);
ZERO_STRUCTP(conn);
talloc_destroy(conn);
}
/****************************************************************************
Free a conn structure.
****************************************************************************/
void conn_free(connection_struct *conn)
{
DLIST_REMOVE(Connections, conn);
bitmap_clear(bmap, conn->cnum);
SMB_ASSERT(num_open > 0);
num_open--;
conn_free_internal(conn);
}
/****************************************************************************
receive a smbcontrol message to forcibly unmount a share
the message contains just a share name and all instances of that
share are unmounted
the special sharename '*' forces unmount of all shares
****************************************************************************/
void msg_force_tdis(struct messaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
DATA_BLOB *data)
{
connection_struct *conn, *next;
fstring sharename;
fstrcpy(sharename, (const char *)data->data);
if (strcmp(sharename, "*") == 0) {
DEBUG(1,("Forcing close of all shares\n"));
conn_close_all();
return;
}
for (conn=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, (uint16)-1);
}
}
}