forked from Telodendria/Telodendria
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:
parent
f56a067456
commit
cd584c1e93
2 changed files with 64 additions and 64 deletions
123
src/Db.c
123
src/Db.c
|
@ -28,6 +28,7 @@
|
||||||
#include <Util.h>
|
#include <Util.h>
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
struct Db
|
struct Db
|
||||||
{
|
{
|
||||||
|
@ -55,6 +56,8 @@ struct DbRef
|
||||||
|
|
||||||
DbRef *prev;
|
DbRef *prev;
|
||||||
DbRef *next;
|
DbRef *next;
|
||||||
|
|
||||||
|
FILE *fp;
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t DbComputeSize(HashMap *);
|
static ssize_t DbComputeSize(HashMap *);
|
||||||
|
@ -274,19 +277,16 @@ DbClose(Db * db)
|
||||||
|
|
||||||
pthread_mutex_destroy(&db->lock);
|
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(key);
|
Free(val->prefix);
|
||||||
JsonFree(val->json);
|
Free(val->key);
|
||||||
Free(val->prefix);
|
pthread_mutex_destroy(&val->lock);
|
||||||
Free(val->key);
|
Free(val);
|
||||||
pthread_mutex_destroy(&val->lock);
|
|
||||||
Free(val);
|
|
||||||
}
|
|
||||||
HashMapFree(db->cache);
|
|
||||||
}
|
}
|
||||||
|
HashMapFree(db->cache);
|
||||||
|
|
||||||
Free(db);
|
Free(db);
|
||||||
}
|
}
|
||||||
|
@ -330,6 +330,8 @@ DbLock(Db * db, char *prefix, char *key)
|
||||||
char *file;
|
char *file;
|
||||||
char *hash;
|
char *hash;
|
||||||
DbRef *ref;
|
DbRef *ref;
|
||||||
|
FILE *fp;
|
||||||
|
struct flock lock;
|
||||||
|
|
||||||
if (!db || !prefix || !key)
|
if (!db || !prefix || !key)
|
||||||
{
|
{
|
||||||
|
@ -346,31 +348,43 @@ DbLock(Db * db, char *prefix, char *key)
|
||||||
ref = HashMapGet(db->cache, hash);
|
ref = HashMapGet(db->cache, hash);
|
||||||
file = DbFileName(db, prefix, key);
|
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 */
|
if (ref) /* In cache */
|
||||||
{
|
{
|
||||||
unsigned long diskTs = UtilLastModified(file);
|
unsigned long diskTs = UtilLastModified(file);
|
||||||
|
|
||||||
pthread_mutex_lock(&ref->lock);
|
pthread_mutex_lock(&ref->lock);
|
||||||
|
ref->fp = fp;
|
||||||
|
|
||||||
if (diskTs > ref->ts)
|
if (diskTs > ref->ts)
|
||||||
{
|
{
|
||||||
/* File was modified on disk since it was cached */
|
/* File was modified on disk since it was cached */
|
||||||
FILE *fp = fopen(file, "r");
|
HashMap *json = JsonDecode(fp);
|
||||||
HashMap *json;
|
|
||||||
|
|
||||||
if (!fp)
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&ref->lock);
|
|
||||||
ref = NULL;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
json = JsonDecode(fp);
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
if (!json)
|
if (!json)
|
||||||
{
|
{
|
||||||
pthread_mutex_unlock(&ref->lock);
|
pthread_mutex_unlock(&ref->lock);
|
||||||
|
fclose(fp);
|
||||||
ref = NULL;
|
ref = NULL;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
@ -380,37 +394,31 @@ DbLock(Db * db, char *prefix, char *key)
|
||||||
ref->ts = diskTs;
|
ref->ts = diskTs;
|
||||||
ref->size = DbComputeSize(ref->json);
|
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;
|
db->leastRecent = ref->next;
|
||||||
ref->prev->next = ref->next;
|
|
||||||
|
|
||||||
if (!ref->prev)
|
|
||||||
{
|
|
||||||
db->leastRecent = ref->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref->prev = db->mostRecent;
|
|
||||||
ref->next = NULL;
|
|
||||||
db->mostRecent = ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The file on disk may be larger than what we have in
|
ref->prev = db->mostRecent;
|
||||||
* memory, which may require items in cache to be evicted. */
|
ref->next = NULL;
|
||||||
DbCacheEvict(db);
|
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
|
else
|
||||||
{
|
{
|
||||||
/* Not in cache; load from disk */
|
/* Not in cache; load from disk */
|
||||||
FILE *fp = fopen(file, "r");
|
|
||||||
|
|
||||||
if (!fp)
|
|
||||||
{
|
|
||||||
ref = NULL;
|
|
||||||
goto finish;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref = Malloc(sizeof(DbRef));
|
ref = Malloc(sizeof(DbRef));
|
||||||
if (!ref)
|
if (!ref)
|
||||||
|
@ -420,11 +428,12 @@ DbLock(Db * db, char *prefix, char *key)
|
||||||
}
|
}
|
||||||
|
|
||||||
ref->json = JsonDecode(fp);
|
ref->json = JsonDecode(fp);
|
||||||
fclose(fp);
|
ref->fp = fp;
|
||||||
|
|
||||||
if (!ref->json)
|
if (!ref->json)
|
||||||
{
|
{
|
||||||
Free(ref);
|
Free(ref);
|
||||||
|
fclose(fp);
|
||||||
ref = NULL;
|
ref = NULL;
|
||||||
goto finish;
|
goto finish;
|
||||||
}
|
}
|
||||||
|
@ -461,9 +470,6 @@ finish:
|
||||||
int
|
int
|
||||||
DbUnlock(Db * db, DbRef * ref)
|
DbUnlock(Db * db, DbRef * ref)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
|
||||||
char *file;
|
|
||||||
|
|
||||||
if (!db || !ref)
|
if (!db || !ref)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -471,20 +477,13 @@ DbUnlock(Db * db, DbRef * ref)
|
||||||
|
|
||||||
pthread_mutex_lock(&db->lock);
|
pthread_mutex_lock(&db->lock);
|
||||||
|
|
||||||
file = DbFileName(db, ref->prefix, ref->key);
|
rewind(ref->fp);
|
||||||
fp = fopen(file, "w");
|
ftruncate(fileno(ref->fp), 0);
|
||||||
Free(file);
|
|
||||||
|
|
||||||
if (!fp)
|
JsonEncode(ref->json, ref->fp);
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&db->lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonEncode(ref->json, fp);
|
|
||||||
fflush(fp);
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
|
fflush(ref->fp);
|
||||||
|
fclose(ref->fp);
|
||||||
|
|
||||||
db->cacheSize -= ref->size;
|
db->cacheSize -= ref->size;
|
||||||
ref->size = DbComputeSize(ref->json);
|
ref->size = DbComputeSize(ref->json);
|
||||||
|
|
|
@ -168,7 +168,7 @@ main(int argc, char **argv)
|
||||||
#ifdef __OpenBSD__
|
#ifdef __OpenBSD__
|
||||||
Log(lc, LOG_DEBUG, "Attempting pledge...");
|
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));
|
Log(lc, LOG_ERROR, "Pledge failed: %s", strerror(errno));
|
||||||
exit = EXIT_FAILURE;
|
exit = EXIT_FAILURE;
|
||||||
|
@ -386,7 +386,8 @@ main(int argc, char **argv)
|
||||||
|
|
||||||
if (getuid() == 0)
|
if (getuid() == 0)
|
||||||
{
|
{
|
||||||
#ifndef __OpenBSD__ /* chroot() is only useful without unveil() */
|
#ifndef __OpenBSD__ /* chroot() is only useful without
|
||||||
|
* unveil() */
|
||||||
if (chroot(".") == 0)
|
if (chroot(".") == 0)
|
||||||
{
|
{
|
||||||
Log(lc, LOG_DEBUG, "Changed the root directory to: %s.", tConfig->dataDir);
|
Log(lc, LOG_DEBUG, "Changed the root directory to: %s.", tConfig->dataDir);
|
||||||
|
|
Loading…
Reference in a new issue