Coverage Report

Created: 2026-04-01 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/dbwrap/dbwrap.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Database interface wrapper
4
   Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2006
5
6
   Major code contributions from Aleksey Fedoseev (fedoseev@ru.ibm.com)
7
8
   This program is free software; you can redistribute it and/or modify
9
   it under the terms of the GNU General Public License as published by
10
   the Free Software Foundation; either version 3 of the License, or
11
   (at your option) any later version.
12
13
   This program is distributed in the hope that it will be useful,
14
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
   GNU General Public License for more details.
17
18
   You should have received a copy of the GNU General Public License
19
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
*/
21
22
#include "replace.h"
23
#include "lib/util/debug.h"
24
#include "lib/util/fault.h"
25
#include "lib/util/talloc_stack.h"
26
#include "dbwrap/dbwrap.h"
27
#include "dbwrap/dbwrap_private.h"
28
#include "lib/util/util_tdb.h"
29
#include "lib/util/tevent_ntstatus.h"
30
31
/*
32
 * Fall back using fetch if no genuine exists operation is provided
33
 */
34
35
static int dbwrap_fallback_exists(struct db_context *db, TDB_DATA key)
36
0
{
37
0
  NTSTATUS status = dbwrap_parse_record(db, key, NULL, NULL);
38
0
  return NT_STATUS_IS_OK(status) ? 1 : 0;
39
0
}
40
41
static int delete_record(struct db_record *rec, void *data)
42
0
{
43
0
  NTSTATUS status = dbwrap_record_delete(rec);
44
0
  return NT_STATUS_IS_OK(status) ? 0 : -1;
45
0
}
46
47
/*
48
 * Fallback wipe implementation using traverse and delete if no genuine
49
 * wipe operation is provided
50
 */
51
static int dbwrap_fallback_wipe(struct db_context *db)
52
0
{
53
0
  NTSTATUS status = dbwrap_trans_traverse(db, delete_record, NULL);
54
0
  return NT_STATUS_IS_OK(status) ? 0 : -1;
55
0
}
56
57
static int do_nothing(struct db_record *rec, void *unused)
58
0
{
59
0
  return 0;
60
0
}
61
62
/*
63
 * Fallback check operation: just traverse.
64
 */
65
static int dbwrap_fallback_check(struct db_context *db)
66
0
{
67
0
  NTSTATUS status = dbwrap_traverse_read(db, do_nothing, NULL, NULL);
68
0
  return NT_STATUS_IS_OK(status) ? 0 : -1;
69
0
}
70
71
/*
72
 * Wrapper functions for the backend methods
73
 */
74
75
TDB_DATA dbwrap_record_get_key(const struct db_record *rec)
76
0
{
77
0
  return rec->key;
78
0
}
79
80
TDB_DATA dbwrap_record_get_value(const struct db_record *rec)
81
0
{
82
0
  SMB_ASSERT(rec->value_valid);
83
0
  return rec->value;
84
0
}
85
86
NTSTATUS dbwrap_record_storev(struct db_record *rec,
87
            const TDB_DATA *dbufs, int num_dbufs, int flags)
88
0
{
89
0
  NTSTATUS status;
90
91
  /*
92
   * Invalidate before rec->storev() is called, give
93
   * rec->storev() the chance to re-validate rec->value.
94
   */
95
0
  rec->value_valid = false;
96
97
0
  status = rec->storev(rec, dbufs, num_dbufs, flags);
98
0
  if (!NT_STATUS_IS_OK(status)) {
99
0
    return status;
100
0
  }
101
0
  return NT_STATUS_OK;
102
0
}
103
104
NTSTATUS dbwrap_record_store(struct db_record *rec, TDB_DATA data, int flags)
105
0
{
106
0
  return dbwrap_record_storev(rec, &data, 1, flags);
107
0
}
108
109
NTSTATUS dbwrap_record_delete(struct db_record *rec)
110
0
{
111
0
  NTSTATUS status;
112
113
0
  status = rec->delete_rec(rec);
114
0
  if (!NT_STATUS_IS_OK(status)) {
115
0
    return status;
116
0
  }
117
118
0
  rec->value = tdb_null;
119
120
0
  return NT_STATUS_OK;
121
0
}
122
123
struct dbwrap_merge_dbs_state {
124
  struct db_context *to;
125
  int flags;
126
};
127
128
/* Copy a single record to the db_context passed in private_data */
129
static int dbwrap_merge_dbs_copy_record(struct db_record *rec,
130
              void *private_data)
