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
mkmap_db_after_close(MKMAP * mp)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
mkmap_db_after_open(MKMAP * mp)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
mkmap_db_before_open(const char * path,DICT * (* db_open)(const char *,int,int))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
mkmap_hash_open(const char * path)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
mkmap_btree_open(const char * path)169 MKMAP *mkmap_btree_open(const char *path)
170 {
171 return (mkmap_db_before_open(path, dict_btree_open));
172 }
173
174 #endif
175