Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/smbd/smbXsrv_tcon.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
4
   Copyright (C) Stefan Metzmacher 2011-2012
5
   Copyright (C) Michael Adam 2012
6
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.
11
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.
16
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/>.
19
*/
20
21
#include "includes.h"
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"
29
#include "messages.h"
30
#include "lib/util/util_tdb.h"
31
#include "librpc/gen_ndr/ndr_smbXsrv.h"
32
#include "serverid.h"
33
#include "source3/include/util_tdb.h"
34
35
struct smbXsrv_tcon_table {
36
  struct {
37
    struct db_context *db_ctx;
38
    uint32_t lowest_id;
39
    uint32_t highest_id;
40
    uint32_t max_tcons;
41
    uint32_t num_tcons;
42
  } local;
43
  struct {
44
    struct db_context *db_ctx;
45
  } global;
46
};
47
48
static struct db_context *smbXsrv_tcon_global_db_ctx = NULL;
49
50
NTSTATUS smbXsrv_tcon_global_init(void)
51
0
{
52
0
  char *global_path = NULL;
53
0
  struct db_context *db_ctx = NULL;
54
55
0
  if (smbXsrv_tcon_global_db_ctx != NULL) {
56
0
    return NT_STATUS_OK;
57
0
  }
58
59
0
  global_path = lock_path(talloc_tos(), "smbXsrv_tcon_global.tdb");
60
0
  if (global_path == NULL) {
61
0
    return NT_STATUS_NO_MEMORY;
62
0
  }
63
64
0
  db_ctx = db_open(NULL, global_path,
65
0
       SMBD_VOLATILE_TDB_HASH_SIZE,
66
0
       SMBD_VOLATILE_TDB_FLAGS,
67
0
       O_RDWR | O_CREAT, 0600,
68
0
       DBWRAP_LOCK_ORDER_1,
69
0
       DBWRAP_FLAG_NONE);
70
0
  TALLOC_FREE(global_path);
71
0
  if (db_ctx == NULL) {
72
0
    NTSTATUS status;
73
74
0
    status = map_nt_error_from_unix_common(errno);
75
76
0
    return status;
77
0
  }
78
79
0
  smbXsrv_tcon_global_db_ctx = db_ctx;
80
81
0
  return NT_STATUS_OK;
82
0
}
83
84
/*
85
 * NOTE:
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
88
 * values.
89
 *
90
 * TODO: implement string based key
91
 */
92
93
0
#define SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE sizeof(uint32_t)
94
95
static TDB_DATA smbXsrv_tcon_global_id_to_key(uint32_t id,
96
                uint8_t *key_buf)
97
0
{
98
0
  TDB_DATA key;
99
100
0
  RSIVAL(key_buf, 0, id);
101
102
0
  key = make_tdb_data(key_buf, SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE);
103
104
0
  return key;
105
0
}
106
107
#if 0
108
static NTSTATUS smbXsrv_tcon_global_key_to_id(TDB_DATA key, uint32_t *id)
109
{
110
  if (id == NULL) {
111
    return NT_STATUS_INVALID_PARAMETER;
112
  }
113
114
  if (key.dsize != SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE) {
115
    return NT_STATUS_INTERNAL_DB_CORRUPTION;
116
  }
117
118
  *id = RIVAL(key.dptr, 0);
119
120
  return NT_STATUS_OK;
121
}
122
#endif
123
124
0
#define SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE sizeof(uint32_t)
125
126
static TDB_DATA smbXsrv_tcon_local_id_to_key(uint32_t id,
127
               uint8_t *key_buf)
128
0
{
129
0
  TDB_DATA key;
130
131
0
  RSIVAL(key_buf, 0, id);
132
133
0
  key = make_tdb_data(key_buf, SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE);
134
135
0
  return key;
136
0
}
137
138
static NTSTATUS smbXsrv_tcon_local_key_to_id(TDB_DATA key, uint32_t *id)
139
0
{
140
0
  if (id == NULL) {
141
0
    return NT_STATUS_INVALID_PARAMETER;
142
0
  }
143
144
0
  if (key.dsize != SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE) {
145
0
    return NT_STATUS_INTERNAL_DB_CORRUPTION;
146
0
  }
147
148
0
  *id = RIVAL(key.dptr, 0);
149
150
0
  return NT_STATUS_OK;
151
0
}
152
153
static struct db_record *smbXsrv_tcon_global_fetch_locked(
154
      struct db_context *db,
155
      uint32_t id,
156
      TALLOC_CTX *mem_ctx)
157
0
{
158
0
  TDB_DATA key;
159
0
  uint8_t key_buf[SMBXSRV_TCON_GLOBAL_TDB_KEY_SIZE];
160
0
  struct db_record *rec = NULL;
161
162
0
  key = smbXsrv_tcon_global_id_to_key(id, key_buf);
163
164
0
  rec = dbwrap_fetch_locked(db, mem_ctx, key);
165
166
0
  if (rec == NULL) {
167
0
    DBG_DEBUG("Failed to lock global id 0x%08x, key '%s'\n", id,
168
0
        tdb_data_dbg(key));
169
0
  }
170
171
0
  return rec;
172
0
}
173
174
static struct db_record *smbXsrv_tcon_local_fetch_locked(
175
      struct db_context *db,
176
      uint32_t id,
177
      TALLOC_CTX *mem_ctx)
178
0
{
179
0
  TDB_DATA key;
180
0
  uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
181
0
  struct db_record *rec = NULL;
182
183
0
  key = smbXsrv_tcon_local_id_to_key(id, key_buf);
184
185
0
  rec = dbwrap_fetch_locked(db, mem_ctx, key);
186
187
0
  if (rec == NULL) {
188
0
    DBG_DEBUG("Failed to lock local id 0x%08x, key '%s'\n", id,
189
0
        tdb_data_dbg(key));
190
0
  }
191
192
0
  return rec;
193
0
}
194
195
static NTSTATUS smbXsrv_tcon_table_init(TALLOC_CTX *mem_ctx,
196
          struct smbXsrv_tcon_table *table,
197
          uint32_t lowest_id,
198
          uint32_t highest_id,
199
          uint32_t max_tcons)
