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 65 typedef 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 73 static 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 84 static 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 98 static 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 162 MKMAP *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 169 MKMAP *mkmap_btree_open(const char *path) 170 { 171 return (mkmap_db_before_open(path, dict_btree_open)); 172 } 173 174 #endif 175