summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJose Bollo <jose.bollo@iot.bzh>2019-05-13 16:31:55 +0200
committerJose Bollo <jose.bollo@iot.bzh>2019-05-13 16:33:10 +0200
commitc451f53b4f3acd2157f9c7e7365ecc5663e9ada7 (patch)
treedcdee6a94c2a82523b359012786f84f78a953ce4
parentc5f4d75b492052f3c3a4e115c5f6a2060812a639 (diff)
Switch to filedb
Signed-off-by: Jose Bollo <jose.bollo@iot.bzh>
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/anydb.c27
-rw-r--r--src/anydb.h22
-rw-r--r--src/db.c35
-rw-r--r--src/db.h5
-rw-r--r--src/fdb.c865
-rw-r--r--src/fdb.h99
-rw-r--r--src/filedb.c776
-rw-r--r--src/filedb.h27
-rw-r--r--src/memdb.c46
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
+);
diff --git a/src/db.c b/src/db.c
index 5d28246..e6eb3d1 100644
--- a/src/db.c
+++ b/src/db.c
@@ -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;
+}
+
diff --git a/src/db.h b/src/db.h
index 995fd65..edb4f29 100644
--- a/src/db.h
+++ b/src/db.h
@@ -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;