200
0
{
201
0
  NTSTATUS status;
202
0
  uint64_t max_range;
203
204
0
  if (lowest_id > highest_id) {
205
0
    return NT_STATUS_INTERNAL_ERROR;
206
0
  }
207
208
0
  max_range = highest_id;
209
0
  max_range -= lowest_id;
210
0
  max_range += 1;
211
212
0
  if (max_tcons > max_range) {
213
0
    return NT_STATUS_INTERNAL_ERROR;
214
0
  }
215
216
0
  ZERO_STRUCTP(table);
217
0
  table->local.db_ctx = db_open_rbt(table);
218
0
  if (table->local.db_ctx == NULL) {
219
0
    return NT_STATUS_NO_MEMORY;
220
0
  }
221
0
  table->local.lowest_id = lowest_id;
222
0
  table->local.highest_id = highest_id;
223
0
  table->local.max_tcons = max_tcons;
224
225
0
  status = smbXsrv_tcon_global_init();
226
0
  if (!NT_STATUS_IS_OK(status)) {
227
0
    return status;
228
0
  }
229
230
0
  table->global.db_ctx = smbXsrv_tcon_global_db_ctx;
231
232
0
  return NT_STATUS_OK;
233
0
}
234
235
struct smb1srv_tcon_local_allocate_state {
236
  const uint32_t lowest_id;
237
  const uint32_t highest_id;
238
  uint32_t last_id;
239
  uint32_t useable_id;
240
  NTSTATUS status;
241
};
242
243
static int smb1srv_tcon_local_allocate_traverse(struct db_record *rec,
244
               void *private_data)
245
0
{
246
0
  struct smb1srv_tcon_local_allocate_state *state =
247
0
    (struct smb1srv_tcon_local_allocate_state *)private_data;
248
0
  TDB_DATA key = dbwrap_record_get_key(rec);
249
0
  uint32_t id = 0;
250
0
  NTSTATUS status;
251
252
0
  status = smbXsrv_tcon_local_key_to_id(key, &id);
253
0
  if (!NT_STATUS_IS_OK(status)) {
254
0
    state->status = status;
255
0
    return -1;
256
0
  }
257
258
0
  if (id <= state->last_id) {
259
0
    state->status = NT_STATUS_INTERNAL_DB_CORRUPTION;
260
0
    return -1;
261
0
  }
262
0
  state->last_id = id;
263
264
0
  if (id > state->useable_id) {
265
0
    state->status = NT_STATUS_OK;
266
0
    return -1;
267
0
  }
268
269
0
  if (state->useable_id == state->highest_id) {
270
0
    state->status = NT_STATUS_INSUFFICIENT_RESOURCES;
271
0
    return -1;
272
0
  }
273
274
0
  state->useable_id +=1;
275
0
  return 0;
276
0
}
277
278
static NTSTATUS smb1srv_tcon_local_allocate_id(struct db_context *db,
279
                 uint32_t lowest_id,
280
                 uint32_t highest_id,
281
                 TALLOC_CTX *mem_ctx,
282
                 struct db_record **_rec,
283
                 uint32_t *_id)
284
0
{
285
0
  struct smb1srv_tcon_local_allocate_state state = {
286
0
    .lowest_id = lowest_id,
287
0
    .highest_id = highest_id,
288
0
    .last_id = 0,
289
0
    .useable_id = lowest_id,
290
0
    .status = NT_STATUS_INTERNAL_ERROR,
291
0
  };
292
0
  uint32_t i;
293
0
  uint32_t range;
294
0
  NTSTATUS status;
295
0
  int count = 0;
296
297
0
  *_rec = NULL;
298
0
  *_id = 0;
299
300
0
  if (lowest_id > highest_id) {
301
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
302
0
  }
303
304
  /*
305
   * first we try randomly
306
   */
307
0
  range = (highest_id - lowest_id) + 1;
308
309
0
  for (i = 0; i < (range / 2); i++) {
310
0
    uint32_t id;
311
0
    TDB_DATA val;
312
0
    struct db_record *rec = NULL;
313
314
0
    id = generate_random() % range;
315
0
    id += lowest_id;
316
317
0
    if (id < lowest_id) {
318
0
      id = lowest_id;
319
0
    }
320
0
    if (id > highest_id) {
321
0
      id = highest_id;
322
0
    }
323
324
0
    rec = smbXsrv_tcon_local_fetch_locked(db, id, mem_ctx);
325
0
    if (rec == NULL) {
326
0
      return NT_STATUS_INSUFFICIENT_RESOURCES;
327
0
    }
328
329
0
    val = dbwrap_record_get_value(rec);
330
0
    if (val.dsize != 0) {
331
0
      TALLOC_FREE(rec);
332
0
      continue;
333
0
    }
334
335
0
    *_rec = rec;
336
0
    *_id = id;
337
0
    return NT_STATUS_OK;
338
0
  }
339
340
  /*
341
   * if the range is almost full,
342
   * we traverse the whole table
343
   * (this relies on sorted behavior of dbwrap_rbt)
344
   */
345
0
  status = dbwrap_traverse_read(db, smb1srv_tcon_local_allocate_traverse,
346
0
              &state, &count);
347
0
  if (NT_STATUS_IS_OK(status)) {
348
0
    if (NT_STATUS_IS_OK(state.status)) {
349
0
      return NT_STATUS_INTERNAL_ERROR;
350
0
    }
351
352
0
    if (!NT_STATUS_EQUAL(state.status, NT_STATUS_INTERNAL_ERROR)) {
353
0
      return state.status;
354
0
    }
355
356
0
    if (state.useable_id <= state.highest_id) {
357
0
      state.status = NT_STATUS_OK;
358
0
    } else {
359
0
      return NT_STATUS_INSUFFICIENT_RESOURCES;
360
0
    }
361
0
  } else if (!NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_DB_CORRUPTION)) {
362
    /*
363
     * Here we really expect NT_STATUS_INTERNAL_DB_CORRUPTION!
364
     *
365
     * If we get anything else it is an error, because it
366
     * means we did not manage to find a free slot in
367
     * the db.
368
     */
369
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
370
0
  }
