Postfix3.3.1
dict_dbm.c
[詳解]
1 /*++
2 /* NAME
3 /* dict_dbm 3
4 /* SUMMARY
5 /* dictionary manager interface to DBM files
6 /* SYNOPSIS
7 /* #include <dict_dbm.h>
8 /*
9 /* DICT *dict_dbm_open(path, open_flags, dict_flags)
10 /* const char *name;
11 /* const char *path;
12 /* int open_flags;
13 /* int dict_flags;
14 /* DESCRIPTION
15 /* dict_dbm_open() opens the named DBM database and makes it available
16 /* via the generic interface described in dict_open(3).
17 /* DIAGNOSTICS
18 /* Fatal errors: cannot open file, file write error, out of memory.
19 /* SEE ALSO
20 /* dict(3) generic dictionary manager
21 /* ndbm(3) data base subroutines
22 /* LICENSE
23 /* .ad
24 /* .fi
25 /* The Secure Mailer license must be distributed with this software.
26 /* AUTHOR(S)
27 /* Wietse Venema
28 /* IBM T.J. Watson Research
29 /* P.O. Box 704
30 /* Yorktown Heights, NY 10598, USA
31 /*--*/
32 
33 #include "sys_defs.h"
34 
35 #ifdef HAS_DBM
36 
37 /* System library. */
38 
39 #include <sys/stat.h>
40 #ifdef PATH_NDBM_H
41 #include PATH_NDBM_H
42 #else
43 #include <ndbm.h>
44 #endif
45 #ifdef R_FIRST
46 #error "Error: you are including the Berkeley DB version of ndbm.h"
47 #error "To build with Postfix NDBM support, delete the Berkeley DB ndbm.h file"
48 #endif
49 #include <string.h>
50 #include <unistd.h>
51 
52 /* Utility library. */
53 
54 #include "msg.h"
55 #include "mymalloc.h"
56 #include "htable.h"
57 #include "iostuff.h"
58 #include "vstring.h"
59 #include "myflock.h"
60 #include "stringops.h"
61 #include "dict.h"
62 #include "dict_dbm.h"
63 #include "warn_stat.h"
64 
65 /* Application-specific. */
66 
67 typedef struct {
68  DICT dict; /* generic members */
69  DBM *dbm; /* open database */
70  VSTRING *key_buf; /* key buffer */
71  VSTRING *val_buf; /* result buffer */
72 } DICT_DBM;
73 
74 #define SCOPY(buf, data, size) \
75  vstring_str(vstring_strncpy(buf ? buf : (buf = vstring_alloc(10)), data, size))
76 
77 /* dict_dbm_lookup - find database entry */
78 
79 static const char *dict_dbm_lookup(DICT *dict, const char *name)
80 {
81  DICT_DBM *dict_dbm = (DICT_DBM *) dict;
82  datum dbm_key;
83  datum dbm_value;
84  const char *result = 0;
85 
86  dict->error = 0;
87 
88  /*
89  * Sanity check.
90  */
91  if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
92  msg_panic("dict_dbm_lookup: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
93 
94  /*
95  * Optionally fold the key.
96  */
97  if (dict->flags & DICT_FLAG_FOLD_FIX) {
98  if (dict->fold_buf == 0)
99  dict->fold_buf = vstring_alloc(10);
100  vstring_strcpy(dict->fold_buf, name);
101  name = lowercase(vstring_str(dict->fold_buf));
102  }
103 
104  /*
105  * Acquire an exclusive lock.
106  */
107  if ((dict->flags & DICT_FLAG_LOCK)
108  && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
109  msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
110 
111  /*
112  * See if this DBM file was written with one null byte appended to key
113  * and value.
114  */
115  if (dict->flags & DICT_FLAG_TRY1NULL) {
116  dbm_key.dptr = (void *) name;
117  dbm_key.dsize = strlen(name) + 1;
118  dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
119  if (dbm_value.dptr != 0) {
120  dict->flags &= ~DICT_FLAG_TRY0NULL;
121  result = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
122  }
123  }
124 
125  /*
126  * See if this DBM file was written with no null byte appended to key and
127  * value.
128  */
129  if (result == 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
130  dbm_key.dptr = (void *) name;
131  dbm_key.dsize = strlen(name);
132  dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
133  if (dbm_value.dptr != 0) {
134  dict->flags &= ~DICT_FLAG_TRY1NULL;
135  result = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
136  }
137  }
138 
139  /*
140  * Release the exclusive lock.
141  */
142  if ((dict->flags & DICT_FLAG_LOCK)
143  && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
144  msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
145 
146  return (result);
147 }
148 
149 /* dict_dbm_update - add or update database entry */
150 
151 static int dict_dbm_update(DICT *dict, const char *name, const char *value)
152 {
153  DICT_DBM *dict_dbm = (DICT_DBM *) dict;
154  datum dbm_key;
155  datum dbm_value;
156  int status;
157 
158  dict->error = 0;
159 
160  /*
161  * Sanity check.
162  */
163  if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
164  msg_panic("dict_dbm_update: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
165 
166  /*
167  * Optionally fold the key.
168  */
169  if (dict->flags & DICT_FLAG_FOLD_FIX) {
170  if (dict->fold_buf == 0)
171  dict->fold_buf = vstring_alloc(10);
172  vstring_strcpy(dict->fold_buf, name);
173  name = lowercase(vstring_str(dict->fold_buf));
174  }
175  dbm_key.dptr = (void *) name;
176  dbm_value.dptr = (void *) value;
177  dbm_key.dsize = strlen(name);
178  dbm_value.dsize = strlen(value);
179 
180  /*
181  * If undecided about appending a null byte to key and value, choose a
182  * default depending on the platform.
183  */
184  if ((dict->flags & DICT_FLAG_TRY1NULL)
185  && (dict->flags & DICT_FLAG_TRY0NULL)) {
186 #ifdef DBM_NO_TRAILING_NULL
187  dict->flags &= ~DICT_FLAG_TRY1NULL;
188 #else
189  dict->flags &= ~DICT_FLAG_TRY0NULL;
190 #endif
191  }
192 
193  /*
194  * Optionally append a null byte to key and value.
195  */
196  if (dict->flags & DICT_FLAG_TRY1NULL) {
197  dbm_key.dsize++;
198  dbm_value.dsize++;
199  }
200 
201  /*
202  * Acquire an exclusive lock.
203  */
204  if ((dict->flags & DICT_FLAG_LOCK)
205  && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
206  msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
207 
208  /*
209  * Do the update.
210  */
211  if ((status = dbm_store(dict_dbm->dbm, dbm_key, dbm_value,
212  (dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0)
213  msg_fatal("error writing DBM database %s: %m", dict_dbm->dict.name);
214  if (status) {
215  if (dict->flags & DICT_FLAG_DUP_IGNORE)
216  /* void */ ;
217  else if (dict->flags & DICT_FLAG_DUP_WARN)
218  msg_warn("%s: duplicate entry: \"%s\"", dict_dbm->dict.name, name);
219  else
220  msg_fatal("%s: duplicate entry: \"%s\"", dict_dbm->dict.name, name);
221  }
222 
223  /*
224  * Release the exclusive lock.
225  */
226  if ((dict->flags & DICT_FLAG_LOCK)
227  && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
228  msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
229 
230  return (status);
231 }
232 
233 /* dict_dbm_delete - delete one entry from the dictionary */
234 
235 static int dict_dbm_delete(DICT *dict, const char *name)
236 {
237  DICT_DBM *dict_dbm = (DICT_DBM *) dict;
238  datum dbm_key;
239  int status = 1;
240 
241  dict->error = 0;
242 
243  /*
244  * Sanity check.
245  */
246  if ((dict->flags & (DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL)) == 0)
247  msg_panic("dict_dbm_delete: no DICT_FLAG_TRY1NULL | DICT_FLAG_TRY0NULL flag");
248 
249  /*
250  * Optionally fold the key.
251  */
252  if (dict->flags & DICT_FLAG_FOLD_FIX) {
253  if (dict->fold_buf == 0)
254  dict->fold_buf = vstring_alloc(10);
255  vstring_strcpy(dict->fold_buf, name);
256  name = lowercase(vstring_str(dict->fold_buf));
257  }
258 
259  /*
260  * Acquire an exclusive lock.
261  */
262  if ((dict->flags & DICT_FLAG_LOCK)
263  && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0)
264  msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
265 
266  /*
267  * See if this DBM file was written with one null byte appended to key
268  * and value.
269  */
270  if (dict->flags & DICT_FLAG_TRY1NULL) {
271  dbm_key.dptr = (void *) name;
272  dbm_key.dsize = strlen(name) + 1;
273  dbm_clearerr(dict_dbm->dbm);
274  if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) {
275  if (dbm_error(dict_dbm->dbm) != 0) /* fatal error */
276  msg_fatal("error deleting from %s: %m", dict_dbm->dict.name);
277  status = 1; /* not found */
278  } else {
279  dict->flags &= ~DICT_FLAG_TRY0NULL; /* found */
280  }
281  }
282 
283  /*
284  * See if this DBM file was written with no null byte appended to key and
285  * value.
286  */
287  if (status > 0 && (dict->flags & DICT_FLAG_TRY0NULL)) {
288  dbm_key.dptr = (void *) name;
289  dbm_key.dsize = strlen(name);
290  dbm_clearerr(dict_dbm->dbm);
291  if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) {
292  if (dbm_error(dict_dbm->dbm) != 0) /* fatal error */
293  msg_fatal("error deleting from %s: %m", dict_dbm->dict.name);
294  status = 1; /* not found */
295  } else {
296  dict->flags &= ~DICT_FLAG_TRY1NULL; /* found */
297  }
298  }
299 
300  /*
301  * Release the exclusive lock.
302  */
303  if ((dict->flags & DICT_FLAG_LOCK)
304  && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
305  msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
306 
307  return (status);
308 }
309 
310 /* traverse the dictionary */
311 
312 static int dict_dbm_sequence(DICT *dict, int function,
313  const char **key, const char **value)
314 {
315  const char *myname = "dict_dbm_sequence";
316  DICT_DBM *dict_dbm = (DICT_DBM *) dict;
317  datum dbm_key;
318  datum dbm_value;
319  int status;
320 
321  dict->error = 0;
322 
323  /*
324  * Acquire a shared lock.
325  */
326  if ((dict->flags & DICT_FLAG_LOCK)
327  && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
328  msg_fatal("%s: lock dictionary: %m", dict_dbm->dict.name);
329 
330  /*
331  * Determine and execute the seek function. It returns the key.
332  */
333  switch (function) {
334  case DICT_SEQ_FUN_FIRST:
335  dbm_key = dbm_firstkey(dict_dbm->dbm);
336  break;
337  case DICT_SEQ_FUN_NEXT:
338  dbm_key = dbm_nextkey(dict_dbm->dbm);
339  break;
340  default:
341  msg_panic("%s: invalid function: %d", myname, function);
342  }
343 
344  if (dbm_key.dptr != 0 && dbm_key.dsize > 0) {
345 
346  /*
347  * Copy the key so that it is guaranteed null terminated.
348  */
349  *key = SCOPY(dict_dbm->key_buf, dbm_key.dptr, dbm_key.dsize);
350 
351  /*
352  * Fetch the corresponding value.
353  */
354  dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key);
355 
356  if (dbm_value.dptr != 0 && dbm_value.dsize > 0) {
357 
358  /*
359  * Copy the value so that it is guaranteed null terminated.
360  */
361  *value = SCOPY(dict_dbm->val_buf, dbm_value.dptr, dbm_value.dsize);
362  status = 0;
363  } else {
364 
365  /*
366  * Determine if we have hit the last record or an error
367  * condition.
368  */
369  if (dbm_error(dict_dbm->dbm))
370  msg_fatal("error seeking %s: %m", dict_dbm->dict.name);
371  status = 1; /* no error: eof/not found
372  * (should not happen!) */
373  }
374  } else {
375 
376  /*
377  * Determine if we have hit the last record or an error condition.
378  */
379  if (dbm_error(dict_dbm->dbm))
380  msg_fatal("error seeking %s: %m", dict_dbm->dict.name);
381  status = 1; /* no error: eof/not found */
382  }
383 
384  /*
385  * Release the shared lock.
386  */
387  if ((dict->flags & DICT_FLAG_LOCK)
388  && myflock(dict->lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
389  msg_fatal("%s: unlock dictionary: %m", dict_dbm->dict.name);
390 
391  return (status);
392 }
393 
394 /* dict_dbm_close - disassociate from data base */
395 
396 static void dict_dbm_close(DICT *dict)
397 {
398  DICT_DBM *dict_dbm = (DICT_DBM *) dict;
399 
400  dbm_close(dict_dbm->dbm);
401  if (dict_dbm->key_buf)
402  vstring_free(dict_dbm->key_buf);
403  if (dict_dbm->val_buf)
404  vstring_free(dict_dbm->val_buf);
405  if (dict->fold_buf)
406  vstring_free(dict->fold_buf);
407  dict_free(dict);
408 }
409 
410 /* dict_dbm_open - open DBM data base */
411 
412 DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags)
413 {
414  DICT_DBM *dict_dbm;
415  struct stat st;
416  DBM *dbm;
417  char *dbm_path = 0;
418  int lock_fd;
419 
420  /*
421  * Let the optimizer worry about eliminating redundant code.
422  */
423 #define DICT_DBM_OPEN_RETURN(d) do { \
424  DICT *__d = (d); \
425  if (dbm_path != 0) \
426  myfree(dbm_path); \
427  return (__d); \
428  } while (0)
429 
430  /*
431  * Note: DICT_FLAG_LOCK is used only by programs that do fine-grained (in
432  * the time domain) locking while accessing individual database records.
433  *
434  * Programs such as postmap/postalias use their own large-grained (in the
435  * time domain) locks while rewriting the entire file.
436  */
437  if (dict_flags & DICT_FLAG_LOCK) {
438  dbm_path = concatenate(path, ".dir", (char *) 0);
439  if ((lock_fd = open(dbm_path, open_flags, 0644)) < 0)
440  DICT_DBM_OPEN_RETURN(dict_surrogate(DICT_TYPE_DBM, path,
441  open_flags, dict_flags,
442  "open database %s: %m",
443  dbm_path));
444  if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_SHARED) < 0)
445  msg_fatal("shared-lock database %s for open: %m", dbm_path);
446  }
447 
448  /*
449  * XXX SunOS 5.x has no const in dbm_open() prototype.
450  */
451  if ((dbm = dbm_open((char *) path, open_flags, 0644)) == 0)
452  DICT_DBM_OPEN_RETURN(dict_surrogate(DICT_TYPE_DBM, path,
453  open_flags, dict_flags,
454  "open database %s.{dir,pag}: %m",
455  path));
456 
457  if (dict_flags & DICT_FLAG_LOCK) {
458  if (myflock(lock_fd, INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0)
459  msg_fatal("unlock database %s for open: %m", dbm_path);
460  if (close(lock_fd) < 0)
461  msg_fatal("close database %s: %m", dbm_path);
462  }
463  dict_dbm = (DICT_DBM *) dict_alloc(DICT_TYPE_DBM, path, sizeof(*dict_dbm));
464  dict_dbm->dict.lookup = dict_dbm_lookup;
465  dict_dbm->dict.update = dict_dbm_update;
466  dict_dbm->dict.delete = dict_dbm_delete;
467  dict_dbm->dict.sequence = dict_dbm_sequence;
468  dict_dbm->dict.close = dict_dbm_close;
469  dict_dbm->dict.lock_fd = dbm_dirfno(dbm);
470  dict_dbm->dict.stat_fd = dbm_pagfno(dbm);
471  if (dict_dbm->dict.lock_fd == dict_dbm->dict.stat_fd)
472  msg_fatal("open database %s: cannot support GDBM", path);
473  if (fstat(dict_dbm->dict.stat_fd, &st) < 0)
474  msg_fatal("dict_dbm_open: fstat: %m");
475  dict_dbm->dict.mtime = st.st_mtime;
476  dict_dbm->dict.owner.uid = st.st_uid;
477  dict_dbm->dict.owner.status = (st.st_uid != 0);
478 
479  /*
480  * Warn if the source file is newer than the indexed file, except when
481  * the source file changed only seconds ago.
482  */
483  if ((dict_flags & DICT_FLAG_LOCK) != 0
484  && stat(path, &st) == 0
485  && st.st_mtime > dict_dbm->dict.mtime
486  && st.st_mtime < time((time_t *) 0) - 100)
487  msg_warn("database %s is older than source file %s", dbm_path, path);
488 
489  close_on_exec(dbm_pagfno(dbm), CLOSE_ON_EXEC);
490  close_on_exec(dbm_dirfno(dbm), CLOSE_ON_EXEC);
491  dict_dbm->dict.flags = dict_flags | DICT_FLAG_FIXED;
492  if ((dict_flags & (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL)) == 0)
493  dict_dbm->dict.flags |= (DICT_FLAG_TRY0NULL | DICT_FLAG_TRY1NULL);
494  if (dict_flags & DICT_FLAG_FOLD_FIX)
495  dict_dbm->dict.fold_buf = vstring_alloc(10);
496  dict_dbm->dbm = dbm;
497  dict_dbm->key_buf = 0;
498  dict_dbm->val_buf = 0;
499 
500  DICT_DBM_OPEN_RETURN(DICT_DEBUG (&dict_dbm->dict));
501 }
502 
503 #endif
#define DICT_FLAG_DUP_IGNORE
Definition: dict.h:111
#define MYFLOCK_OP_SHARED
Definition: myflock.h:29
#define DICT_SEQ_FUN_FIRST
Definition: dict.h:200
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
#define stat(p, s)
Definition: warn_stat.h:18
#define DICT_FLAG_FIXED
Definition: dict.h:114
#define DICT_SEQ_FUN_NEXT
Definition: dict.h:201
int flags
Definition: dict.h:81
#define MYFLOCK_OP_EXCLUSIVE
Definition: myflock.h:30
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
Definition: dict.h:78
#define DICT_FLAG_DUP_REPLACE
Definition: dict.h:117
#define DICT_FLAG_TRY1NULL
Definition: dict.h:113
#define DICT_FLAG_LOCK
Definition: dict.h:116
int lock_fd
Definition: dict.h:89
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int myflock(int fd, int lock_style, int operation)
Definition: myflock.c:87
int error
Definition: dict.h:94
char * lowercase(char *string)
Definition: lowercase.c:34
const char *(* lookup)(struct DICT *, const char *)
Definition: dict.h:82
#define DICT_FLAG_DUP_WARN
Definition: dict.h:110
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
#define MYFLOCK_OP_NONE
Definition: myflock.h:28
void dict_free(DICT *)
Definition: dict_alloc.c:163
char * concatenate(const char *arg0,...)
Definition: concatenate.c:42
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define DICT_TYPE_DBM
Definition: dict_dbm.h:22
DICT * dict_dbm_open(const char *, int, int)
#define CLOSE_ON_EXEC
Definition: iostuff.h:51
DICT * dict_alloc(const char *, const char *, ssize_t)
Definition: dict_alloc.c:135
VSTRING * fold_buf
Definition: dict.h:92
#define DICT_FLAG_TRY0NULL
Definition: dict.h:112
int close_on_exec(int fd, int on)
Definition: close_on_exec.c:49
DICT * dict_surrogate(const char *dict_type, const char *dict_name, int open_flags, int dict_flags, const char *fmt,...)
#define fstat(f, s)
Definition: warn_stat.h:20