131
0
{
132
0
  struct dbwrap_merge_dbs_state *state = private_data;
133
134
0
  TDB_DATA data = dbwrap_record_get_value(rec);
135
0
  TDB_DATA key = dbwrap_record_get_key(rec);
136
0
  NTSTATUS status = dbwrap_store(state->to, key, data, state->flags);
137
138
0
  return NT_STATUS_IS_OK(status) ? 0 : 1;
139
0
}
140
141
NTSTATUS
142
dbwrap_merge_dbs(struct db_context *to, struct db_context *from, int flags)
143
0
{
144
0
  struct dbwrap_merge_dbs_state state = {.to = to, .flags = flags};
145
146
0
  return dbwrap_traverse(from,
147
0
             dbwrap_merge_dbs_copy_record,
148
0
             &state,
149
0
             NULL);
150
0
}
151
152
const char *locked_dbs[DBWRAP_LOCK_ORDER_MAX];
153
154
static const char *lock_order_dbg(void)
155
0
{
156
0
  int i;
157
0
  char *msg = talloc_strdup(talloc_tos(), "lock order:");
158
159
0
  for (i = 0; i < DBWRAP_LOCK_ORDER_MAX; i++) {
160
0
    talloc_asprintf_addbuf(
161
0
      &msg,
162
0
      " %d:%s",
163
0
      i + 1,
164
0
      locked_dbs[i] != NULL ? locked_dbs[i] : "<none>");
165
0
  }
166
167
0
  if (msg == NULL) {
168
0
    return "lock order: <out of memory>\n";
169
0
  }
170
171
0
  return msg;
172
0
}
173
174
void dbwrap_lock_order_lock(const char *db_name,
175
          enum dbwrap_lock_order lock_order)
176
0
{
177
0
  int idx;
178
179
0
  DBG_INFO("check lock order %d for %s\n",
180
0
     (int)lock_order,
181
0
     db_name);
182
183
0
  if (!DBWRAP_LOCK_ORDER_VALID(lock_order)) {
184
0
    DBG_ERR("Invalid lock order %d of %s\n",
185
0
      lock_order,
186
0
      db_name);
187
0
    smb_panic("lock order violation");
188
0
  }
189
190
0
  for (idx=lock_order-1; idx<DBWRAP_LOCK_ORDER_MAX; idx++) {
191
0
    if (locked_dbs[idx] != NULL) {
192
0
      DBG_ERR("Lock order violation: Trying %s at %d while "
193
0
        "%s at %d is locked\n%s\n",
194
0
        db_name,
195
0
        (int)lock_order,
196
0
        locked_dbs[idx],
197
0
        idx + 1,
198
0
        lock_order_dbg());
199
0
      smb_panic("lock order violation");
200
0
    }
201
0
  }
202
203
0
  locked_dbs[lock_order-1] = db_name;
204
205
0
  DBG_DEBUG("%s\n", lock_order_dbg());
206
0
}
207
208
void dbwrap_lock_order_unlock(const char *db_name,
209
            enum dbwrap_lock_order lock_order)
210
0
{
211
0
  DBG_INFO("release lock order %d for %s\n",
212
0
     (int)lock_order,
213
0
     db_name);
214
215
0
  if (!DBWRAP_LOCK_ORDER_VALID(lock_order)) {
216
0
    DBG_ERR("Invalid lock order %d of %s\n",
217
0
      lock_order,
218
0
      db_name);
219
0
    smb_panic("lock order violation");
220
0
  }
221
222
0
  if (locked_dbs[lock_order-1] == NULL) {
223
0
    DBG_ERR("db %s at order %d unlocked\n",
224
0
      db_name,
225
0
      (int)lock_order);
226
0
    smb_panic("lock order violation");
227
0
  }
228
229
0
  if (locked_dbs[lock_order-1] != db_name) {
230
0
    DBG_ERR("locked db at lock order %d is %s, expected %s\n",
231
0
      (int)lock_order,
232
0
      locked_dbs[lock_order-1],
233
0
      db_name);
234
0
    smb_panic("lock order violation");
235
0
  }
236
237
0
  locked_dbs[lock_order-1] = NULL;
238
0
}
239
240
struct dbwrap_lock_order_state {
241
  struct db_context *db;
242
};
243
244
static int dbwrap_lock_order_state_destructor(
245
  struct dbwrap_lock_order_state *s)