371
372
0
  if (NT_STATUS_IS_OK(state.status)) {
373
0
    uint32_t id;
374
0
    TDB_DATA val;
375
0
    struct db_record *rec = NULL;
376
377
0
    id = state.useable_id;
378
379
0
    rec = smbXsrv_tcon_local_fetch_locked(db, id, mem_ctx);
380
0
    if (rec == NULL) {
381
0
      return NT_STATUS_INSUFFICIENT_RESOURCES;
382
0
    }
383
384
0
    val = dbwrap_record_get_value(rec);
385
0
    if (val.dsize != 0) {
386
0
      TALLOC_FREE(rec);
387
0
      return NT_STATUS_INTERNAL_DB_CORRUPTION;
388
0
    }
389
390
0
    *_rec = rec;
391
0
    *_id = id;
392
0
    return NT_STATUS_OK;
393
0
  }
394
395
0
  return state.status;
396
0
}
397
398
struct smbXsrv_tcon_local_fetch_state {
399
  struct smbXsrv_tcon *tcon;
400
  NTSTATUS status;
401
};
402
403
static void smbXsrv_tcon_local_fetch_parser(TDB_DATA key, TDB_DATA data,
404
              void *private_data)
405
0
{
406
0
  struct smbXsrv_tcon_local_fetch_state *state =
407
0
    (struct smbXsrv_tcon_local_fetch_state *)private_data;
408
0
  void *ptr;
409
410
0
  if (data.dsize != sizeof(ptr)) {
411
0
    state->status = NT_STATUS_INTERNAL_DB_ERROR;
412
0
    return;
413
0
  }
414
415
0
  memcpy(&ptr, data.dptr, data.dsize);
416
0
  state->tcon = talloc_get_type_abort(ptr, struct smbXsrv_tcon);
417
0
  state->status = NT_STATUS_OK;
418
0
}
419
420
static NTSTATUS smbXsrv_tcon_local_lookup(struct smbXsrv_tcon_table *table,
421
            uint32_t tcon_local_id,
422
            NTTIME now,
423
            struct smbXsrv_tcon **_tcon)
424
0
{
425
0
  struct smbXsrv_tcon_local_fetch_state state = {
426
0
    .tcon = NULL,
427
0
    .status = NT_STATUS_INTERNAL_ERROR,
428
0
  };
429
0
  uint8_t key_buf[SMBXSRV_TCON_LOCAL_TDB_KEY_SIZE];
430
0
  TDB_DATA key;
431
0
  NTSTATUS status;
432
433
0
  *_tcon = NULL;
434
435
0
  if (tcon_local_id == 0) {
436
0
    return NT_STATUS_NETWORK_NAME_DELETED;
437
0
  }
438
439
0
  if (table == NULL) {
440
    /* this might happen before the end of negprot */
441
0
    return NT_STATUS_NETWORK_NAME_DELETED;
442
0
  }
443
444
0
  if (table->local.db_ctx == NULL) {
445
0
    return NT_STATUS_INTERNAL_ERROR;
446
0
  }
447
448
0
  key = smbXsrv_tcon_local_id_to_key(tcon_local_id, key_buf);
449
450
0
  status = dbwrap_parse_record(table->local.db_ctx, key,
451
0
             smbXsrv_tcon_local_fetch_parser,
452
0
             &state);
453
0
  if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
454
0
    return NT_STATUS_NETWORK_NAME_DELETED;
455
0
  } else if (!NT_STATUS_IS_OK(status)) {
456
0
    return status;
457
0
  }
458
0
  if (!NT_STATUS_IS_OK(state.status)) {
459
0
    return state.status;
460
0
  }
461
462
0
  if (NT_STATUS_EQUAL(state.tcon->status, NT_STATUS_NETWORK_NAME_DELETED)) {
463
0
    return NT_STATUS_NETWORK_NAME_DELETED;
464
0
  }
465
466
0
  state.tcon->idle_time = now;
467
468
0
  *_tcon = state.tcon;
469
0
  return state.tcon->status;
470
0
}
471
472
static int smbXsrv_tcon_global_destructor(struct smbXsrv_tcon_global0 *global)
473
0
{
474
0
  return 0;
475
0
}
476
477
static void smbXsrv_tcon_global_verify_record(struct db_record *db_rec,
478
          bool *is_free,
479
          bool *was_free,
480
          TALLOC_CTX *mem_ctx,
481
          struct smbXsrv_tcon_global0 **_g);
482
483
static NTSTATUS smbXsrv_tcon_global_allocate(struct db_context *db,
484
          TALLOC_CTX *mem_ctx,
485
          struct smbXsrv_tcon_global0 **_global)
