summaryrefslogtreecommitdiffstats
path: root/src/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cache.c')
-rw-r--r--src/cache.c229
1 files changed, 229 insertions, 0 deletions
diff --git a/src/cache.c b/src/cache.c
new file mode 100644
index 0000000..860b4fb
--- /dev/null
+++ b/src/cache.c
@@ -0,0 +1,229 @@
+#include <db.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <time.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "cache.h"
+#include "md5util.h"
+
+struct syrep_cache_key {
+ uint64_t dev;
+ uint64_t inode;
+ uint32_t date;
+ uint64_t size;
+};
+
+struct syrep_cache_data {
+ uint8_t digest[16];
+ uint32_t timestamp;
+};
+
+struct syrep_md_cache {
+ DB* db;
+ uint32_t timestamp;
+ int ro;
+};
+
+struct syrep_md_cache* md_cache_open(const char* fn, int ro) {
+ struct syrep_md_cache *c = NULL;
+ int ret;
+
+ assert(fn);
+
+ if (!(c = malloc(sizeof(struct syrep_md_cache))))
+ goto fail;
+
+ c->timestamp = time(NULL);
+ c->ro = ro;
+
+ if ((ret = db_create(&c->db, NULL, 0))) {
+ fprintf(stderr, "db_create: %s\n", db_strerror(ret));
+ goto fail;
+ }
+
+ if ((ret = c->db->open(c->db, NULL, fn, NULL, DB_BTREE, ro ? DB_RDONLY : DB_CREATE, 0664))) {
+ c->db->err(c->db, ret, "open(%s)", fn);
+ goto fail;
+ }
+
+ return c;
+
+fail:
+
+ if (c) {
+ if (c->db)
+ c->db->close(c->db, 0);
+
+ free(c);
+ }
+
+ return NULL;
+}
+
+void md_cache_close(struct syrep_md_cache *c) {
+ assert(c);
+
+ if (c->db)
+ c->db->close(c->db, 0);
+
+ free(c);
+}
+
+static int get(struct syrep_md_cache *c, const struct syrep_cache_key *k, uint8_t digest[16]) {
+ int ret;
+ DBT key, data;
+
+ assert(c && c->db && k);
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ key.data = (void*) k;
+ key.size = sizeof(struct syrep_cache_key);
+
+ if ((ret = c->db->get(c->db, NULL, &key, &data, 0)) != 0) {
+ if (ret == DB_NOTFOUND) {
+ //fprintf(stderr, "Cache MISS!\n");
+ return 0;
+ }
+
+ c->db->err(c->db, ret, "cache::get");
+ return -1;
+ }
+
+ //fprintf(stderr, "Cache HIT\n");
+
+ assert(data.data);
+ memcpy(digest, &((struct syrep_cache_data*) data.data)->digest, 16);
+
+ return 1;
+}
+
+static int put(struct syrep_md_cache *c, const struct syrep_cache_key *k, const uint8_t digest[16], uint32_t timestamp) {
+ int ret;
+ DBT key, data;
+ struct syrep_cache_data d;
+
+ assert(c && c->db && k);
+
+ memset(&key, 0, sizeof(key));
+ key.data = (void*) k;
+ key.size = sizeof(struct syrep_cache_key);
+
+ memcpy(d.digest, digest, 16);
+ d.timestamp = timestamp;
+
+ memset(&data, 0, sizeof(data));
+ data.data = &d;
+ data.size = sizeof(struct syrep_cache_data);
+
+ if ((ret = c->db->put(c->db, NULL, &key, &data, 0)) != 0) {
+ c->db->err(c->db, ret, "cache::put");
+ return -1;
+ }
+
+ return 0;
+}
+
+int md_cache_get(struct syrep_md_cache *c, const char *path, uint8_t digest[16]) {
+ struct syrep_cache_key k;
+ int r = -1, fd = -1, j;
+ struct stat st;
+
+ if ((fd = open(path, O_RDONLY)) < 0) {
+ fprintf(stderr, "open(%s): %s\n", path, strerror(errno));
+ goto finish;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ fprintf(stderr, "fstat(%s): %s\n", path, strerror(errno));
+ goto finish;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ fprintf(stderr, "<%s> not a regular file: %s\n", path, strerror(errno));
+ goto finish;
+ }
+
+ memset(&k, 0, sizeof(k));
+ k.dev = (uint64_t) st.st_dev;
+ k.inode = (uint64_t) st.st_ino;
+ k.date = (uint32_t) st.st_mtime;
+ k.size = (uint64_t) st.st_size;
+
+ if (!c)
+ j = 0;
+ else
+ if ((j = get(c, &k, digest)) < 0)
+ goto finish;
+
+
+ if (!j)
+ if (fdmd5(fd, st.st_size, digest) < 0)
+ goto finish;
+
+ if (c && !c->ro)
+ put(c, &k, digest, c->timestamp);
+
+ r = 0;
+
+finish:
+
+ if (fd >= 0)
+ close(fd);
+
+ return r;
+}
+
+int md_cache_vacuum(struct syrep_md_cache*c) {
+ int r = -1, ret;
+ DBC *cursor;
+ DBT key, data;
+ int ndel = 0, ntotal = 0;
+
+ if (c->ro)
+ return 0;
+
+ assert(c && c->db);
+
+ if ((ret = c->db->cursor(c->db, NULL, &cursor, 0)) != 0) {
+ c->db->err(c->db, ret, "cache::vacuum");
+ return -1;
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ while ((ret = cursor->c_get(cursor, &key, &data, DB_NEXT)) == 0) {
+ struct syrep_cache_data *d;
+
+ ntotal++;
+ d = (struct syrep_cache_data*) data.data;
+ assert(d);
+ if (d->timestamp < c->timestamp) {
+ cursor->c_del(cursor, 0);
+ ndel++;
+ }
+ }
+
+ if (ret != DB_NOTFOUND) {
+ c->db->err(c->db, ret, "cache::vacuum()");
+ goto finish;
+ }
+
+ /*fprintf(stderr, "Cache vacuum successfully completed, %i of %i entries deleted.\n", ndel, ntotal);*/
+
+ r = 0;
+
+finish:
+
+ cursor->c_close(cursor);
+
+ return r;
+}