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