486
0
{
487
0
  uint32_t i;
488
0
  struct smbXsrv_tcon_global0 *global = NULL;
489
0
  uint32_t last_free = 0;
490
0
  const uint32_t min_tries = 3;
491
492
0
  *_global = NULL;
493
494
0
  global = talloc_zero(mem_ctx, struct smbXsrv_tcon_global0);
495
0
  if (global == NULL) {
496
0
    return NT_STATUS_NO_MEMORY;
497
0
  }
498
0
  talloc_set_destructor(global, smbXsrv_tcon_global_destructor);
499
500
  /*
501
   * Here we just randomly try the whole 32-bit space
502
   *
503
   * We use just 32-bit, because we want to reuse the
504
   * ID for SRVSVC.
505
   */
506
0
  for (i = 0; i < UINT32_MAX; i++) {
507
0
    bool is_free = false;
508
0
    bool was_free = false;
509
0
    uint32_t id;
510
511
0
    if (i >= min_tries && last_free != 0) {
512
0
      id = last_free;
513
0
    } else {
514
0
      id = generate_random();
515
0
    }
516
0
    if (id == 0) {
517
0
      id++;
518
0
    }
519
0
    if (id == UINT32_MAX) {
520
0
      id--;
521
0
    }
522
523
0
    global->db_rec = smbXsrv_tcon_global_fetch_locked(db, id,
524
0
                  mem_ctx);
525
0
    if (global->db_rec == NULL) {
526
0
      talloc_free(global);
527
0
      return NT_STATUS_INSUFFICIENT_RESOURCES;
528
0
    }
529
530
0
    smbXsrv_tcon_global_verify_record(global->db_rec,
531
0
              &is_free,
532
0
              &was_free,
533
0
              NULL, NULL);
534
535
0
    if (!is_free) {
536
0
      TALLOC_FREE(global->db_rec);
537
0
      continue;
538
0
    }
539
540
0
    if (!was_free && i < min_tries) {
541
      /*
542
       * The session_id is free now,
543
       * but was not free before.
544
       *
545
       * This happens if a smbd crashed
546
       * and did not cleanup the record.
547
       *
548
       * If this is one of our first tries,
549
       * then we try to find a real free one.
550
       */
551
0
      if (last_free == 0) {
552
0
        last_free = id;
553
0
      }
554
0
      TALLOC_FREE(global->db_rec);
555
0
      continue;
556
0
    }
557
558
0
    global->tcon_global_id = id;
559
560
0
    *_global = global;
561
0
    return NT_STATUS_OK;
562
0
  }
563
564
  /* should not be reached */
565
0
  talloc_free(global);
566
0
  return NT_STATUS_INTERNAL_ERROR;
567
0
}
568
569
static void smbXsrv_tcon_global_verify_record(struct db_record *db_rec,
570
          bool *is_free,
571
          bool *was_free,
572
          TALLOC_CTX *mem_ctx,
573
          struct smbXsrv_tcon_global0 **_g)
574
0
{
575
0
  TDB_DATA key;
576
0
  TDB_DATA val;
577
0
  DATA_BLOB blob;
578
0
  struct smbXsrv_tcon_globalB global_blob;
579
0
  enum ndr_err_code ndr_err;
580
0
  struct smbXsrv_tcon_global0 *global = NULL;
581
0
  bool exists;
582
0
  TALLOC_CTX *frame = talloc_stackframe();
583
584
0
  *is_free = false;
585
586
0
  if (was_free) {
587
0
    *was_free = false;
588
0
  }
589
0
  if (_g) {
590
0
    *_g = NULL;
591
0
  }
592
593
0
  key = dbwrap_record_get_key(db_rec);
594
595
0
  val = dbwrap_record_get_value(db_rec);
596
0
  if (val.dsize == 0) {
597
0
    TALLOC_FREE(frame);
598
0
    *is_free = true;
599
0
    if (was_free) {
600
0
      *was_free = true;
601
0
    }
602
0
    return;
603
0
  }
604
605
0
  blob = data_blob_const(val.dptr, val.dsize);
606
607
0
  ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
608
0
      (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_tcon_globalB);
609
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
610
0
    NTSTATUS status = ndr_map_error2ntstatus(ndr_err);
611
0
    DBG_WARNING("key '%s' ndr_pull_struct_blob - %s\n",
612
0
          tdb_data_dbg(key),
613
0
          nt_errstr(status));
614
0
    TALLOC_FREE(frame);
615
0
    return;
616
0
  }
617
618
0
  DBG_DEBUG("smbXsrv_tcon_global_verify_record\n");
619
0
  if (DEBUGLVL(DBGLVL_DEBUG)) {
620
0
    NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
621
0
  }
622
623
0
  if (global_blob.version != SMBXSRV_VERSION_0) {
624
0
    DBG_ERR("key '%s' uses unsupported version %u\n",
625
0
      tdb_data_dbg(key),
626
0
      global_blob.version);
627
0
    NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
628
0
    TALLOC_FREE(frame);
629
0
    return;
630
0
  }
631
632
0
  global = global_blob.info.info0;
633
634
0
  exists = serverid_exists(&global->server_id);
635
0
  if (!exists) {
636
0
    struct server_id_buf idbuf;
637
0
    DBG_NOTICE("key '%s' server_id %s does not exist.\n",
638
0
         tdb_data_dbg(key),
639
0
         server_id_str_buf(global->server_id, &idbuf));
640
0
    if (DEBUGLVL(DBGLVL_NOTICE)) {
641
0
      NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
642
0
    }
643
0
    TALLOC_FREE(frame);
644
0
    dbwrap_record_delete(db_rec);
645
0
    *is_free = true;
646
0
    return;
647
0
  }
648
649
0
  if (_g) {
650
0
    *_g = talloc_move(mem_ctx, &global);
651
0
  }
652
0
  TALLOC_FREE(frame);
653
0
}
654
655
static NTSTATUS smbXsrv_tcon_global_store(struct smbXsrv_tcon_global0 *global)
656
0
{
657
0
  struct smbXsrv_tcon_globalB global_blob;
658
0
  DATA_BLOB blob = data_blob_null;
659
0
  TDB_DATA key;
660
0
  TDB_DATA val;
661
0
  NTSTATUS status;
662
0
  enum ndr_err_code ndr_err;
663
664
  /*
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.
668
   */
669
670
0
  if (global->db_rec == NULL) {
671
0
    return NT_STATUS_INTERNAL_ERROR;
672
0
  }
673
674
0
  key = dbwrap_record_get_key(global->db_rec);
675
0
  val = dbwrap_record_get_value(global->db_rec);
676
677
0
  ZERO_STRUCT(global_blob);
678
0
  global_blob.version = smbXsrv_version_global_current();
679
0
  if (val.dsize >= 8) {
680
0
    global_blob.seqnum = IVAL(val.dptr, 4);
681
0
  }
682
0
  global_blob.seqnum += 1;
683
0
  global_blob.info.info0 = global;
684
685
0
  ndr_err = ndr_push_struct_blob(&blob, global->db_rec, &global_blob,
686
0
      (ndr_push_flags_fn_t)ndr_push_smbXsrv_tcon_globalB);
687
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
688
0
    status = ndr_map_error2ntstatus(ndr_err);
689
0
    DBG_WARNING("key '%s' ndr_push - %s\n",
690
0
          tdb_data_dbg(key),
691
0
          nt_errstr(status));
692
0
    TALLOC_FREE(global->db_rec);
693
0
    return status;
694
0
  }
