summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorVladimír Čunát <vladimir.cunat@nic.cz>2024-11-04 15:54:48 +0100
committerVladimír Čunát <vladimir.cunat@nic.cz>2024-11-04 16:08:02 +0100
commit19c4c0b59666554f8ef072aac0699bde9069184c (patch)
tree9a4506b0b7a023cf4f46681bf3ebc5f46151562c /lib
parentdaemon/mmapped: use static_assert on undefined padding (diff)
downloadknot-resolver-19c4c0b59666554f8ef072aac0699bde9069184c.tar.xz
knot-resolver-19c4c0b59666554f8ef072aac0699bde9069184c.zip
mmapped_* nit: move from daemon/ to lib/
We'll utilize this for cache, so this will be an easy way for GC to access the mmapped_* symbols. In lib/ we usually prefix symbols by kr_ but I don't think it's worth the hassle in this case, as mmapped_ seems like a good enough prefix.
Diffstat (limited to 'lib')
-rw-r--r--lib/meson.build2
-rw-r--r--lib/mmapped.c113
-rw-r--r--lib/mmapped.h33
3 files changed, 148 insertions, 0 deletions
diff --git a/lib/meson.build b/lib/meson.build
index e369a452..94bb80c7 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -25,6 +25,7 @@ libkres_src = files([
'layer/iterate.c',
'layer/validate.c',
'log.c',
+ 'mmapped.c',
'proto.c',
'rules/api.c',
'rules/defaults.c',
@@ -63,6 +64,7 @@ libkres_headers = files([
'layer.h',
'layer/iterate.h',
'log.h',
+ 'mmapped.h',
'module.h',
'proto.h',
'resolve.h',
diff --git a/lib/mmapped.c b/lib/mmapped.c
new file mode 100644
index 00000000..f37748b7
--- /dev/null
+++ b/lib/mmapped.c
@@ -0,0 +1,113 @@
+/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <string.h>
+
+#include "lib/mmapped.h"
+#include "lib/utils.h"
+
+static inline bool fcntl_flock_whole(int fd, short int type, bool wait)
+{
+ struct flock fl = {
+ .l_type = type, // F_WRLCK, F_RDLCK, F_UNLCK
+ .l_whence = SEEK_SET,
+ .l_start = 0,
+ .l_len = 0 };
+ return fcntl(fd, (wait ? F_SETLKW : F_SETLK), &fl) != -1;
+}
+
+int mmapped_init(struct mmapped *mmapped, const char *mmap_file, size_t size, void *header, size_t header_size)
+{
+ int ret = 0;
+ int fd = mmapped->fd = open(mmap_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ ret = kr_error(errno);
+ kr_log_crit(SYSTEM, "Cannot open file %s with shared data: %s\n",
+ mmap_file, strerror(errno));
+ goto fail;
+ }
+
+ // try to acquire write lock; copy header on success
+ if (fcntl_flock_whole(fd, F_WRLCK, false)) {
+ if (ftruncate(fd, 0) == -1 || ftruncate(fd, size) == -1) { // get all zeroed
+ ret = kr_error(errno);
+ kr_log_crit(SYSTEM, "Cannot change size of file %s containing shared data: %s\n",
+ mmap_file, strerror(errno));
+ goto fail;
+ }
+ mmapped->mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mmapped->mem == MAP_FAILED) goto fail_errno;
+
+ memcpy(mmapped->mem, header, header_size);
+
+ return MMAPPED_WAS_FIRST;
+ }
+
+ // wait for acquiring shared lock; check header on success
+ if (!fcntl_flock_whole(fd, F_RDLCK, true)) goto fail_errno;
+
+ struct stat s;
+ bool succ = (fstat(fd, &s) == 0);
+ if (!succ) goto fail_errno;
+ if (s.st_size != size) goto fail_header_mismatch;
+
+ mmapped->mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mmapped->mem == MAP_FAILED) goto fail_errno;
+ if (memcmp(mmapped->mem, header, header_size) != 0) {
+ munmap(mmapped->mem, size);
+ goto fail_header_mismatch;
+ }
+
+ return 0;
+
+
+fail_header_mismatch:
+ kr_log_crit(SYSTEM, "Another instance of kresd uses file %s with different configuration.", mmap_file);
+ errno = ENOTRECOVERABLE;
+
+fail_errno:
+ ret = kr_error(errno);
+
+fail:
+ if (fd >= 0) {
+ fcntl_flock_whole(fd, F_UNLCK, false);
+ close(fd);
+ }
+ mmapped->mem = NULL;
+ return ret;
+}
+
+int mmapped_init_continue(struct mmapped *mmapped)
+{
+ if (!fcntl_flock_whole(mmapped->fd, F_RDLCK, false)) return kr_error(errno);
+ return 0;
+}
+
+void mmapped_deinit(struct mmapped *mmapped)
+{
+ if (mmapped->mem == NULL) return;
+ int fd = mmapped->fd;
+
+ munmap(mmapped->mem, mmapped->size);
+ mmapped->mem = NULL;
+
+ fcntl_flock_whole(fd, F_UNLCK, false);
+
+ // remove file data unless it is still locked by other processes
+ if (fcntl_flock_whole(fd, F_WRLCK, false)) {
+
+ /* If the configuration is updated at runtime, manager may remove the file
+ * and the new processes create it again while old processes are still using the old data.
+ * Here we keep zero-size file not to accidentally remove the new file instead of the old one.
+ * Still truncating the file will cause currently starting processes waiting for read lock on the same file to fail,
+ * but such processes are not expected to exist. */
+ ftruncate(fd, 0);
+
+ fcntl_flock_whole(fd, F_UNLCK, false);
+ }
+ close(fd);
+}
diff --git a/lib/mmapped.h b/lib/mmapped.h
new file mode 100644
index 00000000..eb542096
--- /dev/null
+++ b/lib/mmapped.h
@@ -0,0 +1,33 @@
+/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include "lib/defines.h"
+
+#define MMAPPED_WAS_FIRST 1
+
+struct mmapped {
+ void *mem;
+ size_t size;
+ int fd;
+};
+
+/* Initialize/Use file data as mmapped memory.
+ *
+ * If write flock can be acquired, the file is resized, zeroed and mmapped,
+ * header is copied at its beginning and MMAPPED_WAS_FIRST is returned;
+ * you should finish initialization and call mmapped_init_continue to degrade flock to shared.
+ * Otherwise, it waits for shared flock, calls mmap, verifies that header is byte-wise identical and returns zero.
+ * On header mismatch, kr_error(ENOTRECOVERABLE) is returned; on a system error, kr_error(errno) is returned. */
+KR_EXPORT
+int mmapped_init(struct mmapped *mmapped, const char *mmap_file, size_t size, void *header, size_t header_size);
+
+/* Degrade flock to shared after getting MMAPPED_WAS_FIRST from mmapped_init.
+ *
+ * Returns zero on success and kr_error(errno) on system error. */
+KR_EXPORT
+int mmapped_init_continue(struct mmapped *mmapped);
+
+/* Free mmapped memory and, unless the underlying file is used by other processes, truncate it to zero size. */
+KR_EXPORT
+void mmapped_deinit(struct mmapped *mmapped);