1/* $NetBSD$ */ 2 3/*++ 4/* NAME 5/* mkmap_db 3 6/* SUMMARY 7/* create or open database, DB style 8/* SYNOPSIS 9/* #include <mkmap.h> 10/* 11/* MKMAP *mkmap_hash_open(path) 12/* const char *path; 13/* 14/* MKMAP *mkmap_btree_open(path) 15/* const char *path; 16/* DESCRIPTION 17/* This module implements support for creating DB databases. 18/* 19/* mkmap_hash_open() and mkmap_btree_open() take a file name, 20/* append the ".db" suffix, and do whatever initialization is 21/* required before the Berkeley DB open routine is called. 22/* 23/* All errors are fatal. 24/* SEE ALSO 25/* dict_db(3), DB dictionary interface. 26/* LICENSE 27/* .ad 28/* .fi 29/* The Secure Mailer license must be distributed with this software. 30/* AUTHOR(S) 31/* Wietse Venema 32/* IBM T.J. Watson Research 33/* P.O. Box 704 34/* Yorktown Heights, NY 10598, USA 35/*--*/ 36 37/* System library. */ 38 39#include <sys_defs.h> 40#include <sys/stat.h> 41#include <unistd.h> 42#include <errno.h> 43 44/* Utility library. */ 45 46#include <msg.h> 47#include <mymalloc.h> 48#include <stringops.h> 49#include <dict.h> 50#include <dict_db.h> 51#include <myflock.h> 52 53/* Global library. */ 54 55#include <mail_params.h> 56 57/* Application-specific. */ 58 59#include "mkmap.h" 60 61#ifdef HAS_DB 62#ifdef PATH_DB_H 63#include PATH_DB_H 64#else 65#include <db.h> 66#endif 67 68typedef struct MKMAP_DB { 69 MKMAP mkmap; /* parent class */ 70 char *lock_file; /* path name */ 71 int lock_fd; /* -1 or open locked file */ 72} MKMAP_DB; 73 74/* mkmap_db_after_close - clean up after closing database */ 75 76static void mkmap_db_after_close(MKMAP *mp) 77{ 78 MKMAP_DB *mkmap = (MKMAP_DB *) mp; 79 80 if (mkmap->lock_fd >= 0 && close(mkmap->lock_fd) < 0) 81 msg_warn("close %s: %m", mkmap->lock_file); 82 myfree(mkmap->lock_file); 83} 84 85/* mkmap_db_after_open - lock newly created database */ 86 87static void mkmap_db_after_open(MKMAP *mp) 88{ 89 MKMAP_DB *mkmap = (MKMAP_DB *) mp; 90 91 if (mkmap->lock_fd < 0) { 92 if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) 93 msg_fatal("open lockfile %s: %m", mkmap->lock_file); 94 if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) 95 msg_fatal("lock %s: %m", mkmap->lock_file); 96 } 97} 98 99/* mkmap_db_before_open - lock existing database */ 100 101static MKMAP *mkmap_db_before_open(const char *path, 102 DICT *(*db_open) (const char *, int, int)) 103{ 104 MKMAP_DB *mkmap = (MKMAP_DB *) mymalloc(sizeof(*mkmap)); 105 struct stat st; 106 107 /* 108 * Override the default per-table cache size for map (re)builds. 109 * 110 * db_cache_size" is defined in util/dict_db.c and defaults to 128kB, which 111 * works well for the lookup code. 112 * 113 * We use a larger per-table cache when building ".db" files. For "hash" 114 * files performance degrades rapidly unless the memory pool is O(file 115 * size). 116 * 117 * For "btree" files peformance is good with sorted input even for small 118 * memory pools, but with random input degrades rapidly unless the memory 119 * pool is O(file size). 120 * 121 * XXX This should be specified via the DICT interface so that the buffer 122 * size becomes an object property, instead of being specified by poking 123 * a global variable so that it becomes a class property. 124 */ 125 dict_db_cache_size = var_db_create_buf; 126 127 /* 128 * Fill in the generic members. 129 */ 130 mkmap->lock_file = concatenate(path, ".db", (char *) 0); 131 mkmap->mkmap.open = db_open; 132 mkmap->mkmap.after_open = mkmap_db_after_open; 133 mkmap->mkmap.after_close = mkmap_db_after_close; 134 135 /* 136 * Unfortunately, not all systems that might support db databases do 137 * support locking on open(), so we open the file before updating it. 138 * 139 * XXX Berkeley DB 4.1 refuses to open a zero-length file. This means we can 140 * open and lock only an existing file, and that we must not truncate it. 141 */ 142 if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) { 143 if (errno != ENOENT) 144 msg_fatal("open %s: %m", mkmap->lock_file); 145 } 146 147 /* 148 * Get an exclusive lock - we're going to change the database so we can't 149 * have any spectators. 150 * 151 * XXX Horror. Berkeley DB 4.1 refuses to open a zero-length file. This 152 * means that we must examine the size while the file is locked, and that 153 * we must unlink a zero-length file while it is locked. Avoid a race 154 * condition where two processes try to open the same zero-length file 155 * and where the second process ends up deleting the wrong file. 156 */ 157 else { 158 if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) 159 msg_fatal("lock %s: %m", mkmap->lock_file); 160 if (fstat(mkmap->lock_fd, &st) < 0) 161 msg_fatal("fstat %s: %m", mkmap->lock_file); 162 if (st.st_size == 0) { 163 if (st.st_nlink > 0) { 164 if (unlink(mkmap->lock_file) < 0) 165 msg_fatal("cannot remove zero-length database file %s: %m", 166 mkmap->lock_file); 167 msg_warn("removing zero-length database file: %s", 168 mkmap->lock_file); 169 } 170 close(mkmap->lock_fd); 171 mkmap->lock_fd = -1; 172 } 173 } 174 175 return (&mkmap->mkmap); 176} 177 178/* mkmap_hash_open - create or open hashed DB file */ 179 180MKMAP *mkmap_hash_open(const char *path) 181{ 182 return (mkmap_db_before_open(path, dict_hash_open)); 183} 184 185/* mkmap_btree_open - create or open btree DB file */ 186 187MKMAP *mkmap_btree_open(const char *path) 188{ 189 return (mkmap_db_before_open(path, dict_btree_open)); 190} 191 192#endif 193