/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/
/* configure all options BEFORE including system stuff. */
#include "config.h"
#ifdef DB_DBMFILE
#ifdef OLDDBM
#ifdef WIN32
#include "dbm.h"
#else
#include <dbm.h>
#endif
#else
#ifdef WIN32
#include "ndbm.h"
#else
#include <ndbm.h>
#endif
#endif
#include "mud.h"
#include "sbuf.h"
/*
#define DBMCHUNK_DEBUG
*/
/* default block size to use for bitmapped chunks */
#define DDDB_BLOCK 256
/* bitmap growth increment in BLOCKS not bytes (512 is 64 BYTES) */
#define DDDB_BITBLOCK 512
#define LOGICAL_BLOCK(off) (off/bsiz)
#define BLOCK_OFFSET(block) (block*bsiz)
#define BLOCKS_NEEDED(siz) (siz % bsiz ? (siz / bsiz) + 1 : (siz / bsiz))
/*
dbm-based object storage routines. Somewhat trickier than the default
directory hash. a free space list is maintained as a bitmap of free
blocks, and an object-pointer list is maintained in a dbm database.
*/
struct hrec {
off_t off;
int siz;
};
static void dddb_mark (off_t lbn, int siz, int taken);
static void growbit (int maxblok);
static char *dbfile = DEFAULT_DBMCHUNKFILE;
static int bsiz = DDDB_BLOCK;
static int db_initted = 0;
static int last_free = 0; /* last known or suspected free block */
#ifdef OLDDBM
static int dbp = 0;
#else
static DBM *dbp = (DBM *) 0;
#endif
static FILE *dbf = (FILE *) 0;
static struct hrec hbuf;
static int startrav = 0;
static char *bitm = (char *) 0;
static int bitblox = 0;
static datum dat;
static datum key;
int dddb_init (void)
{
static char *copen = "db_init cannot open ";
char fnam[MAXPATHLEN];
struct stat sbuf;
#ifdef COMPRESS_OIF
comp_init ();
comp_on (1);
#endif
/* now open chunk file */
sprintf (fnam, "%s.db", dbfile);
/* first try to open existing file then try opening new file */
if ((dbf = fopen (fnam, "rb+")) == (FILE *) 0) {
if ((dbf = fopen (fnam, "wb+")) == (FILE *) 0) {
log_printf (copen, fnam, " ", (char *) -1, "\n", (char *) 0);
return (1);
}
}
/* open hash table */
#ifdef OLDDBM
if ((dbp = dbminit (dbfile)) < 0) {
int fxp;
/* dbm (old) is stupid about nonexistent files */
if (errno != ENOENT) {
log_printf (copen, dbfile, " ", (char *) -1, "\n", (char *) 0);
return (1);
}
sprintf (fnam, "%s.dir", dbfile);
fxp = open (fnam, O_CREAT | O_EXCL | O_WRONLY, S_IREAD | S_IWRITE);
if (fxp < 0) {
log_printf (copen, fnam, " ", (char *) -1, "\n", (char *) 0);
return (1);
}
(void) close (fxp);
sprintf (fnam, "%s.pag", dbfile);
fxp = open (fnam, O_CREAT | O_EXCL | O_WRONLY, S_IREAD | S_IWRITE);
if (fxp < 0) {
log_printf (copen, fnam, " ", (char *) -1, "\n", (char *) 0);
return (1);
}
(void) close (fxp);
/* one MORE try */
if ((dbp = dbminit (dbfile)) < 0) {
log_printf (copen, dbfile, " ", (char *) -1, "\n", (char *) 0);
return (1);
}
}
#else
if ((dbp = dbm_open (dbfile, O_RDWR | O_CREAT, S_IREAD | S_IWRITE)) == (DBM *) 0) {
log_printf (copen, dbfile, " ", (char *) -1, "\n", (char *) 0);
return (1);
}
#endif
/* determine size of chunk file for allocation bitmap */
sprintf (fnam, "%s.db", dbfile);
if (stat (fnam, &sbuf)) {
log_printf ("db_init cannot stat ", fnam, " ", (char *) -1, "\n",
(char *) 0);
return (1);
}
/* allocate bitmap */
growbit (LOGICAL_BLOCK (sbuf.st_size) + DDDB_BITBLOCK);
#ifdef OLDDBM
key = firstkey ();
#else
key = dbm_firstkey (dbp);
#endif
while (key.dptr != (char *) 0) {
#ifdef OLDDBM
dat = fetch (key);
#else
dat = dbm_fetch (dbp, key);
#endif
if (dat.dptr == (char *) 0) {
log_printf ("db_init index inconsistent\n", (char *) 0);
return (1);
}
bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf)); /* alignment */
/* mark it as busy in the bitmap */
dddb_mark (LOGICAL_BLOCK (hbuf.off), hbuf.siz, 1);
#ifdef OLDDBM
key = nextkey (key);
#else
key = dbm_nextkey (dbp);
#endif
}
db_initted = 1;
return (0);
}
int dddb_initted (void)
{
return (db_initted);
}
int dddb_setbsiz (int nbsiz)
{
return (bsiz = nbsiz);
}
int dddb_setfile (char *fil)
{
char *xp;
if (db_initted)
return (1);
/* KNOWN memory leak. can't help it. it's small */
xp = (char *) malloc ((unsigned) strlen (fil) + 1);
if (xp == (char *) 0)
return (1);
(void) strcpy (xp, fil);
dbfile = xp;
return (0);
}
int dddb_close (void)
{
if (dbf != (FILE *) 0) {
fclose (dbf);
dbf = (FILE *) 0;
}
#ifndef OLDDBM
if (dbp != (DBM *) 0) {
dbm_close (dbp);
dbp = (DBM *) 0;
}
#endif
if (bitm != (char *) 0) {
free ((mall_t) bitm);
bitm = (char *) 0;
bitblox = 0;
}
db_initted = 0;
return (0);
}
/* grow the bitmap to given size */
static void growbit (int maxblok)
{
int nsiz;
char *nbit;
/* round up to eight and then some */
nsiz = (maxblok + 8) + (8 - (maxblok % 8));
if (nsiz <= bitblox)
return;
/* this done because some old realloc()s are busted */
nbit = (char *) malloc ((unsigned) (nsiz / 8));
if (bitm != (char *) 0) {
bcopy (bitm, nbit, (unsigned) ((bitblox / 8) - 1));
free ((mall_t) bitm);
}
bitm = nbit;
if (bitm == (char *) 0)
fatal ("db_init cannot grow bitmap ", (char *) -1, "\n", (char *) 0);
bzero (bitm + (bitblox / 8), ((nsiz / 8) - (bitblox / 8)) - 1);
bitblox = nsiz - 8;
}
static void dddb_mark (off_t lbn, int siz, int taken)
{
int bcnt;
bcnt = BLOCKS_NEEDED (siz);
/* remember a free block was here */
if (!taken)
last_free = lbn;
while (bcnt--) {
if (lbn >= bitblox - 32)
growbit (lbn + DDDB_BITBLOCK);
if (taken)
bitm[lbn >> 3] |= (1 << (lbn & 7));
else
bitm[lbn >> 3] &= ~(1 << (lbn & 7));
lbn++;
}
}
static int dddb_alloc (int siz)
{
int bcnt; /* # of blocks to operate on */
int lbn; /* logical block offset */
int tbcnt;
int slbn;
int overthetop = 0;
lbn = last_free;
bcnt = siz % bsiz ? (siz / bsiz) + 1 : (siz / bsiz);
while (1) {
if (lbn >= bitblox - 32) {
/* only check here. can't break around the top */
if (!overthetop) {
lbn = 0;
overthetop++;
} else {
growbit (lbn + DDDB_BITBLOCK);
}
}
slbn = lbn;
tbcnt = bcnt;
while (1) {
if ((bitm[lbn >> 3] & (1 << (lbn & 7))) != 0)
break;
/* enough free blocks - mark and done */
if (--tbcnt == 0) {
for (tbcnt = slbn; bcnt > 0; tbcnt++, bcnt--)
bitm[tbcnt >> 3] |= (1 << (tbcnt & 7));
last_free = lbn;
return (slbn);
}
lbn++;
if (lbn >= bitblox - 32)
growbit (lbn + DDDB_BITBLOCK);
}
lbn++;
}
}
Obj *dddb_get (char *nam)
{
Obj *ret;
if (!db_initted)
return ((Obj *) 0);
key.dptr = nam;
key.dsize = strlen (nam) + 1;
#ifdef OLDDBM
dat = fetch (key);
#else
dat = dbm_fetch (dbp, key);
#endif
if (dat.dptr == (char *) 0)
return ((Obj *) 0);
bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf));
/* seek to location */
if (fseek (dbf, (long) hbuf.off, 0))
return ((Obj *) 0);
/* if the file is badly formatted, ret == Obj * 0 */
if ((ret = oiffromFILE (dbf, (char *) 0)) == (Obj *) 0)
log_printf ("db_get: cannot decode ", nam, "\n", (char *) 0);
return (ret);
}
int dddb_put (Obj * obj, char *nam)
{
int nsiz;
if (!db_initted)
return (1);
nsiz = oif_objsiz (obj, nam);
key.dptr = nam;
key.dsize = strlen (nam) + 1;
#ifdef OLDDBM
dat = fetch (key);
#else
dat = dbm_fetch (dbp, key);
#endif
if (dat.dptr != (char *) 0) {
bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf)); /* align */
if (BLOCKS_NEEDED (nsiz) > BLOCKS_NEEDED (hbuf.siz)) {
#ifdef DBMCHUNK_DEBUG
printf ("put: %s old %d < %d - free %d\n", nam, hbuf.siz, nsiz,
hbuf.off);
#endif
/* mark free in bitmap */
dddb_mark (LOGICAL_BLOCK (hbuf.off), hbuf.siz, 0);
hbuf.off = BLOCK_OFFSET (dddb_alloc (nsiz));
hbuf.siz = nsiz;
#ifdef DBMCHUNK_DEBUG
printf ("put: %s moved to offset %d, size %d\n", nam, hbuf.off,
hbuf.siz);
#endif
} else {
hbuf.siz = nsiz;
#ifdef DBMCHUNK_DEBUG
printf ("put: %s replaced within offset %d, size %d\n", nam, hbuf.off,
hbuf.siz);
#endif
}
} else {
hbuf.off = BLOCK_OFFSET (dddb_alloc (nsiz));
hbuf.siz = nsiz;
#ifdef DBMCHUNK_DEBUG
printf ("put: %s (new) at offset %d, size %d\n", nam, hbuf.off, hbuf.siz);
#endif
}
/* make table entry */
dat.dptr = (char *) &hbuf;
dat.dsize = sizeof (hbuf);
#ifdef OLDDBM
if (store (key, dat)) {
log_printf ("db_put: can't store ", nam, " ", (char *) -1, "\n",
(char *) 0);
return (1);
}
#else
if (dbm_store (dbp, key, dat, DBM_REPLACE)) {
log_printf ("db_put: can't dbm_store ", nam, " ", (char *) -1, "\n",
(char *) 0);
return (1);
}
#endif
#ifdef DBMCHUNK_DEBUG
printf ("%s offset %d size %d\n", nam, hbuf.off, hbuf.siz);
#endif
if (fseek (dbf, (long) hbuf.off, 0)) {
log_printf ("db_put: can't seek ", nam, " ", (char *) -1, "\n",
(char *) 0);
return (1);
}
if (oiftoFILE (obj, dbf, nam) != 0 || fflush (dbf) != 0) {
log_printf ("db_put: can't save ", nam, " ", (char *) -1, "\n",
(char *) 0);
return (1);
}
return (0);
}
int dddb_check (char *nam)
{
if (!db_initted)
return (0);
key.dptr = nam;
key.dsize = strlen (nam) + 1;
#ifdef OLDDBM
dat = fetch (key);
#else
dat = dbm_fetch (dbp, key);
#endif
if (dat.dptr == (char *) 0)
return (0);
return (1);
}
int dddb_del (char *nam, int flg)
{
if (!db_initted)
return (-1);
key.dptr = nam;
key.dsize = strlen (nam) + 1;
#ifdef OLDDBM
dat = fetch (key);
#else
dat = dbm_fetch (dbp, key);
#endif
/* not there? */
if (dat.dptr == (char *) 0)
return (0);
bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf));
/* drop key from db */
#ifdef OLDDBM
if (delete (key)) {
#else
if (dbm_delete (dbp, key)) {
#endif
log_printf ("db_del: can't delete key ", nam, "\n", (char *) 0);
return (1);
}
/* mark free space in bitmap */
dddb_mark (LOGICAL_BLOCK (hbuf.off), hbuf.siz, 0);
#ifdef DBMCHUNK_DEBUG
printf ("del: %s free offset %d, size %d\n", nam, hbuf.off, hbuf.siz);
#endif
/* mark object dead in file */
if (fseek (dbf, (long) hbuf.off, 0))
log_printf ("db_del: can't seek ", nam, " ", (char *) -1, "\n",
(char *) 0);
else {
fprintf (dbf, "delobj");
fflush (dbf);
}
return (0);
}
int dddb_travstart ()
{
startrav = 0;
return (0);
}
int dddb_traverse (char *obuf)
{
if (!db_initted)
return (0);
if (!startrav) {
#ifdef OLDDBM
key = firstkey ();
#else
key = dbm_firstkey (dbp);
#endif
startrav = 1;
} else
#ifdef OLDDBM
key = nextkey (key);
#else
key = dbm_nextkey (dbp);
#endif
if (key.dptr == (char *) 0)
return (0);
strncpy (obuf, key.dptr, MAXOID);
return (1);
}
int dddb_travend ()
{
startrav = 0;
return (0);
}
int dddb_backup (char *out)
{
FILE *ou;
char vuf[BUFSIZ];
int tor;
if (!db_initted)
return (1);
if ((ou = fopen (out, "wb")) == (FILE *) 0) {
log_printf ("db_backup: ", out, " ", (char *) -1, "\n", (char *) 0);
return (1);
}
#ifdef OLDDBM
key = firstkey ();
#else
key = dbm_firstkey (dbp);
#endif
while (key.dptr != (char *) 0) {
#ifdef OLDDBM
dat = fetch (key);
#else
dat = dbm_fetch (dbp, key);
#endif
if (dat.dptr == (char *) 0) {
log_printf ("db_init index inconsistent\n", (char *) 0);
continue;
}
bcopy (dat.dptr, (char *) &hbuf, sizeof (hbuf));
if (fseek (dbf, (long) hbuf.off, 0))
log_printf ("db_backup: can't seek ", key.dptr, " ", (char *) -1, "\n",
(char *) 0);
else {
/* copy out the object, sans interpretation */
while (hbuf.siz > 0) {
if ((tor = hbuf.siz) > sizeof (vuf))
tor = sizeof (vuf);
if (fread (vuf, sizeof (char), tor, dbf) <= 0) {
log_printf ("db_backup: can't read ", key.dptr, " ", (char *) -1,
"\n", (char *) 0);
break;
}
/* what are we gonna do if it fails, anyhow? */
fwrite (vuf, sizeof (char), tor, ou);
hbuf.siz -= tor;
}
}
#ifdef OLDDBM
key = nextkey (key);
#else
key = dbm_nextkey (dbp);
#endif
}
fclose (ou);
return (0);
}
#endif // DB_DBMFILE