695
696
0
  val = make_tdb_data(blob.data, blob.length);
697
0
  status = dbwrap_record_store(global->db_rec, val, TDB_REPLACE);
698
0
  if (!NT_STATUS_IS_OK(status)) {
699
0
    DBG_WARNING("key '%s' store - %s\n",
700
0
          tdb_data_dbg(key),
701
0
          nt_errstr(status));
702
0
    TALLOC_FREE(global->db_rec);
703
0
    return status;
704
0
  }
705
706
0
  if (DEBUGLVL(DBGLVL_DEBUG)) {
707
0
    DBG_DEBUG("key '%s' stored\n", tdb_data_dbg(key));
708
0
    NDR_PRINT_DEBUG(smbXsrv_tcon_globalB, &global_blob);
709
0
  }
710
711
0
  TALLOC_FREE(global->db_rec);
712
713
0
  return NT_STATUS_OK;
714
0
}
715
716
static int smbXsrv_tcon_destructor(struct smbXsrv_tcon *tcon)
717
0
{
718
0
  NTSTATUS status;
719
720
0
  status = smbXsrv_tcon_disconnect(tcon, 0);
721
0
  if (!NT_STATUS_IS_OK(status)) {
722
0
    DBG_ERR("smbXsrv_tcon_disconnect() failed - %s\n",
723
0
      nt_errstr(status));
724
0
  }
725
726
0
  TALLOC_FREE(tcon->global);
727
728
0
  return 0;
729
0
}
730
731
static NTSTATUS smbXsrv_tcon_create(struct smbXsrv_tcon_table *table,
732
            enum protocol_types protocol,
733
            struct server_id server_id,
734
            NTTIME now,
735
            uint32_t session_global_id,
736
            uint8_t encryption_flags,
737
            const char *share_name,
738
            struct smbXsrv_tcon **_tcon)
