/* ** Copyright (c) 1999-2003, 2009 Proofpoint, Inc. and its suppliers. ** All rights reserved. ** ** By using this file, you agree to the terms and conditions set ** forth in the LICENSE file which can be found at the top level of ** the sendmail distribution. */ #include SM_RCSID("@(#)$Id: smdb2.c,v 8.83 2013-11-22 20:51:49 ca Exp $") #include #include #include #include #include #if (DB_VERSION_MAJOR >= 2) struct smdb_db2_database { DB *smdb2_db; int smdb2_lock_fd; }; typedef struct smdb_db2_database SMDB_DB2_DATABASE; /* ** SMDB_TYPE_TO_DB2_TYPE -- Translates smdb database type to db2 type. ** ** Parameters: ** type -- The type to translate. ** ** Returns: ** The DB2 type that corresponsds to the passed in SMDB type. ** Returns -1 if there is no equivalent type. ** */ static DBTYPE smdb_type_to_db2_type(type) SMDB_DBTYPE type; { if (type == SMDB_TYPE_DEFAULT) return DB_HASH; if (SMDB_IS_TYPE_HASH(type)) return DB_HASH; if (SMDB_IS_TYPE_BTREE(type)) return DB_BTREE; return DB_UNKNOWN; } /* ** DB2_ERROR_TO_SMDB -- Translates db2 errors to smdbe errors ** ** Parameters: ** error -- The error to translate. ** ** Returns: ** The SMDBE error corresponding to the db2 error. ** If we don't have a corresponding error, it returns errno. ** */ static int db2_error_to_smdb(error) int error; { int result; switch (error) { # ifdef DB_INCOMPLETE case DB_INCOMPLETE: result = SMDBE_INCOMPLETE; break; # endif # ifdef DB_NOTFOUND case DB_NOTFOUND: result = SMDBE_NOT_FOUND; break; # endif # ifdef DB_KEYEMPTY case DB_KEYEMPTY: result = SMDBE_KEY_EMPTY; break; # endif # ifdef DB_KEYEXIST case DB_KEYEXIST: result = SMDBE_KEY_EXIST; break; # endif # ifdef DB_LOCK_DEADLOCK case DB_LOCK_DEADLOCK: result = SMDBE_LOCK_DEADLOCK; break; # endif # ifdef DB_LOCK_NOTGRANTED case DB_LOCK_NOTGRANTED: result = SMDBE_LOCK_NOT_GRANTED; break; # endif # ifdef DB_LOCK_NOTHELD case DB_LOCK_NOTHELD: result = SMDBE_LOCK_NOT_HELD; break; # endif # ifdef DB_RUNRECOVERY case DB_RUNRECOVERY: result = SMDBE_RUN_RECOVERY; break; # endif # ifdef DB_OLD_VERSION case DB_OLD_VERSION: result = SMDBE_OLD_VERSION; break; # endif case 0: result = SMDBE_OK; break; default: result = error; } return result; } /* ** SMDB_PUT_FLAGS_TO_DB2_FLAGS -- Translates smdb put flags to db2 put flags. ** ** Parameters: ** flags -- The flags to translate. ** ** Returns: ** The db2 flags that are equivalent to the smdb flags. ** ** Notes: ** Any invalid flags are ignored. ** */ static unsigned int smdb_put_flags_to_db2_flags(flags) SMDB_FLAG flags; { int return_flags; return_flags = 0; if (bitset(SMDBF_NO_OVERWRITE, flags)) return_flags |= DB_NOOVERWRITE; return return_flags; } /* ** SMDB_CURSOR_GET_FLAGS_TO_DB2 -- Translates smdb cursor get flags to db2 ** getflags. ** ** Parameters: ** flags -- The flags to translate. ** ** Returns: ** The db2 flags that are equivalent to the smdb flags. ** ** Notes: ** -1 is returned if flag is unknown. ** */ static int smdb_cursor_get_flags_to_db2(flags) SMDB_FLAG flags; { switch (flags) { case SMDB_CURSOR_GET_FIRST: return DB_FIRST; case SMDB_CURSOR_GET_LAST: return DB_LAST; case SMDB_CURSOR_GET_NEXT: return DB_NEXT; case SMDB_CURSOR_GET_RANGE: return DB_SET_RANGE; default: return -1; } } /* ** Except for smdb_db_open, the rest of these functions correspond to the ** interface laid out in smdb.h. */ static SMDB_DB2_DATABASE * smdb2_malloc_database() { SMDB_DB2_DATABASE *db2; db2 = (SMDB_DB2_DATABASE *) malloc(sizeof(SMDB_DB2_DATABASE)); if (db2 != NULL) db2->smdb2_lock_fd = -1; return db2; } static int smdb2_close(database) SMDB_DATABASE *database; { int result; SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl; DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; result = db2_error_to_smdb(db->close(db, 0)); if (db2->smdb2_lock_fd != -1) close(db2->smdb2_lock_fd); free(db2); database->smdb_impl = NULL; return result; } static int smdb2_del(database, key, flags) SMDB_DATABASE *database; SMDB_DBENT *key; unsigned int flags; { DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; DBT dbkey; (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; return db2_error_to_smdb(db->del(db, NULL, &dbkey, flags)); } static int smdb2_fd(database, fd) SMDB_DATABASE *database; int *fd; { DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; return db2_error_to_smdb(db->fd(db, fd)); } static int smdb2_lockfd(database) SMDB_DATABASE *database; { SMDB_DB2_DATABASE *db2 = (SMDB_DB2_DATABASE *) database->smdb_impl; return db2->smdb2_lock_fd; } static int smdb2_get(database, key, data, flags) SMDB_DATABASE *database; SMDB_DBENT *key; SMDB_DBENT *data; unsigned int flags; { int result; DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; DBT dbkey, dbdata; (void) memset(&dbdata, '\0', sizeof dbdata); (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; result = db->get(db, NULL, &dbkey, &dbdata, flags); data->data = dbdata.data; data->size = dbdata.size; return db2_error_to_smdb(result); } static int smdb2_put(database, key, data, flags) SMDB_DATABASE *database; SMDB_DBENT *key; SMDB_DBENT *data; unsigned int flags; { DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; DBT dbkey, dbdata; (void) memset(&dbdata, '\0', sizeof dbdata); (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; dbdata.data = data->data; dbdata.size = data->size; return db2_error_to_smdb(db->put(db, NULL, &dbkey, &dbdata, smdb_put_flags_to_db2_flags(flags))); } static int smdb2_set_owner(database, uid, gid) SMDB_DATABASE *database; uid_t uid; gid_t gid; { # if HASFCHOWN int fd; int result; DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; result = db->fd(db, &fd); if (result != 0) return result; result = fchown(fd, uid, gid); if (result < 0) return errno; # endif /* HASFCHOWN */ return SMDBE_OK; } static int smdb2_sync(database, flags) SMDB_DATABASE *database; unsigned int flags; { DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; return db2_error_to_smdb(db->sync(db, flags)); } static int smdb2_cursor_close(cursor) SMDB_CURSOR *cursor; { int ret; DBC *dbc = (DBC *) cursor->smdbc_impl; ret = db2_error_to_smdb(dbc->c_close(dbc)); free(cursor); return ret; } static int smdb2_cursor_del(cursor, flags) SMDB_CURSOR *cursor; SMDB_FLAG flags; { DBC *dbc = (DBC *) cursor->smdbc_impl; return db2_error_to_smdb(dbc->c_del(dbc, 0)); } static int smdb2_cursor_get(cursor, key, value, flags) SMDB_CURSOR *cursor; SMDB_DBENT *key; SMDB_DBENT *value; SMDB_FLAG flags; { int db2_flags; int result; DBC *dbc = (DBC *) cursor->smdbc_impl; DBT dbkey, dbdata; (void) memset(&dbdata, '\0', sizeof dbdata); (void) memset(&dbkey, '\0', sizeof dbkey); db2_flags = smdb_cursor_get_flags_to_db2(flags); result = dbc->c_get(dbc, &dbkey, &dbdata, db2_flags); if (result == DB_NOTFOUND) return SMDBE_LAST_ENTRY; key->data = dbkey.data; key->size = dbkey.size; value->data = dbdata.data; value->size = dbdata.size; return db2_error_to_smdb(result); } static int smdb2_cursor_put(cursor, key, value, flags) SMDB_CURSOR *cursor; SMDB_DBENT *key; SMDB_DBENT *value; SMDB_FLAG flags; { DBC *dbc = (DBC *) cursor->smdbc_impl; DBT dbkey, dbdata; (void) memset(&dbdata, '\0', sizeof dbdata); (void) memset(&dbkey, '\0', sizeof dbkey); dbkey.data = key->data; dbkey.size = key->size; dbdata.data = value->data; dbdata.size = value->size; return db2_error_to_smdb(dbc->c_put(dbc, &dbkey, &dbdata, 0)); } static int smdb2_cursor(database, cursor, flags) SMDB_DATABASE *database; SMDB_CURSOR **cursor; SMDB_FLAG flags; { int result; DB *db = ((SMDB_DB2_DATABASE *) database->smdb_impl)->smdb2_db; DBC *db2_cursor; # if DB_VERSION_MAJOR > 2 || DB_VERSION_MINOR >= 6 result = db->cursor(db, NULL, &db2_cursor, 0); # else result = db->cursor(db, NULL, &db2_cursor); # endif if (result != 0) return db2_error_to_smdb(result); *cursor = (SMDB_CURSOR *) malloc(sizeof(SMDB_CURSOR)); if (*cursor == NULL) return SMDBE_MALLOC; (*cursor)->smdbc_close = smdb2_cursor_close; (*cursor)->smdbc_del = smdb2_cursor_del; (*cursor)->smdbc_get = smdb2_cursor_get; (*cursor)->smdbc_put = smdb2_cursor_put; (*cursor)->smdbc_impl = db2_cursor; return SMDBE_OK; } # if DB_VERSION_MAJOR == 2 static int smdb_db_open_internal(db_name, db_type, db_flags, db_params, db) char *db_name; DBTYPE db_type; int db_flags; SMDB_DBPARAMS *db_params; DB **db; { void *params; DB_INFO db_info; params = NULL; (void) memset(&db_info, '\0', sizeof db_info); if (db_params != NULL) { db_info.db_cachesize = db_params->smdbp_cache_size; if (db_type == DB_HASH) db_info.h_nelem = db_params->smdbp_num_elements; if (db_params->smdbp_allow_dup) db_info.flags |= DB_DUP; params = &db_info; } return db_open(db_name, db_type, db_flags, DBMMODE, NULL, params, db); } # endif /* DB_VERSION_MAJOR == 2 */ # if DB_VERSION_MAJOR > 2 static void db_err_cb( # if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3 dbenv, # endif errpfx, msg) # if DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3 const DB_ENV *dbenv; const char *errpfx; const char *msg; # else const char *errpfx; char *msg; # endif { /* do not print/log any errors... */ return; } static int smdb_db_open_internal(db_name, db_type, db_flags, db_params, db) char *db_name; DBTYPE db_type; int db_flags; SMDB_DBPARAMS *db_params; DB **db; { int result; result = db_create(db, NULL, 0); if (result != 0 || *db == NULL) return result; (*db)->set_errcall(*db, db_err_cb); if (db_params != NULL) { result = (*db)->set_cachesize(*db, 0, db_params->smdbp_cache_size, 0); if (result != 0) goto error; if (db_type == DB_HASH) { result = (*db)->set_h_nelem(*db, db_params->smdbp_num_elements); if (result != 0) goto error; } if (db_params->smdbp_allow_dup) { result = (*db)->set_flags(*db, DB_DUP); if (result != 0) goto error; } } result = (*db)->open(*db, DBTXN /* transaction for DB 4.1 */ db_name, NULL, db_type, db_flags, DBMMODE); error: if (result != 0) { (void) (*db)->close(*db, 0); *db = NULL; } return db2_error_to_smdb(result); } # endif /* DB_VERSION_MAJOR > 2 */ /* ** SMDB_DB_OPEN -- Opens a db database. ** ** Parameters: ** database -- An unallocated database pointer to a pointer. ** db_name -- The name of the database without extension. ** mode -- File permisions for a created database. ** mode_mask -- Mode bits that must match on an opened database. ** sff -- Flags for safefile. ** type -- The type of database to open ** See smdb_type_to_db2_type for valid types. ** user_info -- User information for file permissions. ** db_params -- ** An SMDB_DBPARAMS struct including params. These ** are processed according to the type of the ** database. Currently supported params (only for ** HASH type) are: ** num_elements ** cache_size ** ** Returns: ** SMDBE_OK -- Success, other errno: ** SMDBE_MALLOC -- Cannot allocate memory. ** SMDBE_BAD_OPEN -- db_open didn't return an error, but ** somehow the DB pointer is NULL. ** Anything else: translated error from db2 */ int smdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info, db_params) SMDB_DATABASE **database; char *db_name; int mode; int mode_mask; long sff; SMDB_DBTYPE type; SMDB_USER_INFO *user_info; SMDB_DBPARAMS *db_params; { bool lockcreated = false; int result; int db_flags; int lock_fd; int db_fd; int major_v, minor_v, patch_v; SMDB_DATABASE *smdb_db; SMDB_DB2_DATABASE *db2; DB *db; DBTYPE db_type; struct stat stat_info; char db_file_name[MAXPATHLEN]; (void) db_version(&major_v, &minor_v, &patch_v); if (major_v != DB_VERSION_MAJOR || minor_v != DB_VERSION_MINOR) return SMDBE_VERSION_MISMATCH; *database = NULL; result = smdb_add_extension(db_file_name, sizeof db_file_name, db_name, SMDB2_FILE_EXTENSION); if (result != SMDBE_OK) return result; result = smdb_setup_file(db_name, SMDB2_FILE_EXTENSION, mode_mask, sff, user_info, &stat_info); if (result != SMDBE_OK) return result; lock_fd = -1; if (stat_info.st_mode == ST_MODE_NOFILE && bitset(mode, O_CREAT)) lockcreated = true; result = smdb_lock_file(&lock_fd, db_name, mode, sff, SMDB2_FILE_EXTENSION); if (result != SMDBE_OK) return result; if (lockcreated) { mode |= O_TRUNC; mode &= ~(O_CREAT|O_EXCL); } smdb_db = smdb_malloc_database(); db2 = smdb2_malloc_database(); if (db2 == NULL || smdb_db == NULL) { smdb_unlock_file(lock_fd); smdb_free_database(smdb_db); /* ok to be NULL */ if (db2 != NULL) free(db2); return SMDBE_MALLOC; } db2->smdb2_lock_fd = lock_fd; db_type = smdb_type_to_db2_type(type); db = NULL; db_flags = 0; if (bitset(O_CREAT, mode)) db_flags |= DB_CREATE; if (bitset(O_TRUNC, mode)) db_flags |= DB_TRUNCATE; if (mode == O_RDONLY) db_flags |= DB_RDONLY; SM_DB_FLAG_ADD(db_flags); result = smdb_db_open_internal(db_file_name, db_type, db_flags, db_params, &db); if (result == 0 && db != NULL) { result = db->fd(db, &db_fd); if (result == 0) result = SMDBE_OK; } else { /* Try and narrow down on the problem */ if (result != 0) result = db2_error_to_smdb(result); else result = SMDBE_BAD_OPEN; } if (result == SMDBE_OK) result = smdb_filechanged(db_name, SMDB2_FILE_EXTENSION, db_fd, &stat_info); if (result == SMDBE_OK) { /* Everything is ok. Setup driver */ db2->smdb2_db = db; smdb_db->smdb_close = smdb2_close; smdb_db->smdb_del = smdb2_del; smdb_db->smdb_fd = smdb2_fd; smdb_db->smdb_lockfd = smdb2_lockfd; smdb_db->smdb_get = smdb2_get; smdb_db->smdb_put = smdb2_put; smdb_db->smdb_set_owner = smdb2_set_owner; smdb_db->smdb_sync = smdb2_sync; smdb_db->smdb_cursor = smdb2_cursor; smdb_db->smdb_impl = db2; *database = smdb_db; return SMDBE_OK; } if (db != NULL) db->close(db, 0); smdb_unlock_file(db2->smdb2_lock_fd); free(db2); smdb_free_database(smdb_db); return result; } #endif /* (DB_VERSION_MAJOR >= 2) */