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.
This commit is contained in:
Jordan Bancino 2022-11-21 16:13:11 +00:00
parent f56a067456
commit cd584c1e93
2 changed files with 64 additions and 64 deletions

123
src/Db.c
View file

@ -28,6 +28,7 @@
#include <Util.h>
#include <pthread.h>
#include <fcntl.h>
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);

View file

@ -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);