1N/A/*
1N/A** Copyright (c) 1999-2002, 2004, 2009 Sendmail, Inc. and its suppliers.
1N/A** All rights reserved.
1N/A**
1N/A** By using this file, you agree to the terms and conditions set
1N/A** forth in the LICENSE file which can be found at the top level of
1N/A** the sendmail distribution.
1N/A*/
1N/A
1N/A#include <sm/gen.h>
1N/ASM_RCSID("@(#)$Id: smdb1.c,v 8.62 2009/11/12 23:04:18 ca Exp $")
1N/A
1N/A#include <unistd.h>
1N/A#include <stdlib.h>
1N/A#include <fcntl.h>
1N/A
1N/A#include <sendmail/sendmail.h>
1N/A#include <libsmdb/smdb.h>
1N/A
1N/A#if (DB_VERSION_MAJOR == 1)
1N/A
1N/A# define SMDB1_FILE_EXTENSION "db"
1N/A
1N/Astruct smdb_db1_struct
1N/A{
1N/A DB *smdb1_db;
1N/A int smdb1_lock_fd;
1N/A bool smdb1_cursor_in_use;
1N/A};
1N/Atypedef struct smdb_db1_struct SMDB_DB1_DATABASE;
1N/A
1N/Astruct smdb_db1_cursor
1N/A{
1N/A SMDB_DB1_DATABASE *db;
1N/A};
1N/Atypedef struct smdb_db1_cursor SMDB_DB1_CURSOR;
1N/A
1N/Astatic DBTYPE smdb_type_to_db1_type __P((SMDB_DBTYPE));
1N/Astatic unsigned int smdb_put_flags_to_db1_flags __P((SMDB_FLAG));
1N/Astatic int smdb_cursor_get_flags_to_smdb1 __P((SMDB_FLAG));
1N/Astatic SMDB_DB1_DATABASE *smdb1_malloc_database __P((void));
1N/Astatic int smdb1_close __P((SMDB_DATABASE *));
1N/Astatic int smdb1_del __P((SMDB_DATABASE *, SMDB_DBENT *, unsigned int));
1N/Astatic int smdb1_fd __P((SMDB_DATABASE *, int *));
1N/Astatic int smdb1_lockfd __P((SMDB_DATABASE *));
1N/Astatic int smdb1_get __P((SMDB_DATABASE *, SMDB_DBENT *, SMDB_DBENT *, unsigned int));
1N/Astatic int smdb1_put __P((SMDB_DATABASE *, SMDB_DBENT *, SMDB_DBENT *, unsigned int));
1N/Astatic int smdb1_set_owner __P((SMDB_DATABASE *, uid_t, gid_t));
1N/Astatic int smdb1_sync __P((SMDB_DATABASE *, unsigned int));
1N/Astatic int smdb1_cursor_close __P((SMDB_CURSOR *));
1N/Astatic int smdb1_cursor_del __P((SMDB_CURSOR *, unsigned int));
1N/Astatic int smdb1_cursor_get __P((SMDB_CURSOR *, SMDB_DBENT *, SMDB_DBENT *, SMDB_FLAG));
1N/Astatic int smdb1_cursor_put __P((SMDB_CURSOR *, SMDB_DBENT *, SMDB_DBENT *, SMDB_FLAG));
1N/Astatic int smdb1_cursor __P((SMDB_DATABASE *, SMDB_CURSOR **, unsigned int));
1N/A
1N/A/*
1N/A** SMDB_TYPE_TO_DB1_TYPE -- Translates smdb database type to db1 type.
1N/A**
1N/A** Parameters:
1N/A** type -- The type to translate.
1N/A**
1N/A** Returns:
1N/A** The DB1 type that corresponsds to the passed in SMDB type.
1N/A** Returns -1 if there is no equivalent type.
1N/A**
1N/A*/
1N/A
1N/Astatic DBTYPE
1N/Asmdb_type_to_db1_type(type)
1N/A SMDB_DBTYPE type;
1N/A{
1N/A if (type == SMDB_TYPE_DEFAULT)
1N/A return DB_HASH;
1N/A
1N/A if (strncmp(type, SMDB_TYPE_HASH, SMDB_TYPE_HASH_LEN) == 0)
1N/A return DB_HASH;
1N/A
1N/A if (strncmp(type, SMDB_TYPE_BTREE, SMDB_TYPE_BTREE_LEN) == 0)
1N/A return DB_BTREE;
1N/A
1N/A /* Should never get here thanks to test in smdb_db_open() */
1N/A return DB_HASH;
1N/A}
1N/A/*
1N/A** SMDB_PUT_FLAGS_TO_DB1_FLAGS -- Translates smdb put flags to db1 put flags.
1N/A**
1N/A** Parameters:
1N/A** flags -- The flags to translate.
1N/A**
1N/A** Returns:
1N/A** The db1 flags that are equivalent to the smdb flags.
1N/A**
1N/A** Notes:
1N/A** Any invalid flags are ignored.
1N/A**
1N/A*/
1N/A
1N/Astatic unsigned int
1N/Asmdb_put_flags_to_db1_flags(flags)
1N/A SMDB_FLAG flags;
1N/A{
1N/A int return_flags;
1N/A
1N/A return_flags = 0;
1N/A
1N/A if (bitset(SMDBF_NO_OVERWRITE, flags))
1N/A return_flags |= R_NOOVERWRITE;
1N/A
1N/A return return_flags;
1N/A}
1N/A/*
1N/A** SMDB_CURSOR_GET_FLAGS_TO_SMDB1
1N/A**
1N/A** Parameters:
1N/A** flags -- The flags to translate.
1N/A**
1N/A** Returns:
1N/A** The db1 flags that are equivalent to the smdb flags.
1N/A**
1N/A** Notes:
1N/A** Returns -1 if we don't support the flag.
1N/A**
1N/A*/
1N/A
1N/Astatic int
1N/Asmdb_cursor_get_flags_to_smdb1(flags)
1N/A SMDB_FLAG flags;
1N/A{
1N/A switch(flags)
1N/A {
1N/A case SMDB_CURSOR_GET_FIRST:
1N/A return R_FIRST;
1N/A
1N/A case SMDB_CURSOR_GET_LAST:
1N/A return R_LAST;
1N/A
1N/A case SMDB_CURSOR_GET_NEXT:
1N/A return R_NEXT;
1N/A
1N/A case SMDB_CURSOR_GET_RANGE:
1N/A return R_CURSOR;
1N/A
1N/A default:
1N/A return -1;
1N/A }
1N/A}
1N/A
1N/A/*
1N/A** The rest of these functions correspond to the interface laid out in smdb.h.
1N/A*/
1N/A
1N/Astatic SMDB_DB1_DATABASE *
1N/Asmdb1_malloc_database()
1N/A{
1N/A SMDB_DB1_DATABASE *db1;
1N/A
1N/A db1 = (SMDB_DB1_DATABASE *) malloc(sizeof(SMDB_DB1_DATABASE));
1N/A
1N/A if (db1 != NULL)
1N/A {
1N/A db1->smdb1_lock_fd = -1;
1N/A db1->smdb1_cursor_in_use = false;
1N/A }
1N/A
1N/A return db1;
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_close(database)
1N/A SMDB_DATABASE *database;
1N/A{
1N/A int result;
1N/A SMDB_DB1_DATABASE *db1 = (SMDB_DB1_DATABASE *) database->smdb_impl;
1N/A DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
1N/A
1N/A result = db->close(db);
1N/A if (db1->smdb1_lock_fd != -1)
1N/A (void) close(db1->smdb1_lock_fd);
1N/A
1N/A free(db1);
1N/A database->smdb_impl = NULL;
1N/A
1N/A return result;
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_del(database, key, flags)
1N/A SMDB_DATABASE *database;
1N/A SMDB_DBENT *key;
1N/A unsigned int flags;
1N/A{
1N/A DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
1N/A DBT dbkey;
1N/A
1N/A (void) memset(&dbkey, '\0', sizeof dbkey);
1N/A dbkey.data = key->data;
1N/A dbkey.size = key->size;
1N/A return db->del(db, &dbkey, flags);
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_fd(database, fd)
1N/A SMDB_DATABASE *database;
1N/A int *fd;
1N/A{
1N/A DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
1N/A
1N/A *fd = db->fd(db);
1N/A if (*fd == -1)
1N/A return errno;
1N/A
1N/A return SMDBE_OK;
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_lockfd(database)
1N/A SMDB_DATABASE *database;
1N/A{
1N/A SMDB_DB1_DATABASE *db1 = (SMDB_DB1_DATABASE *) database->smdb_impl;
1N/A
1N/A return db1->smdb1_lock_fd;
1N/A}
1N/A
1N/A
1N/Astatic int
1N/Asmdb1_get(database, key, data, flags)
1N/A SMDB_DATABASE *database;
1N/A SMDB_DBENT *key;
1N/A SMDB_DBENT *data;
1N/A unsigned int flags;
1N/A{
1N/A int result;
1N/A DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
1N/A DBT dbkey, dbdata;
1N/A
1N/A (void) memset(&dbdata, '\0', sizeof dbdata);
1N/A (void) memset(&dbkey, '\0', sizeof dbkey);
1N/A dbkey.data = key->data;
1N/A dbkey.size = key->size;
1N/A
1N/A result = db->get(db, &dbkey, &dbdata, flags);
1N/A if (result != 0)
1N/A {
1N/A if (result == 1)
1N/A return SMDBE_NOT_FOUND;
1N/A return errno;
1N/A }
1N/A data->data = dbdata.data;
1N/A data->size = dbdata.size;
1N/A return SMDBE_OK;
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_put(database, key, data, flags)
1N/A SMDB_DATABASE *database;
1N/A SMDB_DBENT *key;
1N/A SMDB_DBENT *data;
1N/A unsigned int flags;
1N/A{
1N/A DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
1N/A DBT dbkey, dbdata;
1N/A
1N/A (void) memset(&dbdata, '\0', sizeof dbdata);
1N/A (void) memset(&dbkey, '\0', sizeof dbkey);
1N/A dbkey.data = key->data;
1N/A dbkey.size = key->size;
1N/A dbdata.data = data->data;
1N/A dbdata.size = data->size;
1N/A
1N/A return db->put(db, &dbkey, &dbdata,
1N/A smdb_put_flags_to_db1_flags(flags));
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_set_owner(database, uid, gid)
1N/A SMDB_DATABASE *database;
1N/A uid_t uid;
1N/A gid_t gid;
1N/A{
1N/A# if HASFCHOWN
1N/A int fd;
1N/A int result;
1N/A DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
1N/A
1N/A fd = db->fd(db);
1N/A if (fd == -1)
1N/A return errno;
1N/A
1N/A result = fchown(fd, uid, gid);
1N/A if (result < 0)
1N/A return errno;
1N/A# endif /* HASFCHOWN */
1N/A
1N/A return SMDBE_OK;
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_sync(database, flags)
1N/A SMDB_DATABASE *database;
1N/A unsigned int flags;
1N/A{
1N/A DB *db = ((SMDB_DB1_DATABASE *) database->smdb_impl)->smdb1_db;
1N/A
1N/A return db->sync(db, flags);
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_cursor_close(cursor)
1N/A SMDB_CURSOR *cursor;
1N/A{
1N/A SMDB_DB1_CURSOR *db1_cursor = (SMDB_DB1_CURSOR *) cursor->smdbc_impl;
1N/A SMDB_DB1_DATABASE *db1 = db1_cursor->db;
1N/A
1N/A if (!db1->smdb1_cursor_in_use)
1N/A return SMDBE_NOT_A_VALID_CURSOR;
1N/A
1N/A db1->smdb1_cursor_in_use = false;
1N/A free(cursor);
1N/A
1N/A return SMDBE_OK;
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_cursor_del(cursor, flags)
1N/A SMDB_CURSOR *cursor;
1N/A unsigned int flags;
1N/A{
1N/A SMDB_DB1_CURSOR *db1_cursor = (SMDB_DB1_CURSOR *) cursor->smdbc_impl;
1N/A SMDB_DB1_DATABASE *db1 = db1_cursor->db;
1N/A DB *db = db1->smdb1_db;
1N/A
1N/A return db->del(db, NULL, R_CURSOR);
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_cursor_get(cursor, key, value, flags)
1N/A SMDB_CURSOR *cursor;
1N/A SMDB_DBENT *key;
1N/A SMDB_DBENT *value;
1N/A SMDB_FLAG flags;
1N/A{
1N/A int db1_flags;
1N/A int result;
1N/A SMDB_DB1_CURSOR *db1_cursor = (SMDB_DB1_CURSOR *) cursor->smdbc_impl;
1N/A SMDB_DB1_DATABASE *db1 = db1_cursor->db;
1N/A DB *db = db1->smdb1_db;
1N/A DBT dbkey, dbdata;
1N/A
1N/A (void) memset(&dbdata, '\0', sizeof dbdata);
1N/A (void) memset(&dbkey, '\0', sizeof dbkey);
1N/A
1N/A db1_flags = smdb_cursor_get_flags_to_smdb1(flags);
1N/A result = db->seq(db, &dbkey, &dbdata, db1_flags);
1N/A if (result == -1)
1N/A return errno;
1N/A if (result == 1)
1N/A return SMDBE_LAST_ENTRY;
1N/A value->data = dbdata.data;
1N/A value->size = dbdata.size;
1N/A key->data = dbkey.data;
1N/A key->size = dbkey.size;
1N/A return SMDBE_OK;
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_cursor_put(cursor, key, value, flags)
1N/A SMDB_CURSOR *cursor;
1N/A SMDB_DBENT *key;
1N/A SMDB_DBENT *value;
1N/A SMDB_FLAG flags;
1N/A{
1N/A SMDB_DB1_CURSOR *db1_cursor = (SMDB_DB1_CURSOR *) cursor->smdbc_impl;
1N/A SMDB_DB1_DATABASE *db1 = db1_cursor->db;
1N/A DB *db = db1->smdb1_db;
1N/A DBT dbkey, dbdata;
1N/A
1N/A (void) memset(&dbdata, '\0', sizeof dbdata);
1N/A (void) memset(&dbkey, '\0', sizeof dbkey);
1N/A dbkey.data = key->data;
1N/A dbkey.size = key->size;
1N/A dbdata.data = value->data;
1N/A dbdata.size = value->size;
1N/A
1N/A return db->put(db, &dbkey, &dbdata, R_CURSOR);
1N/A}
1N/A
1N/Astatic int
1N/Asmdb1_cursor(database, cursor, flags)
1N/A SMDB_DATABASE *database;
1N/A SMDB_CURSOR **cursor;
1N/A unsigned int flags;
1N/A{
1N/A SMDB_DB1_DATABASE *db1 = (SMDB_DB1_DATABASE *) database->smdb_impl;
1N/A SMDB_CURSOR *cur;
1N/A SMDB_DB1_CURSOR *db1_cursor;
1N/A
1N/A if (db1->smdb1_cursor_in_use)
1N/A return SMDBE_ONLY_SUPPORTS_ONE_CURSOR;
1N/A
1N/A db1_cursor = (SMDB_DB1_CURSOR *) malloc(sizeof(SMDB_DB1_CURSOR));
1N/A if (db1_cursor == NULL)
1N/A return SMDBE_MALLOC;
1N/A
1N/A cur = (SMDB_CURSOR *) malloc(sizeof(SMDB_CURSOR));
1N/A if (cur == NULL)
1N/A {
1N/A free(db1_cursor);
1N/A return SMDBE_MALLOC;
1N/A }
1N/A
1N/A db1->smdb1_cursor_in_use = true;
1N/A db1_cursor->db = db1;
1N/A cur->smdbc_impl = db1_cursor;
1N/A cur->smdbc_close = smdb1_cursor_close;
1N/A cur->smdbc_del = smdb1_cursor_del;
1N/A cur->smdbc_get = smdb1_cursor_get;
1N/A cur->smdbc_put = smdb1_cursor_put;
1N/A *cursor = cur;
1N/A
1N/A return SMDBE_OK;
1N/A}
1N/A/*
1N/A** SMDB_DB_OPEN -- Opens a db1 database.
1N/A**
1N/A** Parameters:
1N/A** database -- An unallocated database pointer to a pointer.
1N/A** db_name -- The name of the database without extension.
1N/A** mode -- File permisions on the database if created.
1N/A** mode_mask -- Mode bits that must match on an existing database.
1N/A** sff -- Flags for safefile.
1N/A** type -- The type of database to open
1N/A** See smdb_type_to_db1_type for valid types.
1N/A** user_info -- Information on the user to use for file
1N/A** permissions.
1N/A** db_params --
1N/A** An SMDB_DBPARAMS struct including params. These
1N/A** are processed according to the type of the
1N/A** database. Currently supported params (only for
1N/A** HASH type) are:
1N/A** num_elements
1N/A** cache_size
1N/A**
1N/A** Returns:
1N/A** SMDBE_OK -- Success, otherwise errno.
1N/A*/
1N/A
1N/Aint
1N/Asmdb_db_open(database, db_name, mode, mode_mask, sff, type, user_info,
1N/A db_params)
1N/A SMDB_DATABASE **database;
1N/A char *db_name;
1N/A int mode;
1N/A int mode_mask;
1N/A long sff;
1N/A SMDB_DBTYPE type;
1N/A SMDB_USER_INFO *user_info;
1N/A SMDB_DBPARAMS *db_params;
1N/A{
1N/A bool lockcreated = false;
1N/A int db_fd;
1N/A int lock_fd;
1N/A int result;
1N/A void *params;
1N/A SMDB_DATABASE *smdb_db;
1N/A SMDB_DB1_DATABASE *db1;
1N/A DB *db;
1N/A HASHINFO hash_info;
1N/A BTREEINFO btree_info;
1N/A DBTYPE db_type;
1N/A struct stat stat_info;
1N/A char db_file_name[MAXPATHLEN];
1N/A
1N/A if (type == NULL ||
1N/A (strncmp(SMDB_TYPE_HASH, type, SMDB_TYPE_HASH_LEN) != 0 &&
1N/A strncmp(SMDB_TYPE_BTREE, type, SMDB_TYPE_BTREE_LEN) != 0))
1N/A return SMDBE_UNKNOWN_DB_TYPE;
1N/A
1N/A result = smdb_add_extension(db_file_name, sizeof db_file_name,
1N/A db_name, SMDB1_FILE_EXTENSION);
1N/A if (result != SMDBE_OK)
1N/A return result;
1N/A
1N/A result = smdb_setup_file(db_name, SMDB1_FILE_EXTENSION, mode_mask,
1N/A sff, user_info, &stat_info);
1N/A if (result != SMDBE_OK)
1N/A return result;
1N/A
1N/A if (stat_info.st_mode == ST_MODE_NOFILE &&
1N/A bitset(mode, O_CREAT))
1N/A lockcreated = true;
1N/A
1N/A lock_fd = -1;
1N/A result = smdb_lock_file(&lock_fd, db_name, mode, sff,
1N/A SMDB1_FILE_EXTENSION);
1N/A if (result != SMDBE_OK)
1N/A return result;
1N/A
1N/A if (lockcreated)
1N/A {
1N/A mode |= O_TRUNC;
1N/A mode &= ~(O_CREAT|O_EXCL);
1N/A }
1N/A
1N/A *database = NULL;
1N/A
1N/A smdb_db = smdb_malloc_database();
1N/A db1 = smdb1_malloc_database();
1N/A if (smdb_db == NULL || db1 == NULL)
1N/A {
1N/A (void) smdb_unlock_file(lock_fd);
1N/A smdb_free_database(smdb_db);
1N/A free(db1);
1N/A return SMDBE_MALLOC;
1N/A }
1N/A db1->smdb1_lock_fd = lock_fd;
1N/A
1N/A params = NULL;
1N/A if (db_params != NULL &&
1N/A (strncmp(SMDB_TYPE_HASH, type, SMDB_TYPE_HASH_LEN) == 0))
1N/A {
1N/A (void) memset(&hash_info, '\0', sizeof hash_info);
1N/A hash_info.nelem = db_params->smdbp_num_elements;
1N/A hash_info.cachesize = db_params->smdbp_cache_size;
1N/A params = &hash_info;
1N/A }
1N/A
1N/A if (db_params != NULL &&
1N/A (strncmp(SMDB_TYPE_BTREE, type, SMDB_TYPE_BTREE_LEN) == 0))
1N/A {
1N/A (void) memset(&btree_info, '\0', sizeof btree_info);
1N/A btree_info.cachesize = db_params->smdbp_cache_size;
1N/A if (db_params->smdbp_allow_dup)
1N/A btree_info.flags |= R_DUP;
1N/A params = &btree_info;
1N/A }
1N/A
1N/A db_type = smdb_type_to_db1_type(type);
1N/A db = dbopen(db_file_name, mode, DBMMODE, db_type, params);
1N/A if (db != NULL)
1N/A {
1N/A db_fd = db->fd(db);
1N/A result = smdb_filechanged(db_name, SMDB1_FILE_EXTENSION, db_fd,
1N/A &stat_info);
1N/A }
1N/A else
1N/A {
1N/A if (errno == 0)
1N/A result = SMDBE_BAD_OPEN;
1N/A else
1N/A result = errno;
1N/A }
1N/A
1N/A if (result == SMDBE_OK)
1N/A {
1N/A /* Everything is ok. Setup driver */
1N/A db1->smdb1_db = db;
1N/A
1N/A smdb_db->smdb_close = smdb1_close;
1N/A smdb_db->smdb_del = smdb1_del;
1N/A smdb_db->smdb_fd = smdb1_fd;
1N/A smdb_db->smdb_lockfd = smdb1_lockfd;
1N/A smdb_db->smdb_get = smdb1_get;
1N/A smdb_db->smdb_put = smdb1_put;
1N/A smdb_db->smdb_set_owner = smdb1_set_owner;
1N/A smdb_db->smdb_sync = smdb1_sync;
1N/A smdb_db->smdb_cursor = smdb1_cursor;
1N/A smdb_db->smdb_impl = db1;
1N/A
1N/A *database = smdb_db;
1N/A return SMDBE_OK;
1N/A }
1N/A
1N/A if (db != NULL)
1N/A (void) db->close(db);
1N/A
1N/A /* Error opening database */
1N/A (void) smdb_unlock_file(db1->smdb1_lock_fd);
1N/A free(db1);
1N/A smdb_free_database(smdb_db);
1N/A
1N/A return result;
1N/A}
1N/A
1N/A#endif /* (DB_VERSION_MAJOR == 1) */