summaryrefslogtreecommitdiffstats
path: root/src/util/mkmap_db.c
blob: b2d830f807e5268d1f287f99cfead91f250f52be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*++
/* NAME
/*	mkmap_db 3
/* SUMMARY
/*	create or open database, DB style
/* SYNOPSIS
/*	#include <dict_db.h>
/*
/*	MKMAP	*mkmap_hash_open(path)
/*	const char *path;
/*
/*	MKMAP	*mkmap_btree_open(path)
/*	const char *path;
/* DESCRIPTION
/*	This module implements support for creating DB databases.
/*
/*	mkmap_hash_open() and mkmap_btree_open() take a file name,
/*	append the ".db" suffix, and do whatever initialization is
/*	required before the Berkeley DB open routine is called.
/*
/*	All errors are fatal.
/* SEE ALSO
/*	dict_db(3), DB dictionary interface.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <stringops.h>
#include <dict_db.h>
#include <myflock.h>
#include <warn_stat.h>

#ifdef HAS_DB
#ifdef PATH_DB_H
#include PATH_DB_H
#else
#include <db.h>
#endif

typedef struct MKMAP_DB {
    MKMAP   mkmap;			/* parent class */
    char   *lock_file;			/* path name */
    int     lock_fd;			/* -1 or open locked file */
} MKMAP_DB;

/* mkmap_db_after_close - clean up after closing database */

static void mkmap_db_after_close(MKMAP *mp)
{
    MKMAP_DB *mkmap = (MKMAP_DB *) mp;

    if (mkmap->lock_fd >= 0 && close(mkmap->lock_fd) < 0)
	msg_warn("close %s: %m", mkmap->lock_file);
    myfree(mkmap->lock_file);
}

/* mkmap_db_after_open - lock newly created database */

static void mkmap_db_after_open(MKMAP *mp)
{
    MKMAP_DB *mkmap = (MKMAP_DB *) mp;

    if (mkmap->lock_fd < 0) {
	if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0)
	    msg_fatal("open lockfile %s: %m", mkmap->lock_file);
	if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("lock %s: %m", mkmap->lock_file);
    }
}

/* mkmap_db_before_open - lock existing database */

static MKMAP *mkmap_db_before_open(const char *path,
			          DICT *(*db_open) (const char *, int, int))
{
    MKMAP_DB *mkmap = (MKMAP_DB *) mymalloc(sizeof(*mkmap));
    struct stat st;

    /*
     * Assumes that  dict_db_cache_size = var_db_create_buf was done in the
     * caller, because this code has no access to Postfix variables.
     */

    /*
     * Fill in the generic members.
     */
    mkmap->lock_file = concatenate(path, ".db", (char *) 0);
    mkmap->mkmap.open = db_open;
    mkmap->mkmap.after_open = mkmap_db_after_open;
    mkmap->mkmap.after_close = mkmap_db_after_close;

    /*
     * Unfortunately, not all systems that might support db databases do
     * support locking on open(), so we open the file before updating it.
     * 
     * XXX Berkeley DB 4.1 refuses to open a zero-length file. This means we can
     * open and lock only an existing file, and that we must not truncate it.
     */
    if ((mkmap->lock_fd = open(mkmap->lock_file, O_RDWR, 0644)) < 0) {
	if (errno != ENOENT)
	    msg_fatal("open %s: %m", mkmap->lock_file);
    }

    /*
     * Get an exclusive lock - we're going to change the database so we can't
     * have any spectators.
     * 
     * XXX Horror. Berkeley DB 4.1 refuses to open a zero-length file. This
     * means that we must examine the size while the file is locked, and that
     * we must unlink a zero-length file while it is locked. Avoid a race
     * condition where two processes try to open the same zero-length file
     * and where the second process ends up deleting the wrong file.
     */
    else {
	if (myflock(mkmap->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("lock %s: %m", mkmap->lock_file);
	if (fstat(mkmap->lock_fd, &st) < 0)
	    msg_fatal("fstat %s: %m", mkmap->lock_file);
	if (st.st_size == 0) {
	    if (st.st_nlink > 0) {
		if (unlink(mkmap->lock_file) < 0)
		    msg_fatal("cannot remove zero-length database file %s: %m",
			      mkmap->lock_file);
		msg_warn("removing zero-length database file: %s",
			 mkmap->lock_file);
	    }
	    close(mkmap->lock_fd);
	    mkmap->lock_fd = -1;
	}
    }

    return (&mkmap->mkmap);
}

/* mkmap_hash_open - create or open hashed DB file */

MKMAP  *mkmap_hash_open(const char *path)
{
    return (mkmap_db_before_open(path, dict_hash_open));
}

/* mkmap_btree_open - create or open btree DB file */

MKMAP  *mkmap_btree_open(const char *path)
{
    return (mkmap_db_before_open(path, dict_btree_open));
}

#endif