246
0
{
247
0
  struct db_context *db = s->db;
248
0
  dbwrap_lock_order_unlock(db->name, db->lock_order);
249
0
  return 0;
250
0
}
251
252
static struct dbwrap_lock_order_state *dbwrap_check_lock_order(
253
  struct db_context *db, TALLOC_CTX *mem_ctx)
254
0
{
255
0
  struct dbwrap_lock_order_state *state;
256
257
0
  state = talloc(mem_ctx, struct dbwrap_lock_order_state);
258
0
  if (state == NULL) {
259
0
    DBG_WARNING("talloc failed\n");
260
0
    return NULL;
261
0
  }
262
0
  state->db = db;
263
264
0
  dbwrap_lock_order_lock(db->name, db->lock_order);
265
0
  talloc_set_destructor(state, dbwrap_lock_order_state_destructor);
266
267
0
  return state;
268
0
}
269
270
static struct db_record *dbwrap_fetch_locked_internal(
271
  struct db_context *db, TALLOC_CTX *mem_ctx, TDB_DATA key,
272
  struct db_record *(*db_fn)(struct db_context *db, TALLOC_CTX *mem_ctx,
273
           TDB_DATA key))
274
0
{
275
0
  struct db_record *rec;
276
0
  struct dbwrap_lock_order_state *lock_order = NULL;
277
278
0
  if (db->lock_order != DBWRAP_LOCK_ORDER_NONE) {
279
0
    lock_order = dbwrap_check_lock_order(db, mem_ctx);
280
0
    if (lock_order == NULL) {
281
0
      return NULL;
282
0
    }
283
0
  }
284
0
  rec = db_fn(db, mem_ctx, key);
285
0
  if (rec == NULL) {
286
0
    TALLOC_FREE(lock_order);
287
0
    return NULL;
288
0
  }
289
0
  (void)talloc_steal(rec, lock_order);
290
0
  rec->db = db;
291
0
  return rec;
292
0
}
293
294
struct db_record *dbwrap_fetch_locked(struct db_context *db,
295
              TALLOC_CTX *mem_ctx,
296
              TDB_DATA key)
297
0
{
298
0
  return dbwrap_fetch_locked_internal(db, mem_ctx, key,
299
0
              db->fetch_locked);
300
0
}
301
302
struct db_context *dbwrap_record_get_db(struct db_record *rec)
303
0
{
304
0
  return rec->db;
305
0
}
306
307
struct dbwrap_fetch_state {
308
  TALLOC_CTX *mem_ctx;
309
  TDB_DATA data;
310
};
311
312
static void dbwrap_fetch_parser(TDB_DATA key, TDB_DATA data,
313
        void *private_data)
314
0
{
315
0
  struct dbwrap_fetch_state *state =
316
0
    (struct dbwrap_fetch_state *)private_data;
317
318
0
  state->data.dsize = data.dsize;
319
0
  state->data.dptr = (uint8_t *)talloc_memdup(state->mem_ctx, data.dptr,
320
0
                data.dsize);
321
0
}
322
323
NTSTATUS dbwrap_fetch(struct db_context *db, TALLOC_CTX *mem_ctx,
324
          TDB_DATA key, TDB_DATA *value)
325
0
{
326
0
  struct dbwrap_fetch_state state;
327
0
  NTSTATUS status;
328
329
0
  if (value == NULL) {
330
0
    return NT_STATUS_INVALID_PARAMETER;
331
0
  }
332
333
0
  state.mem_ctx = mem_ctx;
334
335
0
  status = dbwrap_parse_record(db, key, dbwrap_fetch_parser, &state);
336
0
  if (!NT_STATUS_IS_OK(status)) {
337
0
    return status;
338
0
  }
339
0
  if ((state.data.dsize != 0) && (state.data.dptr == NULL)) {
340
0
    return NT_STATUS_NO_MEMORY;
341
0
  }
342
0
  *value = state.data;
343
0
  return NT_STATUS_OK;
344
0
}
345
346
bool dbwrap_exists(struct db_context *db, TDB_DATA key)
347
0
{
348
0
  int result;
349
0
  if (db->exists != NULL) {
350
0
    result = db->exists(db, key);
351
0
  } else {
352
0
    result = dbwrap_fallback_exists(db,key);
353
0
  }
354
0
  return (result == 1);
355
0
}
356
357
struct dbwrap_store_state {
358
  TDB_DATA data;
359
  int flags;
360
  NTSTATUS status;
361
};
362
363
static void dbwrap_store_fn(
364
  struct db_record *rec,
365
  TDB_DATA value,
366
  void *private_data)
