/*
dynamic.c dynamic files (in theory)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <unistd.h>
#include <fcntl.h>
/* just in case youre on a dippy system */
#ifndef S_IRUSR
#define S_IRUSR 00400
#endif
#ifndef S_IWUSR
#define S_IWUSR 00200
#endif
#include "config.h"
#include "player.h"
#include "dynamic.h"
/* External functions */
extern char *end_string(char *);
extern char *store_int(char *,int);
extern char *get_int(int *,char *);
extern dfile *room_df;
extern saved_player **saved_hash[];
/* throw the keylist to disk */
void dynamic_key_sync(dfile *df)
{
int fd,length;
char *oldstack,*to;
oldstack=stack;
store_int((char *)df->keylist,df->first_free_block);
store_int((char *)(df->keylist+1),df->first_free_key);
/* doing a straight open could mean losing the key data
dont risk it by moving the file first
this is grossly slow, so provide a means of escaping it */
sprintf(oldstack,"files/%s/keys",df->fname);
if (!(sys_flags&SECURE_DYNAMIC)) {
stack=end_string(oldstack);
to=stack;
sprintf(to,"files/%s/keys.b",df->fname);
rename(oldstack,to);
}
fd=open(oldstack,O_CREAT|O_WRONLY|O_TRUNC,S_IRUSR|S_IWUSR);
if (fd<0) handle_error("Failed to open key file");
length=(df->nkeys+1)*8;
if (write(fd,df->keylist,length)!=length)
handle_error("Failed to write key data");
close(fd);
stack=oldstack;
}
/* set up a dfile structure */
dfile *dynamic_init(char *file,int granularity)
{
dfile *df;
char *oldstack;
int fd,length,i,*fill;
oldstack=stack;
if (sys_flags&VERBOSE) {
sprintf(oldstack,"Loading dynamic file '%s'",file);
stack=end_string(oldstack);
log("sync",oldstack);
stack=oldstack;
}
df=(dfile *)MALLOC(sizeof(dfile));
memset(df,0,sizeof(dfile));
df->granularity=granularity;
sprintf(oldstack,"%skeys",rc_options->prooms_path);
fd=open(oldstack,O_RDONLY|O_NDELAY);
/* just in case this is the first time round */
if (fd<0) {
printf("in if fd<0\n");
sprintf(oldstack,"Failed to load room keys");
stack=end_string(oldstack);
log("error",oldstack);
stack=oldstack;
df->first_free_block=0;
df->first_free_key=0;
df->nkeys=0;
df->keylist=0;
}
else {
length=lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
if ((length%8)!=0) handle_error("Corrupt key data");
df->nkeys=(length/8)-1;
df->keylist=(int *)MALLOC(length);
if (read(fd,df->keylist,length)!=length)
handle_error("Failed to read keys");
close(fd);
(void)get_int(&(df->first_free_block),(char *)df->keylist);
(void)get_int(&(df->first_free_key),(char *)(df->keylist+1));
}
/* keep the data file open all the time */
sprintf(oldstack,"%sdata",rc_options->prooms_path);
df->data_fd=open(oldstack,O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
if (df->data_fd<0) handle_error("Failed to open dynamic data file");
lseek(df->data_fd,0,SEEK_SET);
stack=oldstack;
return df;
}
/* free up a dfile structure */
void dynamic_close(dfile *df)
{
dynamic_key_sync(df);
close(df->data_fd);
free(df->keylist);
free(df);
}
/* get the block and length info from the keylist */
int convert_key(dfile *df,int key,int *block,int *length)
{
if ((key<=0) || (key>(df->nkeys-1))) return 0;
key*=2;
(void) get_int(block,(char *)(df->keylist+key));
if (*block<=0) return 0;
(void) get_int(length,(char *)(df->keylist+key+1));
if (*length<=0) return 0;
return 1;
}
/* grab a block from the data file */
int load_block(dfile *df,int *block,char *data)
{
if (*block<=0) return 0;
dynamic_seek_block(df,*block);
if (read(df->data_fd,data,4)!=4) handle_error("Failed to read next block 1");
get_int(block,data);
if (read(df->data_fd,data,df->granularity-4)!=(df->granularity-4))
handle_error("Failed to read block");
return 1;
}
/* load an entire entry from the data file */
int dynamic_load(dfile *df,int key,char *data)
{
int block,length,l;
/* printf("Dynamic load key=%d\n",key); */
if (!convert_key(df,key,&block,&length)) return -1;
l=length;
/* printf("DL block %d\n",block); */
while(l>0) {
if (!load_block(df,&block,data))
handle_error("Failed to load block of data");
/* printf("DL block %d\n",block); */
l-=(df->granularity-4);
data+=(df->granularity-4);
}
return length;
}
/* returns a free key, if necessary it will go and create
some new key space (in chunks of 100 keys at a time) */
int dynamic_find_free_key(dfile *df)
{
int *newlist,oldnkeys,i,key;
if (!df->first_free_key) {
oldnkeys=df->nkeys;
df->nkeys+=100;
newlist=(int *)MALLOC((df->nkeys+1)*8);
if (df->keylist) {
memcpy(newlist+2,df->keylist+2,(oldnkeys+1)*8);
FREE(df->keylist);
}
df->keylist=newlist;
df->first_free_key=oldnkeys+1;
newlist+=df->first_free_key*2;
for(i=-df->first_free_key-1;i>=-df->nkeys;i--) {
newlist=(int *)store_int((char *)newlist,i);
newlist=(int *)store_int((char *)newlist,0);
}
newlist=(int *)store_int((char *)newlist,0);
newlist=(int *)store_int((char *)newlist,0);
}
key=df->first_free_key;
(void) get_int(&(df->first_free_key),(char *)(df->keylist+key*2));
df->first_free_key=-df->first_free_key;
return key;
}
/* read in the index from the start of a block */
int dynamic_get_next_block(dfile *df,int block)
{
int next_block,ret;
if (block<=0) return;
dynamic_seek_block(df,block);
ret=read(df->data_fd,stack,4);
if (!ret) return 0;
if (ret!=4) handle_error("Failed to read next block 2");
get_int(&next_block,stack);
return next_block;
}
/* go through all the blocks in a file and free them up */
void dynamic_free(dfile *df,int key)
{
char *oldstack;
int block,length,next_block;
if (!convert_key(df,key,&block,&length))
return;
(void) store_int((char *)(df->keylist+key*2),-df->first_free_key);
(void) store_int((char *)(df->keylist+key*2+1),0);
df->first_free_key=key;
/* simply keep adding blocks onto the top of the free list */
while(length>0)
{
if (block<=0)
return;
next_block = dynamic_get_next_block(df,block);
dynamic_seek_block(df,block);
store_int(stack,-df->first_free_block);
if (write(df->data_fd,stack,4)!=4)
handle_error("Failed to write next block");
df->first_free_block=block;
block = next_block;
length -= (df->granularity-4);
}
if (!(sys_flags&SECURE_DYNAMIC))
dynamic_key_sync(df);
}
/* go hunting for a free block, if necessary, create enough
space on the end of the file */
int dynamic_find_free_block(dfile *df)
{
int length,new_block;
/* printf("Called find_free_block ffb=%d\n",df->first_free_block); */
if (df->first_free_block) {
new_block=df->first_free_block;
df->first_free_block=-dynamic_get_next_block(df,new_block);
}
else {
length=lseek(df->data_fd,0,SEEK_END);
memset(stack,0,df->granularity);
if (!length) {
if (write(df->data_fd,stack,df->granularity)!=df->granularity)
handle_error("Failed to make data block");
length=df->granularity;
}
if (write(df->data_fd,stack,df->granularity)!=df->granularity)
handle_error("Failed to make data block");
new_block=(length/df->granularity);
}
return new_block;
}
/* save an entry to the data file */
int dynamic_save(dfile *df,char *data,int l,int key)
{
int block,length,next_block,blength,free_block;
/* key==0 means this is a new entry */
if (!key) {
key=dynamic_find_free_key(df);
block=dynamic_find_free_block(df);
}
else if (!convert_key(df,key,&block,&length)) return;
(void) store_int((char *)(df->keylist+key*2),block);
(void) store_int((char *)(df->keylist+key*2+1),l);
blength=df->granularity-4;
/* printf("Dynamic Save key=%d\n",key); */
while(l>0) {
next_block=dynamic_get_next_block(df,block);
/* printf("DS block %d / next block %d\n",block,next_block); */
/* do we need more space ? */
if (l>blength) {
if (next_block<=0) next_block=dynamic_find_free_block(df);
}
else {
/* can we free up any blocks at the end ? */
free_block=next_block;
while(free_block>0) {
next_block=dynamic_get_next_block(df,free_block);
dynamic_seek_block(df,free_block);
store_int(stack,-df->first_free_block);
if (write(df->data_fd,stack,4)!=4)
handle_error("Failed to write next block");
df->first_free_block=free_block;
free_block=next_block;
}
next_block=0;
}
store_int(stack,next_block);
dynamic_seek_block(df,block);
if (write(df->data_fd,stack,4)!=4)
handle_error("Failed to write next block");
if (write(df->data_fd,data,blength)!=blength)
handle_error("Failed to write block data");
block=next_block;
l-=blength;
data+=blength;
}
/* make sure the keylist is up to date */
if (!(sys_flags&SECURE_DYNAMIC)) dynamic_key_sync(df);
return key;
}