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_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 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