1/* $NetBSD: mkmap_db.c,v 1.2 2023/12/23 20:30:46 christos Exp $ */ 2 3/*++ 4/* NAME 5/* mkmap_db 3 6/* SUMMARY 7/* create or open database, DB style 8/* SYNOPSIS 9/* #include <dict_db.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/* Wietse Venema 37/* Google, Inc. 38/* 111 8th Avenue 39/* New York, NY 10011, USA 40/*--*/ 41 42/* System library. */ 43 44#include <sys_defs.h> 45#include <sys/stat.h> 46#include <unistd.h> 47#include <errno.h> 48 49/* Utility library. */ 50 51#include <msg.h> 52#include <mymalloc.h> 53#include <stringops.h> 54#include <dict_db.h> 55#include <myflock.h> 56#include <warn_stat.h> 57 58#ifdef HAS_DB 59#ifdef PATH_DB_H 60#include PATH_DB_H 61#else 62#include <db.h> 63#endif 64 65typedef struct MKMAP_DB { 66 MKMAP mkmap; /* parent class */ 67 char *lock_file; /* path name */ 68 int lock_fd; /* -1 or open locked file */ 69} MKMAP_DB; 70 71/* mkmap_db_after_close - clean up after closing database */ 72 73static void mkmap_db_after_close(MKMAP *mp) 74{ 75 MKMAP_DB *mkmap = (MKMAP_DB *) mp; 76 77 if (mkmap->lock_fd >= 0 && close(mkmap->lock_fd) < 0) 78 msg_warn("close %s: %m", mkmap->lock_file); 79 myfree(mkmap->lock_file); 80} 81 82/* mkmap_db_after_open - lock newly created database */ 83 84static void mkmap_db_after_open(MKMAP *mp) 85{ 86 MKMAP_DB *mkmap = (MKMAP_DB *) mp; 87 88 if (mkmap->lock_fd < 0) { 89 if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) 90 msg_fatal("open lockfile %s: %m", mkmap->lock_file); 91 if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) 92 msg_fatal("lock %s: %m", mkmap->lock_file); 93 } 94} 95 96/* mkmap_db_before_open - lock existing database */ 97 98static MKMAP *mkmap_db_before_open(const char *path, 99 DICT *(*db_open) (const char *, int, int)) 100{ 101 MKMAP_DB *mkmap = (MKMAP_DB *) mymalloc(sizeof(*mkmap)); 102 struct stat st; 103 104 /* 105 * Assumes that dict_db_cache_size = var_db_create_buf was done in the 106 * caller, because this code has no access to Postfix variables. 107 */ 108 109 /* 110 * Fill in the generic members. 111 */ 112 mkmap->lock_file = concatenate(path, ".db", (char *) 0); 113 mkmap->mkmap.open = db_open; 114 mkmap->mkmap.after_open = mkmap_db_after_open; 115 mkmap->mkmap.after_close = mkmap_db_after_close; 116 117 /* 118 * Unfortunately, not all systems that might support db databases do 119 * support locking on open(), so we open the file before updating it. 120 * 121 * XXX Berkeley DB 4.1 refuses to open a zero-length file. This means we can 122 * open and lock only an existing file, and that we must not truncate it. 123 */ 124 if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) { 125 if (errno != ENOENT) 126 msg_fatal("open %s: %m", mkmap->lock_file); 127 } 128 129 /* 130 * Get an exclusive lock - we're going to change the database so we can't 131 * have any spectators. 132 * 133 * XXX Horror. Berkeley DB 4.1 refuses to open a zero-length file. This 134 * means that we must examine the size while the file is locked, and that 135 * we must unlink a zero-length file while it is locked. Avoid a race 136 * condition where two processes try to open the same zero-length file 137 * and where the second process ends up deleting the wrong file. 138 */ 139 else { 140 if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) 141 msg_fatal("lock %s: %m", mkmap->lock_file); 142 if (fstat(mkmap->lock_fd, &st) < 0) 143 msg_fatal("fstat %s: %m", mkmap->lock_file); 144 if (st.st_size == 0) { 145 if (st.st_nlink > 0) { 146 if (unlink(mkmap->lock_file) < 0) 147 msg_fatal("cannot remove zero-length database file %s: %m", 148 mkmap->lock_file); 149 msg_warn("removing zero-length database file: %s", 150 mkmap->lock_file); 151 } 152 close(mkmap->lock_fd); 153 mkmap->lock_fd = -1; 154 } 155 } 156 157 return (&mkmap->mkmap); 158} 159 160/* mkmap_hash_open - create or open hashed DB file */ 161 162MKMAP *mkmap_hash_open(const char *path) 163{ 164 return (mkmap_db_before_open(path, dict_hash_open)); 165} 166 167/* mkmap_btree_open - create or open btree DB file */ 168 169MKMAP *mkmap_btree_open(const char *path) 170{ 171 return (mkmap_db_before_open(path, dict_btree_open)); 172} 173 174#endif 175