2 * This file is part of GNUnet
3 * Copyright (C) 2009-2015 GNUnet e.V.
5 * GNUnet is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License,
8 * or (at your option) any later version.
10 * GNUnet is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Affero General Public License for more details.
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @file namecache/plugin_namecache_flat.c
23 * @brief flat file-based namecache backend
24 * @author Martin Schanzenbach
28 #include "gnunet_namecache_plugin.h"
29 #include "gnunet_namecache_service.h"
30 #include "gnunet_gnsrecord_lib.h"
31 #include "namecache.h"
34 * Context for all functions in this plugin.
37 const struct GNUNET_CONFIGURATION_Handle *cfg;
47 struct GNUNET_CONTAINER_MultiHashMap *hm;
50 struct FlatFileEntry {
54 struct GNUNET_GNSRECORD_Block *block;
59 struct GNUNET_HashCode query;
63 * Initialize the database connections and associated
64 * data structures (create tables and indices
67 * @param plugin the plugin context (state for this module)
68 * @return #GNUNET_OK on success
71 database_setup(struct Plugin *plugin)
80 struct FlatFileEntry *entry;
81 struct GNUNET_DISK_FileHandle *fh;
84 GNUNET_CONFIGURATION_get_value_filename(plugin->cfg,
89 GNUNET_log_config_missing(GNUNET_ERROR_TYPE_ERROR,
90 "namecache-flat", "FILENAME");
93 if (GNUNET_OK != GNUNET_DISK_file_test(afsdir))
95 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file(afsdir))
102 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
105 /* Load data from file into hashmap */
106 plugin->hm = GNUNET_CONTAINER_multihashmap_create(10,
108 fh = GNUNET_DISK_file_open(afsdir,
109 GNUNET_DISK_OPEN_CREATE |
110 GNUNET_DISK_OPEN_READWRITE,
111 GNUNET_DISK_PERM_USER_WRITE |
112 GNUNET_DISK_PERM_USER_READ);
115 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
116 _("Unable to initialize file: %s.\n"),
118 return GNUNET_SYSERR;
121 if (GNUNET_SYSERR == GNUNET_DISK_file_size(afsdir,
126 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
127 _("Unable to get filesize: %s.\n"),
129 GNUNET_DISK_file_close(fh);
130 return GNUNET_SYSERR;
135 GNUNET_DISK_file_close(fh);
139 buffer = GNUNET_malloc(size + 1);
141 if (GNUNET_SYSERR == GNUNET_DISK_file_read(fh,
145 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
146 _("Unable to read file: %s.\n"),
149 GNUNET_DISK_file_close(fh);
150 return GNUNET_SYSERR;
154 GNUNET_DISK_file_close(fh);
157 line = strtok(buffer, "\n");
160 query = strtok(line, ",");
163 block = strtok(NULL, ",");
166 line = strtok(NULL, "\n");
167 entry = GNUNET_malloc(sizeof(struct FlatFileEntry));
168 GNUNET_assert(GNUNET_OK == GNUNET_CRYPTO_hash_from_string(query,
170 GNUNET_STRINGS_base64_decode(block,
172 (void**)&block_buffer);
173 entry->block = (struct GNUNET_GNSRECORD_Block *)block_buffer;
175 GNUNET_CONTAINER_multihashmap_put(plugin->hm,
178 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
190 * Store values in hashmap in file and free data
192 * @param plugin the plugin context
195 store_and_free_entries(void *cls,
196 const struct GNUNET_HashCode *key,
199 struct GNUNET_DISK_FileHandle *fh = cls;
200 struct FlatFileEntry *entry = value;
204 struct GNUNET_CRYPTO_HashAsciiEncoded query;
207 block_size = ntohl(entry->block->purpose.size) +
208 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) +
209 sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
211 GNUNET_STRINGS_base64_encode((char*)entry->block,
214 GNUNET_CRYPTO_hash_to_enc(&entry->query,
216 GNUNET_asprintf(&line,
221 GNUNET_free(block_b64);
223 GNUNET_DISK_file_write(fh,
227 GNUNET_free(entry->block);
234 * Shutdown database connection and associate data
236 * @param plugin the plugin context (state for this module)
239 database_shutdown(struct Plugin *plugin)
241 struct GNUNET_DISK_FileHandle *fh;
243 fh = GNUNET_DISK_file_open(plugin->fn,
244 GNUNET_DISK_OPEN_CREATE |
245 GNUNET_DISK_OPEN_TRUNCATE |
246 GNUNET_DISK_OPEN_READWRITE,
247 GNUNET_DISK_PERM_USER_WRITE |
248 GNUNET_DISK_PERM_USER_READ);
251 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
252 _("Unable to initialize file: %s.\n"),
257 GNUNET_CONTAINER_multihashmap_iterate(plugin->hm,
258 &store_and_free_entries,
260 GNUNET_CONTAINER_multihashmap_destroy(plugin->hm);
261 GNUNET_DISK_file_close(fh);
265 expire_blocks(void *cls,
266 const struct GNUNET_HashCode *key,
269 struct Plugin *plugin = cls;
270 struct FlatFileEntry *entry = value;
271 struct GNUNET_TIME_Absolute now;
272 struct GNUNET_TIME_Absolute expiration;
274 now = GNUNET_TIME_absolute_get();
275 expiration = GNUNET_TIME_absolute_ntoh(entry->block->expiration_time);
277 if (0 == GNUNET_TIME_absolute_get_difference(now,
278 expiration).rel_value_us)
280 GNUNET_CONTAINER_multihashmap_remove_all(plugin->hm, key);
288 * Removes any expired block.
290 * @param plugin the plugin
293 namecache_expire_blocks(struct Plugin *plugin)
295 GNUNET_CONTAINER_multihashmap_iterate(plugin->hm,
302 * Cache a block in the datastore.
304 * @param cls closure (internal context for the plugin)
305 * @param block block to cache
306 * @return #GNUNET_OK on success, else #GNUNET_SYSERR
309 namecache_cache_block(void *cls,
310 const struct GNUNET_GNSRECORD_Block *block)
312 struct Plugin *plugin = cls;
313 struct GNUNET_HashCode query;
314 struct FlatFileEntry *entry;
317 namecache_expire_blocks(plugin);
318 GNUNET_CRYPTO_hash(&block->derived_key,
319 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey),
321 block_size = ntohl(block->purpose.size) +
322 sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey) +
323 sizeof(struct GNUNET_CRYPTO_EcdsaSignature);
324 if (block_size > 64 * 65536)
327 return GNUNET_SYSERR;
329 entry = GNUNET_malloc(sizeof(struct FlatFileEntry));
330 entry->block = GNUNET_malloc(block_size);
331 GNUNET_memcpy(entry->block, block, block_size);
332 GNUNET_CONTAINER_multihashmap_remove_all(plugin->hm, &query);
334 GNUNET_CONTAINER_multihashmap_put(plugin->hm,
337 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
341 return GNUNET_SYSERR;
343 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
344 "Caching block under derived key `%s'\n",
345 GNUNET_h2s_full(&query));
351 * Get the block for a particular zone and label in the
352 * datastore. Will return at most one result to the iterator.
354 * @param cls closure (internal context for the plugin)
355 * @param query hash of public key derived from the zone and the label
356 * @param iter function to call with the result
357 * @param iter_cls closure for @a iter
358 * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
361 namecache_lookup_block(void *cls,
362 const struct GNUNET_HashCode *query,
363 GNUNET_NAMECACHE_BlockCallback iter, void *iter_cls)
365 struct Plugin *plugin = cls;
366 const struct GNUNET_GNSRECORD_Block *block;
368 block = GNUNET_CONTAINER_multihashmap_get(plugin->hm, query);
371 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
372 "Found block under derived key `%s'\n",
373 GNUNET_h2s_full(query));
374 iter(iter_cls, block);
380 * Entry point for the plugin.
382 * @param cls the "struct GNUNET_NAMECACHE_PluginEnvironment*"
383 * @return NULL on error, otherwise the plugin context
386 libgnunet_plugin_namecache_flat_init(void *cls)
388 static struct Plugin plugin;
389 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
390 struct GNUNET_NAMECACHE_PluginFunctions *api;
392 if (NULL != plugin.cfg)
393 return NULL; /* can only initialize once! */
394 memset(&plugin, 0, sizeof(struct Plugin));
396 if (GNUNET_OK != database_setup(&plugin))
398 database_shutdown(&plugin);
401 api = GNUNET_new(struct GNUNET_NAMECACHE_PluginFunctions);
403 api->cache_block = &namecache_cache_block;
404 api->lookup_block = &namecache_lookup_block;
405 GNUNET_log(GNUNET_ERROR_TYPE_INFO,
406 _("flat plugin running\n"));
412 * Exit point from the plugin.
414 * @param cls the plugin context (as returned by "init")
415 * @return always NULL
418 libgnunet_plugin_namecache_flat_done(void *cls)
420 struct GNUNET_NAMECACHE_PluginFunctions *api = cls;
421 struct Plugin *plugin = api->cls;
423 database_shutdown(plugin);
426 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
427 "flat plugin is finished\n");
431 /* end of plugin_namecache_sqlite.c */