739
0
{
740
0
  struct db_record *local_rec = NULL;
741
0
  struct smbXsrv_tcon *tcon = NULL;
742
0
  void *ptr = NULL;
743
0
  TDB_DATA val;
744
0
  struct smbXsrv_tcon_global0 *global = NULL;
745
0
  NTSTATUS status;
746
747
0
  if (table->local.num_tcons >= table->local.max_tcons) {
748
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
749
0
  }
750
751
0
  tcon = talloc_zero(table, struct smbXsrv_tcon);
752
0
  if (tcon == NULL) {
753
0
    return NT_STATUS_NO_MEMORY;
754
0
  }
755
0
  tcon->table = table;
756
0
  tcon->status = NT_STATUS_INTERNAL_ERROR;
757
0
  tcon->idle_time = now;
758
759
0
  status = smbXsrv_tcon_global_allocate(table->global.db_ctx,
760
0
                tcon, &global);
761
0
  if (!NT_STATUS_IS_OK(status)) {
762
0
    TALLOC_FREE(tcon);
763
0
    return status;
764
0
  }
765
0
  tcon->global = global;
766
767
0
  global->session_global_id = session_global_id;
768
0
  global->encryption_flags = encryption_flags;
769
0
  global->share_name = talloc_strdup(global, share_name);
770
0
  if (global->share_name == NULL) {
771
0
    TALLOC_FREE(tcon);
772
0
    return NT_STATUS_NO_MEMORY;
773
0
  }
774
775
0
  if (protocol >= PROTOCOL_SMB2_02) {
776
0
    uint64_t id = global->tcon_global_id;
777
778
0
    global->tcon_wire_id = id;
779
780
0
    tcon->local_id = global->tcon_global_id;
781
782
0
    local_rec = smbXsrv_tcon_local_fetch_locked(table->local.db_ctx,
783
0
              tcon->local_id,
784
0
              tcon /* TALLOC_CTX */);
785
0
    if (local_rec == NULL) {
786
0
      TALLOC_FREE(tcon);
787
0
      return NT_STATUS_NO_MEMORY;
788
0
    }
789
790
0
    val = dbwrap_record_get_value(local_rec);
791
0
    if (val.dsize != 0) {
792
0
      TALLOC_FREE(tcon);
793
0
      return NT_STATUS_INTERNAL_DB_CORRUPTION;
794
0
    }
795
0
  } else {
796
797
0
    status = smb1srv_tcon_local_allocate_id(table->local.db_ctx,
798
0
              table->local.lowest_id,
799
0
              table->local.highest_id,
800
0
              tcon,
801
0
              &local_rec,
802
0
              &tcon->local_id);
803
0
    if (!NT_STATUS_IS_OK(status)) {
804
0
      TALLOC_FREE(tcon);
805
0
      return status;
806
0
    }
807
808
0
    global->tcon_wire_id = tcon->local_id;
809
0
  }
810
811
0
  global->creation_time = now;
812
813
0
  global->server_id = server_id;
814
815
0
  ptr = tcon;
816
0
  val = make_tdb_data((uint8_t const *)&ptr, sizeof(ptr));
817
0
  status = dbwrap_record_store(local_rec, val, TDB_REPLACE);
818
0
  TALLOC_FREE(local_rec);
819
0
  if (!NT_STATUS_IS_OK(status)) {
820
0
    TALLOC_FREE(tcon);
821
0
    return status;
822
0
  }
823
0
  table->local.num_tcons += 1;
824
825
0
  talloc_set_destructor(tcon, smbXsrv_tcon_destructor);
826
827
0
  status = smbXsrv_tcon_global_store(global);
828
0
  if (!NT_STATUS_IS_OK(status)) {
829
0
    DBG_ERR("global_id (0x%08x) store failed - %s\n",
830
0
      tcon->global->tcon_global_id,
831
0
      nt_errstr(status));
832
0
    TALLOC_FREE(tcon);
833
0
    return status;
834
0
  }
835
836
0
  if (DEBUGLVL(DBGLVL_DEBUG)) {
837
0
    struct smbXsrv_tconB tcon_blob = {
838
0
      .version = SMBXSRV_VERSION_0,
839
0
      .info.info0 = tcon,
840
0
    };
841
842
0
    DBG_DEBUG("global_id (0x%08x) stored\n",
843
0
        tcon->global->tcon_global_id);
844
0
    NDR_PRINT_DEBUG(smbXsrv_tconB, &tcon_blob);
845
0
  }
846
847
0
  *_tcon = tcon;
848
0
  return NT_STATUS_OK;
849
0
}
850
851
NTSTATUS smbXsrv_tcon_update(struct smbXsrv_tcon *tcon)
852
0
{
853
0
  struct smbXsrv_tcon_table *table = tcon->table;
854
0
  NTSTATUS status;
855
856
0
  if (tcon->global->db_rec != NULL) {
857
0
    DBG_ERR("update(0x%08x): "
858
0
      "Called with db_rec != NULL'\n",
859
0
      tcon->global->tcon_global_id);
860
0
    return NT_STATUS_INTERNAL_ERROR;
861
0
  }
862
863
0
  tcon->global->db_rec = smbXsrv_tcon_global_fetch_locked(
864
0
            table->global.db_ctx,
865
0
            tcon->global->tcon_global_id,
866
0
            tcon->global /* TALLOC_CTX */);
867
0
  if (tcon->global->db_rec == NULL) {
868
0
    return NT_STATUS_INTERNAL_DB_ERROR;
869
0
  }
870
871
0
  status = smbXsrv_tcon_global_store(tcon->global);
872
0
  if (!NT_STATUS_IS_OK(status)) {
873
0
    DBG_ERR("global_id (0x%08x) store failed - %s\n",
874
0
      tcon->global->tcon_global_id,
875
0
      nt_errstr(status));
876
0
    return status;
877
0
  }
878
879
0
  if (DEBUGLVL(DBGLVL_DEBUG)) {
880
0
    struct smbXsrv_tconB tcon_blob = {
881
0
      .version = SMBXSRV_VERSION_0,
882
0
      .info.info0 = tcon,
883
0
    };
884
885
0
    DBG_DEBUG("global_id (0x%08x) stored\n",
886
0
        tcon->global->tcon_global_id);
887
0
    NDR_PRINT_DEBUG(smbXsrv_tconB, &tcon_blob);
888
0
  }
889
890
0
  return NT_STATUS_OK;
891
0
}
892
893
NTSTATUS smbXsrv_tcon_disconnect(struct smbXsrv_tcon *tcon, uint64_t vuid)
894
0
{
895
0
  struct smbXsrv_tcon_table *table;
896
0
  struct db_record *local_rec = NULL;
897
0
  struct db_record *global_rec = NULL;
898
0
  NTSTATUS status;
899
0
  NTSTATUS error = NT_STATUS_OK;
900
901
0
  if (tcon->table == NULL) {
902
0
    return NT_STATUS_OK;
903
0
  }
904
905
0
  table = tcon->table;
906
0
  tcon->table = NULL;
907
908
0
  if (tcon->compat) {
909
0
    bool ok;
910
911
0
    ok = chdir_current_service(tcon->compat);
912
0
    if (!ok) {
913
0
      status = NT_STATUS_INTERNAL_ERROR;
914
0
      DBG_ERR("disconnect(0x%08x, '%s'): "
915
0
        "chdir_current_service() failed: %s\n",
916
0
        tcon->global->tcon_global_id,
917
0
        tcon->global->share_name,
918
0
        nt_errstr(status));
919
      /*
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.
926
       */
927
0
      close_cnum(tcon->compat, vuid, ERROR_CLOSE);
928
0
      tcon->compat = NULL;
929
0
      return status;
930
0
    }
931
932
0
    close_cnum(tcon->compat, vuid, SHUTDOWN_CLOSE);
933
0
    tcon->compat = NULL;
934
0
  }
935
936
0
  tcon->status = NT_STATUS_NETWORK_NAME_DELETED;
937
938
0
  global_rec = tcon->global->db_rec;
939
0
  tcon->global->db_rec = NULL;
940
0
  if (global_rec == NULL) {
941
0
    global_rec = smbXsrv_tcon_global_fetch_locked(
942
0
            table->global.db_ctx,
943
0
            tcon->global->tcon_global_id,
944
0
            tcon->global /* TALLOC_CTX */);
945
0
    if (global_rec == NULL) {
946
0
      error = NT_STATUS_INTERNAL_ERROR;
947
0
    }
948
0
  }
949
950
0
  if (global_rec != NULL) {
951
0
    status = dbwrap_record_delete(global_rec);
952
0
    if (!NT_STATUS_IS_OK(status)) {
953
0
      TDB_DATA key = dbwrap_record_get_key(global_rec);
954
955
0
      DBG_ERR("disconnect(0x%08x, '%s'): "
956
0
        "failed to delete global key '%s': %s\n",
957
0
        tcon->global->tcon_global_id,
958
0
        tcon->global->share_name,
959
0
        tdb_data_dbg(key),
960
0
        nt_errstr(status));
961
0
      error = status;
962
0
    }
963
0
  }
964
0
  TALLOC_FREE(global_rec);
965
966
0
  local_rec = tcon->db_rec;
967
0
  if (local_rec == NULL) {
968
0
    local_rec = smbXsrv_tcon_local_fetch_locked(table->local.db_ctx,
969
0
              tcon->local_id,
970
0
              tcon /* TALLOC_CTX */);
971
0
    if (local_rec == NULL) {
972
0
      error = NT_STATUS_INTERNAL_ERROR;
973
0
    }
974
0
  }
975
976
0
  if (local_rec != NULL) {
977
0
    status = dbwrap_record_delete(local_rec);
978
0
    if (!NT_STATUS_IS_OK(status)) {
979
0
      TDB_DATA key = dbwrap_record_get_key(local_rec);
980
981
0
      DBG_ERR("disconnect(0x%08x, '%s'): "
982
0
        "failed to delete local key '%s': %s\n",
983
0
        tcon->global->tcon_global_id,
984
0
        tcon->global->share_name,
985
0
        tdb_data_dbg(key),
986
0
        nt_errstr(status));
987
0
      error = status;
988
0
    }
989
0
    table->local.num_tcons -= 1;
990
0
  }
991
0
  if (tcon->db_rec == NULL) {
992
0
    TALLOC_FREE(local_rec);
993
0
  }
994
0
  tcon->db_rec = NULL;
995
996
0
  return error;
997
0
}
998
999
struct smbXsrv_tcon_disconnect_all_state {
1000
  uint64_t vuid;
1001
  NTSTATUS first_status;
1002
  int errors;
1003
};
1004
1005
static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
1006
            void *private_data);
