/*
Copyright (C) 1991, Marcus J. Ranum. All rights reserved.
*/
#ifndef lint
static char RCSid[] = "$Header: /usr/users/mjr/hacks/umud/DB/RCS/dbmchunk.c,v 1.7 91/08/18 21:49:38 mjr Exp $";
#endif
/* configure all options BEFORE including system stuff. */
#include "config.h"
#ifdef DB_DBMFILE
#ifdef VMS
#include <stdio.h>
#include <types.h>
#include <file.h>
#include <unixio.h>
#include "vms_dbm.h"
#else
#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#ifdef FCNTL_H
#include <fcntl.h>
#endif
#ifdef OLDDBM
#include <dbm.h>
#include <sys/errno.h>
extern int errno;
#else
#include <ndbm.h>
#endif
#endif VMS
#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 int dddb_mark();
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;
static void growbit();
int
dddb_init()
{
static char *copen = "db_init cannot open ";
char fnam[MAXPATHLEN];
struct stat sbuf;
struct hrec *hp;
#ifdef COMPRESS_OIF
comp_init();
comp_on(1);
#endif
/* now open chunk file */
sprintf(fnam,"%s.db",dbfile);
if((dbf = fopen(fnam,FOPEN_BINARY_RW)) == (FILE *)0) {
logf(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) {
logf(copen,dbfile," ",(char *)-1,"\n",(char *)0);
return(1);
}
sprintf(fnam,"%s.dir",dbfile);
fxp = open(fnam,O_CREAT|O_EXCL|O_WRONLY,0600);
if(fxp < 0) {
logf(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,0600);
if(fxp < 0) {
logf(copen,fnam," ",(char *)-1,"\n",(char *)0);
return(1);
}
(void)close(fxp);
/* one MORE try */
if((dbp = dbminit(dbfile)) < 0) {
logf(copen,dbfile," ",(char *)-1,"\n",(char *)0);
return(1);
}
}
#else
if((dbp = dbm_open(dbfile,O_RDWR|O_CREAT,0600)) == (DBM *)0) {
logf(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)) {
logf("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) {
logf("db_init index inconsistent\n",(char *)0);
return(1);
}
bcopy(dat.dptr,&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);
}
dddb_initted()
{
return(db_initted);
}
dddb_setbsiz(nbsiz)
int nbsiz;
{
return(bsiz = nbsiz);
}
dddb_setfile(fil)
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()
{
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(maxblok)
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,(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 int
dddb_mark(lbn,siz,taken)
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(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 = 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(nam)
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,&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)
logf("db_get: cannot decode ",nam,"\n",(char *)0);
return(ret);
}
int
dddb_put(obj,nam)
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,&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)) {
logf("db_put: can't store ",nam," ",(char *)-1,"\n",(char *)0);
return(1);
}
#else
if(dbm_store(dbp,key,dat,DBM_REPLACE)) {
logf("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)) {
logf("db_put: can't seek ",nam," ",(char *)-1,"\n",(char *)0);
return(1);
}
if(oiftoFILE(obj,dbf,nam) != 0 || fflush(dbf) != 0) {
logf("db_put: can't save ",nam," ",(char *)-1,"\n",(char *)0);
return(1);
}
return(0);
}
int
dddb_check(nam)
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(nam,flg)
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,&hbuf,sizeof(hbuf));
/* drop key from db */
#ifdef OLDDBM
if(delete(key)) {
#else
if(dbm_delete(dbp,key)) {
#endif
logf("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))
logf("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(obuf)
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(out)
char *out;
{
FILE *ou;
char vuf[BUFSIZ];
int tor;
char *xx;
if(!db_initted)
return(1);
if((ou = fopen(out,"w")) == (FILE *)0) {
logf("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) {
logf("db_init index inconsistent\n",(char *)0);
continue;
}
bcopy(dat.dptr,&hbuf,sizeof(hbuf));
if(fseek(dbf,(long)hbuf.off,0))
logf("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) {
logf("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