dict_lmdb.c revision 1.2
1/* $NetBSD: dict_lmdb.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */ 2 3/*++ 4/* NAME 5/* dict_lmdb 3 6/* SUMMARY 7/* dictionary manager interface to OpenLDAP LMDB files 8/* SYNOPSIS 9/* #include <dict_lmdb.h> 10/* 11/* extern size_t dict_lmdb_map_size; 12/* 13/* DEFINE_DICT_LMDB_MAP_SIZE; 14/* 15/* DICT *dict_lmdb_open(path, open_flags, dict_flags) 16/* const char *name; 17/* const char *path; 18/* int open_flags; 19/* int dict_flags; 20/* DESCRIPTION 21/* dict_lmdb_open() opens the named LMDB database and makes 22/* it available via the generic interface described in 23/* dict_open(3). 24/* 25/* The dict_lmdb_map_size variable specifies the initial 26/* database memory map size. When a map becomes full its size 27/* is doubled, and other programs pick up the size change. 28/* 29/* This variable cannot be exported via the dict(3) API and 30/* must therefore be defined in the calling program by invoking 31/* the DEFINE_DICT_LMDB_MAP_SIZE macro at the global level. 32/* DIAGNOSTICS 33/* Fatal errors: cannot open file, file write error, out of 34/* memory. 35/* BUGS 36/* The on-the-fly map resize operations require no concurrent 37/* activity in the same database by other threads in the same 38/* memory address space. 39/* SEE ALSO 40/* dict(3) generic dictionary manager 41/* LICENSE 42/* .ad 43/* .fi 44/* The Secure Mailer license must be distributed with this software. 45/* AUTHOR(S) 46/* Howard Chu 47/* Symas Corporation 48/* 49/* Wietse Venema 50/* IBM T.J. Watson Research 51/* P.O. Box 704 52/* Yorktown Heights, NY 10598, USA 53/*--*/ 54 55#include <sys_defs.h> 56 57#ifdef HAS_LMDB 58 59/* System library. */ 60 61#include <sys/stat.h> 62#include <string.h> 63#include <unistd.h> 64#include <limits.h> 65 66/* Utility library. */ 67 68#include <msg.h> 69#include <mymalloc.h> 70#include <htable.h> 71#include <iostuff.h> 72#include <vstring.h> 73#include <myflock.h> 74#include <stringops.h> 75#include <slmdb.h> 76#include <dict.h> 77#include <dict_lmdb.h> 78#include <warn_stat.h> 79 80/* Application-specific. */ 81 82typedef struct { 83 DICT dict; /* generic members */ 84 SLMDB slmdb; /* sane LMDB API */ 85 VSTRING *key_buf; /* key buffer */ 86 VSTRING *val_buf; /* value buffer */ 87} DICT_LMDB; 88 89 /* 90 * The LMDB database filename suffix happens to equal our DICT_TYPE_LMDB 91 * prefix, but that doesn't mean it is kosher to use DICT_TYPE_LMDB where a 92 * suffix is needed, so we define an explicit suffix here. 93 */ 94#define DICT_LMDB_SUFFIX "lmdb" 95 96 /* 97 * Make a safe string copy that is guaranteed to be null-terminated. 98 */ 99#define SCOPY(buf, data, size) \ 100 vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size)) 101 102 /* 103 * Postfix writers recover from a "map full" error by increasing the memory 104 * map size with a factor DICT_LMDB_SIZE_INCR (up to some limit) and 105 * retrying the transaction. 106 * 107 * Each dict(3) API call is retried no more than a few times. For bulk-mode 108 * transactions the number of retries is proportional to the size of the 109 * address space. 110 * 111 * We do not expose these details to the Postfix user interface. The purpose of 112 * Postfix is to solve problems, not punt them to the user. 113 */ 114#ifndef SSIZE_T_MAX /* The maximum map size */ 115#define SSIZE_T_MAX __MAXINT__(ssize_t) /* XXX Assumes two's complement */ 116#endif 117 118#define DICT_LMDB_SIZE_INCR 2 /* Increase size by 1 bit on retry */ 119#define DICT_LMDB_SIZE_MAX SSIZE_T_MAX 120 121#define DICT_LMDB_API_RETRY_LIMIT 2 /* Retries per dict(3) API call */ 122#define DICT_LMDB_BULK_RETRY_LIMIT \ 123 ((int) (2 * sizeof(size_t) * CHAR_BIT)) /* Retries per bulk-mode 124 * transaction */ 125 126/* #define msg_verbose 1 */ 127 128/* dict_lmdb_lookup - find database entry */ 129 130static const char *dict_lmdb_lookup(DICT *dict, const char *name) 131{ 132 DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict; 133 MDB_val mdb_key; 134 MDB_val mdb_value; 135 const char *result = 0; 136 int status; 137 ssize_t klen; 138 139 dict->error = 0; 140 klen = strlen(name); 141 142 /* 143 * Sanity check. 144 */ 145 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0) 146 msg_panic("dict_lmdb_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag"); 147 148 /* 149 * Optionally fold the key. 150 */ 151 if (dict->flags & DICT_FLAG_FOLD_FIX) { 152 if (dict->fold_buf == 0) 153 dict->fold_buf = vstring_alloc(10); 154 vstring_strcpy(dict->fold_buf, name); 155 name = lowercase(vstring_str(dict->fold_buf)); 156 } 157 158 /* 159 * Acquire a shared lock. 160 */ 161 if ((dict->flags & DICT_FLAG_LOCK) 162 && myflock(dict->lock_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_SHARED) < 0) 163 msg_fatal("%s: lock dictionary: %m", dict->name); 164 165 /* 166 * See if this LMDB file was written with one null byte appended to key 167 * and value. 168 */ 169 if (dict->flags & DICT_FLAG_TRY1NULL) { 170 mdb_key.mv_data = (void *) name; 171 mdb_key.mv_size = klen + 1; 172 status = slmdb_get(&dict_lmdb->slmdb, &mdb_key, &mdb_value); 173 if (status == 0) { 174 dict->flags &= ~DICT_FLAG_TRY0NULL; 175 result = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data, 176 mdb_value.mv_size); 177 } else if (status != MDB_NOTFOUND) { 178 msg_fatal("error reading %s:%s: %s", 179 dict_lmdb->dict.type, dict_lmdb->dict.name, 180 mdb_strerror(status)); 181 } 182 } 183 184 /* 185 * See if this LMDB file was written with no null byte appended to key 186 * and value. 187 */ 188 if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) { 189 mdb_key.mv_data = (void *) name; 190 mdb_key.mv_size = klen; 191 status = slmdb_get(&dict_lmdb->slmdb, &mdb_key, &mdb_value); 192 if (status == 0) { 193 dict->flags &= ~DICT_FLAG_TRY1NULL; 194 result = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data, 195 mdb_value.mv_size); 196 } else if (status != MDB_NOTFOUND) { 197 msg_fatal("error reading %s:%s: %s", 198 dict_lmdb->dict.type, dict_lmdb->dict.name, 199 mdb_strerror(status)); 200 } 201 } 202 203 /* 204 * Release the shared lock. 205 */ 206 if ((dict->flags & DICT_FLAG_LOCK) 207 && myflock(dict->lock_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_NONE) < 0) 208 msg_fatal("%s: unlock dictionary: %m", dict->name); 209 210 return (result); 211} 212 213/* dict_lmdb_update - add or update database entry */ 214 215static int dict_lmdb_update(DICT *dict, const char *name, const char *value) 216{ 217 DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict; 218 MDB_val mdb_key; 219 MDB_val mdb_value; 220 int status; 221 222 dict->error = 0; 223 224 /* 225 * Sanity check. 226 */ 227 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0) 228 msg_panic("dict_lmdb_update: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag"); 229 230 /* 231 * Optionally fold the key. 232 */ 233 if (dict->flags & DICT_FLAG_FOLD_FIX) { 234 if (dict->fold_buf == 0) 235 dict->fold_buf = vstring_alloc(10); 236 vstring_strcpy(dict->fold_buf, name); 237 name = lowercase(vstring_str(dict->fold_buf)); 238 } 239 mdb_key.mv_data = (void *) name; 240 mdb_value.mv_data = (void *) value; 241 mdb_key.mv_size = strlen(name); 242 mdb_value.mv_size = strlen(value); 243 244 /* 245 * If undecided about appending a null byte to key and value, choose a 246 * default depending on the platform. 247 */ 248 if ((dict->flags & DICT_FLAG_TRY1NULL) 249 && (dict->flags & DICT_FLAG_TRY0NULL)) { 250#ifdef LMDB_NO_TRAILING_NULL 251 dict->flags &= ~DICT_FLAG_TRY1NULL; 252#else 253 dict->flags &= ~DICT_FLAG_TRY0NULL; 254#endif 255 } 256 257 /* 258 * Optionally append a null byte to key and value. 259 */ 260 if (dict->flags & DICT_FLAG_TRY1NULL) { 261 mdb_key.mv_size++; 262 mdb_value.mv_size++; 263 } 264 265 /* 266 * Acquire an exclusive lock. 267 */ 268 if ((dict->flags & DICT_FLAG_LOCK) 269 && myflock(dict->lock_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_EXCLUSIVE) < 0) 270 msg_fatal("%s: lock dictionary: %m", dict->name); 271 272 /* 273 * Do the update. 274 */ 275 status = slmdb_put(&dict_lmdb->slmdb, &mdb_key, &mdb_value, 276 (dict->flags & DICT_FLAG_DUP_REPLACE) ? 0 : MDB_NOOVERWRITE); 277 if (status != 0) { 278 if (status == MDB_KEYEXIST) { 279 if (dict->flags & DICT_FLAG_DUP_IGNORE) 280 /* void */ ; 281 else if (dict->flags & DICT_FLAG_DUP_WARN) 282 msg_warn("%s:%s: duplicate entry: \"%s\"", 283 dict_lmdb->dict.type, dict_lmdb->dict.name, name); 284 else 285 msg_fatal("%s:%s: duplicate entry: \"%s\"", 286 dict_lmdb->dict.type, dict_lmdb->dict.name, name); 287 } else { 288 msg_fatal("error updating %s:%s: %s", 289 dict_lmdb->dict.type, dict_lmdb->dict.name, 290 mdb_strerror(status)); 291 } 292 } 293 294 /* 295 * Release the exclusive lock. 296 */ 297 if ((dict->flags & DICT_FLAG_LOCK) 298 && myflock(dict->lock_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_NONE) < 0) 299 msg_fatal("%s: unlock dictionary: %m", dict->name); 300 301 return (status); 302} 303 304/* dict_lmdb_delete - delete one entry from the dictionary */ 305 306static int dict_lmdb_delete(DICT *dict, const char *name) 307{ 308 DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict; 309 MDB_val mdb_key; 310 int status = 1; 311 ssize_t klen; 312 313 dict->error = 0; 314 klen = strlen(name); 315 316 /* 317 * Sanity check. 318 */ 319 if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0) 320 msg_panic("dict_lmdb_delete: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag"); 321 322 /* 323 * Optionally fold the key. 324 */ 325 if (dict->flags & DICT_FLAG_FOLD_FIX) { 326 if (dict->fold_buf == 0) 327 dict->fold_buf = vstring_alloc(10); 328 vstring_strcpy(dict->fold_buf, name); 329 name = lowercase(vstring_str(dict->fold_buf)); 330 } 331 332 /* 333 * Acquire an exclusive lock. 334 */ 335 if ((dict->flags & DICT_FLAG_LOCK) 336 && myflock(dict->lock_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_EXCLUSIVE) < 0) 337 msg_fatal("%s: lock dictionary: %m", dict->name); 338 339 /* 340 * See if this LMDB file was written with one null byte appended to key 341 * and value. 342 */ 343 if (dict->flags & DICT_FLAG_TRY1NULL) { 344 mdb_key.mv_data = (void *) name; 345 mdb_key.mv_size = klen + 1; 346 status = slmdb_del(&dict_lmdb->slmdb, &mdb_key); 347 if (status != 0) { 348 if (status == MDB_NOTFOUND) 349 status = 1; 350 else 351 msg_fatal("error deleting from %s:%s: %s", 352 dict_lmdb->dict.type, dict_lmdb->dict.name, 353 mdb_strerror(status)); 354 } else { 355 dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */ 356 } 357 } 358 359 /* 360 * See if this LMDB file was written with no null byte appended to key 361 * and value. 362 */ 363 if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) { 364 mdb_key.mv_data = (void *) name; 365 mdb_key.mv_size = klen; 366 status = slmdb_del(&dict_lmdb->slmdb, &mdb_key); 367 if (status != 0) { 368 if (status == MDB_NOTFOUND) 369 status = 1; 370 else 371 msg_fatal("error deleting from %s:%s: %s", 372 dict_lmdb->dict.type, dict_lmdb->dict.name, 373 mdb_strerror(status)); 374 } else { 375 dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */ 376 } 377 } 378 379 /* 380 * Release the exclusive lock. 381 */ 382 if ((dict->flags & DICT_FLAG_LOCK) 383 && myflock(dict->lock_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_NONE) < 0) 384 msg_fatal("%s: unlock dictionary: %m", dict->name); 385 386 return (status); 387} 388 389/* dict_lmdb_sequence - traverse the dictionary */ 390 391static int dict_lmdb_sequence(DICT *dict, int function, 392 const char **key, const char **value) 393{ 394 const char *myname = "dict_lmdb_sequence"; 395 DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict; 396 MDB_val mdb_key; 397 MDB_val mdb_value; 398 MDB_cursor_op op; 399 int status; 400 401 dict->error = 0; 402 403 /* 404 * Determine the seek function. 405 */ 406 switch (function) { 407 case DICT_SEQ_FUN_FIRST: 408 op = MDB_FIRST; 409 break; 410 case DICT_SEQ_FUN_NEXT: 411 op = MDB_NEXT; 412 break; 413 default: 414 msg_panic("%s: invalid function: %d", myname, function); 415 } 416 417 /* 418 * Acquire a shared lock. 419 */ 420 if ((dict->flags & DICT_FLAG_LOCK) 421 && myflock(dict->lock_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_SHARED) < 0) 422 msg_fatal("%s: lock dictionary: %m", dict->name); 423 424 /* 425 * Database lookup. 426 */ 427 status = slmdb_cursor_get(&dict_lmdb->slmdb, &mdb_key, &mdb_value, op); 428 429 switch (status) { 430 431 /* 432 * Copy the key and value so they are guaranteed null terminated. 433 */ 434 case 0: 435 *key = SCOPY(dict_lmdb->key_buf, mdb_key.mv_data, mdb_key.mv_size); 436 if (mdb_value.mv_data != 0 && mdb_value.mv_size > 0) 437 *value = SCOPY(dict_lmdb->val_buf, mdb_value.mv_data, 438 mdb_value.mv_size); 439 else 440 *value = ""; /* XXX */ 441 break; 442 443 /* 444 * End-of-database. 445 */ 446 case MDB_NOTFOUND: 447 status = 1; 448 /* Not: mdb_cursor_close(). Wrong abstraction level. */ 449 break; 450 451 /* 452 * Bust. 453 */ 454 default: 455 msg_fatal("error seeking %s:%s: %s", 456 dict_lmdb->dict.type, dict_lmdb->dict.name, 457 mdb_strerror(status)); 458 } 459 460 /* 461 * Release the shared lock. 462 */ 463 if ((dict->flags & DICT_FLAG_LOCK) 464 && myflock(dict->lock_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_NONE) < 0) 465 msg_fatal("%s: unlock dictionary: %m", dict->name); 466 467 return (status); 468} 469 470/* dict_lmdb_close - disassociate from data base */ 471 472static void dict_lmdb_close(DICT *dict) 473{ 474 DICT_LMDB *dict_lmdb = (DICT_LMDB *) dict; 475 476 slmdb_close(&dict_lmdb->slmdb); 477 if (dict_lmdb->key_buf) 478 vstring_free(dict_lmdb->key_buf); 479 if (dict_lmdb->val_buf) 480 vstring_free(dict_lmdb->val_buf); 481 if (dict->fold_buf) 482 vstring_free(dict->fold_buf); 483 dict_free(dict); 484} 485 486/* dict_lmdb_longjmp - repeat bulk transaction */ 487 488static void dict_lmdb_longjmp(void *context, int val) 489{ 490 DICT_LMDB *dict_lmdb = (DICT_LMDB *) context; 491 492 dict_longjmp(&dict_lmdb->dict, val); 493} 494 495/* dict_lmdb_notify - debug logging */ 496 497static void dict_lmdb_notify(void *context, int error_code,...) 498{ 499 DICT_LMDB *dict_lmdb = (DICT_LMDB *) context; 500 va_list ap; 501 502 va_start(ap, error_code); 503 switch (error_code) { 504 case MDB_SUCCESS: 505 msg_info("database %s:%s: using size limit %lu during open", 506 dict_lmdb->dict.type, dict_lmdb->dict.name, 507 (unsigned long) va_arg(ap, size_t)); 508 break; 509 case MDB_MAP_FULL: 510 msg_info("database %s:%s: using size limit %lu after MDB_MAP_FULL", 511 dict_lmdb->dict.type, dict_lmdb->dict.name, 512 (unsigned long) va_arg(ap, size_t)); 513 break; 514 case MDB_MAP_RESIZED: 515 msg_info("database %s:%s: using size limit %lu after MDB_MAP_RESIZED", 516 dict_lmdb->dict.type, dict_lmdb->dict.name, 517 (unsigned long) va_arg(ap, size_t)); 518 break; 519 case MDB_READERS_FULL: 520 msg_info("database %s:%s: pausing after MDB_READERS_FULL", 521 dict_lmdb->dict.type, dict_lmdb->dict.name); 522 break; 523 default: 524 msg_warn("unknown MDB error code: %d", error_code); 525 break; 526 } 527 va_end(ap); 528} 529 530/* dict_lmdb_assert - report LMDB internal assertion failure */ 531 532static void dict_lmdb_assert(void *context, const char *text) 533{ 534 DICT_LMDB *dict_lmdb = (DICT_LMDB *) context; 535 536 msg_fatal("%s:%s: internal error: %s", 537 dict_lmdb->dict.type, dict_lmdb->dict.name, text); 538} 539 540/* dict_lmdb_open - open LMDB data base */ 541 542DICT *dict_lmdb_open(const char *path, int open_flags, int dict_flags) 543{ 544 DICT_LMDB *dict_lmdb; 545 DICT *dict; 546 struct stat st; 547 SLMDB slmdb; 548 char *mdb_path; 549 int mdb_flags, slmdb_flags, status; 550 int db_fd; 551 552 /* 553 * Let the optimizer worry about eliminating redundant code. 554 */ 555#define DICT_LMDB_OPEN_RETURN(d) do { \ 556 DICT *__d = (d); \ 557 myfree(mdb_path); \ 558 return (__d); \ 559 } while (0) 560 561 mdb_path = concatenate(path, "." DICT_TYPE_LMDB, (char *) 0); 562 563 /* 564 * Impedance adapters. 565 */ 566 mdb_flags = MDB_NOSUBDIR | MDB_NOLOCK; 567 if (open_flags == O_RDONLY) 568 mdb_flags |= MDB_RDONLY; 569 570 slmdb_flags = 0; 571 if (dict_flags & DICT_FLAG_BULK_UPDATE) 572 slmdb_flags |= SLMDB_FLAG_BULK; 573 574 /* 575 * Security violation. 576 * 577 * By default, LMDB 0.9.9 writes uninitialized heap memory to a 578 * world-readable database file, as chunks of up to 4096 bytes. This is a 579 * huge memory disclosure vulnerability: memory content that a program 580 * does not intend to share ends up in a world-readable file. The content 581 * of uninitialized heap memory depends on program execution history. 582 * That history includes code execution in other libraries that are 583 * linked into the program. 584 * 585 * This is a problem whenever the user who writes the database file differs 586 * from the user who reads the database file. For example, a privileged 587 * writer and an unprivileged reader. In the case of Postfix, the 588 * postmap(1) and postalias(1) commands would leak uninitialized heap 589 * memory, as chunks of up to 4096 bytes, from a root-privileged process 590 * that writes to a database file, to unprivileged processes that read 591 * from that database file. 592 * 593 * As a workaround the postmap(1) and postalias(1) commands turn on 594 * MDB_WRITEMAP which disables the use of malloc() in LMDB. However, that 595 * does not address several disclosures of stack memory. We don't enable 596 * this workaround for Postfix databases are maintained by Postfix daemon 597 * processes, because those are accessible only by the postfix user. 598 * 599 * LMDB 0.9.10 by default does not write uninitialized heap memory to file 600 * (specify MDB_NOMEMINIT to revert that change). We use the MDB_WRITEMAP 601 * workaround for older LMDB versions. 602 */ 603#ifndef MDB_NOMEMINIT 604 if (dict_flags & DICT_FLAG_BULK_UPDATE) /* XXX Good enough */ 605 mdb_flags |= MDB_WRITEMAP; 606#endif 607 608 /* 609 * Gracefully handle most database open errors. 610 */ 611 if ((status = slmdb_init(&slmdb, dict_lmdb_map_size, DICT_LMDB_SIZE_INCR, 612 DICT_LMDB_SIZE_MAX)) != 0 613 || (status = slmdb_open(&slmdb, mdb_path, open_flags, mdb_flags, 614 slmdb_flags)) != 0) { 615 /* This leaks a little memory that would have been used otherwise. */ 616 dict = dict_surrogate(DICT_TYPE_LMDB, path, open_flags, dict_flags, 617 "open database %s: %s", mdb_path, mdb_strerror(status)); 618 DICT_LMDB_OPEN_RETURN(dict); 619 } 620 621 /* 622 * XXX Persistent locking belongs in mkmap_lmdb. 623 * 624 * We just need to acquire exclusive access momentarily. This establishes 625 * that no readers are accessing old (obsoleted by copy-on-write) txn 626 * snapshots, so we are free to reuse all eligible old pages. Downgrade 627 * the lock right after acquiring it. This is sufficient to keep out 628 * other writers until we are done. 629 */ 630 db_fd = slmdb_fd(&slmdb); 631 if (dict_flags & DICT_FLAG_BULK_UPDATE) { 632 if (myflock(db_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_EXCLUSIVE) < 0) 633 msg_fatal("%s: lock dictionary: %m", mdb_path); 634 if (myflock(db_fd, MYFLOCK_STYLE_FCNTL, MYFLOCK_OP_SHARED) < 0) 635 msg_fatal("%s: unlock dictionary: %m", mdb_path); 636 } 637 638 /* 639 * Bundle up. From here on no more assignments to slmdb. 640 */ 641 dict_lmdb = (DICT_LMDB *) dict_alloc(DICT_TYPE_LMDB, path, sizeof(*dict_lmdb)); 642 dict_lmdb->slmdb = slmdb; 643 dict_lmdb->dict.lookup = dict_lmdb_lookup; 644 dict_lmdb->dict.update = dict_lmdb_update; 645 dict_lmdb->dict.delete = dict_lmdb_delete; 646 dict_lmdb->dict.sequence = dict_lmdb_sequence; 647 dict_lmdb->dict.close = dict_lmdb_close; 648 649 if (fstat(db_fd, &st) < 0) 650 msg_fatal("dict_lmdb_open: fstat: %m"); 651 dict_lmdb->dict.lock_fd = dict_lmdb->dict.stat_fd = db_fd; 652 dict_lmdb->dict.lock_type = MYFLOCK_STYLE_FCNTL; 653 dict_lmdb->dict.mtime = st.st_mtime; 654 dict_lmdb->dict.owner.uid = st.st_uid; 655 dict_lmdb->dict.owner.status = (st.st_uid != 0); 656 657 dict_lmdb->key_buf = 0; 658 dict_lmdb->val_buf = 0; 659 660 /* 661 * Warn if the source file is newer than the indexed file, except when 662 * the source file changed only seconds ago. 663 */ 664 if ((dict_flags & DICT_FLAG_LOCK) != 0 665 && stat(path, &st) == 0 666 && st.st_mtime > dict_lmdb->dict.mtime 667 && st.st_mtime < time((time_t *) 0) - 100) 668 msg_warn("database %s is older than source file %s", mdb_path, path); 669 670#define DICT_LMDB_IMPL_FLAGS (DICT_FLAG_FIXED | DICT_FLAG_MULTI_WRITER) 671 672 dict_lmdb->dict.flags = dict_flags | DICT_LMDB_IMPL_FLAGS; 673 if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0) 674 dict_lmdb->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL); 675 if (dict_flags & DICT_FLAG_FOLD_FIX) 676 dict_lmdb->dict.fold_buf = vstring_alloc(10); 677 678 if (dict_flags & DICT_FLAG_BULK_UPDATE) 679 dict_jmp_alloc(&dict_lmdb->dict); 680 681 /* 682 * The following requests return an error result only if we have serious 683 * memory corruption problem. 684 */ 685 if (slmdb_control(&dict_lmdb->slmdb, 686 CA_SLMDB_CTL_API_RETRY_LIMIT(DICT_LMDB_API_RETRY_LIMIT), 687 CA_SLMDB_CTL_BULK_RETRY_LIMIT(DICT_LMDB_BULK_RETRY_LIMIT), 688 CA_SLMDB_CTL_LONGJMP_FN(dict_lmdb_longjmp), 689 CA_SLMDB_CTL_NOTIFY_FN(msg_verbose ? 690 dict_lmdb_notify : (SLMDB_NOTIFY_FN) 0), 691 CA_SLMDB_CTL_ASSERT_FN(dict_lmdb_assert), 692 CA_SLMDB_CTL_CB_CONTEXT((void *) dict_lmdb), 693 CA_SLMDB_CTL_END) != 0) 694 msg_panic("dict_lmdb_open: slmdb_control: %m"); 695 696 if (msg_verbose) 697 dict_lmdb_notify((void *) dict_lmdb, MDB_SUCCESS, 698 slmdb_curr_limit(&dict_lmdb->slmdb)); 699 700 DICT_LMDB_OPEN_RETURN(DICT_DEBUG (&dict_lmdb->dict)); 701} 702 703#endif 704