/* $NetBSD: mkmap_db.c,v 1.2 2020/03/18 19:05:16 christos Exp $ */
/*++
/* 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