1007
1008
static NTSTATUS smbXsrv_tcon_disconnect_all(struct smbXsrv_tcon_table *table,
1009
              uint64_t vuid)
1010
0
{
1011
0
  struct smbXsrv_tcon_disconnect_all_state state = { .vuid = vuid };
1012
0
  NTSTATUS status;
1013
0
  int count = 0;
1014
1015
0
  if (table == NULL) {
1016
0
    return NT_STATUS_OK;
1017
0
  }
1018
1019
0
  status = dbwrap_traverse(table->local.db_ctx,
1020
0
         smbXsrv_tcon_disconnect_all_callback,
1021
0
         &state, &count);
1022
0
  if (!NT_STATUS_IS_OK(status)) {
1023
0
    DBG_ERR("dbwrap_traverse() failed: %s\n", nt_errstr(status));
1024
0
    return status;
1025
0
  }
1026
1027
0
  if (!NT_STATUS_IS_OK(state.first_status)) {
1028
0
    DBG_ERR("count[%d] errors[%d] first[%s]\n",
1029
0
      count,
1030
0
      state.errors,
1031
0
      nt_errstr(state.first_status));
1032
0
    return state.first_status;
1033
0
  }
1034
1035
0
  return NT_STATUS_OK;
1036
0
}
1037
1038
static int smbXsrv_tcon_disconnect_all_callback(struct db_record *local_rec,
1039
            void *private_data)
1040
0
{
1041
0
  struct smbXsrv_tcon_disconnect_all_state *state =
1042
0
    (struct smbXsrv_tcon_disconnect_all_state *)private_data;
1043
0
  TDB_DATA val;
1044
0
  void *ptr = NULL;
1045
0
  struct smbXsrv_tcon *tcon = NULL;
1046
0
  uint64_t vuid;
1047
0
  NTSTATUS status;
1048
1049
0
  val = dbwrap_record_get_value(local_rec);
1050
0
  if (val.dsize != sizeof(ptr)) {
1051
0
    status = NT_STATUS_INTERNAL_ERROR;
1052
0
    if (NT_STATUS_IS_OK(state->first_status)) {
1053
0
      state->first_status = status;
1054
0
    }
1055
0
    state->errors++;
1056
0
    return 0;
1057
0
  }
1058
1059
0
  memcpy(&ptr, val.dptr, val.dsize);
1060
0
  tcon = talloc_get_type_abort(ptr, struct smbXsrv_tcon);
1061
1062
0
  vuid = state->vuid;
1063
0
  if (vuid == 0 && tcon->compat) {
1064
0
    vuid = tcon->compat->vuid;
1065
0
  }
1066
1067
0
  tcon->db_rec = local_rec;
1068
0
  status = smbXsrv_tcon_disconnect(tcon, vuid);
1069
0
  tcon->db_rec = NULL;
1070
0
  if (!NT_STATUS_IS_OK(status)) {
1071
0
    if (NT_STATUS_IS_OK(state->first_status)) {
1072
0
      state->first_status = status;
1073
0
    }
1074
0
    state->errors++;
1075
0
    return 0;
1076
0
  }
1077
1078
0
  return 0;
1079
0
}
1080
1081
NTSTATUS smb1srv_tcon_table_init(struct smbXsrv_connection *conn)
1082
0
{
1083
0
  struct smbXsrv_client *client = conn->client;
1084
1085
  /*
1086
   * Allow a range from 1..65534 with 65534 values.
1087
   */
1088
0
  client->tcon_table = talloc_zero(client, struct smbXsrv_tcon_table);
1089
0
  if (client->tcon_table == NULL) {
1090
0
    return NT_STATUS_NO_MEMORY;
1091
0
  }
1092
1093
0
  return smbXsrv_tcon_table_init(client, client->tcon_table,
1094
0
               1, UINT16_MAX - 1,
1095
0
               UINT16_MAX - 1);
1096
0
}
1097
1098
NTSTATUS smb1srv_tcon_create(struct smbXsrv_connection *conn,
1099
           uint32_t session_global_id,
1100
           const char *share_name,
1101
           NTTIME now,
1102
           struct smbXsrv_tcon **_tcon)
1103
0
{
1104
0
  struct server_id id = messaging_server_id(conn->client->msg_ctx);
1105
0
  const uint8_t encryption_flags = 0;
1106
1107
0
  return smbXsrv_tcon_create(conn->client->tcon_table,
1108
0
           conn->protocol,
1109
0
           id, now,
1110
0
           session_global_id,
1111
0
           encryption_flags,
1112
0
           share_name,
1113
0
           _tcon);
1114
0
}
1115
1116
NTSTATUS smb1srv_tcon_lookup(struct smbXsrv_connection *conn,
1117
           uint16_t tree_id, NTTIME now,
1118
           struct smbXsrv_tcon **tcon)
