1*c48c605cSchristos /* $NetBSD: mkmap_db.c,v 1.2 2023/12/23 20:30:46 christos Exp $ */
2059c16a8Schristos
3059c16a8Schristos /*++
4059c16a8Schristos /* NAME
5059c16a8Schristos /* mkmap_db 3
6059c16a8Schristos /* SUMMARY
7059c16a8Schristos /* create or open database, DB style
8059c16a8Schristos /* SYNOPSIS
9059c16a8Schristos /* #include <dict_db.h>
10059c16a8Schristos /*
11059c16a8Schristos /* MKMAP *mkmap_hash_open(path)
12059c16a8Schristos /* const char *path;
13059c16a8Schristos /*
14059c16a8Schristos /* MKMAP *mkmap_btree_open(path)
15059c16a8Schristos /* const char *path;
16059c16a8Schristos /* DESCRIPTION
17059c16a8Schristos /* This module implements support for creating DB databases.
18059c16a8Schristos /*
19059c16a8Schristos /* mkmap_hash_open() and mkmap_btree_open() take a file name,
20059c16a8Schristos /* append the ".db" suffix, and do whatever initialization is
21059c16a8Schristos /* required before the Berkeley DB open routine is called.
22059c16a8Schristos /*
23059c16a8Schristos /* All errors are fatal.
24059c16a8Schristos /* SEE ALSO
25059c16a8Schristos /* dict_db(3), DB dictionary interface.
26059c16a8Schristos /* LICENSE
27059c16a8Schristos /* .ad
28059c16a8Schristos /* .fi
29059c16a8Schristos /* The Secure Mailer license must be distributed with this software.
30059c16a8Schristos /* AUTHOR(S)
31059c16a8Schristos /* Wietse Venema
32059c16a8Schristos /* IBM T.J. Watson Research
33059c16a8Schristos /* P.O. Box 704
34059c16a8Schristos /* Yorktown Heights, NY 10598, USA
35059c16a8Schristos /*
36059c16a8Schristos /* Wietse Venema
37059c16a8Schristos /* Google, Inc.
38059c16a8Schristos /* 111 8th Avenue
39059c16a8Schristos /* New York, NY 10011, USA
40059c16a8Schristos /*--*/
41059c16a8Schristos
42059c16a8Schristos /* System library. */
43059c16a8Schristos
44059c16a8Schristos #include <sys_defs.h>
45059c16a8Schristos #include <sys/stat.h>
46059c16a8Schristos #include <unistd.h>
47059c16a8Schristos #include <errno.h>
48059c16a8Schristos
49059c16a8Schristos /* Utility library. */
50059c16a8Schristos
51059c16a8Schristos #include <msg.h>
52059c16a8Schristos #include <mymalloc.h>
53059c16a8Schristos #include <stringops.h>
54059c16a8Schristos #include <dict_db.h>
55059c16a8Schristos #include <myflock.h>
56059c16a8Schristos #include <warn_stat.h>
57059c16a8Schristos
58059c16a8Schristos #ifdef HAS_DB
59059c16a8Schristos #ifdef PATH_DB_H
60059c16a8Schristos #include PATH_DB_H
61059c16a8Schristos #else
62059c16a8Schristos #include <db.h>
63059c16a8Schristos #endif
64059c16a8Schristos
65059c16a8Schristos typedef struct MKMAP_DB {
66059c16a8Schristos MKMAP mkmap; /* parent class */
67059c16a8Schristos char *lock_file; /* path name */
68059c16a8Schristos int lock_fd; /* -1 or open locked file */
69059c16a8Schristos } MKMAP_DB;
70059c16a8Schristos
71059c16a8Schristos /* mkmap_db_after_close - clean up after closing database */
72059c16a8Schristos
mkmap_db_after_close(MKMAP * mp)73059c16a8Schristos static void mkmap_db_after_close(MKMAP *mp)
74059c16a8Schristos {
75059c16a8Schristos MKMAP_DB *mkmap = (MKMAP_DB *) mp;
76059c16a8Schristos
77059c16a8Schristos if (mkmap->lock_fd >= 0 && close(mkmap->lock_fd) < 0)
78059c16a8Schristos msg_warn("close %s: %m", mkmap->lock_file);
79059c16a8Schristos myfree(mkmap->lock_file);
80059c16a8Schristos }
81059c16a8Schristos
82059c16a8Schristos /* mkmap_db_after_open - lock newly created database */
83059c16a8Schristos
mkmap_db_after_open(MKMAP * mp)84059c16a8Schristos static void mkmap_db_after_open(MKMAP *mp)
85059c16a8Schristos {
86059c16a8Schristos MKMAP_DB *mkmap = (MKMAP_DB *) mp;
87059c16a8Schristos
88059c16a8Schristos if (mkmap->lock_fd < 0) {
89059c16a8Schristos if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0)
90059c16a8Schristos msg_fatal("open lockfile %s: %m", mkmap->lock_file);
91059c16a8Schristos if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
92059c16a8Schristos msg_fatal("lock %s: %m", mkmap->lock_file);
93059c16a8Schristos }
94059c16a8Schristos }
95059c16a8Schristos
96059c16a8Schristos /* mkmap_db_before_open - lock existing database */
97059c16a8Schristos
mkmap_db_before_open(const char * path,DICT * (* db_open)(const char *,int,int))98059c16a8Schristos static MKMAP *mkmap_db_before_open(const char *path,
99059c16a8Schristos DICT *(*db_open) (const char *, int, int))
100059c16a8Schristos {
101059c16a8Schristos MKMAP_DB *mkmap = (MKMAP_DB *) mymalloc(sizeof(*mkmap));
102059c16a8Schristos struct stat st;
103059c16a8Schristos
104059c16a8Schristos /*
105059c16a8Schristos * Assumes that dict_db_cache_size = var_db_create_buf was done in the
106059c16a8Schristos * caller, because this code has no access to Postfix variables.
107059c16a8Schristos */
108059c16a8Schristos
109059c16a8Schristos /*
110059c16a8Schristos * Fill in the generic members.
111059c16a8Schristos */
112059c16a8Schristos mkmap->lock_file = concatenate(path, ".db", (char *) 0);
113059c16a8Schristos mkmap->mkmap.open = db_open;
114059c16a8Schristos mkmap->mkmap.after_open = mkmap_db_after_open;
115059c16a8Schristos mkmap->mkmap.after_close = mkmap_db_after_close;
116059c16a8Schristos
117059c16a8Schristos /*
118059c16a8Schristos * Unfortunately, not all systems that might support db databases do
119059c16a8Schristos * support locking on open(), so we open the file before updating it.
120059c16a8Schristos *
121059c16a8Schristos * XXX Berkeley DB 4.1 refuses to open a zero-length file. This means we can
122059c16a8Schristos * open and lock only an existing file, and that we must not truncate it.
123059c16a8Schristos */
124059c16a8Schristos if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) {
125059c16a8Schristos if (errno != ENOENT)
126059c16a8Schristos msg_fatal("open %s: %m", mkmap->lock_file);
127059c16a8Schristos }
128059c16a8Schristos
129059c16a8Schristos /*
130059c16a8Schristos * Get an exclusive lock - we're going to change the database so we can't
131059c16a8Schristos * have any spectators.
132059c16a8Schristos *
133059c16a8Schristos * XXX Horror. Berkeley DB 4.1 refuses to open a zero-length file. This
134059c16a8Schristos * means that we must examine the size while the file is locked, and that
135059c16a8Schristos * we must unlink a zero-length file while it is locked. Avoid a race
136059c16a8Schristos * condition where two processes try to open the same zero-length file
137059c16a8Schristos * and where the second process ends up deleting the wrong file.
138059c16a8Schristos */
139059c16a8Schristos else {
140059c16a8Schristos if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
141059c16a8Schristos msg_fatal("lock %s: %m", mkmap->lock_file);
142059c16a8Schristos if (fstat(mkmap->lock_fd, &st) < 0)
143059c16a8Schristos msg_fatal("fstat %s: %m", mkmap->lock_file);
144059c16a8Schristos if (st.st_size == 0) {
145059c16a8Schristos if (st.st_nlink > 0) {
146059c16a8Schristos if (unlink(mkmap->lock_file) < 0)
147059c16a8Schristos msg_fatal("cannot remove zero-length database file %s: %m",
148059c16a8Schristos mkmap->lock_file);
149059c16a8Schristos msg_warn("removing zero-length database file: %s",
150059c16a8Schristos mkmap->lock_file);
151059c16a8Schristos }
152059c16a8Schristos close(mkmap->lock_fd);
153059c16a8Schristos mkmap->lock_fd = -1;
154059c16a8Schristos }
155059c16a8Schristos }
156059c16a8Schristos
157059c16a8Schristos return (&mkmap->mkmap);
158059c16a8Schristos }
159059c16a8Schristos
160059c16a8Schristos /* mkmap_hash_open - create or open hashed DB file */
161059c16a8Schristos
mkmap_hash_open(const char * path)162059c16a8Schristos MKMAP *mkmap_hash_open(const char *path)
163059c16a8Schristos {
164059c16a8Schristos return (mkmap_db_before_open(path, dict_hash_open));
165059c16a8Schristos }
166059c16a8Schristos
167059c16a8Schristos /* mkmap_btree_open - create or open btree DB file */
168059c16a8Schristos
mkmap_btree_open(const char * path)169059c16a8Schristos MKMAP *mkmap_btree_open(const char *path)
170059c16a8Schristos {
171059c16a8Schristos return (mkmap_db_before_open(path, dict_btree_open));
172059c16a8Schristos }
173059c16a8Schristos
174059c16a8Schristos #endif
175