/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2011-2021 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
. */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mudbm.h"
#ifndef O_EXCL
# define O_EXCL 0
#endif
#if defined(WITH_BDB)
#include
struct bdb_file
{
DB *db;
DBC *dbc;
mu_locker_t locker;
};
static int
_bdb_file_safety (mu_dbm_file_t db, int mode, uid_t owner)
{
return mu_file_safety_check (db->db_name, mode, owner, NULL);
}
static int
_bdb_get_fd (mu_dbm_file_t db, int *pag, int *dir)
{
struct bdb_file *bdb_file = db->db_descr;
int rc = bdb_file->db->fd (bdb_file->db, pag);
if (rc)
{
db->db_errno.n = rc;
return MU_ERR_FAILURE;
}
if (dir)
*dir = *pag;
return 0;
}
static int
do_bdb_open (mu_dbm_file_t mdb, int flags, int mode)
{
struct bdb_file *bdb_file = mdb->db_descr;
int f, rc;
enum mu_locker_mode locker_mode;
mu_locker_hints_t hints = { .flags = MU_LOCKER_FLAG_RETRY };
int tfd = -1;
switch (flags)
{
case MU_STREAM_CREAT:
f = DB_CREATE|DB_TRUNCATE;
locker_mode = mu_lck_exc;
break;
case MU_STREAM_READ:
f = DB_RDONLY;
locker_mode = mu_lck_shr;
break;
case MU_STREAM_RDWR:
f = DB_CREATE;
locker_mode = mu_lck_exc;
break;
default:
return EINVAL;
}
#ifdef DB_FCNTL_LOCKING
f |= DB_FCNTL_LOCKING;
hints.flags |= MU_LOCKER_FLAG_TYPE;
hints.type = MU_LOCKER_TYPE_KERNEL;
#endif
rc = mu_locker_create_ext (&bdb_file->locker, mdb->db_name, &hints);
if (rc)
return rc;
if (access (mdb->db_name, R_OK) && errno == ENOENT)
{
tfd = open (mdb->db_name, O_CREAT|O_RDONLY|O_EXCL,
mu_safety_criteria_to_file_mode (mdb->db_safety_flags));
if (tfd == -1)
{
mu_locker_destroy (&bdb_file->locker);
return errno;
}
}
rc = mu_locker_lock_mode (bdb_file->locker, locker_mode);
if (tfd != -1)
close (tfd);
switch (rc)
{
case 0:
break;
case EACCES:
mu_locker_destroy (&bdb_file->locker);
break;
default:
return rc;
}
rc = db_create (&bdb_file->db, NULL, 0);
if (rc != 0 || bdb_file->db == NULL)
return MU_ERR_FAILURE;
#if DB_VERSION_MAJOR == 3
rc = bdb_file->db->open (bdb_file->db, mdb->db_name, NULL, DB_HASH, f, mode);
#else
rc = bdb_file->db->open (bdb_file->db, NULL, mdb->db_name, NULL, DB_HASH,
f, mode);
#endif
if (rc)
return MU_ERR_FAILURE;
return 0;
}
static int
_bdb_close (mu_dbm_file_t db)
{
if (db->db_descr)
{
struct bdb_file *bdb_file = db->db_descr;
if (bdb_file->db)
bdb_file->db->close (bdb_file->db, 0);
if (bdb_file->locker)
{
mu_locker_unlock (bdb_file->locker);
mu_locker_destroy (&bdb_file->locker);
}
free (bdb_file);
db->db_descr = NULL;
}
return 0;
}
static int
_bdb_open (mu_dbm_file_t mdb, int flags, int mode)
{
int rc;
struct bdb_file *bdb_file;
bdb_file = calloc (1, sizeof *bdb_file);
if (!bdb_file)
return ENOMEM;
mdb->db_descr = bdb_file;
rc = do_bdb_open (mdb, flags, mode);
if (rc)
_bdb_close (mdb);
return rc;
}
static int
_bdb_fetch (mu_dbm_file_t db, struct mu_dbm_datum const *key,
struct mu_dbm_datum *ret)
{
DBT keydat, content;
struct bdb_file *bdb_file = db->db_descr;
int rc;
memset (&keydat, 0, sizeof keydat);
keydat.data = key->mu_dptr;
keydat.size = key->mu_dsize;
memset (&content, 0, sizeof content);
content.flags = DB_DBT_MALLOC;
rc = bdb_file->db->get (bdb_file->db, NULL, &keydat, &content, 0);
mu_dbm_datum_free (ret);
switch (rc)
{
case 0:
ret->mu_dptr = content.data;
ret->mu_dsize = content.size;
ret->mu_sys = db->db_sys;
break;
case DB_NOTFOUND:
db->db_errno.n = rc;
rc = MU_ERR_NOENT;
break;
default:
db->db_errno.n = rc;
rc = MU_ERR_FAILURE;
}
return rc;
}
static int
_bdb_store (mu_dbm_file_t db,
struct mu_dbm_datum const *key,
struct mu_dbm_datum const *contents,
int replace)
{
struct bdb_file *bdb_file = db->db_descr;
int rc;
DBT keydat, condat;
memset (&keydat, 0, sizeof keydat);
keydat.data = key->mu_dptr;
keydat.size = key->mu_dsize;
memset (&condat, 0, sizeof condat);
condat.data = contents->mu_dptr;
condat.size = contents->mu_dsize;
rc = bdb_file->db->put (bdb_file->db, NULL, &keydat, &condat,
replace ? 0 : DB_NOOVERWRITE);
db->db_errno.n = rc;
switch (rc)
{
case 0:
break;
case DB_KEYEXIST:
rc = MU_ERR_EXISTS;
break;
default:
rc = MU_ERR_FAILURE;
break;
}
return rc;
}
static int
_bdb_delete (mu_dbm_file_t db, struct mu_dbm_datum const *key)
{
DBT keydat;
struct bdb_file *bdb_file = db->db_descr;
int rc;
memset (&keydat, 0, sizeof keydat);
keydat.data = key->mu_dptr;
keydat.size = key->mu_dsize;
rc = bdb_file->db->del (bdb_file->db, NULL, &keydat, 0);
switch (rc)
{
case 0:
break;
case DB_NOTFOUND:
db->db_errno.n = rc;
rc = MU_ERR_NOENT;
break;
default:
db->db_errno.n = rc;
rc = MU_ERR_FAILURE;
}
return rc;
}
static int
_bdb_firstkey (mu_dbm_file_t db, struct mu_dbm_datum *ret)
{
struct bdb_file *bdb_file = db->db_descr;
int rc;
DBT key, dbt;
if (!bdb_file->dbc)
{
rc = bdb_file->db->cursor (bdb_file->db, NULL, &bdb_file->dbc
BDB2_CURSOR_LASTARG);
if (rc)
{
db->db_errno.n = rc;
return MU_ERR_FAILURE;
}
}
memset (&key, 0, sizeof key);
key.flags = DB_DBT_MALLOC;
memset (&dbt, 0, sizeof dbt);
dbt.flags = DB_DBT_MALLOC;
rc = bdb_file->dbc->c_get (bdb_file->dbc, &key, &dbt, DB_FIRST);
mu_dbm_datum_free (ret);
switch (rc)
{
case 0:
free (dbt.data); /* FIXME: cache it for the eventual fetch that can
follow */
ret->mu_dptr = key.data;
ret->mu_dsize = key.size;
ret->mu_sys = db->db_sys;
break;
case DB_NOTFOUND:
db->db_errno.n = rc;
rc = MU_ERR_NOENT;
break;
default:
db->db_errno.n = rc;
rc = MU_ERR_FAILURE;
}
return rc;
}
static int
_bdb_nextkey (mu_dbm_file_t db, struct mu_dbm_datum *ret)
{
struct bdb_file *bdb_file = db->db_descr;
int rc;
DBT key, dbt;
if (!bdb_file->dbc)
return MU_ERR_SEQ;
memset (&key, 0, sizeof key);
key.flags = DB_DBT_MALLOC;
memset (&dbt, 0, sizeof dbt);
dbt.flags = DB_DBT_MALLOC;
rc = bdb_file->dbc->c_get (bdb_file->dbc, &key, &dbt, DB_NEXT);
mu_dbm_datum_free (ret);
switch (rc)
{
case 0:
free (dbt.data); /* FIXME: cache it for the eventual fetch that can
follow */
ret->mu_dptr = key.data;
ret->mu_dsize = key.size;
ret->mu_sys = db->db_sys;
break;
case DB_NOTFOUND:
db->db_errno.n = rc;
rc = MU_ERR_NOENT;
break;
default:
db->db_errno.n = rc;
rc = MU_ERR_FAILURE;
}
return rc;
}
static void
_bdb_datum_free (struct mu_dbm_datum *datum)
{
free (datum->mu_dptr);
}
static char const *
_bdb_strerror (mu_dbm_file_t db)
{
return db_strerror (db->db_errno.n);
}
struct mu_dbm_impl _mu_dbm_bdb = {
"bdb",
_bdb_file_safety,
_bdb_get_fd,
_bdb_open,
_bdb_close,
_bdb_fetch,
_bdb_store,
_bdb_delete,
_bdb_firstkey,
_bdb_nextkey,
_bdb_datum_free,
_bdb_strerror
};
#endif