2 Unix SMB/CIFS implementation.
4 Copyright (C) Stefan Metzmacher 2011-2012
5 Copyright (C) Michael Adam 2012
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "system/filesys.h"
23 #include "lib/util/server_id.h"
24 #include "smbd/smbd.h"
25 #include "smbd/globals.h"
26 #include "dbwrap/dbwrap.h"
27 #include "dbwrap/dbwrap_rbt.h"
28 #include "dbwrap/dbwrap_open.h"
30 #include "lib/util/util_tdb.h"
31 #include "librpc/gen_ndr/ndr_smbXsrv.h"
33 #include "source3/include/util_tdb.h"
35 struct smbXsrv_tcon_table
{
37 struct db_context
*db_ctx
;
44 struct db_context
*db_ctx
;
48 static struct db_context
*smbXsrv_tcon_global_db_ctx
= NULL
;
50 NTSTATUS
smbXsrv_tcon_global_init(void)
52 char *global_path
= NULL
;
53 struct db_context
*db_ctx
= NULL
;
55 if (smbXsrv_tcon_global_db_ctx
!= NULL
) {
59 global_path
= lock_path(talloc_tos(), "smbXsrv_tcon_global.tdb");
60 if (global_path
== NULL
) {
61 return NT_STATUS_NO_MEMORY
;
64 db_ctx
= db_open(NULL
, global_path
,
65 SMBD_VOLATILE_TDB_HASH_SIZE
,
66 SMBD_VOLATILE_TDB_FLAGS
,
67 O_RDWR
| O_CREAT
, 0600,
70 TALLOC_FREE(global_path
);
74 status
= map_nt_error_from_unix_common(errno
);
79 smbXsrv_tcon_global_db_ctx
= db_ctx
;
86 * We need to store the keys in big endian so that dbwrap_rbt's memcmp
87 * has the same result as integer comparison between the uint32_t
90 * TODO: implement string based key
93 #define SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE sizeof(uint32_t)
95 static TDB_DATA
smbXsrv_tcon_global_id_to_key(uint32_t id
,
100 RSIVAL(key_buf
, 0, id
);
102 key
= make_tdb_data(key_buf
, SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE
);
108 static NTSTATUS
smbXsrv_tcon_global_key_to_id(TDB_DATA key
, uint32_t *id
)
111 return NT_STATUS_INVALID_PARAMETER
;
114 if (key
.dsize
!= SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE
) {
115 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
118 *id
= RIVAL(key
.dptr
, 0);
124 #define SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE sizeof(uint32_t)
126 static TDB_DATA
smbXsrv_tcon_local_id_to_key(uint32_t id
,
131 RSIVAL(key_buf
, 0, id
);
133 key
= make_tdb_data(key_buf
, SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE
);
138 static NTSTATUS
smbXsrv_tcon_local_key_to_id(TDB_DATA key
, uint32_t *id
)
141 return NT_STATUS_INVALID_PARAMETER
;
144 if (key
.dsize
!= SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE
) {
145 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
148 *id
= RIVAL(key
.dptr
, 0);
153 static struct db_record
*smbXsrv_tcon_global_fetch_locked(
154 struct db_context
*db
,
159 uint8_t key_buf
[SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE
];
160 struct db_record
*rec
= NULL
;
162 key
= smbXsrv_tcon_global_id_to_key(id
, key_buf
);
164 rec
= dbwrap_fetch_locked(db
, mem_ctx
, key
);
167 DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id
,
174 static struct db_record
*smbXsrv_tcon_local_fetch_locked(
175 struct db_context
*db
,
180 uint8_t key_buf
[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE
];
181 struct db_record
*rec
= NULL
;
183 key
= smbXsrv_tcon_local_id_to_key(id
, key_buf
);
185 rec
= dbwrap_fetch_locked(db
, mem_ctx
, key
);
188 DBG_DEBUG("Failed to lock local id 0x%08x, key '%s'\n", id
,
195 static NTSTATUS
smbXsrv_tcon_table_init(TALLOC_CTX
*mem_ctx
,
196 struct smbXsrv_tcon_table
*table
,
204 if (lowest_id
> highest_id
) {
205 return NT_STATUS_INTERNAL_ERROR
;
208 max_range
= highest_id
;
209 max_range
-= lowest_id
;
212 if (max_tcons
> max_range
) {
213 return NT_STATUS_INTERNAL_ERROR
;
217 table
->local
.db_ctx
= db_open_rbt(table
);
218 if (table
->local
.db_ctx
== NULL
) {
219 return NT_STATUS_NO_MEMORY
;
221 table
->local
.lowest_id
= lowest_id
;
222 table
->local
.highest_id
= highest_id
;
223 table
->local
.max_tcons
= max_tcons
;
225 status
= smbXsrv_tcon_global_init();
226 if (!NT_STATUS_IS_OK(status
)) {
230 table
->global
.db_ctx
= smbXsrv_tcon_global_db_ctx
;
235 struct smb1srv_tcon_local_allocate_state
{
236 const uint32_t lowest_id
;
237 const uint32_t highest_id
;
243 static int smb1srv_tcon_local_allocate_traverse(struct db_record
*rec
,
246 struct smb1srv_tcon_local_allocate_state
*state
=
247 (struct smb1srv_tcon_local_allocate_state
*)private_data
;
248 TDB_DATA key
= dbwrap_record_get_key(rec
);
252 status
= smbXsrv_tcon_local_key_to_id(key
, &id
);
253 if (!NT_STATUS_IS_OK(status
)) {
254 state
->status
= status
;
258 if (id
<= state
->last_id
) {
259 state
->status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
264 if (id
> state
->useable_id
) {
265 state
->status
= NT_STATUS_OK
;
269 if (state
->useable_id
== state
->highest_id
) {
270 state
->status
= NT_STATUS_INSUFFICIENT_RESOURCES
;
274 state
->useable_id
+=1;
278 static NTSTATUS
smb1srv_tcon_local_allocate_id(struct db_context
*db
,
282 struct db_record
**_rec
,
285 struct smb1srv_tcon_local_allocate_state state
= {
286 .lowest_id
= lowest_id
,
287 .highest_id
= highest_id
,
289 .useable_id
= lowest_id
,
290 .status
= NT_STATUS_INTERNAL_ERROR
,
300 if (lowest_id
> highest_id
) {
301 return NT_STATUS_INSUFFICIENT_RESOURCES
;
305 * first we try randomly
307 range
= (highest_id
- lowest_id
) + 1;
309 for (i
= 0; i
< (range
/ 2); i
++) {
312 struct db_record
*rec
= NULL
;
314 id
= generate_random() % range
;
317 if (id
< lowest_id
) {
320 if (id
> highest_id
) {
324 rec
= smbXsrv_tcon_local_fetch_locked(db
, id
, mem_ctx
);
326 return NT_STATUS_INSUFFICIENT_RESOURCES
;
329 val
= dbwrap_record_get_value(rec
);
330 if (val
.dsize
!= 0) {
341 * if the range is almost full,
342 * we traverse the whole table
343 * (this relies on sorted behavior of dbwrap_rbt)
345 status
= dbwrap_traverse_read(db
, smb1srv_tcon_local_allocate_traverse
,
347 if (NT_STATUS_IS_OK(status
)) {
348 if (NT_STATUS_IS_OK(state
.status
)) {
349 return NT_STATUS_INTERNAL_ERROR
;
352 if (!NT_STATUS_EQUAL(state
.status
, NT_STATUS_INTERNAL_ERROR
)) {
356 if (state
.useable_id
<= state
.highest_id
) {
357 state
.status
= NT_STATUS_OK
;
359 return NT_STATUS_INSUFFICIENT_RESOURCES
;
361 } else if (!NT_STATUS_EQUAL(status
, NT_STATUS_INTERNAL_DB_CORRUPTION
)) {
363 * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
365 * If we get anything else it is an error, because it
366 * means we did not manage to find a free slot in
369 return NT_STATUS_INSUFFICIENT_RESOURCES
;
372 if (NT_STATUS_IS_OK(state
.status
)) {
375 struct db_record
*rec
= NULL
;
377 id
= state
.useable_id
;
379 rec
= smbXsrv_tcon_local_fetch_locked(db
, id
, mem_ctx
);
381 return NT_STATUS_INSUFFICIENT_RESOURCES
;
384 val
= dbwrap_record_get_value(rec
);
385 if (val
.dsize
!= 0) {
387 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
398 struct smbXsrv_tcon_local_fetch_state
{
399 struct smbXsrv_tcon
*tcon
;
403 static void smbXsrv_tcon_local_fetch_parser(TDB_DATA key
, TDB_DATA data
,
406 struct smbXsrv_tcon_local_fetch_state
*state
=
407 (struct smbXsrv_tcon_local_fetch_state
*)private_data
;
410 if (data
.dsize
!= sizeof(ptr
)) {
411 state
->status
= NT_STATUS_INTERNAL_DB_ERROR
;
415 memcpy(&ptr
, data
.dptr
, data
.dsize
);
416 state
->tcon
= talloc_get_type_abort(ptr
, struct smbXsrv_tcon
);
417 state
->status
= NT_STATUS_OK
;
420 static NTSTATUS
smbXsrv_tcon_local_lookup(struct smbXsrv_tcon_table
*table
,
421 uint32_t tcon_local_id
,
423 struct smbXsrv_tcon
**_tcon
)
425 struct smbXsrv_tcon_local_fetch_state state
= {
427 .status
= NT_STATUS_INTERNAL_ERROR
,
429 uint8_t key_buf
[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE
];
435 if (tcon_local_id
== 0) {
436 return NT_STATUS_NETWORK_NAME_DELETED
;
440 /* this might happen before the end of negprot */
441 return NT_STATUS_NETWORK_NAME_DELETED
;
444 if (table
->local
.db_ctx
== NULL
) {
445 return NT_STATUS_INTERNAL_ERROR
;
448 key
= smbXsrv_tcon_local_id_to_key(tcon_local_id
, key_buf
);
450 status
= dbwrap_parse_record(table
->local
.db_ctx
, key
,
451 smbXsrv_tcon_local_fetch_parser
,
453 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
454 return NT_STATUS_NETWORK_NAME_DELETED
;
455 } else if (!NT_STATUS_IS_OK(status
)) {
458 if (!NT_STATUS_IS_OK(state
.status
)) {
462 if (NT_STATUS_EQUAL(state
.tcon
->status
, NT_STATUS_NETWORK_NAME_DELETED
)) {
463 return NT_STATUS_NETWORK_NAME_DELETED
;
466 state
.tcon
->idle_time
= now
;
469 return state
.tcon
->status
;
472 static int smbXsrv_tcon_global_destructor(struct smbXsrv_tcon_global0
*global
)
477 static void smbXsrv_tcon_global_verify_record(struct db_record
*db_rec
,
481 struct smbXsrv_tcon_global0
**_g
);
483 static NTSTATUS
smbXsrv_tcon_global_allocate(struct db_context
*db
,
485 struct smbXsrv_tcon_global0
**_global
)
488 struct smbXsrv_tcon_global0
*global
= NULL
;
489 uint32_t last_free
= 0;
490 const uint32_t min_tries
= 3;
494 global
= talloc_zero(mem_ctx
, struct smbXsrv_tcon_global0
);
495 if (global
== NULL
) {
496 return NT_STATUS_NO_MEMORY
;
498 talloc_set_destructor(global
, smbXsrv_tcon_global_destructor
);
501 * Here we just randomly try the whole 32-bit space
503 * We use just 32-bit, because we want to reuse the
506 for (i
= 0; i
< UINT32_MAX
; i
++) {
507 bool is_free
= false;
508 bool was_free
= false;
511 if (i
>= min_tries
&& last_free
!= 0) {
514 id
= generate_random();
519 if (id
== UINT32_MAX
) {
523 global
->db_rec
= smbXsrv_tcon_global_fetch_locked(db
, id
,
525 if (global
->db_rec
== NULL
) {
527 return NT_STATUS_INSUFFICIENT_RESOURCES
;
530 smbXsrv_tcon_global_verify_record(global
->db_rec
,
536 TALLOC_FREE(global
->db_rec
);
540 if (!was_free
&& i
< min_tries
) {
542 * The session_id is free now,
543 * but was not free before.
545 * This happens if a smbd crashed
546 * and did not cleanup the record.
548 * If this is one of our first tries,
549 * then we try to find a real free one.
551 if (last_free
== 0) {
554 TALLOC_FREE(global
->db_rec
);
558 global
->tcon_global_id
= id
;
564 /* should not be reached */
566 return NT_STATUS_INTERNAL_ERROR
;
569 static void smbXsrv_tcon_global_verify_record(struct db_record
*db_rec
,
573 struct smbXsrv_tcon_global0
**_g
)
578 struct smbXsrv_tcon_globalB global_blob
;
579 enum ndr_err_code ndr_err
;
580 struct smbXsrv_tcon_global0
*global
= NULL
;
582 TALLOC_CTX
*frame
= talloc_stackframe();
593 key
= dbwrap_record_get_key(db_rec
);
595 val
= dbwrap_record_get_value(db_rec
);
596 if (val
.dsize
== 0) {
605 blob
= data_blob_const(val
.dptr
, val
.dsize
);
607 ndr_err
= ndr_pull_struct_blob(&blob
, frame
, &global_blob
,
608 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_tcon_globalB
);
609 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
610 NTSTATUS status
= ndr_map_error2ntstatus(ndr_err
);
611 DBG_WARNING("key '%s' ndr_pull_struct_blob - %s\n",
618 DBG_DEBUG("smbXsrv_tcon_global_verify_record\n");
619 if (DEBUGLVL(DBGLVL_DEBUG
)) {
620 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB
, &global_blob
);
623 if (global_blob
.version
!= SMBXSRV_VERSION_0
) {
624 DBG_ERR("key '%s' uses unsupported version %u\n",
626 global_blob
.version
);
627 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB
, &global_blob
);
632 global
= global_blob
.info
.info0
;
634 exists
= serverid_exists(&global
->server_id
);
636 struct server_id_buf idbuf
;
637 DBG_NOTICE("key '%s' server_id %s does not exist.\n",
639 server_id_str_buf(global
->server_id
, &idbuf
));
640 if (DEBUGLVL(DBGLVL_NOTICE
)) {
641 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB
, &global_blob
);
644 dbwrap_record_delete(db_rec
);
650 *_g
= talloc_move(mem_ctx
, &global
);
655 static NTSTATUS
smbXsrv_tcon_global_store(struct smbXsrv_tcon_global0
*global
)
657 struct smbXsrv_tcon_globalB global_blob
;
658 DATA_BLOB blob
= data_blob_null
;
662 enum ndr_err_code ndr_err
;
665 * TODO: if we use other versions than '0'
666 * we would add glue code here, that would be able to
667 * store the information in the old format.
670 if (global
->db_rec
== NULL
) {
671 return NT_STATUS_INTERNAL_ERROR
;
674 key
= dbwrap_record_get_key(global
->db_rec
);
675 val
= dbwrap_record_get_value(global
->db_rec
);
677 ZERO_STRUCT(global_blob
);
678 global_blob
.version
= smbXsrv_version_global_current();
679 if (val
.dsize
>= 8) {
680 global_blob
.seqnum
= IVAL(val
.dptr
, 4);
682 global_blob
.seqnum
+= 1;
683 global_blob
.info
.info0
= global
;
685 ndr_err
= ndr_push_struct_blob(&blob
, global
->db_rec
, &global_blob
,
686 (ndr_push_flags_fn_t
)ndr_push_smbXsrv_tcon_globalB
);
687 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
688 status
= ndr_map_error2ntstatus(ndr_err
);
689 DBG_WARNING("key '%s' ndr_push - %s\n",
692 TALLOC_FREE(global
->db_rec
);
696 val
= make_tdb_data(blob
.data
, blob
.length
);
697 status
= dbwrap_record_store(global
->db_rec
, val
, TDB_REPLACE
);
698 if (!NT_STATUS_IS_OK(status
)) {
699 DBG_WARNING("key '%s' store - %s\n",
702 TALLOC_FREE(global
->db_rec
);
706 if (DEBUGLVL(DBGLVL_DEBUG
)) {
707 DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key
));
708 NDR_PRINT_DEBUG(smbXsrv_tcon_globalB
, &global_blob
);
711 TALLOC_FREE(global
->db_rec
);
716 static int smbXsrv_tcon_destructor(struct smbXsrv_tcon
*tcon
)
720 status
= smbXsrv_tcon_disconnect(tcon
, 0);
721 if (!NT_STATUS_IS_OK(status
)) {
722 DBG_ERR("smbXsrv_tcon_disconnect() failed - %s\n",
726 TALLOC_FREE(tcon
->global
);
731 static NTSTATUS
smbXsrv_tcon_create(struct smbXsrv_tcon_table
*table
,
732 enum protocol_types protocol
,
733 struct server_id server_id
,
735 uint32_t session_global_id
,
736 uint8_t encryption_flags
,
737 const char *share_name
,
738 struct smbXsrv_tcon
**_tcon
)
740 struct db_record
*local_rec
= NULL
;
741 struct smbXsrv_tcon
*tcon
= NULL
;
744 struct smbXsrv_tcon_global0
*global
= NULL
;
747 if (table
->local
.num_tcons
>= table
->local
.max_tcons
) {
748 return NT_STATUS_INSUFFICIENT_RESOURCES
;
751 tcon
= talloc_zero(table
, struct smbXsrv_tcon
);
753 return NT_STATUS_NO_MEMORY
;
756 tcon
->status
= NT_STATUS_INTERNAL_ERROR
;
757 tcon
->idle_time
= now
;
759 status
= smbXsrv_tcon_global_allocate(table
->global
.db_ctx
,
761 if (!NT_STATUS_IS_OK(status
)) {
765 tcon
->global
= global
;
767 global
->session_global_id
= session_global_id
;
768 global
->encryption_flags
= encryption_flags
;
769 global
->share_name
= talloc_strdup(global
, share_name
);
770 if (global
->share_name
== NULL
) {
772 return NT_STATUS_NO_MEMORY
;
775 if (protocol
>= PROTOCOL_SMB2_02
) {
776 uint64_t id
= global
->tcon_global_id
;
778 global
->tcon_wire_id
= id
;
780 tcon
->local_id
= global
->tcon_global_id
;
782 local_rec
= smbXsrv_tcon_local_fetch_locked(table
->local
.db_ctx
,
784 tcon
/* TALLOC_CTX */);
785 if (local_rec
== NULL
) {
787 return NT_STATUS_NO_MEMORY
;
790 val
= dbwrap_record_get_value(local_rec
);
791 if (val
.dsize
!= 0) {
793 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
797 status
= smb1srv_tcon_local_allocate_id(table
->local
.db_ctx
,
798 table
->local
.lowest_id
,
799 table
->local
.highest_id
,
803 if (!NT_STATUS_IS_OK(status
)) {
808 global
->tcon_wire_id
= tcon
->local_id
;
811 global
->creation_time
= now
;
813 global
->server_id
= server_id
;
816 val
= make_tdb_data((uint8_t const *)&ptr
, sizeof(ptr
));
817 status
= dbwrap_record_store(local_rec
, val
, TDB_REPLACE
);
818 TALLOC_FREE(local_rec
);
819 if (!NT_STATUS_IS_OK(status
)) {
823 table
->local
.num_tcons
+= 1;
825 talloc_set_destructor(tcon
, smbXsrv_tcon_destructor
);
827 status
= smbXsrv_tcon_global_store(global
);
828 if (!NT_STATUS_IS_OK(status
)) {
829 DBG_ERR("global_id (0x%08x) store failed - %s\n",
830 tcon
->global
->tcon_global_id
,
836 if (DEBUGLVL(DBGLVL_DEBUG
)) {
837 struct smbXsrv_tconB tcon_blob
= {
838 .version
= SMBXSRV_VERSION_0
,
842 DBG_DEBUG("global_id (0x%08x) stored\n",
843 tcon
->global
->tcon_global_id
);
844 NDR_PRINT_DEBUG(smbXsrv_tconB
, &tcon_blob
);
851 NTSTATUS
smbXsrv_tcon_update(struct smbXsrv_tcon
*tcon
)
853 struct smbXsrv_tcon_table
*table
= tcon
->table
;
856 if (tcon
->global
->db_rec
!= NULL
) {
857 DBG_ERR("update(0x%08x): "
858 "Called with db_rec != NULL'\n",
859 tcon
->global
->tcon_global_id
);
860 return NT_STATUS_INTERNAL_ERROR
;
863 tcon
->global
->db_rec
= smbXsrv_tcon_global_fetch_locked(
864 table
->global
.db_ctx
,
865 tcon
->global
->tcon_global_id
,
866 tcon
->global
/* TALLOC_CTX */);
867 if (tcon
->global
->db_rec
== NULL
) {
868 return NT_STATUS_INTERNAL_DB_ERROR
;
871 status
= smbXsrv_tcon_global_store(tcon
->global
);
872 if (!NT_STATUS_IS_OK(status
)) {
873 DBG_ERR("global_id (0x%08x) store failed - %s\n",
874 tcon
->global
->tcon_global_id
,
879 if (DEBUGLVL(DBGLVL_DEBUG
)) {
880 struct smbXsrv_tconB tcon_blob
= {
881 .version
= SMBXSRV_VERSION_0
,
885 DBG_DEBUG("global_id (0x%08x) stored\n",
886 tcon
->global
->tcon_global_id
);
887 NDR_PRINT_DEBUG(smbXsrv_tconB
, &tcon_blob
);
893 NTSTATUS
smbXsrv_tcon_disconnect(struct smbXsrv_tcon
*tcon
, uint64_t vuid
)
895 struct smbXsrv_tcon_table
*table
;
896 struct db_record
*local_rec
= NULL
;
897 struct db_record
*global_rec
= NULL
;
899 NTSTATUS error
= NT_STATUS_OK
;
901 if (tcon
->table
== NULL
) {
911 ok
= chdir_current_service(tcon
->compat
);
913 status
= NT_STATUS_INTERNAL_ERROR
;
914 DBG_ERR("disconnect(0x%08x, '%s'): "
915 "chdir_current_service() failed: %s\n",
916 tcon
->global
->tcon_global_id
,
917 tcon
->global
->share_name
,
920 * We must call close_cnum() on
921 * error, as the caller is going
922 * to free tcon and tcon->compat
923 * so we must ensure tcon->compat is
924 * removed from the linked list
925 * conn->sconn->connections.
927 close_cnum(tcon
->compat
, vuid
, ERROR_CLOSE
);
932 close_cnum(tcon
->compat
, vuid
, SHUTDOWN_CLOSE
);
936 tcon
->status
= NT_STATUS_NETWORK_NAME_DELETED
;
938 global_rec
= tcon
->global
->db_rec
;
939 tcon
->global
->db_rec
= NULL
;
940 if (global_rec
== NULL
) {
941 global_rec
= smbXsrv_tcon_global_fetch_locked(
942 table
->global
.db_ctx
,
943 tcon
->global
->tcon_global_id
,
944 tcon
->global
/* TALLOC_CTX */);
945 if (global_rec
== NULL
) {
946 error
= NT_STATUS_INTERNAL_ERROR
;
950 if (global_rec
!= NULL
) {
951 status
= dbwrap_record_delete(global_rec
);
952 if (!NT_STATUS_IS_OK(status
)) {
953 TDB_DATA key
= dbwrap_record_get_key(global_rec
);
955 DBG_ERR("disconnect(0x%08x, '%s'): "
956 "failed to delete global key '%s': %s\n",
957 tcon
->global
->tcon_global_id
,
958 tcon
->global
->share_name
,
964 TALLOC_FREE(global_rec
);
966 local_rec
= tcon
->db_rec
;
967 if (local_rec
== NULL
) {
968 local_rec
= smbXsrv_tcon_local_fetch_locked(table
->local
.db_ctx
,
970 tcon
/* TALLOC_CTX */);
971 if (local_rec
== NULL
) {
972 error
= NT_STATUS_INTERNAL_ERROR
;
976 if (local_rec
!= NULL
) {
977 status
= dbwrap_record_delete(local_rec
);
978 if (!NT_STATUS_IS_OK(status
)) {
979 TDB_DATA key
= dbwrap_record_get_key(local_rec
);
981 DBG_ERR("disconnect(0x%08x, '%s'): "
982 "failed to delete local key '%s': %s\n",
983 tcon
->global
->tcon_global_id
,
984 tcon
->global
->share_name
,
989 table
->local
.num_tcons
-= 1;
991 if (tcon
->db_rec
== NULL
) {
992 TALLOC_FREE(local_rec
);
999 struct smbXsrv_tcon_disconnect_all_state
{
1001 NTSTATUS first_status
;
1005 static int smbXsrv_tcon_disconnect_all_callback(struct db_record
*local_rec
,
1006 void *private_data
);
1008 static NTSTATUS
smbXsrv_tcon_disconnect_all(struct smbXsrv_tcon_table
*table
,
1011 struct smbXsrv_tcon_disconnect_all_state state
= { .vuid
= vuid
};
1015 if (table
== NULL
) {
1016 return NT_STATUS_OK
;
1019 status
= dbwrap_traverse(table
->local
.db_ctx
,
1020 smbXsrv_tcon_disconnect_all_callback
,
1022 if (!NT_STATUS_IS_OK(status
)) {
1023 DBG_ERR("dbwrap_traverse() failed: %s\n", nt_errstr(status
));
1027 if (!NT_STATUS_IS_OK(state
.first_status
)) {
1028 DBG_ERR("count[%d] errors[%d] first[%s]\n",
1031 nt_errstr(state
.first_status
));
1032 return state
.first_status
;
1035 return NT_STATUS_OK
;
1038 static int smbXsrv_tcon_disconnect_all_callback(struct db_record
*local_rec
,
1041 struct smbXsrv_tcon_disconnect_all_state
*state
=
1042 (struct smbXsrv_tcon_disconnect_all_state
*)private_data
;
1045 struct smbXsrv_tcon
*tcon
= NULL
;
1049 val
= dbwrap_record_get_value(local_rec
);
1050 if (val
.dsize
!= sizeof(ptr
)) {
1051 status
= NT_STATUS_INTERNAL_ERROR
;
1052 if (NT_STATUS_IS_OK(state
->first_status
)) {
1053 state
->first_status
= status
;
1059 memcpy(&ptr
, val
.dptr
, val
.dsize
);
1060 tcon
= talloc_get_type_abort(ptr
, struct smbXsrv_tcon
);
1063 if (vuid
== 0 && tcon
->compat
) {
1064 vuid
= tcon
->compat
->vuid
;
1067 tcon
->db_rec
= local_rec
;
1068 status
= smbXsrv_tcon_disconnect(tcon
, vuid
);
1069 tcon
->db_rec
= NULL
;
1070 if (!NT_STATUS_IS_OK(status
)) {
1071 if (NT_STATUS_IS_OK(state
->first_status
)) {
1072 state
->first_status
= status
;
1081 NTSTATUS
smb1srv_tcon_table_init(struct smbXsrv_connection
*conn
)
1083 struct smbXsrv_client
*client
= conn
->client
;
1086 * Allow a range from 1..65534 with 65534 values.
1088 client
->tcon_table
= talloc_zero(client
, struct smbXsrv_tcon_table
);
1089 if (client
->tcon_table
== NULL
) {
1090 return NT_STATUS_NO_MEMORY
;
1093 return smbXsrv_tcon_table_init(client
, client
->tcon_table
,
1098 NTSTATUS
smb1srv_tcon_create(struct smbXsrv_connection
*conn
,
1099 uint32_t session_global_id
,
1100 const char *share_name
,
1102 struct smbXsrv_tcon
**_tcon
)
1104 struct server_id id
= messaging_server_id(conn
->client
->msg_ctx
);
1105 const uint8_t encryption_flags
= 0;
1107 return smbXsrv_tcon_create(conn
->client
->tcon_table
,
1116 NTSTATUS
smb1srv_tcon_lookup(struct smbXsrv_connection
*conn
,
1117 uint16_t tree_id
, NTTIME now
,
1118 struct smbXsrv_tcon
**tcon
)
1120 uint32_t local_id
= tree_id
;
1122 return smbXsrv_tcon_local_lookup(conn
->client
->tcon_table
,
1123 local_id
, now
, tcon
);
1126 NTSTATUS
smb1srv_tcon_disconnect_all(struct smbXsrv_client
*client
)
1130 * We do not pass a vuid here,
1131 * which means the vuid is taken from
1132 * the tcon->compat->vuid.
1134 * NOTE: that tcon->compat->vuid may point to
1135 * a none existing vuid (or the wrong one)
1136 * as the tcon can exist without a session
1139 * This matches the old behavior of
1140 * conn_close_all(), but we should think
1141 * about how to fix this in future.
1143 return smbXsrv_tcon_disconnect_all(client
->tcon_table
, 0);
1146 NTSTATUS
smb2srv_tcon_table_init(struct smbXsrv_session
*session
)
1149 * Allow a range from 1..4294967294 with 65534 (same as SMB1) values.
1151 session
->tcon_table
= talloc_zero(session
, struct smbXsrv_tcon_table
);
1152 if (session
->tcon_table
== NULL
) {
1153 return NT_STATUS_NO_MEMORY
;
1156 return smbXsrv_tcon_table_init(session
, session
->tcon_table
,
1161 NTSTATUS
smb2srv_tcon_create(struct smbXsrv_session
*session
,
1162 uint32_t session_global_id
,
1163 uint8_t encryption_flags
,
1164 const char *share_name
,
1166 struct smbXsrv_tcon
**_tcon
)
1168 struct server_id id
= messaging_server_id(session
->client
->msg_ctx
);
1170 return smbXsrv_tcon_create(session
->tcon_table
,
1179 NTSTATUS
smb2srv_tcon_lookup(struct smbXsrv_session
*session
,
1180 uint32_t tree_id
, NTTIME now
,
1181 struct smbXsrv_tcon
**tcon
)
1183 uint32_t local_id
= tree_id
;
1185 return smbXsrv_tcon_local_lookup(session
->tcon_table
,
1186 local_id
, now
, tcon
);
1189 NTSTATUS
smb2srv_tcon_disconnect_all(struct smbXsrv_session
*session
)
1191 uint64_t vuid
= session
->global
->session_wire_id
;
1193 return smbXsrv_tcon_disconnect_all(session
->tcon_table
, vuid
);
1196 struct smbXsrv_tcon_global_traverse_state
{
1197 int (*fn
)(struct smbXsrv_tcon_global0
*, void *);
1201 static int smbXsrv_tcon_global_traverse_fn(struct db_record
*rec
, void *data
)
1204 struct smbXsrv_tcon_global_traverse_state
*state
=
1205 (struct smbXsrv_tcon_global_traverse_state
*)data
;
1206 TDB_DATA key
= dbwrap_record_get_key(rec
);
1207 TDB_DATA val
= dbwrap_record_get_value(rec
);
1208 DATA_BLOB blob
= data_blob_const(val
.dptr
, val
.dsize
);
1209 struct smbXsrv_tcon_globalB global_blob
;
1210 enum ndr_err_code ndr_err
;
1211 TALLOC_CTX
*frame
= talloc_stackframe();
1213 ndr_err
= ndr_pull_struct_blob(&blob
, frame
, &global_blob
,
1214 (ndr_pull_flags_fn_t
)ndr_pull_smbXsrv_tcon_globalB
);
1215 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
1216 DBG_WARNING("Invalid record in smbXsrv_tcon_global.tdb:"
1217 "key '%s' ndr_pull_struct_blob - %s\n",
1219 ndr_errstr(ndr_err
));
1223 if (global_blob
.version
!= SMBXSRV_VERSION_0
) {
1224 DBG_WARNING("Invalid record in smbXsrv_tcon_global.tdb:"
1225 "key '%s' unsupported version - %d\n",
1227 (int)global_blob
.version
);
1231 if (global_blob
.info
.info0
== NULL
) {
1232 DBG_WARNING("Invalid record in smbXsrv_tcon_global.tdb:"
1233 "key '%s' info0 NULL pointer\n",
1238 global_blob
.info
.info0
->db_rec
= rec
;
1239 ret
= state
->fn(global_blob
.info
.info0
, state
->private_data
);
1245 NTSTATUS
smbXsrv_tcon_global_traverse(
1246 int (*fn
)(struct smbXsrv_tcon_global0
*, void *),
1251 struct smbXsrv_tcon_global_traverse_state state
= {
1253 .private_data
= private_data
,
1257 status
= smbXsrv_tcon_global_init();
1258 if (!NT_STATUS_IS_OK(status
)) {
1260 DBG_ERR("Failed to initialize tcon_global: %s\n",
1265 status
= dbwrap_traverse_read(smbXsrv_tcon_global_db_ctx
,
1266 smbXsrv_tcon_global_traverse_fn
,