1 /*
2 ** Copyright (c) 1999-2002 Proofpoint, Inc. and its suppliers.
3 ** All rights reserved.
4 **
5 ** By using this file, you agree to the terms and conditions set
6 ** forth in the LICENSE file which can be found at the top level of
7 ** the sendmail distribution.
8 */
9
10 #include <sm/gen.h>
11 SM_RCSID("@(#)$Id: smndbm.c,v 8.55 2013-11-22 20:51:49 ca Exp $")
12
13 #include <fcntl.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16
17 #include <sendmail/sendmail.h>
18 #include <libsmdb/smdb.h>
19
20 #if NDBM
21
22 # define SMNDB_PAG_FILE_EXTENSION "pag"
23
24 struct smdb_dbm_database_struct
25 {
26 DBM *smndbm_dbm;
27 int smndbm_lock_fd;
28 bool smndbm_cursor_in_use;
29 };
30 typedef struct smdb_dbm_database_struct SMDB_DBM_DATABASE;
31
32 struct smdb_dbm_cursor_struct
33 {
34 SMDB_DBM_DATABASE *smndbmc_db;
35 datum smndbmc_current_key;
36 };
37 typedef struct smdb_dbm_cursor_struct SMDB_DBM_CURSOR;
38
39 /*
40 ** SMDB_PUT_FLAGS_TO_NDBM_FLAGS -- Translates smdb put flags to ndbm put flags.
41 **
42 ** Parameters:
43 ** flags -- The flags to translate.
44 **
45 ** Returns:
46 ** The ndbm flags that are equivalent to the smdb flags.
47 **
48 ** Notes:
49 ** Any invalid flags are ignored.
50 **
51 */
52
53 int
smdb_put_flags_to_ndbm_flags(flags)54 smdb_put_flags_to_ndbm_flags(flags)
55 SMDB_FLAG flags;
56 {
57 int return_flags;
58
59 return_flags = 0;
60 if (bitset(SMDBF_NO_OVERWRITE, flags))
61 return_flags = DBM_INSERT;
62 else
63 return_flags = DBM_REPLACE;
64
65 return return_flags;
66 }
67
68 /*
69 ** Except for smdb_ndbm_open, the rest of these function correspond to the
70 ** interface laid out in smdb.h.
71 */
72
73 SMDB_DBM_DATABASE *
smdbm_malloc_database()74 smdbm_malloc_database()
75 {
76 SMDB_DBM_DATABASE *db;
77
78 db = (SMDB_DBM_DATABASE *) malloc(sizeof(SMDB_DBM_DATABASE));
79 if (db != NULL)
80 {
81 db->smndbm_dbm = NULL;
82 db->smndbm_lock_fd = -1;
83 db->smndbm_cursor_in_use = false;
84 }
85
86 return db;
87 }
88
89 int
smdbm_close(database)90 smdbm_close(database)
91 SMDB_DATABASE *database;
92 {
93 SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
94 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
95
96 dbm_close(dbm);
97 if (db->smndbm_lock_fd != -1)
98 close(db->smndbm_lock_fd);
99
100 free(db);
101 database->smdb_impl = NULL;
102
103 return SMDBE_OK;
104 }
105
106 int
smdbm_del(database,key,flags)107 smdbm_del(database, key, flags)
108 SMDB_DATABASE *database;
109 SMDB_DBENT *key;
110 unsigned int flags;
111 {
112 int result;
113 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
114 datum dbkey;
115
116 (void) memset(&dbkey, '\0', sizeof dbkey);
117 dbkey.dptr = key->data;
118 dbkey.dsize = key->size;
119
120 errno = 0;
121 result = dbm_delete(dbm, dbkey);
122 if (result != 0)
123 {
124 int save_errno = errno;
125
126 if (dbm_error(dbm))
127 return SMDBE_IO_ERROR;
128
129 if (save_errno != 0)
130 return save_errno;
131
132 return SMDBE_NOT_FOUND;
133 }
134 return SMDBE_OK;
135 }
136
137 int
smdbm_fd(database,fd)138 smdbm_fd(database, fd)
139 SMDB_DATABASE *database;
140 int *fd;
141 {
142 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
143
144 *fd = dbm_dirfno(dbm);
145 if (*fd <= 0)
146 return EINVAL;
147
148 return SMDBE_OK;
149 }
150
151 int
smdbm_lockfd(database)152 smdbm_lockfd(database)
153 SMDB_DATABASE *database;
154 {
155 SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
156
157 return db->smndbm_lock_fd;
158 }
159
160 int
smdbm_get(database,key,data,flags)161 smdbm_get(database, key, data, flags)
162 SMDB_DATABASE *database;
163 SMDB_DBENT *key;
164 SMDB_DBENT *data;
165 unsigned int flags;
166 {
167 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
168 datum dbkey, dbdata;
169
170 (void) memset(&dbkey, '\0', sizeof dbkey);
171 (void) memset(&dbdata, '\0', sizeof dbdata);
172 dbkey.dptr = key->data;
173 dbkey.dsize = key->size;
174
175 errno = 0;
176 dbdata = dbm_fetch(dbm, dbkey);
177 if (dbdata.dptr == NULL)
178 {
179 int save_errno = errno;
180
181 if (dbm_error(dbm))
182 return SMDBE_IO_ERROR;
183
184 if (save_errno != 0)
185 return save_errno;
186
187 return SMDBE_NOT_FOUND;
188 }
189 data->data = dbdata.dptr;
190 data->size = dbdata.dsize;
191 return SMDBE_OK;
192 }
193
194 int
smdbm_put(database,key,data,flags)195 smdbm_put(database, key, data, flags)
196 SMDB_DATABASE *database;
197 SMDB_DBENT *key;
198 SMDB_DBENT *data;
199 unsigned int flags;
200 {
201 int result;
202 int save_errno;
203 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
204 datum dbkey, dbdata;
205
206 (void) memset(&dbkey, '\0', sizeof dbkey);
207 (void) memset(&dbdata, '\0', sizeof dbdata);
208 dbkey.dptr = key->data;
209 dbkey.dsize = key->size;
210 dbdata.dptr = data->data;
211 dbdata.dsize = data->size;
212
213 errno = 0;
214 result = dbm_store(dbm, dbkey, dbdata,
215 smdb_put_flags_to_ndbm_flags(flags));
216 switch (result)
217 {
218 case 1:
219 return SMDBE_DUPLICATE;
220
221 case 0:
222 return SMDBE_OK;
223
224 default:
225 save_errno = errno;
226
227 if (dbm_error(dbm))
228 return SMDBE_IO_ERROR;
229
230 if (save_errno != 0)
231 return save_errno;
232
233 return SMDBE_IO_ERROR;
234 }
235 /* NOTREACHED */
236 }
237
238 int
smndbm_set_owner(database,uid,gid)239 smndbm_set_owner(database, uid, gid)
240 SMDB_DATABASE *database;
241 uid_t uid;
242 gid_t gid;
243 {
244 # if HASFCHOWN
245 int fd;
246 int result;
247 DBM *dbm = ((SMDB_DBM_DATABASE *) database->smdb_impl)->smndbm_dbm;
248
249 fd = dbm_dirfno(dbm);
250 if (fd <= 0)
251 return EINVAL;
252
253 result = fchown(fd, uid, gid);
254 if (result < 0)
255 return errno;
256
257 fd = dbm_pagfno(dbm);
258 if (fd <= 0)
259 return EINVAL;
260
261 result = fchown(fd, uid, gid);
262 if (result < 0)
263 return errno;
264 # endif /* HASFCHOWN */
265
266 return SMDBE_OK;
267 }
268
269 int
smdbm_sync(database,flags)270 smdbm_sync(database, flags)
271 SMDB_DATABASE *database;
272 unsigned int flags;
273 {
274 return SMDBE_UNSUPPORTED;
275 }
276
277 int
smdbm_cursor_close(cursor)278 smdbm_cursor_close(cursor)
279 SMDB_CURSOR *cursor;
280 {
281 SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
282 SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
283
284 if (!db->smndbm_cursor_in_use)
285 return SMDBE_NOT_A_VALID_CURSOR;
286
287 db->smndbm_cursor_in_use = false;
288 free(dbm_cursor);
289 free(cursor);
290
291 return SMDBE_OK;
292 }
293
294 int
smdbm_cursor_del(cursor,flags)295 smdbm_cursor_del(cursor, flags)
296 SMDB_CURSOR *cursor;
297 unsigned int flags;
298 {
299 int result;
300 SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
301 SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
302 DBM *dbm = db->smndbm_dbm;
303
304 errno = 0;
305 result = dbm_delete(dbm, dbm_cursor->smndbmc_current_key);
306 if (result != 0)
307 {
308 int save_errno = errno;
309
310 if (dbm_error(dbm))
311 return SMDBE_IO_ERROR;
312
313 if (save_errno != 0)
314 return save_errno;
315
316 return SMDBE_NOT_FOUND;
317 }
318 return SMDBE_OK;
319 }
320
321 int
smdbm_cursor_get(cursor,key,value,flags)322 smdbm_cursor_get(cursor, key, value, flags)
323 SMDB_CURSOR *cursor;
324 SMDB_DBENT *key;
325 SMDB_DBENT *value;
326 SMDB_FLAG flags;
327 {
328 SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
329 SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
330 DBM *dbm = db->smndbm_dbm;
331 datum dbkey, dbdata;
332
333 (void) memset(&dbkey, '\0', sizeof dbkey);
334 (void) memset(&dbdata, '\0', sizeof dbdata);
335
336 if (flags == SMDB_CURSOR_GET_RANGE)
337 return SMDBE_UNSUPPORTED;
338
339 if (dbm_cursor->smndbmc_current_key.dptr == NULL)
340 {
341 dbm_cursor->smndbmc_current_key = dbm_firstkey(dbm);
342 if (dbm_cursor->smndbmc_current_key.dptr == NULL)
343 {
344 if (dbm_error(dbm))
345 return SMDBE_IO_ERROR;
346 return SMDBE_LAST_ENTRY;
347 }
348 }
349 else
350 {
351 dbm_cursor->smndbmc_current_key = dbm_nextkey(dbm);
352 if (dbm_cursor->smndbmc_current_key.dptr == NULL)
353 {
354 if (dbm_error(dbm))
355 return SMDBE_IO_ERROR;
356 return SMDBE_LAST_ENTRY;
357 }
358 }
359
360 errno = 0;
361 dbdata = dbm_fetch(dbm, dbm_cursor->smndbmc_current_key);
362 if (dbdata.dptr == NULL)
363 {
364 int save_errno = errno;
365
366 if (dbm_error(dbm))
367 return SMDBE_IO_ERROR;
368
369 if (save_errno != 0)
370 return save_errno;
371
372 return SMDBE_NOT_FOUND;
373 }
374 value->data = dbdata.dptr;
375 value->size = dbdata.dsize;
376 key->data = dbm_cursor->smndbmc_current_key.dptr;
377 key->size = dbm_cursor->smndbmc_current_key.dsize;
378
379 return SMDBE_OK;
380 }
381
382 int
smdbm_cursor_put(cursor,key,value,flags)383 smdbm_cursor_put(cursor, key, value, flags)
384 SMDB_CURSOR *cursor;
385 SMDB_DBENT *key;
386 SMDB_DBENT *value;
387 SMDB_FLAG flags;
388 {
389 int result;
390 int save_errno;
391 SMDB_DBM_CURSOR *dbm_cursor = (SMDB_DBM_CURSOR *) cursor->smdbc_impl;
392 SMDB_DBM_DATABASE *db = dbm_cursor->smndbmc_db;
393 DBM *dbm = db->smndbm_dbm;
394 datum dbdata;
395
396 (void) memset(&dbdata, '\0', sizeof dbdata);
397 dbdata.dptr = value->data;
398 dbdata.dsize = value->size;
399
400 errno = 0;
401 result = dbm_store(dbm, dbm_cursor->smndbmc_current_key, dbdata,
402 smdb_put_flags_to_ndbm_flags(flags));
403 switch (result)
404 {
405 case 1:
406 return SMDBE_DUPLICATE;
407
408 case 0:
409 return SMDBE_OK;
410
411 default:
412 save_errno = errno;
413
414 if (dbm_error(dbm))
415 return SMDBE_IO_ERROR;
416
417 if (save_errno != 0)
418 return save_errno;
419
420 return SMDBE_IO_ERROR;
421 }
422 /* NOTREACHED */
423 }
424
425 int
smdbm_cursor(database,cursor,flags)426 smdbm_cursor(database, cursor, flags)
427 SMDB_DATABASE *database;
428 SMDB_CURSOR **cursor;
429 SMDB_FLAG flags;
430 {
431 SMDB_DBM_DATABASE *db = (SMDB_DBM_DATABASE *) database->smdb_impl;
432 SMDB_CURSOR *cur;
433 SMDB_DBM_CURSOR *dbm_cursor;
434
435 if (db->smndbm_cursor_in_use)
436 return SMDBE_ONLY_SUPPORTS_ONE_CURSOR;
437
438 db->smndbm_cursor_in_use = true;
439 dbm_cursor = (SMDB_DBM_CURSOR *) malloc(sizeof(SMDB_DBM_CURSOR));
440 if (dbm_cursor == NULL)
441 return SMDBE_MALLOC;
442 dbm_cursor->smndbmc_db = db;
443 dbm_cursor->smndbmc_current_key.dptr = NULL;
444 dbm_cursor->smndbmc_current_key.dsize = 0;
445
446 cur = (SMDB_CURSOR*) malloc(sizeof(SMDB_CURSOR));
447 if (cur == NULL)
448 {
449 free(dbm_cursor);
450 return SMDBE_MALLOC;
451 }
452
453 cur->smdbc_impl = dbm_cursor;
454 cur->smdbc_close = smdbm_cursor_close;
455 cur->smdbc_del = smdbm_cursor_del;
456 cur->smdbc_get = smdbm_cursor_get;
457 cur->smdbc_put = smdbm_cursor_put;
458 *cursor = cur;
459
460 return SMDBE_OK;
461 }
462 /*
463 ** SMDB_NDBM_OPEN -- Opens a ndbm database.
464 **
465 ** Parameters:
466 ** database -- An unallocated database pointer to a pointer.
467 ** db_name -- The name of the database without extension.
468 ** mode -- File permissions on a created database.
469 ** mode_mask -- Mode bits that much match on an opened database.
470 ** sff -- Flags to safefile.
471 ** type -- The type of database to open.
472 ** Only SMDB_NDBM is supported.
473 ** user_info -- Information on the user to use for file
474 ** permissions.
475 ** db_params -- No params are supported.
476 **
477 ** Returns:
478 ** SMDBE_OK -- Success, otherwise errno:
479 ** SMDBE_MALLOC -- Cannot allocate memory.
480 ** SMDBE_UNSUPPORTED -- The type is not supported.
481 ** SMDBE_GDBM_IS_BAD -- We have detected GDBM and we don't
482 ** like it.
483 ** SMDBE_BAD_OPEN -- dbm_open failed and errno was not set.
484 ** Anything else: errno
485 */
486
487 int
smdb_ndbm_open(database,db_name,mode,mode_mask,sff,type,user_info,db_params)488 smdb_ndbm_open(database, db_name, mode, mode_mask, sff, type, user_info,
489 db_params)
490 SMDB_DATABASE **database;
491 char *db_name;
492 int mode;
493 int mode_mask;
494 long sff;
495 SMDB_DBTYPE type;
496 SMDB_USER_INFO *user_info;
497 SMDB_DBPARAMS *db_params;
498 {
499 bool lockcreated = false;
500 int result;
501 int lock_fd;
502 SMDB_DATABASE *smdb_db;
503 SMDB_DBM_DATABASE *db;
504 DBM *dbm = NULL;
505 struct stat dir_stat_info;
506 struct stat pag_stat_info;
507
508 result = SMDBE_OK;
509 *database = NULL;
510
511 if (type == NULL)
512 return SMDBE_UNKNOWN_DB_TYPE;
513
514 result = smdb_setup_file(db_name, SMNDB_DIR_FILE_EXTENSION, mode_mask,
515 sff, user_info, &dir_stat_info);
516 if (result != SMDBE_OK)
517 return result;
518
519 result = smdb_setup_file(db_name, SMNDB_PAG_FILE_EXTENSION, mode_mask,
520 sff, user_info, &pag_stat_info);
521 if (result != SMDBE_OK)
522 return result;
523
524 if ((dir_stat_info.st_mode == ST_MODE_NOFILE ||
525 pag_stat_info.st_mode == ST_MODE_NOFILE) &&
526 bitset(mode, O_CREAT))
527 lockcreated = true;
528
529 lock_fd = -1;
530 result = smdb_lock_file(&lock_fd, db_name, mode, sff,
531 SMNDB_DIR_FILE_EXTENSION);
532 if (result != SMDBE_OK)
533 return result;
534
535 if (lockcreated)
536 {
537 int pag_fd;
538
539 /* Need to pre-open the .pag file as well with O_EXCL */
540 result = smdb_lock_file(&pag_fd, db_name, mode, sff,
541 SMNDB_PAG_FILE_EXTENSION);
542 if (result != SMDBE_OK)
543 {
544 (void) close(lock_fd);
545 return result;
546 }
547 (void) close(pag_fd);
548
549 mode |= O_TRUNC;
550 mode &= ~(O_CREAT|O_EXCL);
551 }
552
553 smdb_db = smdb_malloc_database();
554 if (smdb_db == NULL)
555 result = SMDBE_MALLOC;
556
557 db = smdbm_malloc_database();
558 if (db == NULL)
559 result = SMDBE_MALLOC;
560
561 /* Try to open database */
562 if (result == SMDBE_OK)
563 {
564 db->smndbm_lock_fd = lock_fd;
565
566 errno = 0;
567 dbm = dbm_open(db_name, mode, DBMMODE);
568 if (dbm == NULL)
569 {
570 if (errno == 0)
571 result = SMDBE_BAD_OPEN;
572 else
573 result = errno;
574 }
575 db->smndbm_dbm = dbm;
576 }
577
578 /* Check for GDBM */
579 if (result == SMDBE_OK)
580 {
581 if (dbm_dirfno(dbm) == dbm_pagfno(dbm))
582 result = SMDBE_GDBM_IS_BAD;
583 }
584
585 /* Check for filechanged */
586 if (result == SMDBE_OK)
587 {
588 result = smdb_filechanged(db_name, SMNDB_DIR_FILE_EXTENSION,
589 dbm_dirfno(dbm), &dir_stat_info);
590 if (result == SMDBE_OK)
591 {
592 result = smdb_filechanged(db_name,
593 SMNDB_PAG_FILE_EXTENSION,
594 dbm_pagfno(dbm),
595 &pag_stat_info);
596 }
597 }
598
599 /* XXX Got to get fchown stuff in here */
600
601 /* Setup driver if everything is ok */
602 if (result == SMDBE_OK)
603 {
604 *database = smdb_db;
605
606 smdb_db->smdb_close = smdbm_close;
607 smdb_db->smdb_del = smdbm_del;
608 smdb_db->smdb_fd = smdbm_fd;
609 smdb_db->smdb_lockfd = smdbm_lockfd;
610 smdb_db->smdb_get = smdbm_get;
611 smdb_db->smdb_put = smdbm_put;
612 smdb_db->smdb_set_owner = smndbm_set_owner;
613 smdb_db->smdb_sync = smdbm_sync;
614 smdb_db->smdb_cursor = smdbm_cursor;
615
616 smdb_db->smdb_impl = db;
617
618 return SMDBE_OK;
619 }
620
621 /* If we're here, something bad happened, clean up */
622 if (dbm != NULL)
623 dbm_close(dbm);
624
625 smdb_unlock_file(db->smndbm_lock_fd);
626 free(db);
627 smdb_free_database(smdb_db);
628
629 return result;
630 }
631 #endif /* NDBM */
632