367
0
{
368
0
  struct dbwrap_store_state *state = private_data;
369
0
  state->status = dbwrap_record_store(rec, state->data, state->flags);
370
0
}
371
372
NTSTATUS dbwrap_store(struct db_context *db, TDB_DATA key,
373
          TDB_DATA data, int flags)
374
0
{
375
0
  struct dbwrap_store_state state = { .data = data, .flags = flags };
376
0
  NTSTATUS status;
377
378
0
  status = dbwrap_do_locked(db, key, dbwrap_store_fn, &state);
379
0
  if (!NT_STATUS_IS_OK(status)) {
380
0
    return status;
381
0
  }
382
383
0
  return state.status;
384
0
}
385
386
struct dbwrap_delete_state {
387
  NTSTATUS status;
388
};
389
390
static void dbwrap_delete_fn(
391
  struct db_record *rec,
392
  TDB_DATA value,
393
  void *private_data)
394
0
{
395
0
  struct dbwrap_delete_state *state = private_data;
396
0
  state->status = dbwrap_record_delete(rec);
397
0
}
398
399
NTSTATUS dbwrap_delete(struct db_context *db, TDB_DATA key)
400
0
{
401
0
  struct dbwrap_delete_state state = { .status = NT_STATUS_NOT_FOUND };
402
0
  NTSTATUS status;
403
404
0
  status = dbwrap_do_locked(db, key, dbwrap_delete_fn, &state);
405
0
  if (!NT_STATUS_IS_OK(status)) {
406
0
    return status;
407
0
  }
408
409
0
  return state.status;
410
0
}
411
412
NTSTATUS dbwrap_traverse(struct db_context *db,
413
       int (*f)(struct db_record*, void*),
414
       void *private_data,
415
       int *count)
416
0
{
417
0
  int ret = db->traverse(db, f, private_data);
418
419
0
  if (ret < 0) {
420
0
    return NT_STATUS_INTERNAL_DB_CORRUPTION;
421
0
  }
422
423
0
  if (count != NULL) {
424
0
    *count = ret;
425
0
  }
426
427
0
  return NT_STATUS_OK;
428
0
}
429
430
NTSTATUS dbwrap_traverse_read(struct db_context *db,
431
            int (*f)(struct db_record*, void*),
432
            void *private_data,
433
            int *count)
434
0
{
435
0
  int ret = db->traverse_read(db, f, private_data);
436
437
0
  if (ret < 0) {
438
0
    return NT_STATUS_INTERNAL_DB_CORRUPTION;
439
0
  }
440
441
0
  if (count != NULL) {
442
0
    *count = ret;
443
0
  }
444
445
0
  return NT_STATUS_OK;
446
0
}
447
448
static void dbwrap_null_parser(TDB_DATA key, TDB_DATA val, void* data)
449
0
{
450
0
  return;
451
0
}
452
453
NTSTATUS dbwrap_parse_record(struct db_context *db, TDB_DATA key,
454
           void (*parser)(TDB_DATA key, TDB_DATA data,
455
              void *private_data),
456
           void *private_data)
457
0
{
458
0
  if (parser == NULL) {
459
0
    parser = dbwrap_null_parser;
460
0
  }
461
0
  return db->parse_record(db, key, parser, private_data);
462
0
}
463
464
struct dbwrap_parse_record_state {
465
  struct db_context *db;
466
  TDB_DATA key;
467
  uint8_t _keybuf[64];
468
};
469
470
static void dbwrap_parse_record_done(struct tevent_req *subreq);
471
472
struct tevent_req *dbwrap_parse_record_send(
473
  TALLOC_CTX *mem_ctx,
474
  struct tevent_context *ev,
475
  struct db_context *db,
476
  TDB_DATA key,
477
  void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data),
478
  void *private_data,
479
  enum dbwrap_req_state *req_state)