1119
0
{
1120
0
  uint32_t local_id = tree_id;
1121
1122
0
  return smbXsrv_tcon_local_lookup(conn->client->tcon_table,
1123
0
           local_id, now, tcon);
1124
0
}
1125
1126
NTSTATUS smb1srv_tcon_disconnect_all(struct smbXsrv_client *client)
1127
0
{
1128
1129
  /*
1130
   * We do not pass a vuid here,
1131
   * which means the vuid is taken from
1132
   * the tcon->compat->vuid.
1133
   *
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
1137
   * in SMB1.
1138
   *
1139
   * This matches the old behavior of
1140
   * conn_close_all(), but we should think
1141
   * about how to fix this in future.
1142
   */
1143
0
  return smbXsrv_tcon_disconnect_all(client->tcon_table, 0);
1144
0
}
1145
1146
NTSTATUS smb2srv_tcon_table_init(struct smbXsrv_session *session)
1147
0
{
1148
  /*
1149
   * Allow a range from 1..4294967294 with 65534 (same as SMB1) values.
1150
   */
1151
0
  session->tcon_table = talloc_zero(session, struct smbXsrv_tcon_table);
1152
0
  if (session->tcon_table == NULL) {
1153
0
    return NT_STATUS_NO_MEMORY;
1154
0
  }
1155
1156
0
  return smbXsrv_tcon_table_init(session, session->tcon_table,
1157
0
               1, UINT32_MAX - 1,
1158
0
               UINT16_MAX - 1);
1159
0
}
1160
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,
1165
           NTTIME now,
1166
           struct smbXsrv_tcon **_tcon)
1167
0
{
1168
0
  struct server_id id = messaging_server_id(session->client->msg_ctx);
1169
1170
0
  return smbXsrv_tcon_create(session->tcon_table,
1171
0
           PROTOCOL_SMB2_02,
1172
0
           id, now,
1173
0
           session_global_id,
1174
0
           encryption_flags,
1175
0
           share_name,
1176
0
           _tcon);
1177
0
}
1178
1179
NTSTATUS smb2srv_tcon_lookup(struct smbXsrv_session *session,
1180
           uint32_t tree_id, NTTIME now,
1181
           struct smbXsrv_tcon **tcon)
1182
0
{
1183
0
  uint32_t local_id = tree_id;
1184
1185
0
  return smbXsrv_tcon_local_lookup(session->tcon_table,
1186
0
           local_id, now, tcon);
1187
0
}
1188
1189
NTSTATUS smb2srv_tcon_disconnect_all(struct smbXsrv_session *session)
1190
0
{
1191
0
  uint64_t vuid = session->global->session_wire_id;
1192
1193
0
  return smbXsrv_tcon_disconnect_all(session->tcon_table, vuid);
1194
0
}
1195
1196
struct smbXsrv_tcon_global_traverse_state {
1197
  int (*fn)(struct smbXsrv_tcon_global0 *, void *);
1198
  void *private_data;
1199
};
1200
1201
static int smbXsrv_tcon_global_traverse_fn(struct db_record *rec, void *data)
1202
0
{
1203
0
  int ret = -1;
1204
0
  struct smbXsrv_tcon_global_traverse_state *state =
1205
0
    (struct smbXsrv_tcon_global_traverse_state*)data;
1206
0
  TDB_DATA key = dbwrap_record_get_key(rec);
1207
0
  TDB_DATA val = dbwrap_record_get_value(rec);
1208
0
  DATA_BLOB blob = data_blob_const(val.dptr, val.dsize);
1209
0
  struct smbXsrv_tcon_globalB global_blob;
1210
0
  enum ndr_err_code ndr_err;
1211
0
  TALLOC_CTX *frame = talloc_stackframe();
1212
1213
0
  ndr_err = ndr_pull_struct_blob(&blob, frame, &global_blob,
1214
0
      (ndr_pull_flags_fn_t)ndr_pull_smbXsrv_tcon_globalB);
1215
0
  if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
1216
0
    DBG_WARNING("Invalid record in smbXsrv_tcon_global.tdb:"
1217
0
       "key '%s' ndr_pull_struct_blob - %s\n",
1218
0
       tdb_data_dbg(key),
1219
0
       ndr_errstr(ndr_err));
1220
0
    goto done;
1221
0
  }
1222
1223
0
  if (global_blob.version != SMBXSRV_VERSION_0) {
1224
0
    DBG_WARNING("Invalid record in smbXsrv_tcon_global.tdb:"
1225
0
       "key '%s' unsupported version - %d\n",
1226
0
       tdb_data_dbg(key),
1227
0
       (int)global_blob.version);
1228
0
    goto done;
1229
0
  }
1230
1231
0
  if (global_blob.info.info0 == NULL) {
1232
0
    DBG_WARNING("Invalid record in smbXsrv_tcon_global.tdb:"
1233
0
       "key '%s' info0 NULL pointer\n",
1234
0
       tdb_data_dbg(key));
1235
0
    goto done;
1236
0
  }
1237
1238
0
  global_blob.info.info0->db_rec = rec;
1239
0
  ret = state->fn(global_blob.info.info0, state->private_data);
1240
0
done:
1241
0
  TALLOC_FREE(frame);
1242
0
  return ret;
1243
0
}
1244
1245
NTSTATUS smbXsrv_tcon_global_traverse(
1246
      int (*fn)(struct smbXsrv_tcon_global0 *, void *),
1247
      void *private_data)
1248
0
{
1249
0
  NTSTATUS status;
1250
0
  int count = 0;
1251
0
  struct smbXsrv_tcon_global_traverse_state state = {
1252
0
    .fn = fn,
1253
0
    .private_data = private_data,
1254
0
  };
1255
1256
0
  become_root();
1257
0
  status = smbXsrv_tcon_global_init();
1258
0
  if (!NT_STATUS_IS_OK(status)) {
1259
0
    unbecome_root();
1260
0
    DBG_ERR("Failed to initialize tcon_global: %s\n",
1261
0
        nt_errstr(status));
1262
0
    return status;
1263
0
  }
1264
1265
0
  status = dbwrap_traverse_read(smbXsrv_tcon_global_db_ctx,
1266
0
              smbXsrv_tcon_global_traverse_fn,
1267
0
              &state,
1268
0
              &count);
1269
0
  unbecome_root();
1270
1271
0
  return status;
1272
0
}