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> 11SM_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 24typedef struct cdb cdb_map_T, *cdb_map_P; 25typedef struct cdb_make cdb_make_T, *cdb_make_P; 26typedef union sm_cdbs_U sm_cdbs_T, *sm_cdbs_P; 27union sm_cdbs_U 28{ 29 cdb_map_T cdbs_cdb_rd; 30 cdb_make_T cdbs_cdb_wr; 31}; 32 33struct 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}; 42typedef struct smdb_cdb_database SMDB_CDB_DATABASE; 43 44/* static int smdb_type_to_cdb_type __P((SMDB_DBTYPE type)); */ 45static int cdb_error_to_smdb __P((int error)); 46static SMDB_CDB_DATABASE * smcdb_malloc_database __P((void)); 47static int smcdb_close __P((SMDB_DATABASE *database)); 48static int smcdb_del __P((SMDB_DATABASE *database, SMDB_DBENT *key, unsigned int flags)); 49static int smcdb_fd __P((SMDB_DATABASE *database, int *fd)); 50static int smcdb_lockfd __P((SMDB_DATABASE *database)); 51static int smcdb_get __P((SMDB_DATABASE *database, SMDB_DBENT *key, SMDB_DBENT *data, unsigned int flags)); 52static int smcdb_put __P((SMDB_DATABASE *database, SMDB_DBENT *key, SMDB_DBENT *data, unsigned int flags)); 53static int smcdb_set_owner __P((SMDB_DATABASE *database, uid_t uid, gid_t gid)); 54static int smcdb_sync __P((SMDB_DATABASE *database, unsigned int flags)); 55static int smcdb_cursor_close __P((SMDB_CURSOR *cursor)); 56static int smcdb_cursor_del __P((SMDB_CURSOR *cursor, SMDB_FLAG flags)); 57static int smcdb_cursor_get __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *value, SMDB_FLAG flags)); 58static int smcdb_cursor_put __P((SMDB_CURSOR *cursor, SMDB_DBENT *key, SMDB_DBENT *value, SMDB_FLAG flags)); 59static 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 74static int 75smdb_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 94static int 95cdb_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 112SMDB_CDB_DATABASE * 113smcdb_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 124static int 125smcdb_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 150static int 151smcdb_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 162static int 163smcdb_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 171static int 172smcdb_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 205static int 206smcdb_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 246static int 247smcdb_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 276static int 277smcdb_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 300static int 301smcdb_sync(database, flags) 302 SMDB_DATABASE *database; 303 unsigned int flags; 304{ 305 return 0; 306} 307 308static int 309smcdb_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 320static int 321smcdb_cursor_del(cursor, flags) 322 SMDB_CURSOR *cursor; 323 SMDB_FLAG flags; 324{ 325 return -1; 326} 327 328static int 329smcdb_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 370static int 371smcdb_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 380static int 381smcdb_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_DB_OPEN -- Opens a db 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 permisions 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 -- 419** An SMDB_DBPARAMS struct including params. These 420** are processed according to the type of the 421** database. Currently supported params (only for 422** HASH type) are: 423** num_elements 424** cache_size 425** 426** Returns: 427** SMDBE_OK -- Success, other errno: 428** SMDBE_MALLOC -- Cannot allocate memory. 429** SMDBE_BAD_OPEN -- db_open didn't return an error, but 430** somehow the DB pointer is NULL. 431** Anything else: translated error from cdb 432*/ 433 434int 435smdb_cdb_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params) 436 SMDB_DATABASE **database; 437 char *db_name; 438 int mode; 439 int mode_mask; 440 long sff; 441 SMDB_DBTYPE type; 442 SMDB_USER_INFO *user_info; 443 SMDB_DBPARAMS *db_params; 444{ 445 bool lockcreated = false; 446 int result; 447 int lock_fd; 448 int db_fd; 449 SMDB_DATABASE *smdb_db; 450 SMDB_CDB_DATABASE *sm_cdbmap; 451 struct stat stat_info; 452 char db_file_name[MAXPATHLEN]; 453 454 *database = NULL; 455 result = smdb_add_extension(db_file_name, sizeof db_file_name, 456 db_name, SMCDB_FILE_EXTENSION); 457 if (result != SMDBE_OK) 458 return result; 459 460 result = smdb_setup_file(db_name, SMCDB_FILE_EXTENSION, 461 mode_mask, sff, user_info, &stat_info); 462 if (result != SMDBE_OK) 463 return result; 464 465 lock_fd = -1; 466 467 if (stat_info.st_mode == ST_MODE_NOFILE && 468 bitset(mode, O_CREAT)) 469 lockcreated = true; 470 471 result = smdb_lock_file(&lock_fd, db_name, mode, sff, 472 SMCDB_FILE_EXTENSION); 473 if (result != SMDBE_OK) 474 return result; 475 476 if (lockcreated) 477 { 478 mode |= O_TRUNC; 479 mode &= ~(O_CREAT|O_EXCL); 480 } 481 482 smdb_db = smdb_malloc_database(); 483 sm_cdbmap = smcdb_malloc_database(); 484 if (sm_cdbmap == NULL || smdb_db == NULL) 485 { 486 smdb_unlock_file(lock_fd); 487 smdb_free_database(smdb_db); /* ok to be NULL */ 488 if (sm_cdbmap != NULL) 489 free(sm_cdbmap); 490 return SMDBE_MALLOC; 491 } 492 493 sm_cdbmap->smcdb_lock_fd = lock_fd; 494 495#if 0 496 db = NULL; 497 db_flags = 0; 498 if (bitset(O_CREAT, mode)) 499 db_flags |= DB_CREATE; 500 if (bitset(O_TRUNC, mode)) 501 db_flags |= DB_TRUNCATE; 502 if (mode == O_RDONLY) 503 db_flags |= DB_RDONLY; 504 SM_DB_FLAG_ADD(db_flags); 505#endif 506 507 result = -1; /* smdb_db_open_internal(db_file_name, db_type, db_flags, db_params, &db); */ 508 db_fd = open(db_file_name, mode, DBMMODE); 509 if (db_fd == -1) 510 { 511 result = SMDBE_BAD_OPEN; 512 goto error; 513 } 514 515 sm_cdbmap->cdbmap_create = (mode != O_RDONLY); 516 if (mode == O_RDONLY) 517 result = cdb_init(&sm_cdbmap->cdbmap_map.cdbs_cdb_rd, db_fd); 518 else 519 result = cdb_make_start(&sm_cdbmap->cdbmap_map.cdbs_cdb_wr, db_fd); 520 if (result != 0) 521 { 522 result = SMDBE_BAD_OPEN; 523 goto error; 524 } 525 526 if (result == 0) 527 result = SMDBE_OK; 528 else 529 { 530 /* Try and narrow down on the problem */ 531 if (result != 0) 532 result = cdb_error_to_smdb(result); 533 else 534 result = SMDBE_BAD_OPEN; 535 } 536 537 if (result == SMDBE_OK) 538 result = smdb_filechanged(db_name, SMCDB_FILE_EXTENSION, db_fd, 539 &stat_info); 540 541 if (result == SMDBE_OK) 542 { 543 /* Everything is ok. Setup driver */ 544 /* smdb_db->smcdb_db = sm_cdbmap; */ 545 546 smdb_db->smdb_close = smcdb_close; 547 smdb_db->smdb_del = smcdb_del; 548 smdb_db->smdb_fd = smcdb_fd; 549 smdb_db->smdb_lockfd = smcdb_lockfd; 550 smdb_db->smdb_get = smcdb_get; 551 smdb_db->smdb_put = smcdb_put; 552 smdb_db->smdb_set_owner = smcdb_set_owner; 553 smdb_db->smdb_sync = smcdb_sync; 554 smdb_db->smdb_cursor = smcdb_cursor; 555 smdb_db->smdb_impl = sm_cdbmap; 556 557 *database = smdb_db; 558 559 return SMDBE_OK; 560 } 561 562 error: 563 if (sm_cdbmap != NULL) 564 { 565 /* close */ 566 } 567 568 smdb_unlock_file(sm_cdbmap->smcdb_lock_fd); 569 free(sm_cdbmap); 570 smdb_free_database(smdb_db); 571 572 return result; 573} 574 575#endif /* CDB */ 576