480
0
{
481
0
  struct tevent_req *req = NULL;
482
0
  struct tevent_req *subreq = NULL;
483
0
  struct dbwrap_parse_record_state *state = NULL;
484
0
  NTSTATUS status;
485
486
0
  req = tevent_req_create(mem_ctx, &state, struct dbwrap_parse_record_state);
487
0
  if (req == NULL) {
488
0
    *req_state = DBWRAP_REQ_ERROR;
489
0
    return NULL;
490
0
  }
491
492
0
  *state = (struct dbwrap_parse_record_state) {
493
0
    .db = db,
494
0
  };
495
496
0
  if (parser == NULL) {
497
0
    parser = dbwrap_null_parser;
498
0
  }
499
500
0
  *req_state = DBWRAP_REQ_INIT;
501
502
0
  if (db->parse_record_send == NULL) {
503
    /*
504
     * Backend doesn't implement async version, call sync one
505
     */
506
0
    status = db->parse_record(db, key, parser, private_data);
507
0
    if (tevent_req_nterror(req, status)) {
508
0
      *req_state = DBWRAP_REQ_DONE;
509
0
      return tevent_req_post(req, ev);
510
0
    }
511
512
0
    *req_state = DBWRAP_REQ_DONE;
513
0
    tevent_req_done(req);
514
0
    return tevent_req_post(req, ev);
515
0
  }
516
517
  /*
518
   * Copy the key into our state ensuring the key data buffer is always
519
   * available to all the dbwrap backends over the entire lifetime of the
520
   * async request. Otherwise the caller might have free'd the key buffer.
521
   */
522
0
  if (key.dsize > sizeof(state->_keybuf)) {
523
0
    state->key.dptr = talloc_memdup(state, key.dptr, key.dsize);
524
0
    if (tevent_req_nomem(state->key.dptr, req)) {
525
0
      return tevent_req_post(req, ev);
526
0
    }
527
0
  } else {
528
0
    memcpy(state->_keybuf, key.dptr, key.dsize);
529
0
    state->key.dptr = state->_keybuf;
530
0
  }
531
0
  state->key.dsize = key.dsize;
532
533
0
  subreq = db->parse_record_send(state,
534
0
               ev,
535
0
               db,
536
0
               state->key,
537
0
               parser,
538
0
               private_data,
539
0
               req_state);
540
0
  if (tevent_req_nomem(subreq, req)) {
541
0
    *req_state = DBWRAP_REQ_ERROR;
542
0
    return tevent_req_post(req, ev);
543
0
  }
544
545
0
  tevent_req_set_callback(subreq,
546
0
        dbwrap_parse_record_done,
547
0
        req);
548
0
  return req;
549
0
}
550
551
static void dbwrap_parse_record_done(struct tevent_req *subreq)
552
0
{
553
0
  struct tevent_req *req = tevent_req_callback_data(
554
0
    subreq, struct tevent_req);
555
0
  struct dbwrap_parse_record_state *state = tevent_req_data(
556
0
    req, struct dbwrap_parse_record_state);
557
0
  NTSTATUS status;
558
559
0
  status = state->db->parse_record_recv(subreq);
560
0
  TALLOC_FREE(subreq);
561
0
  if (!NT_STATUS_IS_OK(status)) {
562
0
    tevent_req_nterror(req, status);
563
0
    return;
564
0
  }
565
566
0
  tevent_req_done(req);
567
0
}
568
569
NTSTATUS dbwrap_parse_record_recv(struct tevent_req *req)
570
0
{
571
0
  return tevent_req_simple_recv_ntstatus(req);
572
0
}
573
574
NTSTATUS dbwrap_do_locked(struct db_context *db, TDB_DATA key,
575
        void (*fn)(struct db_record *rec,
576
             TDB_DATA value,
577
             void *private_data),
578
        void *private_data)
