1 /*
2 ** Copyright (c) 2018 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: smcdb.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 CDB
21 #include <assert.h>
22 #include <cdb.h>
23
24 typedef struct cdb cdb_map_T, *cdb_map_P;
25 typedef struct cdb_make cdb_make_T, *cdb_make_P;
26 typedef union sm_cdbs_U sm_cdbs_T, *sm_cdbs_P;
27 union sm_cdbs_U
28 {
29 cdb_map_T cdbs_cdb_rd;
30 cdb_make_T cdbs_cdb_wr;
31 };
32
33 struct smdb_cdb_database
34 {
35 sm_cdbs_T cdbmap_map;
36 int cdbmap_fd;
37 int smcdb_lock_fd;
38 bool cdbmap_create;
39 unsigned smcdb_pos;
40 int smcdb_n;
41 };
42 typedef struct smdb_cdb_database SMDB_CDB_DATABASE;
43
44 /* static int smdb_type_to_cdb_type __P((SMDB_DBTYPE type)); */
45 static int cdb_error_to_smdb __P((int error));
46 static SMDB_CDB_DATABASE * smcdb_malloc_database __P((void));
47 static int smcdb_close __P((SMDB_DATABASE *database));
48 static int smcdb_del __P((SMDB_DATABASE *database, SMDB_DBENT *key, unsigned int flags));
49 static int smcdb_fd __P((SMDB_DATABASE *database, int *fd));
50 static int smcdb_lockfd __P((SMDB_DATABASE *database));
51 static int smcdb_get __P((SMDB_DATABASE *database, SMDB_DBENT *key, SMDB_DBENT *data, unsigned int flags));
52 static int smcdb_put __P((SMDB_DATABASE *database, SMDB_DBENT *key, SMDB_DBENT *data, unsigned int flags));
53 static int smcdb_set_owner __P((SMDB_DATABASE *database, uid_t uid, gid_t gid));
54 static int smcdb_sync __P((SMDB_DATABASE *database, unsigned int flags));
55 static int smcdb_cursor_close __P((SMDB_CURSOR *cursor));
56 static int smcdb_cursor_del __P((SMDB_CURSOR *cursor, SMDB_FLAG flags));
57 static int smcdb_cursor_get __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *value, SMDB_FLAG flags));
58 static int smcdb_cursor_put __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *value, SMDB_FLAG flags));
59 static int smcdb_cursor __P((SMDB_DATABASE *database, SMDB_CURSOR **cursor, SMDB_FLAG flags));
60
61 /*
62 ** SMDB_TYPE_TO_CDB_TYPE -- Translates smdb database type to cdb type.
63 **
64 ** Parameters:
65 ** type -- The type to translate.
66 **
67 ** Returns:
68 ** The CDB type that corresponsds to the passed in SMDB type.
69 ** Returns -1 if there is no equivalent type.
70 **
71 */
72
73 # if 0
74 static int
75 smdb_type_to_cdb_type(type)
76 SMDB_DBTYPE type;
77 {
78 return 0; /* XXX */
79 }
80 # endif
81
82 /*
83 ** CDB_ERROR_TO_SMDB -- Translates cdb errors to smdbe errors
84 **
85 ** Parameters:
86 ** error -- The error to translate.
87 **
88 ** Returns:
89 ** The SMDBE error corresponding to the cdb error.
90 ** If we don't have a corresponding error, it returns error.
91 **
92 */
93
94 static int
cdb_error_to_smdb(error)95 cdb_error_to_smdb(error)
96 int error;
97 {
98 int result;
99
100 switch (error)
101 {
102 case 0:
103 result = SMDBE_OK;
104 break;
105
106 default:
107 result = error;
108 }
109 return result;
110 }
111
112 SMDB_CDB_DATABASE *
smcdb_malloc_database()113 smcdb_malloc_database()
114 {
115 SMDB_CDB_DATABASE *cdb;
116
117 cdb = (SMDB_CDB_DATABASE *) malloc(sizeof(SMDB_CDB_DATABASE));
118 if (cdb != NULL)
119 cdb->smcdb_lock_fd = -1;
120
121 return cdb;
122 }
123
124 static int
smcdb_close(database)125 smcdb_close(database)
126 SMDB_DATABASE *database;
127 {
128 int result, fd;
129 SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
130
131 if (NULL == sm_cdbmap)
132 return -1;
133 result = 0;
134 if (sm_cdbmap->cdbmap_create)
135 result = cdb_make_finish(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr);
136
137 fd = sm_cdbmap->cdbmap_fd;
138 if (fd >= 0)
139 {
140 close(fd);
141 sm_cdbmap->cdbmap_fd = -1;
142 }
143
144 free(sm_cdbmap);
145 database->smdb_impl = NULL;
146
147 return result;
148 }
149
150 static int
smcdb_del(database,key,flags)151 smcdb_del(database, key, flags)
152 SMDB_DATABASE *database;
153 SMDB_DBENT *key;
154 unsigned int flags;
155 {
156 SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
157
158 assert(sm_cdbmap != NULL);
159 return -1;
160 }
161
162 static int
smcdb_fd(database,fd)163 smcdb_fd(database, fd)
164 SMDB_DATABASE *database;
165 int *fd;
166 {
167 SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
168 return sm_cdbmap->cdbmap_fd;
169 }
170
171 static int
smcdb_lockfd(database)172 smcdb_lockfd(database)
173 SMDB_DATABASE *database;
174 {
175 SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
176
177 return sm_cdbmap->smcdb_lock_fd;
178 }
179
180 /*
181 ** allocate/free: who does it: caller or callee?
182 ** If this code does it: the "last" entry will leak.
183 */
184
185 #define DBEALLOC(dbe, l) \
186 do \
187 { \
188 if ((dbe)->size > 0 && l > (dbe)->size) \
189 { \
190 free((dbe)->data); \
191 (dbe)->size = 0; \
192 } \
193 if ((dbe)->size == 0) \
194 { \
195 (dbe)->data = malloc(l); \
196 if ((dbe)->data == NULL) \
197 return SMDBE_MALLOC; \
198 (dbe)->size = l; \
199 } \
200 if (l > (dbe)->size) \
201 return SMDBE_MALLOC; /* XXX bogus */ \
202 } while (0)
203
204
205 static int
smcdb_get(database,key,data,flags)206 smcdb_get(database, key, data, flags)
207 SMDB_DATABASE *database;
208 SMDB_DBENT *key;
209 SMDB_DBENT *data;
210 unsigned int flags;
211 {
212 SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
213 size_t l;
214 int ret;
215
216 ret = SM_SUCCESS;
217
218 if (NULL == sm_cdbmap )
219 return -1;
220 /* SM_ASSERT(!sm_cdbmap->cdbmap_create); */
221
222 /* need to lock access? single threaded access! */
223 ret = cdb_find(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
224 key->data, key->size);
225 if (ret > 0)
226 {
227 l = cdb_datalen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
228 DBEALLOC(data, l);
229 ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
230 data->data, l,
231 cdb_datapos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
232 if (ret < 0)
233 ret = -1;
234 else
235 {
236 data->size = l;
237 ret = SM_SUCCESS;
238 }
239 }
240 else
241 ret = -1;
242
243 return ret;
244 }
245
246 static int
smcdb_put(database,key,data,flags)247 smcdb_put(database, key, data, flags)
248 SMDB_DATABASE *database;
249 SMDB_DBENT *key;
250 SMDB_DBENT *data;
251 unsigned int flags;
252 {
253 int r, cdb_flags;
254 SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
255
256 assert(sm_cdbmap != NULL);
257 if (bitset(SMDBF_NO_OVERWRITE, flags))
258 cdb_flags = CDB_PUT_INSERT;
259 else
260 cdb_flags = CDB_PUT_REPLACE;
261
262 r = cdb_make_put(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr,
263 key->data, key->size, data->data, data->size,
264 cdb_flags);
265 if (r > 0)
266 {
267 if (bitset(SMDBF_NO_OVERWRITE, flags))
268 return SMDBE_DUPLICATE;
269 else
270 return SMDBE_OK;
271 }
272 return r;
273 }
274
275
276 static int
smcdb_set_owner(database,uid,gid)277 smcdb_set_owner(database, uid, gid)
278 SMDB_DATABASE *database;
279 uid_t uid;
280 gid_t gid;
281 {
282 # if HASFCHOWN
283 int fd;
284 int result;
285 SMDB_CDB_DATABASE *sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
286
287 assert(sm_cdbmap != NULL);
288 fd = sm_cdbmap->cdbmap_fd;
289 if (fd >= 0)
290 {
291 result = fchown(fd, uid, gid);
292 if (result < 0)
293 return errno;
294 }
295 # endif /* HASFCHOWN */
296
297 return SMDBE_OK;
298 }
299
300 static int
smcdb_sync(database,flags)301 smcdb_sync(database, flags)
302 SMDB_DATABASE *database;
303 unsigned int flags;
304 {
305 return 0;
306 }
307
308 static int
smcdb_cursor_close(cursor)309 smcdb_cursor_close(cursor)
310 SMDB_CURSOR *cursor;
311 {
312 int ret;
313
314 ret = SMDBE_OK;
315 if (cursor != NULL)
316 free(cursor);
317 return ret;
318 }
319
320 static int
smcdb_cursor_del(cursor,flags)321 smcdb_cursor_del(cursor, flags)
322 SMDB_CURSOR *cursor;
323 SMDB_FLAG flags;
324 {
325 return -1;
326 }
327
328 static int
smcdb_cursor_get(cursor,key,value,flags)329 smcdb_cursor_get(cursor, key, value, flags)
330 SMDB_CURSOR *cursor;
331 SMDB_DBENT *key;
332 SMDB_DBENT *value;
333 SMDB_FLAG flags;
334 {
335 SMDB_CDB_DATABASE *sm_cdbmap;
336 size_t l;
337 int ret;
338
339 ret = SMDBE_OK;
340 sm_cdbmap = cursor->smdbc_impl;
341 ret = cdb_seqnext(&sm_cdbmap->smcdb_pos, &sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
342 if (ret == 0)
343 return SMDBE_LAST_ENTRY;
344 if (ret < 0)
345 return SMDBE_IO_ERROR;
346
347 l = cdb_keylen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
348 DBEALLOC(key, l);
349
350 ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
351 key->data, l,
352 cdb_keypos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
353 if (ret < 0)
354 return SMDBE_IO_ERROR;
355 key->size = l;
356
357 l = cdb_datalen(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
358
359 DBEALLOC(value, l);
360 ret = cdb_read(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd,
361 value->data, l,
362 cdb_datapos(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd));
363 if (ret < 0)
364 return SMDBE_IO_ERROR;
365 value->size = l;
366
367 return SMDBE_OK;
368 }
369
370 static int
smcdb_cursor_put(cursor,key,value,flags)371 smcdb_cursor_put(cursor, key, value, flags)
372 SMDB_CURSOR *cursor;
373 SMDB_DBENT *key;
374 SMDB_DBENT *value;
375 SMDB_FLAG flags;
376 {
377 return -1;
378 }
379
380 static int
smcdb_cursor(database,cursor,flags)381 smcdb_cursor(database, cursor, flags)
382 SMDB_DATABASE *database;
383 SMDB_CURSOR **cursor;
384 SMDB_FLAG flags;
385 {
386 int result;
387 SMDB_CDB_DATABASE *sm_cdbmap;
388
389 result = SMDBE_OK;
390 *cursor = (SMDB_CURSOR *) malloc(sizeof(SMDB_CURSOR));
391 if (*cursor == NULL)
392 return SMDBE_MALLOC;
393
394 sm_cdbmap = (SMDB_CDB_DATABASE *) database->smdb_impl;
395 (*cursor)->smdbc_close = smcdb_cursor_close;
396 (*cursor)->smdbc_del = smcdb_cursor_del;
397 (*cursor)->smdbc_get = smcdb_cursor_get;
398 (*cursor)->smdbc_put = smcdb_cursor_put;
399 (*cursor)->smdbc_impl = sm_cdbmap;
400
401 cdb_seqinit(&sm_cdbmap->smcdb_pos, &sm_cdbmap->cdbmap_map.cdbs_cdb_rd);
402
403 return result;
404 }
405
406 /*
407 ** SMDB_CDB_OPEN -- Opens a cdb database.
408 **
409 ** Parameters:
410 ** database -- An unallocated database pointer to a pointer.
411 ** db_name -- The name of the database without extension.
412 ** mode -- File permissions for a created database.
413 ** mode_mask -- Mode bits that must match on an opened database.
414 ** sff -- Flags for safefile.
415 ** type -- The type of database to open
416 ** See smdb_type_to_cdb_type for valid types.
417 ** user_info -- User information for file permissions.
418 ** db_params -- unused
419 **
420 ** Returns:
421 ** SMDBE_OK -- Success, other errno:
422 ** SMDBE_MALLOC -- Cannot allocate memory.
423 ** SMDBE_BAD_OPEN -- various (OS) errors.
424 ** Anything else: translated error from cdb
425 */
426
427 int
smdb_cdb_open(database,db_name,mode,mode_mask,sff,type,user_info,db_params)428 smdb_cdb_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params)
429 SMDB_DATABASE **database;
430 char *db_name;
431 int mode;
432 int mode_mask;
433 long sff;
434 SMDB_DBTYPE type;
435 SMDB_USER_INFO *user_info;
436 SMDB_DBPARAMS *db_params;
437 {
438 bool lockcreated = false;
439 int result;
440 int lock_fd;
441 int db_fd;
442 SMDB_DATABASE *smdb_db;
443 SMDB_CDB_DATABASE *sm_cdbmap;
444 struct stat stat_info;
445 char db_file_name[MAXPATHLEN];
446
447 *database = NULL;
448 result = smdb_add_extension(db_file_name, sizeof db_file_name,
449 db_name, SMCDB_FILE_EXTENSION);
450 if (result != SMDBE_OK)
451 return result;
452
453 result = smdb_setup_file(db_name, SMCDB_FILE_EXTENSION,
454 mode_mask, sff, user_info, &stat_info);
455 if (result != SMDBE_OK)
456 return result;
457
458 lock_fd = -1;
459
460 if (stat_info.st_mode == ST_MODE_NOFILE &&
461 bitset(mode, O_CREAT))
462 lockcreated = true;
463
464 result = smdb_lock_file(&lock_fd, db_name, mode, sff,
465 SMCDB_FILE_EXTENSION);
466 if (result != SMDBE_OK)
467 return result;
468
469 if (lockcreated)
470 {
471 mode |= O_TRUNC;
472 mode &= ~(O_CREAT|O_EXCL);
473 }
474
475 smdb_db = smdb_malloc_database();
476 sm_cdbmap = smcdb_malloc_database();
477 if (sm_cdbmap == NULL || smdb_db == NULL)
478 {
479 smdb_unlock_file(lock_fd);
480 smdb_free_database(smdb_db); /* ok to be NULL */
481 if (sm_cdbmap != NULL)
482 free(sm_cdbmap);
483 return SMDBE_MALLOC;
484 }
485
486 sm_cdbmap->smcdb_lock_fd = lock_fd;
487
488 # if 0
489 db = NULL;
490 db_flags = 0;
491 if (bitset(O_CREAT, mode))
492 db_flags |= DB_CREATE;
493 if (bitset(O_TRUNC, mode))
494 db_flags |= DB_TRUNCATE;
495 if (mode == O_RDONLY)
496 db_flags |= DB_RDONLY;
497 SM_DB_FLAG_ADD(db_flags);
498 # endif
499
500 result = -1; /* smdb_db_open_internal(db_file_name, db_type, db_flags, db_params, &db); */
501 db_fd = open(db_file_name, mode, DBMMODE);
502 if (db_fd == -1)
503 {
504 result = SMDBE_BAD_OPEN;
505 goto error;
506 }
507
508 sm_cdbmap->cdbmap_create = (mode != O_RDONLY);
509 if (mode == O_RDONLY)
510 result = cdb_init(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd, db_fd);
511 else
512 result = cdb_make_start(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr, db_fd);
513 if (result != 0)
514 {
515 result = SMDBE_BAD_OPEN;
516 goto error;
517 }
518
519 if (result == 0)
520 result = SMDBE_OK;
521 else
522 {
523 /* Try and narrow down on the problem */
524 if (result != 0)
525 result = cdb_error_to_smdb(result);
526 else
527 result = SMDBE_BAD_OPEN;
528 }
529
530 if (result == SMDBE_OK)
531 result = smdb_filechanged(db_name, SMCDB_FILE_EXTENSION, db_fd,
532 &stat_info);
533
534 if (result == SMDBE_OK)
535 {
536 /* Everything is ok. Setup driver */
537 /* smdb_db->smcdb_db = sm_cdbmap; */
538
539 smdb_db->smdb_close = smcdb_close;
540 smdb_db->smdb_del = smcdb_del;
541 smdb_db->smdb_fd = smcdb_fd;
542 smdb_db->smdb_lockfd = smcdb_lockfd;
543 smdb_db->smdb_get = smcdb_get;
544 smdb_db->smdb_put = smcdb_put;
545 smdb_db->smdb_set_owner = smcdb_set_owner;
546 smdb_db->smdb_sync = smcdb_sync;
547 smdb_db->smdb_cursor = smcdb_cursor;
548 smdb_db->smdb_impl = sm_cdbmap;
549
550 *database = smdb_db;
551
552 return SMDBE_OK;
553 }
554
555 error:
556 if (sm_cdbmap != NULL)
557 {
558 /* close */
559 }
560
561 smdb_unlock_file(sm_cdbmap->smcdb_lock_fd);
562 free(sm_cdbmap);
563 smdb_free_database(smdb_db);
564
565 return result;
566 }
567
568 #endif /* CDB */
569