--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "tlog.h"
+#include "tmalloc.h"
+
+#include "flatdb.h"
+
+static FDBFreeSpace*
+findFreeSpace(FDB *db, size_t length) {
+ FDBFreeSpace *ptr = db->space;
+
+ while(ptr && ptr - db->space < db->listcur) {
+ if ( ptr->length >= length )
+ return ptr;
+ ptr++;
+ }
+
+ return NULL;
+}
+
+static int
+cmpFS(const void* a, const void* b) {
+ if ( ((FDBFreeSpace*)a)->offset == ((FDBFreeSpace*)b)->offset )
+ return 0;
+ return ( ((FDBFreeSpace*)a)->offset > ((FDBFreeSpace*)b)->offset ) ? 1 : -1;
+}
+
+void
+FDBVacuumFreeSpace(FDB *db) {
+ FDBFreeSpace *ptr=db->space+1, *ok=db->space;
+ if ( db->listcur < 2 )
+ return;
+
+ qsort( db->space, db->listcur, sizeof(FDBFreeSpace), cmpFS);
+
+ while( ptr - db->space < db->listcur ) {
+ tassert( ok->offset + ok->length <= ptr->offset );
+ if ( ok->offset + ok->length == ptr->offset || ptr->length==0 ) {
+ ok->length += ptr->length;
+ } else {
+ ok++;
+ if ( ok != ptr-1 )
+ memcpy(ok, ptr, sizeof(FDBFreeSpace));
+ }
+ ptr++;
+ }
+
+ db->listcur = ok - db->space + 1;
+}
+
+int
+FDBOpen(FDB *db, char *file, int readonly) {
+ FDBHeader header;
+ int rc;
+
+ memset(db, 0, sizeof(FDB));
+
+ if ( readonly ) {
+ db->readonly=1;
+ db->fd = open(file, O_RDONLY | O_SHLOCK);
+ } else {
+ db->fd = open(file, O_CREAT | O_RDWR | O_EXLOCK, 0666);
+ }
+
+ if ( db->fd < 0 ) {
+ memset(db, 0, sizeof(FDB));
+ tlog(TL_CRIT,"FDBOpen: open failed: %s", strerror(errno));
+ return FDB_ERROR;
+ }
+
+ rc = read(db->fd, &header, sizeof(FDBHeader));
+
+ if ( rc<0 ) {
+ close(db->fd);
+ tlog(TL_CRIT,"FDBOpen: read failed: %s", strerror(errno));
+ return FDB_ERROR;
+ } else if ( rc==0 ) {
+ memset(&header, 0, sizeof(FDBHeader));
+ } else if ( rc != sizeof(FDBHeader) ) {
+ close(db->fd);
+ tlog(TL_CRIT,"FDBOpen: header fault: %d bytes only", rc);
+ return FDB_ERROR;
+ } else if ( header.isopened ) {
+ close(db->fd);
+ tlog(TL_CRIT,"FDBOpen: file wasn't closed correctly");
+ return FDB_ERROR;
+ }
+
+
+ if ( !db->readonly ) {
+ if ( header.freespace ) {
+ db->listlen = db->listcur = (header.lenfreespace / sizeof(FDBFreeSpace));
+ db->space = (FDBFreeSpace*)tmalloc( header.lenfreespace + sizeof(FDBFreeSpace) );
+
+ if ( lseek(db->fd, header.freespace, SEEK_SET)!=header.freespace ||
+ read( db->fd, db->space, header.lenfreespace ) != header.lenfreespace ) {
+ close(db->fd);
+ tlog(TL_CRIT,"FDBOpen: free space read failed: %s", strerror(errno));
+ return FDB_ERROR;
+ }
+ } else {
+ db->listlen = 8;
+ db->space = (FDBFreeSpace*)tmalloc( db->listlen*sizeof(FDBFreeSpace) );
+ }
+
+ header.freespace = 0;
+ header.lenfreespace = 0;
+ header.isopened = 1;
+
+ if ( lseek(db->fd, 0, SEEK_SET)!=0 || write(db->fd, &header, sizeof(FDBHeader)) != sizeof(FDBHeader) ) {
+ close(db->fd);
+ if ( db->space ) tfree( db->space );
+ tlog(TL_CRIT,"FDBOpen: can't modify header: %s", strerror(errno));
+ return FDB_ERROR;
+ }
+ }
+
+ return FDB_OK;
+}
+
+
+int
+FDBClose(FDB *db) {
+ if ( !db->readonly) {
+ FDBHeader header;
+
+ memset(&header,0,sizeof(FDBHeader));
+
+ if ( db->listcur ) {
+ FDBFreeSpace *ptr;
+
+ vacuumFreeSpace(db);
+ header.lenfreespace = sizeof(FDBFreeSpace)*db->listcur;
+ ptr = findFreeSpace( db, header.lenfreespace );
+
+ if ( ptr ) {
+ header.freespace = ptr->offset;
+ if ( lseek(db->fd, ptr->offset, SEEK_SET) != ptr->offset )
+ tlog(TL_CRIT|TL_EXIT,"FDBClose: lseek failed: %s", strerror(errno));
+ } else {
+ if ( (header.freespace = lseek(db->fd, 0, SEEK_END)) < 0 )
+ tlog(TL_CRIT|TL_EXIT,"FDBClose: lseek failed: %s", strerror(errno));
+ }
+
+ if ( write(db->fd, db->space, header.lenfreespace) != header.lenfreespace )
+ tlog(TL_CRIT|TL_EXIT,"FDBClose: write failed: %s", strerror(errno));
+ }
+
+ header.isopened=0;
+
+ if ( lseek(db->fd,0,SEEK_SET)!=0 || write(db->fd, &header, sizeof(FDBHeader)) != sizeof(FDBHeader) )
+ tlog(TL_CRIT|TL_EXIT,"FDBClose: header write failed: %s", strerror(errno));
+ }
+
+ close(db->fd);
+
+ if ( db->space )
+ tfree( db->space );
+
+ return FDB_OK;
+}
+
+static int
+readLen(FDB *db, off_t offset, size_t *size) {
+ if ( lseek(db->fd,offset,SEEK_SET)!=offset)
+ return FDB_ERROR;
+
+ if ( read(db->fd,size,sizeof(size_t)) != sizeof(size_t) )
+ return FDB_ERROR;
+
+ return FDB_OK;
+}
+
+int
+FDBDelete(FDB *db, off_t offset, size_t length) {
+ if ( db->readonly )
+ return FDB_ERROR;
+
+ if ( length==0 )
+ if ( readLen(db, offset, &length) != FDB_OK )
+ return FDB_ERROR;
+
+ if ( db->listcur >= db->listlen ) {
+ db->listlen *= 2;
+ db->space = (FDBFreeSpace*) trealloc( db->space, db->listlen * sizeof(FDBFreeSpace) );
+ }
+
+ db->space[ db->listcur ].offset=offset;
+ db->space[ db->listcur ].length=length;
+
+ db->listcur++;
+
+ return FDB_OK;
+}
+
+
+int
+FDBGet(FDB *db, off_t offset, size_t length, FDBRecord **record) {
+ size_t rc;
+
+ *record=NULL;
+
+ if ( db->readonly )
+ return FDB_ERROR;
+
+ if ( length==0 )
+ if ( readLen(db, offset, &length) != FDB_OK )
+ return FDB_ERROR;
+
+ *record = (FDBRecord*)tmalloc( length );
+
+ if ( lseek(db->fd,offset,SEEK_SET)!=offset)
+ return FDB_ERROR;
+
+ if ( (rc=read(db->fd,*record,length)) != length ) {
+ (*record)->length = rc;
+ tlog(TL_CRIT,"FDBGet: read (%d bytes) less than needed (%d bytes): %s", rc, length, strerror(errno));
+ return FDB_INCORRECT;
+ }
+
+ if ( (*record)->length != length ) {
+ tlog(TL_ALARM, "FDBGet: wrong length in opts: %d bytes and %d bytes really", length, (*record)->length);
+ if ( (*record)->length > length ) {
+ rc = (*record)->length;
+ tfree( *record );
+ return FDBGet(db, offset, rc, record);
+ }
+ }
+
+ return FDB_OK;
+}
+
+
+int
+FDBPut(FDB *db, FDBRecord *record, off_t *offset ) {
+ FDBFreeSpace *ptr;
+
+ if ( db->readonly )
+ return FDB_ERROR;
+
+ ptr = findFreeSpace( db, record->length );
+ if ( ptr ) {
+ *offset = ptr->offset;
+ ptr->length -= record->length;
+ if ( ptr->length == 0 )
+ vacuumFreeSpace(db);
+ if ( lseek(db->fd, *offset, SEEK_SET) != *offset )
+ tlog(TL_CRIT|TL_EXIT,"FDBPut: lseek failed: %s", strerror(errno));
+ } else {
+ if ( (*offset = lseek(db->fd, 0, SEEK_END)) < 0 )
+ tlog(TL_CRIT|TL_EXIT,"FDBPut: lseek failed: %s", strerror(errno));
+ }
+
+ if ( write(db->fd, record, record->length) != record->length )
+ tlog(TL_CRIT|TL_EXIT,"FDBPut: write lseek failed: %s", strerror(errno));
+
+ return FDB_OK;
+
+}
+
+