s3:smbd: add smbXsrv_tcon_disconnect*
authorMichael Adam <obnox@samba.org>
Mon, 21 May 2012 14:03:27 +0000 (16:03 +0200)
committerStefan Metzmacher <metze@samba.org>
Fri, 1 Jun 2012 09:53:32 +0000 (11:53 +0200)
This is destructor code for tree connects.

* smbXsrv_tcon_disconnect() is called by smbXsrv_tcon_destructor()
  but it is handy to be able to call it separately.
* smbXsrv_tcon_disconnect_all() removes disconnects all tcons
  in a tcon table
* smb1srv_tcon_disconnect_all()

Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>

source3/smbd/globals.h
source3/smbd/smbXsrv_tcon.c

index a66bf5eff23c29ff9508d6128f5ae46098107c9e..ae25a433c8b042f3838910b5e91bbb2fad8fb417 100644 (file)
@@ -380,6 +380,7 @@ NTSTATUS smb2srv_session_lookup(struct smbXsrv_connection *conn,
 
 NTSTATUS smbXsrv_tcon_global_init(void);
 NTSTATUS smbXsrv_tcon_update(struct smbXsrv_tcon *tcon);
+NTSTATUS smbXsrv_tcon_disconnect(struct smbXsrv_tcon *tcon);
 NTSTATUS smb1srv_tcon_table_init(struct smbXsrv_connection *conn);
 NTSTATUS smb1srv_tcon_create(struct smbXsrv_connection *conn,
                             NTTIME now,
@@ -387,6 +388,7 @@ NTSTATUS smb1srv_tcon_create(struct smbXsrv_connection *conn,
 NTSTATUS smb1srv_tcon_lookup(struct smbXsrv_connection *conn,
                             uint16_t tree_id, NTTIME now,
                             struct smbXsrv_tcon **tcon);
+NTSTATUS smb1srv_tcon_disconnect_all(struct smbXsrv_connection *conn);
 NTSTATUS smb2srv_tcon_table_init(struct smbXsrv_session *session);
 NTSTATUS smb2srv_tcon_create(struct smbXsrv_session *session,
                             NTTIME now,
@@ -394,6 +396,7 @@ NTSTATUS smb2srv_tcon_create(struct smbXsrv_session *session,
 NTSTATUS smb2srv_tcon_lookup(struct smbXsrv_session *session,
                             uint32_t tree_id, NTTIME now,
                             struct smbXsrv_tcon **tcon);
+NTSTATUS smb2srv_tcon_disconnect_all(struct smbXsrv_session *session);
 
 struct smbd_smb2_request {
        struct smbd_smb2_request *prev, *next;
index 20d7258fdeaf4d55920f5a8d92f4388a6308f711..a01714fe44ef80e5da63cfce559294f4874aafc5 100644 (file)
@@ -617,51 +617,15 @@ static NTSTATUS smbXsrv_tcon_global_store(struct smbXsrv_tcon_global0 *global)
 
 static int smbXsrv_tcon_destructor(struct smbXsrv_tcon *tcon)
 {
-       struct smbXsrv_tcon_table *table;
-       struct db_record *local_rec = NULL;
-       struct db_record *global_rec = NULL;
        NTSTATUS status;
 
-       if (tcon->table == NULL) {
-               return 0;
-       }
-
-       table = tcon->table;
-       tcon->table = NULL;
-
-       local_rec = tcon->db_rec;
-       tcon->db_rec = NULL;
-       if (local_rec == NULL) {
-               uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
-               TDB_DATA key;
-
-               key = smbXsrv_tcon_local_id_to_key(tcon->local_id, key_buf);
-
-               local_rec = dbwrap_fetch_locked(table->local.db_ctx,
-                                               tcon, key);
-       }
-
-       if (local_rec != NULL) {
-               status = dbwrap_record_delete(local_rec);
-       }
-
-       global_rec = tcon->global->db_rec;
-       tcon->global->db_rec = NULL;
-       if (global_rec == NULL) {
-               uint8_t key_buf[SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE];
-               TDB_DATA key;
-
-               key = smbXsrv_tcon_global_id_to_key(
-                                               tcon->global->tcon_global_id,
-                                               key_buf);
-
-               global_rec = dbwrap_fetch_locked(table->global.db_ctx,
-                                                tcon->global, key);
+       status = smbXsrv_tcon_disconnect(tcon);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("smbXsrv_tcon_destructor: "
+                         "smbXsrv_tcon_disconnect() failed - %s\n",
+                         nt_errstr(status)));
        }
 
-       if (global_rec != NULL) {
-               status = dbwrap_record_delete(global_rec);
-       }
        TALLOC_FREE(tcon->global);
 
        return 0;
@@ -821,6 +785,214 @@ NTSTATUS smbXsrv_tcon_update(struct smbXsrv_tcon *tcon)
        return NT_STATUS_OK;
 }
 
+NTSTATUS smbXsrv_tcon_disconnect(struct smbXsrv_tcon *tcon)
+{
+       struct smbXsrv_tcon_table *table;
+       struct db_record *local_rec = NULL;
+       struct db_record *global_rec = NULL;
+       NTSTATUS status;
+       NTSTATUS error = NT_STATUS_OK;
+
+       if (tcon->table == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       table = tcon->table;
+       tcon->table = NULL;
+
+       tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
+
+       global_rec = tcon->global->db_rec;
+       tcon->global->db_rec = NULL;
+       if (global_rec == NULL) {
+               uint8_t key_buf[SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE];
+               TDB_DATA key;
+
+               key = smbXsrv_tcon_global_id_to_key(
+                                               tcon->global->tcon_global_id,
+                                               key_buf);
+
+               global_rec = dbwrap_fetch_locked(table->global.db_ctx,
+                                                tcon->global, key);
+               if (global_rec == NULL) {
+                       DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
+                                 "Failed to lock global key '%s'\n",
+                                 tcon->global->tcon_wire_id,
+                                 tcon->global->share_name,
+                                 hex_encode_talloc(global_rec, key.dptr,
+                                                   key.dsize)));
+                       error = NT_STATUS_INTERNAL_ERROR;
+               }
+       }
+
+       if (global_rec != NULL) {
+               status = dbwrap_record_delete(global_rec);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TDB_DATA key = dbwrap_record_get_key(global_rec);
+
+                       DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
+                                 "failed to delete global key '%s': %s\n",
+                                 tcon->global->tcon_wire_id,
+                                 tcon->global->share_name,
+                                 hex_encode_talloc(global_rec, key.dptr,
+                                                   key.dsize),
+                                 nt_errstr(status)));
+                       error = status;
+               }
+       }
+       TALLOC_FREE(global_rec);
+
+       local_rec = tcon->db_rec;
+       if (local_rec == NULL) {
+               uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
+               TDB_DATA key;
+
+               key = smbXsrv_tcon_local_id_to_key(tcon->local_id, key_buf);
+
+               local_rec = dbwrap_fetch_locked(table->local.db_ctx,
+                                               tcon, key);
+               if (local_rec == NULL) {
+                       DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
+                                 "Failed to lock local key '%s'\n",
+                                 tcon->global->tcon_wire_id,
+                                 tcon->global->share_name,
+                                 hex_encode_talloc(local_rec, key.dptr,
+                                                   key.dsize)));
+                       error = NT_STATUS_INTERNAL_ERROR;
+               }
+       }
+
+       if (local_rec != NULL) {
+               status = dbwrap_record_delete(local_rec);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TDB_DATA key = dbwrap_record_get_key(local_rec);
+
+                       DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
+                                 "failed to delete local key '%s': %s\n",
+                                 tcon->global->tcon_wire_id,
+                                 tcon->global->share_name,
+                                 hex_encode_talloc(local_rec, key.dptr,
+                                                   key.dsize),
+                                 nt_errstr(status)));
+                       error = status;
+               }
+       }
+       if (tcon->db_rec == NULL) {
+               TALLOC_FREE(local_rec);
+       }
+       tcon->db_rec = NULL;
+
+       if (tcon->compat) {
+               bool ok;
+
+               ok = change_to_user(tcon->compat, tcon->compat->vuid);
+               if (!ok) {
+                       status = NT_STATUS_INTERNAL_ERROR;
+                       DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
+                                 "change_to_user() failed: %s\n",
+                                 tcon->global->tcon_wire_id,
+                                 tcon->global->share_name,
+                                 nt_errstr(status)));
+                       tcon->compat = NULL;
+                       return status;
+               }
+
+               ok = set_current_service(tcon->compat, 0, true);
+               if (!ok) {
+                       status = NT_STATUS_INTERNAL_ERROR;
+                       DEBUG(0, ("smbXsrv_tcon_disconnect(0x%08x, '%s'): "
+                                 "set_current_service() failed: %s\n",
+                                 tcon->global->tcon_wire_id,
+                                 tcon->global->share_name,
+                                 nt_errstr(status)));
+                       tcon->compat = NULL;
+                       return status;
+               }
+
+               close_cnum(tcon->compat, tcon->compat->vuid);
+               tcon->compat = NULL;
+       }
+
+       return error;
+}
+
+struct smbXsrv_tcon_disconnect_all_state {
+       NTSTATUS first_status;
+       int errors;
+};
+
+static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
+                                               void *private_data);
+
+static NTSTATUS smbXsrv_tcon_disconnect_all(struct smbXsrv_tcon_table *table)
+{
+       struct smbXsrv_tcon_disconnect_all_state state;
+       NTSTATUS status;
+       int count = 0;
+
+       ZERO_STRUCT(state);
+
+       if (table == NULL) {
+               return NT_STATUS_OK;
+       }
+
+       status = dbwrap_traverse(table->local.db_ctx,
+                                smbXsrv_tcon_disconnect_all_callback,
+                                &state, &count);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("smbXsrv_tcon_disconnect_all: "
+                         "dbwrap_traverse() failed: %s\n",
+                         nt_errstr(status)));
+               return status;
+       }
+
+       if (!NT_STATUS_IS_OK(state.first_status)) {
+               DEBUG(0, ("smbXsrv_tcon_disconnect_all: "
+                         "count[%d] errors[%d] first[%s]\n",
+                         count, state.errors,
+                         nt_errstr(state.first_status)));
+               return state.first_status;
+       }
+
+       return NT_STATUS_OK;
+}
+
+static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
+                                               void *private_data)
+{
+       struct smbXsrv_tcon_disconnect_all_state *state =
+               (struct smbXsrv_tcon_disconnect_all_state *)private_data;
+       TDB_DATA val;
+       void *ptr = NULL;
+       struct smbXsrv_tcon *tcon = NULL;
+       NTSTATUS status;
+
+       val = dbwrap_record_get_value(local_rec);
+       if (val.dsize != sizeof(ptr)) {
+               status = NT_STATUS_INTERNAL_ERROR;
+               if (NT_STATUS_IS_OK(state->first_status)) {
+                       state->first_status = status;
+               }
+               state->errors++;
+               return 0;
+       }
+
+       memcpy(&ptr, val.dptr, val.dsize);
+       tcon = talloc_get_type_abort(ptr, struct smbXsrv_tcon);
+
+       tcon->db_rec = local_rec;
+       status = smbXsrv_tcon_disconnect(tcon);
+       if (!NT_STATUS_IS_OK(status)) {
+               if (NT_STATUS_IS_OK(state->first_status)) {
+                       state->first_status = status;
+               }
+               state->errors++;
+               return 0;
+       }
+
+       return 0;
+}
+
 NTSTATUS smb1srv_tcon_table_init(struct smbXsrv_connection *conn)
 {
        /*
@@ -853,6 +1025,11 @@ NTSTATUS smb1srv_tcon_lookup(struct smbXsrv_connection *conn,
                                         local_id, now, tcon);
 }
 
+NTSTATUS smb1srv_tcon_disconnect_all(struct smbXsrv_connection *conn)
+{
+       return smbXsrv_tcon_disconnect_all(conn->tcon_table);
+}
+
 NTSTATUS smb2srv_tcon_table_init(struct smbXsrv_session *session)
 {
        /*
@@ -886,3 +1063,8 @@ NTSTATUS smb2srv_tcon_lookup(struct smbXsrv_session *session,
        return smbXsrv_tcon_local_lookup(session->tcon_table,
                                         local_id, now, tcon);
 }
+
+NTSTATUS smb2srv_tcon_disconnect_all(struct smbXsrv_session *session)
+{
+       return smbXsrv_tcon_disconnect_all(session->tcon_table);
+}