summaryrefslogtreecommitdiffstats
path: root/src/global/mkmap_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/global/mkmap_db.c')
-rw-r--r--src/global/mkmap_db.c191
1 files changed, 191 insertions, 0 deletions
diff --git a/src/global/mkmap_db.c b/src/global/mkmap_db.c
new file mode 100644
index 0000000..d2c87ef
--- /dev/null
+++ b/src/global/mkmap_db.c
@@ -0,0 +1,191 @@
+/*++
+/* NAME
+/* mkmap_db 3
+/* SUMMARY
+/* create or open database, DB style
+/* SYNOPSIS
+/* #include <mkmap.h>
+/*
+/* MKMAP *mkmap_hash_open(path)
+/* const char *path;
+/*
+/* MKMAP *mkmap_btree_open(path)
+/* const char *path;
+/* DESCRIPTION
+/* This module implements support for creating DB databases.
+/*
+/* mkmap_hash_open() and mkmap_btree_open() take a file name,
+/* append the ".db" suffix, and do whatever initialization is
+/* required before the Berkeley DB open routine is called.
+/*
+/* All errors are fatal.
+/* SEE ALSO
+/* dict_db(3), DB dictionary interface.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mymalloc.h>
+#include <stringops.h>
+#include <dict.h>
+#include <dict_db.h>
+#include <myflock.h>
+#include <warn_stat.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+
+/* Application-specific. */
+
+#include "mkmap.h"
+
+#ifdef HAS_DB
+#ifdef PATH_DB_H
+#include PATH_DB_H
+#else
+#include <db.h>
+#endif
+
+typedef struct MKMAP_DB {
+ MKMAP mkmap; /* parent class */
+ char *lock_file; /* path name */
+ int lock_fd; /* -1 or open locked file */
+} MKMAP_DB;
+
+/* mkmap_db_after_close - clean up after closing database */
+
+static void mkmap_db_after_close(MKMAP *mp)
+{
+ MKMAP_DB *mkmap = (MKMAP_DB *) mp;
+
+ if (mkmap->lock_fd >= 0 && close(mkmap->lock_fd) < 0)
+ msg_warn("close %s: %m", mkmap->lock_file);
+ myfree(mkmap->lock_file);
+}
+
+/* mkmap_db_after_open - lock newly created database */
+
+static void mkmap_db_after_open(MKMAP *mp)
+{
+ MKMAP_DB *mkmap = (MKMAP_DB *) mp;
+
+ if (mkmap->lock_fd < 0) {
+ if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0)
+ msg_fatal("open lockfile %s: %m", mkmap->lock_file);
+ if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
+ msg_fatal("lock %s: %m", mkmap->lock_file);
+ }
+}
+
+/* mkmap_db_before_open - lock existing database */
+
+static MKMAP *mkmap_db_before_open(const char *path,
+ DICT *(*db_open) (const char *, int, int))
+{
+ MKMAP_DB *mkmap = (MKMAP_DB *) mymalloc(sizeof(*mkmap));
+ struct stat st;
+
+ /*
+ * Override the default per-table cache size for map (re)builds.
+ *
+ * db_cache_size" is defined in util/dict_db.c and defaults to 128kB, which
+ * works well for the lookup code.
+ *
+ * We use a larger per-table cache when building ".db" files. For "hash"
+ * files performance degrades rapidly unless the memory pool is O(file
+ * size).
+ *
+ * For "btree" files performance is good with sorted input even for small
+ * memory pools, but with random input degrades rapidly unless the memory
+ * pool is O(file size).
+ *
+ * XXX This should be specified via the DICT interface so that the buffer
+ * size becomes an object property, instead of being specified by poking
+ * a global variable so that it becomes a class property.
+ */
+ dict_db_cache_size = var_db_create_buf;
+
+ /*
+ * Fill in the generic members.
+ */
+ mkmap->lock_file = concatenate(path, ".db", (char *) 0);
+ mkmap->mkmap.open = db_open;
+ mkmap->mkmap.after_open = mkmap_db_after_open;
+ mkmap->mkmap.after_close = mkmap_db_after_close;
+
+ /*
+ * Unfortunately, not all systems that might support db databases do
+ * support locking on open(), so we open the file before updating it.
+ *
+ * XXX Berkeley DB 4.1 refuses to open a zero-length file. This means we can
+ * open and lock only an existing file, and that we must not truncate it.
+ */
+ if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) {
+ if (errno != ENOENT)
+ msg_fatal("open %s: %m", mkmap->lock_file);
+ }
+
+ /*
+ * Get an exclusive lock - we're going to change the database so we can't
+ * have any spectators.
+ *
+ * XXX Horror. Berkeley DB 4.1 refuses to open a zero-length file. This
+ * means that we must examine the size while the file is locked, and that
+ * we must unlink a zero-length file while it is locked. Avoid a race
+ * condition where two processes try to open the same zero-length file
+ * and where the second process ends up deleting the wrong file.
+ */
+ else {
+ if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
+ msg_fatal("lock %s: %m", mkmap->lock_file);
+ if (fstat(mkmap->lock_fd, &st) < 0)
+ msg_fatal("fstat %s: %m", mkmap->lock_file);
+ if (st.st_size == 0) {
+ if (st.st_nlink > 0) {
+ if (unlink(mkmap->lock_file) < 0)
+ msg_fatal("cannot remove zero-length database file %s: %m",
+ mkmap->lock_file);
+ msg_warn("removing zero-length database file: %s",
+ mkmap->lock_file);
+ }
+ close(mkmap->lock_fd);
+ mkmap->lock_fd = -1;
+ }
+ }
+
+ return (&mkmap->mkmap);
+}
+
+/* mkmap_hash_open - create or open hashed DB file */
+
+MKMAP *mkmap_hash_open(const char *path)
+{
+ return (mkmap_db_before_open(path, dict_hash_open));
+}
+
+/* mkmap_btree_open - create or open btree DB file */
+
+MKMAP *mkmap_btree_open(const char *path)
+{
+ return (mkmap_db_before_open(path, dict_btree_open));
+}
+
+#endif