579
0
{
580
0
  struct db_record *rec;
581
582
0
  if (db->do_locked != NULL) {
583
0
    NTSTATUS status;
584
585
0
    if (db->lock_order != DBWRAP_LOCK_ORDER_NONE) {
586
0
      dbwrap_lock_order_lock(db->name, db->lock_order);
587
0
    }
588
589
0
    status = db->do_locked(db, key, fn, private_data);
590
591
0
    if (db->lock_order != DBWRAP_LOCK_ORDER_NONE) {
592
0
      dbwrap_lock_order_unlock(db->name, db->lock_order);
593
0
    }
594
595
0
    return status;
596
0
  }
597
598
0
  rec = dbwrap_fetch_locked(db, db, key);
599
0
  if (rec == NULL) {
600
0
    return NT_STATUS_NO_MEMORY;
601
0
  }
602
603
  /*
604
   * Invalidate rec->value, nobody shall assume it's set from
605
   * within dbwrap_do_locked().
606
   */
607
0
  rec->value_valid = false;
608
609
0
  fn(rec, rec->value, private_data);
610
611
0
  TALLOC_FREE(rec);
612
613
0
  return NT_STATUS_OK;
614
0
}
615
616
int dbwrap_wipe(struct db_context *db)
617
0
{
618
0
  if (db->wipe == NULL) {
619
0
    return dbwrap_fallback_wipe(db);
620
0
  }
621
0
  return db->wipe(db);
622
0
}
623
624
int dbwrap_check(struct db_context *db)
625
0
{
626
0
  if (db->check == NULL) {
627
0
    return dbwrap_fallback_check(db);
628
0
  }
629
0
  return db->check(db);
630
0
}
631
632
int dbwrap_get_seqnum(struct db_context *db)
633
0
{
634
0
  return db->get_seqnum(db);
635
0
}
636
637
int dbwrap_transaction_start(struct db_context *db)
638
0
{
639
0
  if (!db->persistent) {
640
    /*
641
     * dbwrap_ctdb has two different data models for persistent
642
     * and non-persistent databases. Transactions are supported
643
     * only for the persistent databases. This check is here to
644
     * prevent breakages of the cluster case, autobuild at this
645
     * point only tests non-clustered Samba. Before removing this
646
     * check, please make sure that this facility has also been
647
     * added to dbwrap_ctdb.
648
     *
649
     * Thanks, vl
650
     */
651
0
    DEBUG(1, ("transactions not supported on non-persistent "
652
0
        "database %s\n", db->name));
653
0
    return -1;
654
0
  }
655
0
  return db->transaction_start(db);
656
0
}
657
658
NTSTATUS dbwrap_transaction_start_nonblock(struct db_context *db)
659
0
{
660
0
  if (db->transaction_start_nonblock) {
661
0
    return db->transaction_start_nonblock(db);
662
0
  } else {
663
0
    return dbwrap_transaction_start(db) == 0 ? NT_STATUS_OK
664
0
      : NT_STATUS_UNSUCCESSFUL;
665
0
  }
666
0
}
667
668
int dbwrap_transaction_commit(struct db_context *db)
669
0
{
670
0
  return db->transaction_commit(db);
671
0
}
672
673
int dbwrap_transaction_cancel(struct db_context *db)
674
0
{
675
0
  return db->transaction_cancel(db);
676
0
}
677
678
size_t dbwrap_db_id(struct db_context *db, uint8_t *id, size_t idlen)
679
0
{
680
0
  return db->id(db, id, idlen);
681
0
}
682
683
bool dbwrap_is_persistent(struct db_context *db)
684
0
{
685
0
  return db->persistent;
686
0
}
687
688
const char *dbwrap_name(struct db_context *db)
689
0
{
690
0
  return db->name;
691
0
}
692
693
static ssize_t tdb_data_buf(const TDB_DATA *dbufs, int num_dbufs,
694
          uint8_t *buf, size_t buflen)
695
0
{
696
0
  size_t needed = 0;
697
0
  uint8_t *p = buf;
698
0
  int i;
699
700
0
  for (i=0; i<num_dbufs; i++) {
701
0
    size_t thislen = dbufs[i].dsize;
702
703
0
    needed += thislen;
704
0
    if (needed < thislen) {
705
      /* wrap */
706
0
      return -1;
707
0
    }
708
709
0
    if (p != NULL && (thislen != 0) && (needed <= buflen)) {
710
0
      memcpy(p, dbufs[i].dptr, thislen);
711
0
      p += thislen;
712
0
    }
713
0
  }
714
715
0
  return needed;
716
0
}
717
718
719
NTSTATUS dbwrap_merge_dbufs(TDB_DATA *buf, TALLOC_CTX *mem_ctx,
720
          const TDB_DATA *dbufs, int num_dbufs)
721
0
{
722
0
  ssize_t len = tdb_data_buf(dbufs, num_dbufs, NULL, 0);
723
724
0
  if (len == -1) {
725
0
    return NT_STATUS_INVALID_PARAMETER;
726
0
  }
727
728
0
  if (buf->dsize != len) {
729
0
    uint8_t *tmp;
730
731
0
    tmp = talloc_realloc(mem_ctx, buf->dptr, uint8_t, len);
732
0
    if (tmp == NULL && len != 0) {
733
0
      return NT_STATUS_NO_MEMORY;
734
0
    }
735
736
0
    buf->dptr = tmp;
737
0
    buf->dsize = len;
738
0
  }
739
740
0
  tdb_data_buf(dbufs, num_dbufs, buf->dptr, buf->dsize);
741
742
0
  return NT_STATUS_OK;
743
0
}