1/* 2 Unix SMB/CIFS implementation. 3 tdb utility functions 4 Copyright (C) Andrew Tridgell 1992-1998 5 Copyright (C) Rafal Szczesniak 2002 6 Copyright (C) Michael Adam 2007 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 "includes.h" 23#undef malloc 24#undef realloc 25#undef calloc 26#undef strdup 27 28/* these are little tdb utility functions that are meant to make 29 dealing with a tdb database a little less cumbersome in Samba */ 30 31static SIG_ATOMIC_T gotalarm; 32 33/*************************************************************** 34 Signal function to tell us we timed out. 35****************************************************************/ 36 37static void gotalarm_sig(void) 38{ 39 gotalarm = 1; 40} 41 42/**************************************************************************** 43 Lock a chain with timeout (in seconds). 44****************************************************************************/ 45 46static int tdb_chainlock_with_timeout_internal( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout, int rw_type) 47{ 48 /* Allow tdb_chainlock to be interrupted by an alarm. */ 49 int ret; 50 gotalarm = 0; 51 52 if (timeout) { 53 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig); 54 tdb_setalarm_sigptr(tdb, &gotalarm); 55 alarm(timeout); 56 } 57 58 if (rw_type == F_RDLCK) 59 ret = tdb_chainlock_read(tdb, key); 60 else 61 ret = tdb_chainlock(tdb, key); 62 63 if (timeout) { 64 alarm(0); 65 tdb_setalarm_sigptr(tdb, NULL); 66 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN); 67 if (gotalarm && (ret == -1)) { 68 DEBUG(0,("tdb_chainlock_with_timeout_internal: alarm (%u) timed out for key %s in tdb %s\n", 69 timeout, key.dptr, tdb_name(tdb))); 70 /* TODO: If we time out waiting for a lock, it might 71 * be nice to use F_GETLK to get the pid of the 72 * process currently holding the lock and print that 73 * as part of the debugging message. -- mbp */ 74 return -1; 75 } 76 } 77 78 return ret; 79} 80 81/**************************************************************************** 82 Write lock a chain. Return -1 if timeout or lock failed. 83****************************************************************************/ 84 85int tdb_chainlock_with_timeout( TDB_CONTEXT *tdb, TDB_DATA key, unsigned int timeout) 86{ 87 return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_WRLCK); 88} 89 90int tdb_lock_bystring_with_timeout(TDB_CONTEXT *tdb, const char *keyval, 91 int timeout) 92{ 93 TDB_DATA key = string_term_tdb_data(keyval); 94 95 return tdb_chainlock_with_timeout(tdb, key, timeout); 96} 97 98/**************************************************************************** 99 Read lock a chain by string. Return -1 if timeout or lock failed. 100****************************************************************************/ 101 102int tdb_read_lock_bystring_with_timeout(TDB_CONTEXT *tdb, const char *keyval, unsigned int timeout) 103{ 104 TDB_DATA key = string_term_tdb_data(keyval); 105 106 return tdb_chainlock_with_timeout_internal(tdb, key, timeout, F_RDLCK); 107} 108 109 110 111 112int tdb_trans_store_bystring(TDB_CONTEXT *tdb, const char *keystr, 113 TDB_DATA data, int flags) 114{ 115 TDB_DATA key = string_term_tdb_data(keystr); 116 117 return tdb_trans_store(tdb, key, data, flags); 118} 119 120/**************************************************************************** 121 Useful pair of routines for packing/unpacking data consisting of 122 integers and strings. 123****************************************************************************/ 124 125static size_t tdb_pack_va(uint8 *buf, int bufsize, const char *fmt, va_list ap) 126{ 127 uint8 bt; 128 uint16 w; 129 uint32 d; 130 int i; 131 void *p; 132 int len; 133 char *s; 134 char c; 135 uint8 *buf0 = buf; 136 const char *fmt0 = fmt; 137 int bufsize0 = bufsize; 138 139 while (*fmt) { 140 switch ((c = *fmt++)) { 141 case 'b': /* unsigned 8-bit integer */ 142 len = 1; 143 bt = (uint8)va_arg(ap, int); 144 if (bufsize && bufsize >= len) 145 SSVAL(buf, 0, bt); 146 break; 147 case 'w': /* unsigned 16-bit integer */ 148 len = 2; 149 w = (uint16)va_arg(ap, int); 150 if (bufsize && bufsize >= len) 151 SSVAL(buf, 0, w); 152 break; 153 case 'd': /* signed 32-bit integer (standard int in most systems) */ 154 len = 4; 155 d = va_arg(ap, uint32); 156 if (bufsize && bufsize >= len) 157 SIVAL(buf, 0, d); 158 break; 159 case 'p': /* pointer */ 160 len = 4; 161 p = va_arg(ap, void *); 162 d = p?1:0; 163 if (bufsize && bufsize >= len) 164 SIVAL(buf, 0, d); 165 break; 166 case 'P': /* null-terminated string */ 167 s = va_arg(ap,char *); 168 w = strlen(s); 169 len = w + 1; 170 if (bufsize && bufsize >= len) 171 memcpy(buf, s, len); 172 break; 173 case 'f': /* null-terminated string */ 174 s = va_arg(ap,char *); 175 w = strlen(s); 176 len = w + 1; 177 if (bufsize && bufsize >= len) 178 memcpy(buf, s, len); 179 break; 180 case 'B': /* fixed-length string */ 181 i = va_arg(ap, int); 182 s = va_arg(ap, char *); 183 len = 4+i; 184 if (bufsize && bufsize >= len) { 185 SIVAL(buf, 0, i); 186 memcpy(buf+4, s, i); 187 } 188 break; 189 default: 190 DEBUG(0,("Unknown tdb_pack format %c in %s\n", 191 c, fmt)); 192 len = 0; 193 break; 194 } 195 196 buf += len; 197 if (bufsize) 198 bufsize -= len; 199 if (bufsize < 0) 200 bufsize = 0; 201 } 202 203 DEBUG(18,("tdb_pack_va(%s, %d) -> %d\n", 204 fmt0, bufsize0, (int)PTR_DIFF(buf, buf0))); 205 206 return PTR_DIFF(buf, buf0); 207} 208 209size_t tdb_pack(uint8 *buf, int bufsize, const char *fmt, ...) 210{ 211 va_list ap; 212 size_t result; 213 214 va_start(ap, fmt); 215 result = tdb_pack_va(buf, bufsize, fmt, ap); 216 va_end(ap); 217 return result; 218} 219 220bool tdb_pack_append(TALLOC_CTX *mem_ctx, uint8 **buf, size_t *len, 221 const char *fmt, ...) 222{ 223 va_list ap; 224 size_t len1, len2; 225 226 va_start(ap, fmt); 227 len1 = tdb_pack_va(NULL, 0, fmt, ap); 228 va_end(ap); 229 230 if (mem_ctx != NULL) { 231 *buf = TALLOC_REALLOC_ARRAY(mem_ctx, *buf, uint8, 232 (*len) + len1); 233 } else { 234 *buf = SMB_REALLOC_ARRAY(*buf, uint8, (*len) + len1); 235 } 236 237 if (*buf == NULL) { 238 return False; 239 } 240 241 va_start(ap, fmt); 242 len2 = tdb_pack_va((*buf)+(*len), len1, fmt, ap); 243 va_end(ap); 244 245 if (len1 != len2) { 246 return False; 247 } 248 249 *len += len2; 250 251 return True; 252} 253 254/**************************************************************************** 255 Useful pair of routines for packing/unpacking data consisting of 256 integers and strings. 257****************************************************************************/ 258 259int tdb_unpack(const uint8 *buf, int bufsize, const char *fmt, ...) 260{ 261 va_list ap; 262 uint8 *bt; 263 uint16 *w; 264 uint32 *d; 265 int len; 266 int *i; 267 void **p; 268 char *s, **b, **ps; 269 char c; 270 const uint8 *buf0 = buf; 271 const char *fmt0 = fmt; 272 int bufsize0 = bufsize; 273 274 va_start(ap, fmt); 275 276 while (*fmt) { 277 switch ((c=*fmt++)) { 278 case 'b': /* unsigned 8-bit integer */ 279 len = 1; 280 bt = va_arg(ap, uint8 *); 281 if (bufsize < len) 282 goto no_space; 283 *bt = SVAL(buf, 0); 284 break; 285 case 'w': /* unsigned 16-bit integer */ 286 len = 2; 287 w = va_arg(ap, uint16 *); 288 if (bufsize < len) 289 goto no_space; 290 *w = SVAL(buf, 0); 291 break; 292 case 'd': /* signed 32-bit integer (standard int in most systems) */ 293 len = 4; 294 d = va_arg(ap, uint32 *); 295 if (bufsize < len) 296 goto no_space; 297 *d = IVAL(buf, 0); 298 break; 299 case 'p': /* pointer */ 300 len = 4; 301 p = va_arg(ap, void **); 302 if (bufsize < len) 303 goto no_space; 304 /* 305 * This isn't a real pointer - only a token (1 or 0) 306 * to mark the fact a pointer is present. 307 */ 308 309 *p = (void *)(IVAL(buf, 0) ? (void *)1 : NULL); 310 break; 311 case 'P': /* null-terminated string */ 312 /* Return malloc'ed string. */ 313 ps = va_arg(ap,char **); 314 len = strlen((const char *)buf) + 1; 315 *ps = SMB_STRDUP((const char *)buf); 316 break; 317 case 'f': /* null-terminated string */ 318 s = va_arg(ap,char *); 319 len = strlen((const char *)buf) + 1; 320 if (bufsize < len || len > sizeof(fstring)) 321 goto no_space; 322 memcpy(s, buf, len); 323 break; 324 case 'B': /* fixed-length string */ 325 i = va_arg(ap, int *); 326 b = va_arg(ap, char **); 327 len = 4; 328 if (bufsize < len) 329 goto no_space; 330 *i = IVAL(buf, 0); 331 if (! *i) { 332 *b = NULL; 333 break; 334 } 335 len += *i; 336 if (bufsize < len) 337 goto no_space; 338 *b = (char *)SMB_MALLOC(*i); 339 if (! *b) 340 goto no_space; 341 memcpy(*b, buf+4, *i); 342 break; 343 default: 344 DEBUG(0,("Unknown tdb_unpack format %c in %s\n", 345 c, fmt)); 346 347 len = 0; 348 break; 349 } 350 351 buf += len; 352 bufsize -= len; 353 } 354 355 va_end(ap); 356 357 DEBUG(18,("tdb_unpack(%s, %d) -> %d\n", 358 fmt0, bufsize0, (int)PTR_DIFF(buf, buf0))); 359 360 return PTR_DIFF(buf, buf0); 361 362 no_space: 363 va_end(ap); 364 return -1; 365} 366 367 368/**************************************************************************** 369 Log tdb messages via DEBUG(). 370****************************************************************************/ 371 372static void tdb_log(TDB_CONTEXT *tdb, enum tdb_debug_level level, const char *format, ...) 373{ 374 va_list ap; 375 char *ptr = NULL; 376 int ret; 377 378 va_start(ap, format); 379 ret = vasprintf(&ptr, format, ap); 380 va_end(ap); 381 382 if ((ret == -1) || !*ptr) 383 return; 384 385 DEBUG((int)level, ("tdb(%s): %s", tdb_name(tdb) ? tdb_name(tdb) : "unnamed", ptr)); 386 SAFE_FREE(ptr); 387} 388 389/**************************************************************************** 390 Like tdb_open() but also setup a logging function that redirects to 391 the samba DEBUG() system. 392****************************************************************************/ 393 394TDB_CONTEXT *tdb_open_log(const char *name, int hash_size, int tdb_flags, 395 int open_flags, mode_t mode) 396{ 397 TDB_CONTEXT *tdb; 398 struct tdb_logging_context log_ctx; 399 400 if (!lp_use_mmap()) 401 tdb_flags |= TDB_NOMMAP; 402 403 log_ctx.log_fn = tdb_log; 404 log_ctx.log_private = NULL; 405 406 if ((hash_size == 0) && (name != NULL)) { 407 const char *base = strrchr_m(name, '/'); 408 if (base != NULL) { 409 base += 1; 410 } 411 else { 412 base = name; 413 } 414 hash_size = lp_parm_int(-1, "tdb_hashsize", base, 0); 415 } 416 417 tdb = tdb_open_ex(name, hash_size, tdb_flags, 418 open_flags, mode, &log_ctx, NULL); 419 if (!tdb) 420 return NULL; 421 422 return tdb; 423} 424 425/**************************************************************************** 426 tdb_store, wrapped in a transaction. This way we make sure that a process 427 that dies within writing does not leave a corrupt tdb behind. 428****************************************************************************/ 429 430int tdb_trans_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, 431 int flag) 432{ 433 int res; 434 435 if ((res = tdb_transaction_start(tdb)) != 0) { 436 DEBUG(5, ("tdb_transaction_start failed\n")); 437 return res; 438 } 439 440 if ((res = tdb_store(tdb, key, dbuf, flag)) != 0) { 441 DEBUG(10, ("tdb_store failed\n")); 442 if (tdb_transaction_cancel(tdb) != 0) { 443 smb_panic("Cancelling transaction failed"); 444 } 445 return res; 446 } 447 448 if ((res = tdb_transaction_commit(tdb)) != 0) { 449 DEBUG(5, ("tdb_transaction_commit failed\n")); 450 } 451 452 return res; 453} 454 455/**************************************************************************** 456 tdb_delete, wrapped in a transaction. This way we make sure that a process 457 that dies within deleting does not leave a corrupt tdb behind. 458****************************************************************************/ 459 460int tdb_trans_delete(struct tdb_context *tdb, TDB_DATA key) 461{ 462 int res; 463 464 if ((res = tdb_transaction_start(tdb)) != 0) { 465 DEBUG(5, ("tdb_transaction_start failed\n")); 466 return res; 467 } 468 469 if ((res = tdb_delete(tdb, key)) != 0) { 470 DEBUG(10, ("tdb_delete failed\n")); 471 if (tdb_transaction_cancel(tdb) != 0) { 472 smb_panic("Cancelling transaction failed"); 473 } 474 return res; 475 } 476 477 if ((res = tdb_transaction_commit(tdb)) != 0) { 478 DEBUG(5, ("tdb_transaction_commit failed\n")); 479 } 480 481 return res; 482} 483 484/* 485 Log tdb messages via DEBUG(). 486*/ 487static void tdb_wrap_log(TDB_CONTEXT *tdb, enum tdb_debug_level level, 488 const char *format, ...) PRINTF_ATTRIBUTE(3,4); 489 490static void tdb_wrap_log(TDB_CONTEXT *tdb, enum tdb_debug_level level, 491 const char *format, ...) 492{ 493 va_list ap; 494 char *ptr = NULL; 495 int debuglevel = 0; 496 int ret; 497 498 switch (level) { 499 case TDB_DEBUG_FATAL: 500 debuglevel = 0; 501 break; 502 case TDB_DEBUG_ERROR: 503 debuglevel = 1; 504 break; 505 case TDB_DEBUG_WARNING: 506 debuglevel = 2; 507 break; 508 case TDB_DEBUG_TRACE: 509 debuglevel = 5; 510 break; 511 default: 512 debuglevel = 0; 513 } 514 515 va_start(ap, format); 516 ret = vasprintf(&ptr, format, ap); 517 va_end(ap); 518 519 if (ret != -1) { 520 const char *name = tdb_name(tdb); 521 DEBUG(debuglevel, ("tdb(%s): %s", name ? name : "unnamed", ptr)); 522 free(ptr); 523 } 524} 525 526static struct tdb_wrap *tdb_list; 527 528/* destroy the last connection to a tdb */ 529static int tdb_wrap_destructor(struct tdb_wrap *w) 530{ 531 tdb_close(w->tdb); 532 DLIST_REMOVE(tdb_list, w); 533 return 0; 534} 535 536/* 537 wrapped connection to a tdb database 538 to close just talloc_free() the tdb_wrap pointer 539 */ 540struct tdb_wrap *tdb_wrap_open(TALLOC_CTX *mem_ctx, 541 const char *name, int hash_size, int tdb_flags, 542 int open_flags, mode_t mode) 543{ 544 struct tdb_wrap *w; 545 struct tdb_logging_context log_ctx; 546 log_ctx.log_fn = tdb_wrap_log; 547 548 if (!lp_use_mmap()) 549 tdb_flags |= TDB_NOMMAP; 550 551 for (w=tdb_list;w;w=w->next) { 552 if (strcmp(name, w->name) == 0) { 553 /* 554 * Yes, talloc_reference is exactly what we want 555 * here. Otherwise we would have to implement our own 556 * reference counting. 557 */ 558 return talloc_reference(mem_ctx, w); 559 } 560 } 561 562 w = talloc(mem_ctx, struct tdb_wrap); 563 if (w == NULL) { 564 return NULL; 565 } 566 567 if (!(w->name = talloc_strdup(w, name))) { 568 talloc_free(w); 569 return NULL; 570 } 571 572 if ((hash_size == 0) && (name != NULL)) { 573 const char *base = strrchr_m(name, '/'); 574 if (base != NULL) { 575 base += 1; 576 } 577 else { 578 base = name; 579 } 580 hash_size = lp_parm_int(-1, "tdb_hashsize", base, 0); 581 } 582 583 w->tdb = tdb_open_ex(name, hash_size, tdb_flags, 584 open_flags, mode, &log_ctx, NULL); 585 if (w->tdb == NULL) { 586 talloc_free(w); 587 return NULL; 588 } 589 590 talloc_set_destructor(w, tdb_wrap_destructor); 591 592 DLIST_ADD(tdb_list, w); 593 594 return w; 595} 596 597NTSTATUS map_nt_error_from_tdb(enum TDB_ERROR err) 598{ 599 struct { enum TDB_ERROR err; NTSTATUS status; } map[] = 600 { { TDB_SUCCESS, NT_STATUS_OK }, 601 { TDB_ERR_CORRUPT, NT_STATUS_INTERNAL_DB_CORRUPTION }, 602 { TDB_ERR_IO, NT_STATUS_UNEXPECTED_IO_ERROR }, 603 { TDB_ERR_OOM, NT_STATUS_NO_MEMORY }, 604 { TDB_ERR_EXISTS, NT_STATUS_OBJECT_NAME_COLLISION }, 605 606 /* 607 * TDB_ERR_LOCK is very broad, we could for example 608 * distinguish between fcntl locks and invalid lock 609 * sequences. So NT_STATUS_FILE_LOCK_CONFLICT is a 610 * compromise. 611 */ 612 { TDB_ERR_LOCK, NT_STATUS_FILE_LOCK_CONFLICT }, 613 /* 614 * The next two ones in the enum are not actually used 615 */ 616 { TDB_ERR_NOLOCK, NT_STATUS_FILE_LOCK_CONFLICT }, 617 { TDB_ERR_LOCK_TIMEOUT, NT_STATUS_FILE_LOCK_CONFLICT }, 618 { TDB_ERR_NOEXIST, NT_STATUS_NOT_FOUND }, 619 { TDB_ERR_EINVAL, NT_STATUS_INVALID_PARAMETER }, 620 { TDB_ERR_RDONLY, NT_STATUS_ACCESS_DENIED } 621 }; 622 623 int i; 624 625 for (i=0; i < sizeof(map) / sizeof(map[0]); i++) { 626 if (err == map[i].err) { 627 return map[i].status; 628 } 629 } 630 631 return NT_STATUS_INTERNAL_ERROR; 632} 633 634int tdb_data_cmp(TDB_DATA t1, TDB_DATA t2) 635{ 636 int ret; 637 if (t1.dptr == NULL && t2.dptr != NULL) { 638 return -1; 639 } 640 if (t1.dptr != NULL && t2.dptr == NULL) { 641 return 1; 642 } 643 if (t1.dptr == t2.dptr) { 644 return t1.dsize - t2.dsize; 645 } 646 ret = memcmp(t1.dptr, t2.dptr, MIN(t1.dsize, t2.dsize)); 647 if (ret == 0) { 648 return t1.dsize - t2.dsize; 649 } 650 return ret; 651} 652