diff --git a/src/Db.c b/src/Db.c index 4f91cb1..ab3c8cd 100644 --- a/src/Db.c +++ b/src/Db.c @@ -28,6 +28,7 @@ #include #include +#include struct Db { @@ -55,6 +56,8 @@ struct DbRef DbRef *prev; DbRef *next; + + FILE *fp; }; static ssize_t DbComputeSize(HashMap *); @@ -274,19 +277,16 @@ DbClose(Db * db) pthread_mutex_destroy(&db->lock); - if (db->cache) + while (HashMapIterate(db->cache, &key, (void **) &val)) { - while (HashMapIterate(db->cache, &key, (void **) &val)) - { - Free(key); - JsonFree(val->json); - Free(val->prefix); - Free(val->key); - pthread_mutex_destroy(&val->lock); - Free(val); - } - HashMapFree(db->cache); + Free(key); + JsonFree(val->json); + Free(val->prefix); + Free(val->key); + pthread_mutex_destroy(&val->lock); + Free(val); } + HashMapFree(db->cache); Free(db); } @@ -330,6 +330,8 @@ DbLock(Db * db, char *prefix, char *key) char *file; char *hash; DbRef *ref; + FILE *fp; + struct flock lock; if (!db || !prefix || !key) { @@ -346,31 +348,43 @@ DbLock(Db * db, char *prefix, char *key) ref = HashMapGet(db->cache, hash); file = DbFileName(db, prefix, key); + /* Open the file for reading and writing so we can lock it */ + fp = fopen(file, "r+"); + if (!fp) + { + ref = NULL; + goto finish; + } + + lock.l_start = 0; + lock.l_len = 0; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + + /* Lock the file on the disk */ + if (fcntl(fileno(fp), F_SETLK, &lock) < 0) + { + fclose(fp); + ref = NULL; + goto finish; + } + if (ref) /* In cache */ { unsigned long diskTs = UtilLastModified(file); pthread_mutex_lock(&ref->lock); + ref->fp = fp; if (diskTs > ref->ts) { /* File was modified on disk since it was cached */ - FILE *fp = fopen(file, "r"); - HashMap *json; - - if (!fp) - { - pthread_mutex_unlock(&ref->lock); - ref = NULL; - goto finish; - } - - json = JsonDecode(fp); - fclose(fp); + HashMap *json = JsonDecode(fp); if (!json) { pthread_mutex_unlock(&ref->lock); + fclose(fp); ref = NULL; goto finish; } @@ -380,37 +394,31 @@ DbLock(Db * db, char *prefix, char *key) ref->ts = diskTs; ref->size = DbComputeSize(ref->json); - /* Float this ref to mostRecent */ - if (ref->next) + } + + /* Float this ref to mostRecent */ + if (ref->next) + { + ref->next->prev = ref->prev; + ref->prev->next = ref->next; + + if (!ref->prev) { - ref->next->prev = ref->prev; - ref->prev->next = ref->next; - - if (!ref->prev) - { - db->leastRecent = ref->next; - } - - ref->prev = db->mostRecent; - ref->next = NULL; - db->mostRecent = ref; + db->leastRecent = ref->next; } - /* The file on disk may be larger than what we have in - * memory, which may require items in cache to be evicted. */ - DbCacheEvict(db); + ref->prev = db->mostRecent; + ref->next = NULL; + db->mostRecent = ref; } + + /* The file on disk may be larger than what we have in memory, + * which may require items in cache to be evicted. */ + DbCacheEvict(db); } else { /* Not in cache; load from disk */ - FILE *fp = fopen(file, "r"); - - if (!fp) - { - ref = NULL; - goto finish; - } ref = Malloc(sizeof(DbRef)); if (!ref) @@ -420,11 +428,12 @@ DbLock(Db * db, char *prefix, char *key) } ref->json = JsonDecode(fp); - fclose(fp); + ref->fp = fp; if (!ref->json) { Free(ref); + fclose(fp); ref = NULL; goto finish; } @@ -461,9 +470,6 @@ finish: int DbUnlock(Db * db, DbRef * ref) { - FILE *fp; - char *file; - if (!db || !ref) { return 0; @@ -471,20 +477,13 @@ DbUnlock(Db * db, DbRef * ref) pthread_mutex_lock(&db->lock); - file = DbFileName(db, ref->prefix, ref->key); - fp = fopen(file, "w"); - Free(file); + rewind(ref->fp); + ftruncate(fileno(ref->fp), 0); - if (!fp) - { - pthread_mutex_unlock(&db->lock); - return 0; - } - - JsonEncode(ref->json, fp); - fflush(fp); - fclose(fp); + JsonEncode(ref->json, ref->fp); + fflush(ref->fp); + fclose(ref->fp); db->cacheSize -= ref->size; ref->size = DbComputeSize(ref->json); diff --git a/src/Telodendria.c b/src/Telodendria.c index 6e5a8d5..f863836 100644 --- a/src/Telodendria.c +++ b/src/Telodendria.c @@ -168,7 +168,7 @@ main(int argc, char **argv) #ifdef __OpenBSD__ Log(lc, LOG_DEBUG, "Attempting pledge..."); - if (pledge("stdio rpath wpath cpath inet dns getpw id unveil", NULL) != 0) + if (pledge("stdio rpath wpath cpath flock inet dns getpw id unveil", NULL) != 0) { Log(lc, LOG_ERROR, "Pledge failed: %s", strerror(errno)); exit = EXIT_FAILURE; @@ -386,7 +386,8 @@ main(int argc, char **argv) if (getuid() == 0) { -#ifndef __OpenBSD__ /* chroot() is only useful without unveil() */ +#ifndef __OpenBSD__ /* chroot() is only useful without + * unveil() */ if (chroot(".") == 0) { Log(lc, LOG_DEBUG, "Changed the root directory to: %s.", tConfig->dataDir);