xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/mkmap_db.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
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