From cd584c1e931cc17ecea702b7e541f8286e40f322 Mon Sep 17 00:00:00 2001 From: Jordan Bancino Date: Mon, 21 Nov 2022 16:13:11 +0000 Subject: [PATCH] Lock objects on disk if they're locked in memory. This requires hanging onto an open file handle, and doesn't require explicit unlocking, because POSIX says files are unlocked when their descriptors are closed. --- src/Db.c | 123 +++++++++++++++++++++++----------------------- src/Telodendria.c | 5 +- 2 files changed, 64 insertions(+), 64 deletions(-) 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);