/* 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 "mudbm.h"
#if defined(WITH_GDBM)
#include
struct gdbm_descr
{
GDBM_FILE file; /* GDBM file */
datum prev; /* Previous key for sequential access */
};
static int
_gdbm_file_safety (mu_dbm_file_t db, int mode, uid_t owner)
{
return mu_file_safety_check (db->db_name, mode, owner, NULL);
}
int
_gdbm_get_fd (mu_dbm_file_t db, int *pag, int *dir)
{
struct gdbm_descr *gd = db->db_descr;
*pag = gdbm_fdesc (gd->file);
if (dir)
*dir = *pag;
return 0;
}
static int
_gdbm_open (mu_dbm_file_t db, int flags, int mode)
{
int f;
struct gdbm_descr *gd;
GDBM_FILE file;
switch (flags)
{
case MU_STREAM_CREAT:
f = GDBM_NEWDB;
break;
case MU_STREAM_READ:
f = GDBM_READER;
break;
case MU_STREAM_RDWR:
f = GDBM_WRCREAT;
break;
default:
return EINVAL;
}
file = gdbm_open (db->db_name, 512, f, mode, NULL);
if (!file)
return MU_ERR_FAILURE;
gd = calloc (1, sizeof (*gd));
gd->file = file;
db->db_descr = gd;
return 0;
}
static int
_gdbm_close (mu_dbm_file_t db)
{
if (db->db_descr)
{
struct gdbm_descr *gd = db->db_descr;
gdbm_close (gd->file);
free (gd);
db->db_descr = NULL;
}
return 0;
}
static int
_gdbm_conv_datum (mu_dbm_file_t db, struct mu_dbm_datum *ret, datum content,
int copy)
{
if (copy)
{
ret->mu_dptr = malloc (content.dsize);
if (!ret->mu_dptr)
return errno;
memcpy (ret->mu_dptr, content.dptr, content.dsize);
}
else
{
ret->mu_dptr = content.dptr;
}
ret->mu_dsize = content.dsize;
ret->mu_sys = db->db_sys;
return 0;
}
static int
_gdbm_fetch (mu_dbm_file_t db, struct mu_dbm_datum const *key,
struct mu_dbm_datum *ret)
{
struct gdbm_descr *gd = db->db_descr;
int rc;
datum keydat, content;
keydat.dptr = key->mu_dptr;
keydat.dsize = key->mu_dsize;
gdbm_errno = 0;
content = gdbm_fetch (gd->file, keydat);
if (content.dptr == NULL)
{
if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
return MU_ERR_NOENT;
else
{
db->db_errno.n = gdbm_errno;
return MU_ERR_FAILURE;
}
}
mu_dbm_datum_free (ret);
rc = _gdbm_conv_datum (db, ret, content, 1);
if (rc)
{
free (content.dptr);
return rc;
}
return 0;
}
static int
_gdbm_store (mu_dbm_file_t db,
struct mu_dbm_datum const *key,
struct mu_dbm_datum const *contents,
int replace)
{
struct gdbm_descr *gd = db->db_descr;
datum keydat, condat;
keydat.dptr = key->mu_dptr;
keydat.dsize = key->mu_dsize;
condat.dptr = contents->mu_dptr;
condat.dsize = contents->mu_dsize;
switch (gdbm_store (gd->file, keydat, condat, replace))
{
case 0:
break;
case 1:
return MU_ERR_EXISTS;
case -1:
db->db_errno.n = gdbm_errno;
return MU_ERR_FAILURE;
}
return 0;
}
static int
_gdbm_delete (mu_dbm_file_t db, struct mu_dbm_datum const *key)
{
struct gdbm_descr *gd = db->db_descr;
datum keydat;
keydat.dptr = key->mu_dptr;
keydat.dsize = key->mu_dsize;
gdbm_errno = 0;
if (gdbm_delete (gd->file, keydat))
{
if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
return MU_ERR_NOENT;
else
{
db->db_errno.n = gdbm_errno;
return MU_ERR_FAILURE;
}
}
return 0;
}
static int
_gdbm_firstkey (mu_dbm_file_t db, struct mu_dbm_datum *ret)
{
struct gdbm_descr *gd = db->db_descr;
int rc;
datum key = gdbm_firstkey (gd->file);
if (key.dptr == NULL)
{
if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
return MU_ERR_NOENT;
else
{
db->db_errno.n = gdbm_errno;
return MU_ERR_FAILURE;
}
}
mu_dbm_datum_free (ret);
rc = _gdbm_conv_datum (db, ret, key, 0);
if (rc)
{
free (key.dptr);
return rc;
}
free (gd->prev.dptr);
gd->prev = key;
return 0;
}
static int
_gdbm_nextkey (mu_dbm_file_t db, struct mu_dbm_datum *ret)
{
struct gdbm_descr *gd = db->db_descr;
int rc;
datum key;
if (!gd->prev.dptr)
return MU_ERR_NOENT;
key = gdbm_nextkey (gd->file, gd->prev);
if (key.dptr == NULL)
{
if (gdbm_errno == GDBM_ITEM_NOT_FOUND)
return MU_ERR_NOENT;
else
{
db->db_errno.n = gdbm_errno;
return MU_ERR_FAILURE;
}
}
mu_dbm_datum_free (ret);
rc = _gdbm_conv_datum (db, ret, key, 0);
if (rc)
{
free (key.dptr);
return rc;
}
free (gd->prev.dptr);
gd->prev = key;
return 0;
}
static void
_gdbm_datum_free (struct mu_dbm_datum *datum)
{
free (datum->mu_dptr);
}
static char const *
_gdbm_strerror (mu_dbm_file_t db)
{
return gdbm_strerror (db->db_errno.n);
}
struct mu_dbm_impl _mu_dbm_gdbm = {
"gdbm",
_gdbm_file_safety,
_gdbm_get_fd,
_gdbm_open,
_gdbm_close,
_gdbm_fetch,
_gdbm_store,
_gdbm_delete,
_gdbm_firstkey,
_gdbm_nextkey,
_gdbm_datum_free,
_gdbm_strerror
};
#endif