+ }
+
+ char path[PATH_MAX];
+ make_main_path(mesh, "current", path, sizeof(path));
+
+ mesh->conffile = fopen(path, "r");
+
+ if(!mesh->conffile) {
+ logger(NULL, MESHLINK_ERROR, "Cannot not open %s: %s\n", path, strerror(errno));
+ meshlink_errno = MESHLINK_ESTORAGE;
+ return false;
+ }
+
+#ifdef FD_CLOEXEC
+ fcntl(fileno(mesh->conffile), F_SETFD, FD_CLOEXEC);
+#endif
+
+#ifdef HAVE_MINGW
+ // TODO: use _locking()?
+#else
+
+ if(flock(fileno(mesh->conffile), LOCK_EX | LOCK_NB) != 0) {
+ logger(NULL, MESHLINK_ERROR, "Cannot lock %s: %s\n", path, strerror(errno));
+ fclose(mesh->conffile);
+ mesh->conffile = NULL;
+ meshlink_errno = MESHLINK_EBUSY;
+ return false;
+ }
+
+#endif
+
+ return true;
+}
+
+/// Unlock the main configuration file.
+void main_config_unlock(meshlink_handle_t *mesh) {
+ if(mesh->conffile) {
+ fclose(mesh->conffile);
+ mesh->conffile = NULL;
+ }
+}
+
+/// Read a configuration file from a FILE handle.
+bool config_read_file(meshlink_handle_t *mesh, FILE *f, config_t *config, const void *key) {
+ assert(f);
+
+ long len;
+
+ if(fseek(f, 0, SEEK_END) || !(len = ftell(f)) || fseek(f, 0, SEEK_SET)) {
+ logger(mesh, MESHLINK_ERROR, "Cannot get config file size: %s\n", strerror(errno));
+ meshlink_errno = MESHLINK_ESTORAGE;
+ return false;
+ }
+
+ uint8_t *buf = xmalloc(len);
+
+ if(fread(buf, len, 1, f) != 1) {
+ logger(mesh, MESHLINK_ERROR, "Cannot read config file: %s\n", strerror(errno));
+ meshlink_errno = MESHLINK_ESTORAGE;
+ return false;
+ }
+
+ if(key) {
+ uint8_t *decrypted = xmalloc(len);
+ size_t decrypted_len = len;
+ chacha_poly1305_ctx_t *ctx = chacha_poly1305_init();
+ chacha_poly1305_set_key(ctx, key);
+
+ if(len > 12 && chacha_poly1305_decrypt_iv96(ctx, buf, buf + 12, len - 12, decrypted, &decrypted_len)) {
+ chacha_poly1305_exit(ctx);
+ free(buf);
+ config->buf = decrypted;
+ config->len = decrypted_len;
+ return true;
+ } else {
+ logger(mesh, MESHLINK_ERROR, "Cannot decrypt config file\n");
+ meshlink_errno = MESHLINK_ESTORAGE;
+ chacha_poly1305_exit(ctx);
+ free(decrypted);
+ free(buf);
+ return false;
+ }
+ }