diff options
author | Jose Bollo <jose.bollo@iot.bzh> | 2019-05-13 16:31:55 +0200 |
---|---|---|
committer | Jose Bollo <jose.bollo@iot.bzh> | 2019-05-13 16:33:10 +0200 |
commit | c451f53b4f3acd2157f9c7e7365ecc5663e9ada7 (patch) | |
tree | dcdee6a94c2a82523b359012786f84f78a953ce4 | |
parent | c5f4d75b492052f3c3a4e115c5f6a2060812a639 (diff) |
Switch to filedb
Signed-off-by: Jose Bollo <jose.bollo@iot.bzh>
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/anydb.c | 27 | ||||
-rw-r--r-- | src/anydb.h | 22 | ||||
-rw-r--r-- | src/db.c | 35 | ||||
-rw-r--r-- | src/db.h | 5 | ||||
-rw-r--r-- | src/fdb.c | 865 | ||||
-rw-r--r-- | src/fdb.h | 99 | ||||
-rw-r--r-- | src/filedb.c | 776 | ||||
-rw-r--r-- | src/filedb.h | 27 | ||||
-rw-r--r-- | src/memdb.c | 46 |
10 files changed, 911 insertions, 993 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 319ece1..e8071e8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -23,7 +23,7 @@ set(SERVER_SOURCES dbinit.c expire.c fbuf.c - fdb.c + filedb.c main-cynarad.c memdb.c pollitem.c diff --git a/src/anydb.c b/src/anydb.c index 94a5d58..995cd0a 100644 --- a/src/anydb.c +++ b/src/anydb.c @@ -465,6 +465,33 @@ anydb_test( /******************************************************************************/ /******************************************************************************/ +/*** IS EMPTY ***/ +/******************************************************************************/ +/******************************************************************************/ + +static +anydb_action_t +is_empty_cb( + void *closure, + const anydb_key_t *key, + anydb_value_t *value +) { + bool *result = closure; + *result = false; + return Anydb_Action_Stop; +} + +bool +anydb_is_empty( + anydb_t *db +) { + bool result = true; + db->itf.apply(db->clodb, is_empty_cb, &result); + return result; +} + +/******************************************************************************/ +/******************************************************************************/ /*** CLEANUP ***/ /******************************************************************************/ /******************************************************************************/ diff --git a/src/anydb.h b/src/anydb.h index abc8c4c..843ef29 100644 --- a/src/anydb.h +++ b/src/anydb.h @@ -70,9 +70,10 @@ typedef struct anydb_value anydb_value_t; */ enum anydb_action { - Anydb_Action_Continue = 0, - Anydb_Action_Update_And_Stop = 1, - Anydb_Action_Remove_And_Continue = 2 + Anydb_Action_Stop, + Anydb_Action_Continue, + Anydb_Action_Update_And_Stop, + Anydb_Action_Remove_And_Continue }; typedef enum anydb_action anydb_action_t; @@ -92,6 +93,7 @@ struct anydb_itf void (*apply)(void *clodb, anydb_action_t (*oper)(void *closure, const anydb_key_t *key, anydb_value_t *value), void *closure); int (*add)(void *clodb, const anydb_key_t *key, const anydb_value_t *value); void (*gc)(void *clodb); + int (*sync)(void *clodb); void (*destroy)(void *clodb); }; typedef struct anydb_itf anydb_itf_t; @@ -157,9 +159,23 @@ anydb_cleanup( anydb_t *db ); +/** is the database empty? */ +extern +bool +anydb_is_empty( + anydb_t *db +); + /** destroy the database */ extern void anydb_destroy( anydb_t *db ); + +/** synchronize database */ +extern +int +anydb_sync( + anydb_t *db +); @@ -28,11 +28,12 @@ #include "data.h" #include "anydb.h" -#include "fdb.h" +#include "filedb.h" #include "memdb.h" #include "db.h" static anydb_t *memdb; +static anydb_t *filedb; /** check whether the 'text' fit String_Any, String_Wide, NULL or "" */ static @@ -54,7 +55,7 @@ db_open( rc = memdb_create(&memdb); if (!rc) { - rc = fdb_open(directory); + rc = filedb_create(&filedb, directory, "CYNARA"); if (rc) anydb_destroy(memdb); } @@ -65,7 +66,7 @@ db_open( void db_close( ) { - fdb_close(); + anydb_destroy(filedb); anydb_destroy(memdb); } @@ -73,7 +74,7 @@ db_close( bool db_is_empty( ) { - return fdb_is_empty(); + return anydb_is_empty(filedb); } /** enter atomic mode */ @@ -82,7 +83,7 @@ db_transaction_begin( ) { int rc1, rc2; - rc1 = fdb_backup(); + rc1 = anydb_transaction(filedb, Anydb_Transaction_Start); rc2 = anydb_transaction(memdb, Anydb_Transaction_Start); return rc1 ?: rc2; @@ -96,15 +97,15 @@ db_transaction_end( int rc1, rc2, rc3, rc4; if (commit) { - rc1 = 0; + rc1 = anydb_transaction(filedb, Anydb_Transaction_Commit); rc2 = anydb_transaction(memdb, Anydb_Transaction_Commit); rc3 = db_cleanup(); } else { - rc1 = fdb_recover(); + rc1 = anydb_transaction(filedb, Anydb_Transaction_Cancel); rc2 = anydb_transaction(memdb, Anydb_Transaction_Cancel); rc3 = 0; } - rc4 = fdb_sync(); + rc4 = db_sync(); return rc1 ?: rc2 ?: rc3 ?: rc4; } @@ -120,7 +121,7 @@ db_for_all( const data_value_t *value), const data_key_t *key ) { - fdb_for_all(closure, callback, key); + anydb_for_all(filedb, closure, callback, key); anydb_for_all(memdb, closure, callback, key); } @@ -129,7 +130,7 @@ int db_drop( const data_key_t *key ) { - fdb_drop(key); + anydb_drop(filedb, key); anydb_drop(memdb, key); return 0; } @@ -141,7 +142,7 @@ db_set( const data_value_t *value ) { if (is_any_or_wide(key->session)) - return fdb_set(key, value); + return anydb_set(filedb, key, value); else return anydb_set(memdb, key, value); } @@ -156,7 +157,7 @@ db_test( data_value_t v1, v2; s1 = anydb_test(memdb, key, &v1); - s2 = fdb_test(key, &v2); + s2 = anydb_test(filedb, key, &v2); if (s2 > s1) { *value = v2; return s2; @@ -169,8 +170,16 @@ db_test( int db_cleanup( ) { - fdb_cleanup(); + anydb_cleanup(filedb); anydb_cleanup(memdb); return 0; } +int +db_sync( +) { + int rc1 = anydb_sync(filedb); + int rc2 = anydb_sync(memdb); + return rc1 ?: rc2; +} + @@ -92,3 +92,8 @@ int db_cleanup( ); +/** cleanup the base */ +extern +int +db_sync( +); diff --git a/src/fdb.c b/src/fdb.c deleted file mode 100644 index d13325a..0000000 --- a/src/fdb.c +++ /dev/null @@ -1,865 +0,0 @@ -/* - * Copyright (C) 2018 "IoT.bzh" - * Author José Bollo <jose.bollo@iot.bzh> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <assert.h> -#include <stdlib.h> -#include <stdint.h> -#include <stdbool.h> -#include <stdalign.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> -#include <time.h> -#include <errno.h> - -#include "data.h" -#include "fbuf.h" -#include "fdb.h" - -#define NOEXPIRE 0 -#define NOIDX 0 - -#define ANYIDX 40 -#define ANYSTR "#" - -#define WIDEIDX 42 -#define WIDESTR "*" - -/* - * for the first version, save enougth time up to 4149 - * 4149 = 1970 + (4294967296 * 16) / (365 * 24 * 60 * 60) - * - * in the next version, time will be relative to a stored base - */ -#define exp2time(x) (((time_t)(x)) << 4) -#define time2expl(x) ((uint32_t)((x) >> 4)) -#define time2exph(x) time2expl((x) + 15) - -/** - * A query is a set of 32 bits integers - */ -struct key_ids { - /** client string id */ - uint32_t client; - - /** user string id */ - uint32_t user; - - /** permission string id */ - uint32_t permission; -}; -typedef struct key_ids key_ids_t; - -/** - * A rule is a set of 32 bits integers - */ -struct rule -{ - /** key part */ - key_ids_t key; - - /** value string id */ - uint32_t value; - - /** expiration */ - uint32_t expire; -}; -typedef struct rule rule_t; - -/* - * The cynara-agl database is made of 2 memory mapped files: - * - names: the zero terminated names - * - rules: the rules based on name indexes as 32bits indexes - * These files are normally in /var/lib/cynara - */ -#if !defined(DEFAULT_DB_DIR) -# define DEFAULT_DB_DIR "/var/lib/cynara" -#endif -static const char fdb_default_directory[] = DEFAULT_DB_DIR; - -/** the file for the names */ -static fbuf_t fnames; - -/** the file for the rules */ -static fbuf_t frules; - -/** identification of names version 1 - * $> uuidgen --sha1 -n @url -N urn:AGL:cynara:db:names:1 - * $> uuid -v 5 urn:AGL:cynara:db:names:1 - */ -static const char uuid_names_v1[] = "e9481f9e-b2f4-5716-90cf-c286d98d1868\n--\n"; - -/** identification of rules version 1 - * $> uuidgen --sha1 -n @url -N urn:AGL:cynara:db:rules:1 - * $> uuid -v 5 ns:URL urn:AGL:cynara:db:rules:1 - */ -static const char uuid_rules_v1[] = "8f7a5b21-48b1-57af-96c9-d5d7192be370\n--\n"; - -/** length of the identification */ -static const int uuidlen = 40; - -/** count of names */ -static uint32_t names_count; - -/** the name indexes sorted */ -static uint32_t *names_sorted; - -/** count of rules */ -static uint32_t rules_count; - -/** the rules */ -static rule_t *rules; - -/** return the name of 'index' */ -static -const char* -name_at( - uint32_t index -) { - return (const char*)(fnames.buffer + index); -} - -/** compare names. used by qsort and bsearch */ -static -int -cmpnames( - const void *pa, - const void *pb -) { - uint32_t a = *(const uint32_t*)pa; - uint32_t b = *(const uint32_t*)pb; - return strcmp(name_at(a), name_at(b)); -} - -/** search the index of 'name' and create it if 'create' */ -int -fdb_get_name_index( - uint32_t *index, - const char *name, - bool create -) { - uint32_t lo, up, m, i, *p; - int c; - const char *n; - size_t len; - - /* special names */ - if (!name || !name[0]) - name = ANYSTR; - - /* dichotomic search */ - lo = 0; - up = names_count; - while(lo < up) { - m = (lo + up) >> 1; - i = names_sorted[m]; - n = name_at(i); - c = strcmp(n, name); - - if (c == 0) { - /* found */ - *index = i; - return 0; - } - - /* dichotomic iteration */ - if (c < 0) - lo = m + 1; - else - up = m; - } - - /* not found */ - if (!create) { - errno = ENOENT; - return -1; - } - - /* check length */ - len = strnlen(name, MAX_NAME_LENGTH + 1); - if (len > MAX_NAME_LENGTH) { - errno = EINVAL; - return -1; - } - - /* add the name in the file */ - i = fnames.used; - c = fbuf_append(&fnames, name, 1 + (uint32_t)len); - if (c < 0) - return c; - - /* add the name in sorted array */ - up = names_count; - if (!(up & 1023)) { - p = realloc(names_sorted, (up + 1024) * sizeof *names_sorted); - if (p == NULL) { - fprintf(stderr, "out of memory"); - return -1; - } - names_sorted = p; - } - memmove(&names_sorted[lo + 1], &names_sorted[lo], (up - lo) * sizeof *names_sorted); - names_count = up + 1; - *index = names_sorted[lo] = i; - return 0; -} - -/** initialize names */ -static -int -init_names( -) { - int rc; - uint32_t pos, len, *ns, *p, all, nc; - - all = 0; - nc = 0; - ns = NULL; - - /* iterate over names */ - pos = uuidlen; - while (pos < fnames.used) { - /* get name length */ - len = (uint32_t)strlen(name_at(pos)); - if (pos + len <= pos || pos + len > fnames.used) { - free(ns); - goto bad_file; - } - /* store the position */ - if (all <= nc) { - all += 1024; - p = realloc(ns, all * sizeof *ns); - if (p == NULL) { - free(ns); - fprintf(stderr, "out of memory"); - goto error; - } - ns = p; - } - ns[nc++] = pos; - /* next */ - pos += len + 1; - } - - /* sort and record */ - qsort(ns, nc, sizeof *ns, cmpnames); - names_sorted = ns; - names_count = nc; - - /* predefined symbols */ - rc = fdb_get_name_index(&pos, ANYSTR, true); - if (rc < 0) - goto error; - if (pos != ANYIDX) - goto bad_file; - rc = fdb_get_name_index(&pos, WIDESTR, true); - if (rc < 0) - goto error; - if (pos != WIDEIDX) - goto bad_file; - - return 0; -bad_file: - fprintf(stderr, "bad file %s", fnames.name); - errno = ENOEXEC; -error: - return -1; -} - -/** check whether the 'text' fit ANYSTR, NULL or "" */ -static -bool -is_any( - const char *text -) { - return text == NULL || text[0] == 0 || 0 == strcmp(text, ANYSTR); -} - -/** check whether the 'text' fit ANYSTR, WIDESTR, NULL or "" */ -static -bool -is_any_or_wide( - const char *text -) { - return is_any(text) || 0 == strcmp(text, WIDESTR); -} - -/** set the 'value' to the rule at 'index' */ -static -void -touch_at( - uint32_t index -) { - uint32_t pos; - - pos = (uint32_t)(((void*)&rules[index]) - frules.buffer); - if (pos < frules.saved) - frules.saved = pos; -} - -/** set the 'value' to the rule at 'index' */ -static -void -set_at( - uint32_t index, - uint32_t value, - uint32_t expire -) { - assert(index < rules_count); - rules[index].value = value; - rules[index].expire = expire; - touch_at(index); -} - -/** drop the rule at 'index' */ -static -void -drop_at( - uint32_t index -) { - uint32_t pos; - - assert(index < rules_count); - if (index < --rules_count) - rules[index] = rules[rules_count]; - pos = (uint32_t)(((void*)&rules[rules_count]) - frules.buffer); - frules.used = pos; - touch_at(index); -} - -/** add the rule 'client' x 'user' x 'permission' x 'value' */ -static -int -add_rule( - uint32_t client, - uint32_t user, - uint32_t permission, - uint32_t value, - uint32_t expire -) { - int rc; - uint32_t c; - rule_t *rule; - - c = frules.used + (uint32_t)sizeof *rule; - rc = fbuf_ensure_capacity(&frules, c); - if (rc) - return rc; - rules = (rule_t*)(frules.buffer + uuidlen); - rule = &rules[rules_count++]; - rule->key.client = client; - rule->key.user = user; - rule->key.permission = permission; - rule->value = value; - rule->expire = expire; - frules.used = c; - return 0; -} - -/** init the rules from the file */ -static -void -init_rules( -) { - rules = (rule_t*)(frules.buffer + uuidlen); - rules_count = (frules.used - uuidlen) / sizeof *rules; -} - -/** open a fbuf */ -static -int -open_identify( - fbuf_t *fb, - const char *directory, - const char *name, - const char *id, - uint32_t idlen -) { - int rc; - char *file, *backup; - size_t sd, sn; - - sd = strlen(directory); - sn = strlen(name); - file = malloc(((sd + sn) << 1) + 5); - if (!file) - rc = -ENOMEM; - else { - - memcpy(file, directory, sd); - file[sd] = '/'; - memcpy(&file[sd + 1], name, sn + 1); - backup = &file[sd + sn + 2]; - memcpy(backup, file, sd + sn + 1); - backup[sd + sn + 1] = '~'; - backup[sd + sn + 2] = 0; - rc = fbuf_open_identify(fb, file, backup, id, idlen); - free(file); - } - return rc; -} - -/** open the database for files 'names' and 'rules' (can be NULL) */ -int -fdb_open( - const char *directory -) { - int rc; - - /* provide default directory */ - if (directory == NULL) - directory = fdb_default_directory; - - /* open the names */ - rc = open_identify(&fnames, directory, "cynara.names", uuid_names_v1, uuidlen); - if (rc < 0) - goto error; - - /* open the rules */ - rc = open_identify(&frules, directory, "cynara.rules", uuid_rules_v1, uuidlen); - if (rc < 0) - goto error; - - /* connect internals */ - rc = init_names(); - if (rc < 0) - goto error; - - init_rules(); - return 0; -error: - return -1; -} - -/** close the database */ -void -fdb_close( -) { - assert(fnames.name && frules.name); - fbuf_close(&fnames); - fbuf_close(&frules); -} - -/** is the database empty */ -bool -fdb_is_empty( -) { - return !rules_count; -} - -/** synchronize db on files */ -int -fdb_sync( -) { - int rc; - - assert(fnames.name && frules.name); - rc = fbuf_sync(&fnames); - if (rc == 0) - rc = fbuf_sync(&frules); - return rc; -} - -/** make a backup of the database */ -int -fdb_backup( -) { - int rc; - - assert(fnames.name && frules.name); - rc = fbuf_backup(&fnames); - if (rc == 0) - rc = fbuf_backup(&frules); - return rc; -} - -/** recover the database from latest backup */ -int -fdb_recover( -) { - int rc; - - assert(fnames.name && frules.name); - - rc = fbuf_recover(&fnames); - if (rc < 0) - goto error; - - rc = fbuf_recover(&frules); - if (rc < 0) - goto error; - - rc = init_names(); - if (rc < 0) - goto error; - - init_rules(); - return 0; -error: - fprintf(stderr, "db recovery impossible: %m"); - exit(5); - return rc; -} - -/** enumerate */ -void -fdb_for_all( - void *closure, - void (*callback)( - void *closure, - const data_key_t *key, - const data_value_t *value), - const data_key_t *key -) { - uint32_t ucli, uusr, i; - int anyperm; - data_key_t k; - data_value_t v; - - if (!is_any_or_wide(key->session) - || fdb_get_name_index(&ucli, key->client, false) - || fdb_get_name_index(&uusr, key->user, false)) - return; /* nothing to do! */ - - anyperm = is_any(key->permission); - for (i = 0; i < rules_count; i++) { - if ((ucli == ANYIDX || ucli == rules[i].key.client) - && (uusr == ANYIDX || uusr == rules[i].key.user) - && (anyperm || !strcasecmp(key->permission, name_at(rules[i].key.permission)))) { - k.client = name_at(rules[i].key.client); - k.session = WIDESTR; - k.user = name_at(rules[i].key.user); - k.permission = name_at(rules[i].key.permission); - v.value = name_at(rules[i].value); - v.expire = exp2time(rules[i].expire); - callback(closure, &k, &v); - } - } -} - -/** drop rules */ -int -fdb_drop( - const data_key_t *key -) { - uint32_t ucli, uusr, i; - bool anyperm; - - if (!is_any_or_wide(key->session) - || fdb_get_name_index(&ucli, key->client, false) - || fdb_get_name_index(&uusr, key->user, false)) - return 0; /* nothing to do! */ - - anyperm = is_any(key->permission); - i = 0; - while (i < rules_count) { - if ((ucli == ANYIDX || ucli == rules[i].key.client) - && (uusr == ANYIDX || uusr == rules[i].key.user) - && (anyperm || !strcasecmp(key->permission, name_at(rules[i].key.permission)))) - drop_at(i); - else - i++; - } - return 0; -} - -/** set rules */ -int -fdb_set( - const data_key_t *key, - const data_value_t *value -) { - int rc; - uint32_t ucli, uusr, uperm, uval, i; - const char *perm; - - /* check the session */ - if (!is_any_or_wide(key->session)) { - errno = EINVAL; - rc = -1; - goto error; - } - - /* normalise the perm */ - perm = is_any_or_wide(key->permission) ? WIDESTR : key->permission; - - /* get/create strings */ - rc = fdb_get_name_index(&ucli, is_any_or_wide(key->client) ? WIDESTR : key->client, true); - if (rc) - goto error; - rc = fdb_get_name_index(&uusr, is_any_or_wide(key->user) ? WIDESTR : key->user, true); - if (rc) - goto error; - rc = fdb_get_name_index(&uval, value->value, true); - if (rc) - goto error; - - /* search the existing rule */ - for (i = 0; i < rules_count; i++) { - if (ucli == rules[i].key.client - && uusr == rules[i].key.user - && !strcasecmp(perm, name_at(rules[i].key.permission))) { - /* found */ - set_at(i, uval, time2exph(value->expire)); - return 0; - } - } - - /* create the rule */ - rc = fdb_get_name_index(&uperm, perm, true); - if (rc) - goto error; - - rc = add_rule(ucli, uusr, uperm, uval, time2exph(value->expire)); - - return 0; -error: - return rc; -} - -/** check rules */ -int -fdb_test( - const data_key_t *key, - data_value_t *value -) { - const char *perm; - uint32_t ucli, uusr, i, score, sc, now; - rule_t *rule, *found; - - /* normalize the items */ - if (fdb_get_name_index(&ucli, is_any_or_wide(key->client) ? WIDESTR : key->client, false)) - ucli = NOIDX; - if (fdb_get_name_index(&uusr, is_any_or_wide(key->user) ? WIDESTR : key->user, false)) - uusr = NOIDX; - perm = is_any_or_wide(key->permission) ? WIDESTR : key->permission; - - /* search the existing rule */ - now = time2expl(time(NULL)); - found = NULL; - score = 0; - for (i = 0 ; i < rules_count ; i++) { - rule = &rules[i]; - if ((!rule->expire || rule->expire >= now) - && (ucli == rule->key.client || WIDEIDX == rule->key.client) - && (uusr == rule->key.user || WIDEIDX == rule->key.user) - && (WIDEIDX == rule->key.permission - || !strcasecmp(perm, name_at(rule->key.permission)))) { - /* found */ - sc = 1 + (rule->key.client != WIDEIDX) - + (rule->key.user != WIDEIDX) - + (rule->key.permission != WIDEIDX); - if (sc > score) { - score = sc; - found = rule; - } - } - } - if (!found) { - value->value = NULL; - value->expire = 0; - return 0; - } - - value->value = name_at(found->value); - value->expire = exp2time(found->expire); - - return 1; -} - -typedef struct gc gc_t; -struct gc -{ - uint32_t *befores; - uint32_t *afters; -}; - -/** compare indexes. used by qsort and bsearch */ -static -int -cmpidxs( - const void *pa, - const void *pb -) { - uint32_t a = *(const uint32_t*)pa; - uint32_t b = *(const uint32_t*)pb; - return a < b ? -1 : a != b; -} - -static -uint32_t* -gc_after_ptr( - gc_t *gc, - uint32_t *idx -) { - uint32_t *p = bsearch(idx, gc->befores, names_count, sizeof *gc->befores, cmpidxs); - assert(p != NULL); - return &gc->afters[p - gc->befores]; -} - -static -void -gc_mark( - gc_t *gc, - uint32_t *idx -) { - *gc_after_ptr(gc, idx) = 1; -} - -static -void -gc_mark_id( - gc_t *gc, - uint32_t idx -) { - gc_mark(gc, &idx); -} - -static -int -gc_after( - gc_t *gc, - uint32_t *idx -) { - uint32_t idbef, idaft; - - idbef = *idx; - idaft = *gc_after_ptr(gc, idx); - *idx = idaft; - return (int)(idbef - idaft); -} - -static -int -gc_init( - gc_t *gc -) { - gc->befores = malloc((sizeof *gc->befores + sizeof *gc->afters) * names_count); - if (gc->befores == NULL) - return -ENOMEM; - - names_count = names_count; - memcpy(gc->befores, names_sorted, names_count * sizeof *gc->befores); - qsort(gc->befores, names_count, sizeof *gc->befores, cmpidxs); - gc->afters = &gc->befores[names_count]; - memset(gc->afters, 0, names_count * sizeof *gc->afters); - - gc_mark_id(gc, ANYIDX); - gc_mark_id(gc, WIDEIDX); - return 0; -} - -static -void -gc_end( - gc_t *gc -) { - free(gc->befores); -} - -static -int -gc_pack( - gc_t *gc -) { - uint32_t i, j, n, next, prev; - char *strings; - - /* skip the unchanged initial part */ - n = names_count; - i = 0; - while (i < n && gc->afters[i]) - i++; - - /* at end means no change */ - if (i == n) - return 0; - - /* pack the strings */ - strings = fnames.buffer; - j = i; - memcpy(gc->afters, gc->befores, j * sizeof *gc->afters); - next = gc->befores[i++]; - fnames.saved = next; - while (i < n) { - if (gc->afters[i]) { - gc->befores[j] = prev = gc->befores[i]; - gc->afters[j++] = next; - while ((strings[next++] = strings[prev++])); - } - i++; - } - fnames.used = next; - names_count = j; - memcpy(names_sorted, gc->afters, j * sizeof *gc->afters); - qsort(names_sorted, j, sizeof *names_sorted, cmpnames); - - return 1; -} - -int -fdb_cleanup( -) { - int rc, chg; - uint32_t i, now; - gc_t gc; - rule_t *rule; - - /* init garbage collector */ - rc= gc_init(&gc); - if (rc < 0) - return rc; - - /* default now */ - now = time2expl(time(NULL)); - - /* remove expired entries and mark string ids of remaining ones */ - i = 0; - while (i < rules_count) { - rule = &rules[i]; - if (rule->expire && now >= rule->expire) - drop_at(i); - else { - gc_mark(&gc, &rule->key.client); - gc_mark(&gc, &rule->key.user); - gc_mark(&gc, &rule->key.permission); - gc_mark(&gc, &rule->value); - i++; - } - } - - /* pack the strings */ - if (gc_pack(&gc)) { - /* replace the ids if changed */ - i = 0; - while (i < rules_count) { - rule = &rules[i]; - chg = gc_after(&gc, &rule->key.client); - chg |= gc_after(&gc, &rule->key.user); - chg |= gc_after(&gc, &rule->key.permission); - chg |= gc_after(&gc, &rule->value); - if (chg) - touch_at(i); - i++; - } - } - - /* terminate */ - gc_end(&gc); - - return 0; -} diff --git a/src/fdb.h b/src/fdb.h deleted file mode 100644 index c3ae58b..0000000 --- a/src/fdb.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2018 "IoT.bzh" - * Author José Bollo <jose.bollo@iot.bzh> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define MAX_NAME_LENGTH 32767 - -/** open the database for files 'names' and 'rules' (can be NULL) */ -extern -int -fdb_open( - const char *directory -); - -/** close the database */ -extern -void -fdb_close( -); - -/** is the database empty */ -extern -bool -fdb_is_empty( -); - -/** sync the database */ -extern -int -fdb_sync( -); - -/** make a backup of the database */ -extern -int -fdb_backup( -); - -/** recover the database from latest backup */ -extern -int -fdb_recover( -); - -/** enumerate */ -extern -void -fdb_for_all( - void *closure, - void (*callback)( - void *closure, - const data_key_t *key, - const data_value_t *value), - const data_key_t *key -); - -/** erase rules */ -extern -int -fdb_drop( - const data_key_t *key -); - -/** set rules */ -extern -int -fdb_set( - const data_key_t *key, - const data_value_t *value -); - -/** check rules */ -extern -int -fdb_test( - const data_key_t *key, - data_value_t *value -); - -/** cleanup the base */ -extern -int -fdb_cleanup( -); - diff --git a/src/filedb.c b/src/filedb.c new file mode 100644 index 0000000..2a4de33 --- /dev/null +++ b/src/filedb.c @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <assert.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <stdalign.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <time.h> +#include <errno.h> + +#include "data.h" +#include "anydb.h" +#include "fbuf.h" +#include "filedb.h" + +#define MAX_NAME_LENGTH 32768 + +/* + * for the first version, save enougth time up to 4149 + * 4149 = 1970 + (4294967296 * 16) / (365 * 24 * 60 * 60) + * + * in the next version, time will be relative to a stored base + */ +#define exp2time(x) (((time_t)(x)) << 4) +#define time2exp(x) ((x) ? ((uint32_t)(((x) + 15) >> 4)) : 0) + +/** + * A rule is a set of 32 bits integers + */ +struct rule +{ + /** client string id */ + uint32_t client; + + /** user string id */ + uint32_t user; + + /** permission string id */ + uint32_t permission; + + /** value string id */ + uint32_t value; + + /** expiration */ + uint32_t expire; +}; +typedef struct rule rule_t; + +/* + * The cynara-agl database is made of 2 memory mapped files: + * - names: the zero terminated names + * - rules: the rules based on name indexes as 32bits indexes + * These files are normally in /var/lib/cynara + */ +#if !defined(DEFAULT_DB_DIR) +# define DEFAULT_DB_DIR "/var/lib/cynara" +#endif +#if !defined(DEFAULT_DB_NAME) +# define DEFAULT_DB_NAME "cynara" +#endif +static const char filedb_default_directory[] = DEFAULT_DB_DIR; +static const char filedb_default_name[] = DEFAULT_DB_NAME; + +/** identification of names version 2 + * $> uuidgen --sha1 -n @url -N urn:AGL:cynara:db:names:2 + * $> uuid -v 5 ns:URL urn:AGL:cynara:db:names:2 + */ +static const char uuid_names_v2[] = "6fa114d4-f3d9-58ab-a5d3-4674ee865c8d\n--\n"; + +/** identification of rules version 2 + * $> uuidgen --sha1 -n @url -N urn:AGL:cynara:db:rules:2 + * $> uuid -v 5 ns:URL urn:AGL:cynara:db:rules:2 + */ +static const char uuid_rules_v2[] = "6d48515a-3f64-52b1-9d15-4d13d073d48a\n--\n"; + +/** length of the identification */ +static const int uuidlen = 40; + + +struct filedb +{ + /** the file for the names */ + fbuf_t fnames; + + /** the file for the rules */ + fbuf_t frules; + + /** count of names */ + uint32_t names_count; + + /** the name indexes sorted */ + uint32_t *names_sorted; + + /** count of rules */ + uint32_t rules_count; + + /** the rules */ + rule_t *rules; + + /** is changed? */ + bool is_changed; + + /** needs cleanup? */ + bool need_cleanup; + + /** has backup? */ + bool has_backup; + + /** the anydb interface */ + anydb_t db; +}; +typedef struct filedb filedb_t; + +/** return the name of 'index' */ +static +const char* +name_at( + filedb_t *filedb, + uint32_t index +) { + return (const char*)(filedb->fnames.buffer + index); +} + +/** compare names. used by qsort and bsearch */ +static +int +cmpnames( + const void *pa, + const void *pb, + void *arg +) { + uint32_t a = *(const uint32_t*)pa; + uint32_t b = *(const uint32_t*)pb; + filedb_t *filedb = arg; + return strcmp(name_at(filedb, a), name_at(filedb, b)); +} + +/** initialize names */ +static +int +init_names( + filedb_t *filedb +) { + uint32_t pos, len, *ns, *p, all, nc; + + all = 0; + nc = 0; + ns = NULL; + + /* iterate over names */ + pos = uuidlen; + while (pos < filedb->fnames.used) { + /* get name length */ + len = (uint32_t)strlen(name_at(filedb, pos)); + if (pos + len <= pos || pos + len > filedb->fnames.used) { + free(ns); + goto bad_file; + } + /* store the position */ + if (all <= nc) { + all += 1024; + p = realloc(ns, all * sizeof *ns); + if (p == NULL) { + free(ns); + fprintf(stderr, "out of memory"); + goto error; + } + ns = p; + } + ns[nc++] = pos; + /* next */ + pos += len + 1; + } + + /* sort and record */ + qsort_r(ns, nc, sizeof *ns, cmpnames, filedb); + filedb->names_sorted = ns; + filedb->names_count = nc; + return 0; + +bad_file: + fprintf(stderr, "bad file %s", filedb->fnames.name); + errno = ENOEXEC; +error: + return -1; +} + +/** init the rules from the file */ +static +void +init_rules( + filedb_t *filedb +) { + filedb->rules = (rule_t*)(filedb->frules.buffer + uuidlen); + filedb->rules_count = (filedb->frules.used - uuidlen) / sizeof *filedb->rules; +} + +/** open a fbuf */ +static +int +open_identify( + fbuf_t *fb, + const char *directory, + const char *name, + const char *extension, + const char *id, + uint32_t idlen +) { + char *file, *backup, *p; + size_t ldir, lext, lname; + + ldir = strlen(directory); + lname = strlen(name); + lext = strlen(extension); + file = alloca(((ldir + lname + lext) << 1) + 7); + p = mempcpy(file, directory, ldir); + *p++ = '/'; + p = mempcpy(p, name, lname); + *p++ = '.'; + backup = mempcpy(p, extension, lext + 1); + p = mempcpy(backup, file, ldir + lname + lext + 2); + *p++ = '~'; + *p = 0; + return fbuf_open_identify(fb, file, backup, id, idlen); +} + +/** open the database for files 'names' and 'rules' (can be NULL) */ +static +int +opendb( + filedb_t *filedb, + const char *directory, + const char *name +) { + int rc; + + /* provide default directory */ + if (directory == NULL) + directory = filedb_default_directory; + + /* provide default name */ + if (name == NULL) + name = filedb_default_name; + + /* open the names */ + rc = open_identify(&filedb->fnames, directory, name, "names", uuid_names_v2, uuidlen); + if (rc < 0) + goto error; + + /* open the rules */ + rc = open_identify(&filedb->frules, directory, name, "rules", uuid_rules_v2, uuidlen); + if (rc < 0) + goto error; + + /* connect internals */ + rc = init_names(filedb); + if (rc < 0) + goto error; + + init_rules(filedb); + return 0; +error: + return -1; +} + +/** close the database */ +static +void +closedb( + filedb_t *filedb +) { + assert(filedb->fnames.name && filedb->frules.name); + fbuf_close(&filedb->fnames); + fbuf_close(&filedb->frules); +} + +/** synchronize db on files */ +static +int +syncdb( + filedb_t *filedb +) { + int rc; + + assert(filedb->fnames.name && filedb->frules.name); + if (!filedb->is_changed) + rc = 0; + else { + rc = fbuf_sync(&filedb->fnames); + if (rc == 0) { + rc = fbuf_sync(&filedb->frules); + if (rc == 0) { + filedb->is_changed = false; + filedb->has_backup = false; + } + } + } + return rc; +} + +/** make a backup of the database */ +static +int +backupdb( + filedb_t *filedb +) { + int rc; + + assert(filedb->fnames.name && filedb->frules.name); + if (filedb->has_backup) + rc = 0; + else { + rc = fbuf_backup(&filedb->fnames); + if (rc == 0) { + rc = fbuf_backup(&filedb->frules); + if (rc == 0) { + filedb->has_backup = true; + filedb->is_changed = false; + } + } + } + return rc; +} + +/** recover the database from latest backup */ +static +int +recoverdb( + filedb_t *filedb +) { + int rc; + + assert(filedb->fnames.name && filedb->frules.name); + if (!filedb->is_changed || !filedb->has_backup) + rc = 0; + else { + rc = fbuf_recover(&filedb->fnames); + if (rc < 0) + goto error; + + rc = fbuf_recover(&filedb->frules); + if (rc < 0) + goto error; + + rc = init_names(filedb); + if (rc < 0) + goto error; + + init_rules(filedb); + filedb->is_changed = false; + filedb->need_cleanup = false; + } + return rc; +error: + fprintf(stderr, "db recovery impossible: %m"); + exit(5); + return rc; +} + +static +int +index_itf( + void *clodb, + anydb_idx_t *idx, + const char *name, + bool create +) { + filedb_t *filedb = clodb; + uint32_t lo, up, m, i, *p; + int c; + const char *n; + size_t len; + + /* dichotomic search */ + lo = 0; + up = filedb->names_count; + while(lo < up) { + m = (lo + up) >> 1; + i = filedb->names_sorted[m]; + n = name_at(filedb, i); + c = strcmp(n, name); + + if (c == 0) { + /* found */ + *idx = i; + return 0; + } + + /* dichotomic iteration */ + if (c < 0) + lo = m + 1; + else + up = m; + } + + /* not found */ + if (!create) { + errno = ENOENT; + return -1; + } + + /* check length */ + len = strnlen(name, MAX_NAME_LENGTH + 1); + if (len > MAX_NAME_LENGTH) { + errno = EINVAL; + return -1; + } + + /* add the name in the file */ + i = filedb->fnames.used; + c = fbuf_append(&filedb->fnames, name, 1 + (uint32_t)len); + if (c < 0) + return c; + + /* add the name in sorted array */ + up = filedb->names_count; + if (!(up & 1023)) { + p = realloc(filedb->names_sorted, (up + 1024) * sizeof *p); + if (p == NULL) { + fprintf(stderr, "out of memory"); + return -1; + } + filedb->names_sorted = p; + } + memmove(&filedb->names_sorted[lo + 1], &filedb->names_sorted[lo], (up - lo) * sizeof *filedb->names_sorted); + filedb->names_count = up + 1; + *idx = filedb->names_sorted[lo] = i; + return 0; +} + +static +const char * +string_itf( + void *clodb, + anydb_idx_t idx +) { + filedb_t *filedb = clodb; + + return name_at(filedb, idx); +} + +static +void +apply_itf( + void *clodb, + anydb_action_t (*oper)(void *closure, const anydb_key_t *key, anydb_value_t *value), + void *closure +) { + filedb_t *filedb = clodb; + anydb_action_t a; + rule_t *rule; + anydb_key_t key; + anydb_value_t value; + uint32_t i; + + key.session = AnyIdx_Wide; + i = 0; + while (i < filedb->rules_count) { + rule = &filedb->rules[i]; + key.client = rule->client; + key.user = rule->user; + key.permission = rule->permission; + value.value = rule->value; + value.expire = exp2time(rule->expire); + a = oper(closure, &key, &value); + switch (a) { + case Anydb_Action_Stop: + return; + case Anydb_Action_Continue: + i++; + break; + case Anydb_Action_Update_And_Stop: + rule->value = value.value; + rule->expire = time2exp(value.expire); + filedb->need_cleanup = true; + filedb->is_changed = true; + return; + case Anydb_Action_Remove_And_Continue: + *rule = filedb->rules[--filedb->rules_count]; + filedb->is_changed = true; + filedb->need_cleanup = true; + filedb->frules.used -= (uint32_t)sizeof *rule; + break; + } + } +} + +static +int +transaction_itf( + void *clodb, + anydb_transaction_t oper +) { + filedb_t *filedb = clodb; + int rc; + + switch (oper) { + case Anydb_Transaction_Start: + rc = backupdb(filedb); + break; + case Anydb_Transaction_Commit: + rc = syncdb(filedb); + break; + case Anydb_Transaction_Cancel: + rc = recoverdb(filedb); + break; + } + return rc; +} + +static +int +add_itf( + void *clodb, + const anydb_key_t *key, + const anydb_value_t *value +) { + filedb_t *filedb = clodb; + int rc; + struct rule *rules; + uint32_t alloc; + uint32_t count; + + alloc = filedb->frules.used + (uint32_t)sizeof *rules; + rc = fbuf_ensure_capacity(&filedb->frules, alloc); + if (rc) + return rc; + rules = (rule_t*)(filedb->frules.buffer + uuidlen); + filedb->rules = rules; + count = filedb->rules_count++; + rules = &rules[count]; + rules->client = key->client; + rules->user = key->user; + rules->permission = key->permission; + rules->value = value->value; + rules->expire = time2exp(value->expire); + filedb->frules.used = alloc; + filedb->is_changed = true; + return 0; +} + +static +bool +gc_dig( + uint32_t *array, + uint32_t count, + uint32_t item, + uint32_t *index +) { + uint32_t lo, up, i; + + /* dichotomic search */ + lo = 0; + up = count; + while(lo < up) { + i = (lo + up) >> 1; + if (array[i] == item) { + /* found */ + *index = i; + return true; + } + + /* dichotomic iteration */ + if (array[i] < item) + lo = i + 1; + else + up = i; + } + *index = lo; + return false; +} + +static +uint32_t +gc_add( + uint32_t *array, + uint32_t count, + uint32_t item +) { + uint32_t index, i; + + if (gc_dig(array, count, item, &index)) + return count; + + i = count; + while (i > index) { + array[i] = array[i - 1]; + i = i - 1; + } + array[i] = item; + return count + 1; +} + +static +uint32_t +gc_mark( + uint32_t *array, + uint32_t count, + uint32_t item +) { + return item > AnyIdx_Max ? count : gc_add(array, count, item); +} + +static +bool +gc_new( + uint32_t *array, + uint32_t count, + uint32_t item, + uint32_t *index +) { + return item > AnyIdx_Max ? false : gc_dig(array, count, item, index); +} + +static +void +gc_itf( + void *clodb +) { + filedb_t *filedb = clodb; + uint32_t nr; + uint32_t nn; + struct rule *rules; + uint32_t *used; + uint32_t *sorted; + char *strings; + uint32_t ir, nu, idx, is, ios, lenz; + + /* check cleanup required */ + if (!filedb->need_cleanup) + return; + filedb->need_cleanup = false; + + /* mark items */ + nr = filedb->rules_count; + nn = filedb->names_count; + rules = filedb->rules; + used = alloca(nn * sizeof *used); + nu = 0; + for (ir = 0 ; ir < nr ; ir++) { + nu = gc_mark(used, nu, rules[ir].client); + nu = gc_mark(used, nu, rules[ir].user); + nu = gc_mark(used, nu, rules[ir].permission); + nu = gc_mark(used, nu, rules[ir].value); + } + + /* pack if too much unused */ + if (nu + (nu >> 2) <= nn) + return; + + /* pack the names */ + strings = (char*)filedb->fnames.buffer; + sorted = filedb->names_sorted; + is = ios = uuidlen; + while (is < filedb->fnames.used) { + /* get name length */ + lenz = 1 + (uint32_t)strlen(strings + is); + if (gc_dig(used, nu, is, &idx)) { + sorted[idx] = ios; + if (is != ios) + memcpy(strings + ios, strings + is, lenz); + ios += lenz; + } + /* next */ + is += lenz; + } + + /* renum the rules */ + for (ir = 0 ; ir < nr ; ir++) { + if (gc_new(used, nu, rules[ir].client, &idx)) + rules[ir].client = sorted[idx]; + if (gc_new(used, nu, rules[ir].user, &idx)) + rules[ir].user = sorted[idx]; + if (gc_new(used, nu, rules[ir].permission, &idx)) + rules[ir].permission = sorted[idx]; + if (gc_new(used, nu, rules[ir].value, &idx)) + rules[ir].value = sorted[idx]; + } + + /* record and sort */ + filedb->names_count = nu; + filedb->fnames.used = ios; + qsort_r(sorted, nu, sizeof *sorted, cmpnames, filedb); + + /* set as changed */ + filedb->is_changed = true; +} + +static +int +sync_itf( + void *clodb +) { + filedb_t *filedb = clodb; + return syncdb(filedb); +} + +static +void +destroy_itf( + void *clodb +) { + filedb_t *filedb = clodb; + if (filedb) { + if (filedb->frules.name) + closedb(filedb); + free(filedb); + } +} + +static +void +init( + filedb_t *filedb +) { + filedb->db.clodb = filedb; + + filedb->db.itf.index = index_itf; + filedb->db.itf.string = string_itf; + filedb->db.itf.transaction = transaction_itf; + filedb->db.itf.apply = apply_itf; + filedb->db.itf.add = add_itf; + filedb->db.itf.gc = gc_itf; + filedb->db.itf.sync = sync_itf; + filedb->db.itf.destroy = destroy_itf; +} + +int +filedb_create( + anydb_t **adb, + const char *directory, + const char *basename +) { + int rc; + filedb_t *filedb; + + *adb = NULL; + filedb = calloc(1, sizeof *filedb); + if (!filedb) + return -ENOMEM; + + init(filedb); + + rc = opendb(filedb, directory, basename); + if (rc) + free(filedb); + else + *adb = &filedb->db; + return rc; +} + +/** synchronize database */ +int +anydb_sync( + anydb_t *db +) { + return db->itf.sync ? db->itf.sync(db->clodb) : 0; +} diff --git a/src/filedb.h b/src/filedb.h new file mode 100644 index 0000000..d575e50 --- /dev/null +++ b/src/filedb.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author José Bollo <jose.bollo@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + + +/** is the database empty */ +int +filedb_create( + anydb_t **filedb, + const char *directory, + const char *basename +); diff --git a/src/memdb.c b/src/memdb.c index b07928f..285d7a4 100644 --- a/src/memdb.c +++ b/src/memdb.c @@ -144,6 +144,8 @@ apply_itf( else a = oper(closure, &rules[ir].key, &rules[ir].value); switch (a) { + case Anydb_Action_Stop: + return; case Anydb_Action_Continue: ir++; break; @@ -253,6 +255,25 @@ add_itf( static void +gc_mark( + anydb_idx_t *renum, + anydb_idx_t item +) { + if (item <= AnyIdx_Max) + renum[item] = 1; +} + +static +anydb_idx_t +gc_new( + anydb_idx_t *renum, + anydb_idx_t item +) { + return item > AnyIdx_Max ? item : renum[item]; +} +#include <stdio.h> +static +void gc_itf( void *clodb ) { @@ -268,11 +289,11 @@ gc_itf( renum[i] = 0; for (i = 0 ; i < nr ; i++) { - renum[rules[i].key.client] = 1; - renum[rules[i].key.session] = 1; - renum[rules[i].key.user] = 1; - renum[rules[i].key.permission] = 1; - renum[rules[i].value.value] = 1; + gc_mark(renum, rules[i].key.client); + gc_mark(renum, rules[i].key.session); + gc_mark(renum, rules[i].key.user); + gc_mark(renum, rules[i].key.permission); + gc_mark(renum, rules[i].value.value); } for (i = j = 0 ; i < ns ; i++) { @@ -287,16 +308,16 @@ gc_itf( if (ns != j) { memdb->strings.count = ns = j; for (i = 0 ; i < nr ; i++) { - rules[i].key.client = renum[rules[i].key.client]; - rules[i].key.session = renum[rules[i].key.session]; - rules[i].key.user = renum[rules[i].key.user]; - rules[i].key.permission = renum[rules[i].key.permission]; - rules[i].value.value = renum[rules[i].value.value]; + rules[i].key.client = gc_new(renum, rules[i].key.client); + rules[i].key.session = gc_new(renum, rules[i].key.session); + rules[i].key.user = gc_new(renum, rules[i].key.user); + rules[i].key.permission = gc_new(renum, rules[i].key.permission); + rules[i].value.value = gc_new(renum, rules[i].value.value); } } i = memdb->strings.alloc; - while (ns + SBS > i) + while (ns + SBS < i) i -= SBS; if (i != memdb->strings.alloc) { memdb->strings.alloc = i; @@ -304,7 +325,7 @@ gc_itf( } i = memdb->rules.alloc; - while (ns + RBS > i) + while (nr + RBS < i) i -= RBS; if (i != memdb->rules.alloc) { memdb->rules.alloc = i; @@ -338,6 +359,7 @@ init( memdb->db.itf.apply = apply_itf; memdb->db.itf.add = add_itf; memdb->db.itf.gc = gc_itf; + memdb->db.itf.sync = 0; memdb->db.itf.destroy = destroy_itf; memdb->strings.alloc = 0; |