+ 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;
+ }
+ }
+
+ config->buf = buf;
+ config->len = len;
+
+ return true;
+}
+
+/// Write a configuration file to a FILE handle.
+bool config_write_file(meshlink_handle_t *mesh, FILE *f, const config_t *config, const void *key) {
+ assert(f);
+
+ if(key) {
+ uint8_t buf[config->len + 16];
+ size_t len = sizeof(buf);
+ uint8_t seqbuf[12];
+ randomize(&seqbuf, sizeof(seqbuf));
+ chacha_poly1305_ctx_t *ctx = chacha_poly1305_init();
+ chacha_poly1305_set_key(ctx, key);
+ bool success = false;
+
+ if(chacha_poly1305_encrypt_iv96(ctx, seqbuf, config->buf, config->len, buf, &len)) {
+ success = fwrite(seqbuf, sizeof(seqbuf), 1, f) == 1 && fwrite(buf, len, 1, f) == 1;
+ } else {
+ logger(mesh, MESHLINK_ERROR, "Cannot encrypt config file\n");
+ meshlink_errno = MESHLINK_ESTORAGE;
+ }
+
+ chacha_poly1305_exit(ctx);
+ return success;
+ }
+
+ if(fwrite(config->buf, config->len, 1, f) != 1) {
+ logger(mesh, MESHLINK_ERROR, "Cannot write config file: %s", strerror(errno));
+ meshlink_errno = MESHLINK_ESTORAGE;
+ return false;
+ }
+
+ if(fsync(fileno(f))) {
+ logger(mesh, MESHLINK_ERROR, "Failed to sync file: %s\n", strerror(errno));
+ return false;