/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
Slightly whacked by Andrew Molitor for use with MUSH.
My apologies to the author..
*/
#include "autoconf.h"
#include "copyright.h"
#ifndef lint
static char *RCSid = "$Id: udb_achunk.c,v 1.8 1995/03/21 00:01:20 ambar Exp $";
USE(RCSid);
#endif
/* First */
#include "config.h"
#include "udb.h"
#include "udb_defs.h"
#include "externs.h"
#ifdef VMS
#include <types.h>
#include <file.h>
#include <unixio.h>
#include "vms_dbm.h"
#else
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#ifdef HAVE_NDBM
#include <ndbm.h>
#else
#ifdef HAVE_DBM
#include <dbm.h>
#else
#include "myndbm.h"
#endif
#endif
#endif /* VMS */
extern int FDECL(attr_siz, (Attr *));
extern void VDECL(fatal, (char *,...));
extern void FDECL(log_db_err, (int, int, const char *));
/*
#define DBMCHUNK_DEBUG
*/
/* Default database name */
#define DEFAULT_DBMCHUNKFILE "db_dbm"
/* default block size to use for bitmapped chunks */
#define DDDB_BLOCK 64
/* 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-1)) / 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 int dddb_mark();
static const 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 HAVE_DBM
static int dbp = 0;
#else
static DBM *dbp = (DBM *) NULL;
#endif
static FILE *dbf;
static struct hrec hbuf;
static char *bitm = NULL;
static int bitblox = 0;
static datum dat;
static datum key;
static void growbit();
extern void logf();
int
dddb_init()
{
static const char *copen = "db_init cannot open ";
char fnam[MAXPATHLEN];
struct stat sbuf;
int fxp;
/* now open chunk file */
sprintf(fnam, "%s.db", dbfile);
/* Bah. Posix-y systems break "a+", so use this gore instead */
if (((dbf = fopen(fnam, "r+")) == (FILE *) NULL)
&& ((dbf = fopen(fnam, "w+")) == (FILE *) NULL)) {
logf(copen, fnam, " ", (char *) -1, "\n", NULL);
return (1);
}
/* Some systems (like HP-UX don't like nonexistent binary files.
* Therefore, create empty files if none exist already.
*/
sprintf(fnam, "%s.dir", dbfile);
fxp = open(fnam, O_CREAT | O_RDWR, 0600);
if (fxp < 0) {
logf(copen, fnam, " ", (char *) -1, "\n", NULL);
return (1);
}
(void) close(fxp);
sprintf(fnam, "%s.pag", dbfile);
fxp = open(fnam, O_CREAT | O_RDWR, 0600);
if (fxp < 0) {
logf(copen, fnam, " ", (char *) -1, "\n", NULL);
return (1);
}
(void) close(fxp);
/* open hash table */
#ifdef HAVE_DBM
if ((dbp = dbminit(dbfile)) < 0) {
logf(copen, dbfile, " ", (char *) -1, "\n", NULL);
return (1);
}
#else
if ((dbp = dbm_open(dbfile, O_RDWR | O_CREAT, 0600)) == (DBM *) NULL) {
logf(copen, dbfile, " ", (char *) -1, "\n", NULL);
return (1);
}
#endif
/* determine size of chunk file for allocation bitmap */
sprintf(fnam, "%s.db", dbfile);
if (stat(fnam, &sbuf)) {
logf("db_init cannot stat ", fnam, " ", (char *) -1, "\n", NULL);
return (1);
}
/* allocate bitmap */
growbit(LOGICAL_BLOCK(sbuf.st_size) + DDDB_BITBLOCK);
#ifdef HAVE_DBM
key = firstkey();
#else
key = dbm_firstkey(dbp);
#endif
while (key.dptr != NULL) {
#ifdef HAVE_DBM
dat = fetch(key);
#else
dat = dbm_fetch(dbp, key);
#endif
if (dat.dptr == NULL) {
logf("db_init index inconsistent\n", NULL);
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 HAVE_DBM
key = nextkey(key);
#else
key = dbm_nextkey(dbp);
#endif
}
db_initted = 1;
return (0);
}
int
dddb_initted()
{
return (db_initted);
}
int
dddb_setbsiz(nbsiz)
int nbsiz;
{
return (bsiz = nbsiz);
}
int
dddb_setfile(fil)
char *fil;
{
char *xp;
if (db_initted)
return (1);
/* KNOWN memory leak. can't help it. it's small */
xp = strsave(fil);
if (xp == NULL)
return (1);
dbfile = xp;
return (0);
}
int
dddb_close()
{
if (dbf != (FILE *) NULL) {
fclose(dbf);
dbf = (FILE *) NULL;
}
#ifndef HAVE_DBM
if (dbp != (DBM *) NULL) {
dbm_close(dbp);
dbp = (DBM *) NULL;
}
#endif
if (bitm != NULL) {
free((mall_t) bitm);
bitm = NULL;
bitblox = 0;
}
db_initted = 0;
return (0);
}
/* grow the bitmap to given size */
static void
growbit(maxblok)
int maxblok;
{
int nsiz;
char *nbit;
/* round up to eight and then some */
nsiz = (maxblok + 15) & 0xfffffff8;
if (nsiz <= bitblox)
return;
if (bitm == (char *) NULL)
bitm = XMALLOC(nsiz / 8, "");
else
bitm = realloc(bitm, nsiz / 8);
if (bitm == NULL)
fatal("db_init cannot grow bitmap ", (char *) -1, "\n", NULL);
bzero(bitm + (bitblox / 8), (nsiz - bitblox) / 8);
bitblox = nsiz - 8;
}
static int
dddb_mark(lbn, siz, taken)
off_t lbn;
int siz;
int taken;
{
int bcnt;
if (siz == 0)
return (0);
if (siz < 0) {
logf("Invalid size in dddb_mark.", NULL);
return (0);
}
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++;
}
return 0;
}
static int
dddb_alloc(siz)
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 = BLOCKS_NEEDED(siz);
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++;
}
}
Attr *
dddb_get(nam)
Aname *nam;
{
Attr *ret;
if (!db_initted)
return ((Attr *) NULL);
key.dptr = (char *) nam;
key.dsize = sizeof(Aname);
#ifdef HAVE_DBM
dat = fetch(key);
#else
dat = dbm_fetch(dbp, key);
#endif
if (dat.dptr == NULL)
return (ANULL);
bcopy(dat.dptr, (char *) &hbuf, sizeof(hbuf));
/* seek to location */
if (fseek(dbf, (long) hbuf.off, 0) == -1) {
log_db_err(nam->object, nam->attrnum,
"(Get) Seeking within dbfile");
return (ANULL);
}
/* if the file is badly formatted, ret == Attr * 0 */
ret = attrfromFILE(dbf, hbuf.siz);
if (ret == ANULL)
log_db_err(nam->object, nam->attrnum,
"(Get) Damage detected within dbfile");
return (ret);
}
int
dddb_put(obj, nam)
Attr *obj;
Aname *nam;
{
int nsiz;
if (!db_initted)
return (1);
nsiz = attr_siz(obj);
key.dptr = (char *) nam;
key.dsize = sizeof(Aname);
#ifdef HAVE_DBM
dat = fetch(key);
#else
dat = dbm_fetch(dbp, key);
#endif
if (dat.dptr != NULL) {
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 HAVE_DBM
if (store(key, dat)) {
log_db_err(nam->object, nam->attrnum,
"(Put) Storing index entry");
return (1);
}
#else
if (dbm_store(dbp, key, dat, DBM_REPLACE)) {
log_db_err(nam->object, nam->attrnum,
"(Put) Storing index entry");
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) == -1) {
log_db_err(nam->object, nam->attrnum,
"(Put) Seeking within dbfile");
return (1);
}
if (attrtoFILE(obj, dbf, nsiz) != 0) {
log_db_err(nam->object, nam->attrnum,
"(Put) Writing attribute");
return (1);
}
return (0);
}
int
dddb_check(nam)
Aname *nam;
{
if (!db_initted)
return (0);
key.dptr = (char *) nam;
key.dsize = sizeof(Aname);
#ifdef HAVE_DBM
dat = fetch(key);
#else
dat = dbm_fetch(dbp, key);
#endif
if (dat.dptr == NULL)
return (0);
return (1);
}
int
dddb_del(nam, flg)
Aname *nam;
int flg;
{
if (!db_initted)
return (-1);
key.dptr = (char *) nam;
key.dsize = sizeof(Aname);
#ifdef HAVE_DBM
dat = fetch(key);
#else
dat = dbm_fetch(dbp, key);
#endif
/* not there? */
if (dat.dptr == NULL)
return (0);
bcopy(dat.dptr, (char *) &hbuf, sizeof(hbuf));
/* drop key from db */
#ifdef HAVE_DBM
if (delete(key)) {
#else
if (dbm_delete(dbp, key)) {
#endif
log_db_err(nam->object, nam->attrnum,
"(Del) Removing index entry");
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
return (0);
}