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