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 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 * 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 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 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 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 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 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 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 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 301 smcdb_sync(database, flags) 302 SMDB_DATABASE *database; 303 unsigned int flags; 304 { 305 return 0; 306 } 307 308 static int 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 321 smcdb_cursor_del(cursor, flags) 322 SMDB_CURSOR *cursor; 323 SMDB_FLAG flags; 324 { 325 return -1; 326 } 327 328 static int 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 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 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_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 434 int 435 smdb_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