summaryrefslogtreecommitdiff
path: root/c_src/permdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'c_src/permdb.c')
-rw-r--r--c_src/permdb.c107
1 files changed, 101 insertions, 6 deletions
diff --git a/c_src/permdb.c b/c_src/permdb.c
index a33c43a..f92958d 100644
--- a/c_src/permdb.c
+++ b/c_src/permdb.c
@@ -37,6 +37,7 @@ struct permdb_object {
buffered_file *datafile;
buffered_file *indexfile;
char *error;
+ int write_enabled;
};
static const node_object nullnode = {{0, 0, 0, 0}};
@@ -447,8 +448,40 @@ rebuild_index_file(permdb_object *state)
return committree(state);
}
+unsigned int
+try_reload_database(permdb_object *state) {
+ if (state->write_enabled) {
+ errx(1, "try_reload_database called on write enabled database");
+ }
+
+ uint64_t oldindexsize = bf_total_length(state->indexfile);
+
+ fprintf(stderr, "reloading database: datafile %llu indexfile %llu\n",
+ (unsigned long long) bf_total_length(state->datafile),
+ (unsigned long long) bf_total_length(state->indexfile));
+ bf_reload(state->datafile);
+ bf_reload(state->indexfile);
+ fprintf(stderr, "reloaded database: datafile %llu indexfile %llu\n",
+ (unsigned long long) bf_total_length(state->datafile),
+ (unsigned long long) bf_total_length(state->indexfile));
+
+ if (bf_total_length(state->indexfile) == oldindexsize) {
+ return 0;
+ } else {
+ if (datafile_verify_file(state->datafile) < 0) {
+ warnx("data file verification failed");
+ }
+ if (indexfile_verify_file(state->indexfile) < 0) {
+ warnx("cannot rebuild in readonly mode");
+ }
+
+ delete_all_nodes_in_cache(state);
+ return 1;
+ }
+}
+
permdb_object *
-permdb_alloc(const char *dbpath)
+permdb_alloc(const char *dbpath, int lock)
{
char *idxpath = NULL;
if (asprintf(&idxpath, "%s.idx", dbpath) == -1) {
@@ -464,14 +497,22 @@ permdb_alloc(const char *dbpath)
state->datafile = NULL;
state->indexfile = NULL;
- state->datafile = bf_open(dbpath, O_RDWR|O_CREAT, "datafile", 1);
+ state->write_enabled = lock;
+
+ int mode = O_RDONLY;
+
+ if (state->write_enabled) {
+ mode = O_RDWR|O_CREAT;
+ }
+
+ state->datafile = bf_open(dbpath, mode, "datafile", state->write_enabled);
if (state->datafile == NULL) {
permdb_free(state);
free(idxpath);
return NULL;
}
- state->indexfile = bf_open(idxpath, O_RDWR|O_CREAT, "indexfile", 1);
+ state->indexfile = bf_open(idxpath, mode, "indexfile", state->write_enabled);
if (state->indexfile == NULL) {
permdb_free(state);
free(idxpath);
@@ -482,12 +523,22 @@ permdb_alloc(const char *dbpath)
if (bf_total_length(state->datafile) == 0
&& bf_total_length(state->indexfile) == 0) {
+ if (!state->write_enabled) {
+ warnx("data and index files are empty: %s", dbpath);
+ permdb_free(state);
+ return NULL;
+ }
dprintf(WRITE, (stderr, "writing header\n"));
indexfile_add_header(state->indexfile);
datafile_add_header(state->datafile);
initial_commit(state);
} else if (bf_total_length(state->datafile) > 0
&& bf_total_length(state->indexfile) == 0) {
+ if (!state->write_enabled) {
+ warnx("cannot rebuild in readonly mode: %s", dbpath);
+ permdb_free(state);
+ return NULL;
+ }
if (rebuild_index_file(state) < 0) {
warnx("index file rebuilding failed: %s", dbpath);
permdb_free(state);
@@ -500,6 +551,11 @@ permdb_alloc(const char *dbpath)
return NULL;
}
if (indexfile_verify_file(state->indexfile) < 0) {
+ if (!state->write_enabled) {
+ warnx("cannot rebuild in readonly mode: %s", dbpath);
+ permdb_free(state);
+ return NULL;
+ }
warnx("index file verification failed, rebuilding: %s", dbpath);
if (rebuild_index_file(state) < 0) {
@@ -933,6 +989,10 @@ addvalue(permdb_object *state, const unsigned char *key, unsigned int keylength,
node_entry lastentry = getpath(state, key, nodes);
+ if (!state->write_enabled) {
+ return -1;
+ }
+
if (lastentry == NODE_ENTRY_ERROR_NODE) {
utarray_free(nodes);
return -1;
@@ -1003,8 +1063,8 @@ addvalue(permdb_object *state, const unsigned char *key, unsigned int keylength,
}
unsigned char *
-getvalue(permdb_object *state, const unsigned char *key, size_t keylength,
- size_t *datalen)
+getvalue_try(permdb_object *state, const unsigned char *key, size_t keylength,
+ size_t *datalen)
{
node_entry entry = getlastnode(state, key);
if (entry == 0) {
@@ -1028,8 +1088,24 @@ getvalue(permdb_object *state, const unsigned char *key, size_t keylength,
return readdata(state, olddataoffset, *datalen);
}
+unsigned char *
+getvalue(permdb_object *state, const unsigned char *key, size_t keylength,
+ size_t *datalen)
+{
+ unsigned char *result = getvalue_try(state, key, keylength, datalen);
+
+ if (result == NULL && !state->write_enabled) {
+ int reloaded = try_reload_database(state);
+ if (reloaded) {
+ return getvalue_try(state, key, keylength, datalen);
+ }
+ }
+
+ return result;
+}
+
unsigned int
-keyexists(permdb_object *state, const unsigned char *key, size_t keylength)
+keyexists_try(permdb_object *state, const unsigned char *key, size_t keylength)
{
node_entry entry = getlastnode(state, key);
if (entry == 0) {
@@ -1053,6 +1129,21 @@ keyexists(permdb_object *state, const unsigned char *key, size_t keylength)
return 1;
}
+unsigned int
+keyexists(permdb_object *state, const unsigned char *key, size_t keylength)
+{
+ unsigned int result = keyexists_try(state, key, keylength);
+
+ if (result == 0 && !state->write_enabled) {
+ int reloaded = try_reload_database(state);
+ if (reloaded) {
+ return keyexists_try(state, key, keylength);
+ }
+ }
+
+ return result;
+}
+
static int
string_length_comparison(struct nodecache *a, struct nodecache *b) {
size_t a_len = a->key->length;
@@ -1081,6 +1172,10 @@ string_length_comparison(struct nodecache *a, struct nodecache *b) {
int
committree(permdb_object *state)
{
+ if (!state->write_enabled) {
+ return -1;
+ }
+
if (state->dirtynodes == NULL) {
return 0;
}