2 * This file is part of GNUnet
3 * Copyright (C) 2015 Christian Grothoff (and other contributing authors)
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 peerstore/plugin_peerstore_flat.c
23 * @brief flat file-based peerstore backend
24 * @author Martin Schanzenbach
28 #include "gnunet_peerstore_plugin.h"
29 #include "gnunet_peerstore_service.h"
30 #include "peerstore.h"
33 * Context for all functions in this plugin.
38 * Configuration handle
40 const struct GNUNET_CONFIGURATION_Handle *cfg;
45 struct GNUNET_CONTAINER_MultiHashMap *hm;
50 GNUNET_PEERSTORE_Processor iter;
65 const struct GNUNET_PeerIdentity *iter_peer;
70 const char *iter_sub_system;
75 struct GNUNET_TIME_Absolute iter_now;
80 uint64_t deleted_entries;
95 int iter_result_found;
100 delete_entries (void *cls,
101 const struct GNUNET_HashCode *key,
104 struct Plugin *plugin = cls;
105 struct GNUNET_PEERSTORE_Record *entry = value;
107 if (0 != strcmp (plugin->iter_key, entry->key))
109 if (0 != memcmp (plugin->iter_peer,
111 sizeof(struct GNUNET_PeerIdentity)))
113 if (0 != strcmp (plugin->iter_sub_system, entry->sub_system))
116 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
117 plugin->deleted_entries++;
123 * Delete records with the given key
125 * @param cls closure (internal context for the plugin)
126 * @param sub_system name of sub system
127 * @param peer Peer identity (can be NULL)
128 * @param key entry key string (can be NULL)
129 * @return number of deleted records
132 peerstore_flat_delete_records (void *cls, const char *sub_system,
133 const struct GNUNET_PeerIdentity *peer,
136 struct Plugin *plugin = cls;
138 plugin->iter_sub_system = sub_system;
139 plugin->iter_peer = peer;
140 plugin->iter_key = key;
141 plugin->deleted_entries = 0;
143 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
146 return plugin->deleted_entries;
151 expire_entries (void *cls,
152 const struct GNUNET_HashCode *key,
155 struct Plugin *plugin = cls;
156 struct GNUNET_PEERSTORE_Record *entry = value;
158 if (entry->expiry.abs_value_us < plugin->iter_now.abs_value_us)
160 GNUNET_CONTAINER_multihashmap_remove (plugin->hm, key, value);
161 plugin->exp_changes++;
168 * Delete expired records (expiry < now)
170 * @param cls closure (internal context for the plugin)
171 * @param now time to use as reference
172 * @param cont continuation called with the number of records expired
173 * @param cont_cls continuation closure
174 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and cont is not
178 peerstore_flat_expire_records (void *cls, struct GNUNET_TIME_Absolute now,
179 GNUNET_PEERSTORE_Continuation cont,
182 struct Plugin *plugin = cls;
184 plugin->exp_changes = 0;
185 plugin->iter_now = now;
187 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
192 cont (cont_cls, plugin->exp_changes);
199 iterate_entries (void *cls,
200 const struct GNUNET_HashCode *key,
203 struct Plugin *plugin = cls;
204 struct GNUNET_PEERSTORE_Record *entry = value;
206 if ((NULL != plugin->iter_peer) &&
207 (0 != memcmp (plugin->iter_peer,
209 sizeof(struct GNUNET_PeerIdentity))))
213 if ((NULL != plugin->iter_key) &&
214 (0 != strcmp (plugin->iter_key,
219 if (NULL != plugin->iter)
220 plugin->iter (plugin->iter_cls, entry, NULL);
221 plugin->iter_result_found = GNUNET_YES;
227 * Iterate over the records given an optional peer id
230 * @param cls closure (internal context for the plugin)
231 * @param sub_system name of sub system
232 * @param peer Peer identity (can be NULL)
233 * @param key entry key string (can be NULL)
234 * @param iter function to call asynchronously with the results, terminated
236 * @param iter_cls closure for @a iter
237 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error and iter is not
241 peerstore_flat_iterate_records (void *cls, const char *sub_system,
242 const struct GNUNET_PeerIdentity *peer,
244 GNUNET_PEERSTORE_Processor iter,
247 struct Plugin *plugin = cls;
250 plugin->iter_cls = iter_cls;
251 plugin->iter_peer = peer;
252 plugin->iter_sub_system = sub_system;
253 plugin->iter_key = key;
255 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
259 iter (iter_cls, NULL, NULL);
265 * Store a record in the peerstore.
266 * Key is the combination of sub system and peer identity.
267 * One key can store multiple values.
269 * @param cls closure (internal context for the plugin)
270 * @param sub_system name of the GNUnet sub system responsible
271 * @param peer peer identity
272 * @param key record key string
273 * @param value value to be stored
274 * @param size size of value to be stored
275 * @param expiry absolute time after which the record is (possibly) deleted
276 * @param options options related to the store operation
277 * @param cont continuation called when record is stored
278 * @param cont_cls continuation closure
279 * @return #GNUNET_OK on success, else #GNUNET_SYSERR and cont is not called
282 peerstore_flat_store_record (void *cls, const char *sub_system,
283 const struct GNUNET_PeerIdentity *peer,
284 const char *key, const void *value, size_t size,
285 struct GNUNET_TIME_Absolute expiry,
286 enum GNUNET_PEERSTORE_StoreOption options,
287 GNUNET_PEERSTORE_Continuation cont,
290 struct Plugin *plugin = cls;
291 struct GNUNET_HashCode hkey;
292 struct GNUNET_PEERSTORE_Record *entry;
296 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
297 entry->sub_system = GNUNET_strdup (sub_system);
298 entry->key = GNUNET_strdup (key);
299 entry->value = GNUNET_malloc (size);
300 GNUNET_memcpy (entry->value, value, size);
301 entry->value_size = size;
303 entry->expiry = expiry;
305 peer_id = GNUNET_i2s (peer);
306 GNUNET_CRYPTO_hash (peer_id,
310 if (GNUNET_PEERSTORE_STOREOPTION_REPLACE == options)
312 peerstore_flat_delete_records (cls, sub_system, peer, key);
315 GNUNET_CONTAINER_multihashmap_put (plugin->hm,
318 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
321 cont (cont_cls, GNUNET_OK);
328 * Initialize the database connections and associated
329 * data structures (create tables and indices
330 * as needed as well).
332 * @param plugin the plugin context (state for this module)
333 * @return GNUNET_OK on success
336 database_setup (struct Plugin *plugin)
345 struct GNUNET_DISK_FileHandle *fh;
346 struct GNUNET_PEERSTORE_Record *entry;
347 struct GNUNET_HashCode hkey;
353 GNUNET_CONFIGURATION_get_value_filename (plugin->cfg, "peerstore-flat",
354 "FILENAME", &afsdir))
356 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, "peerstore-flat",
358 return GNUNET_SYSERR;
360 if (GNUNET_OK != GNUNET_DISK_file_test (afsdir))
362 if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (afsdir))
365 GNUNET_free (afsdir);
366 return GNUNET_SYSERR;
369 /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
372 fh = GNUNET_DISK_file_open (afsdir,
373 GNUNET_DISK_OPEN_CREATE
374 | GNUNET_DISK_OPEN_READWRITE,
375 GNUNET_DISK_PERM_USER_WRITE
376 | GNUNET_DISK_PERM_USER_READ);
379 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
380 _ ("Unable to initialize file: %s.\n"),
382 return GNUNET_SYSERR;
385 /* Load data from file into hashmap */
386 plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
389 if (GNUNET_SYSERR == GNUNET_DISK_file_size (afsdir,
394 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
395 _ ("Unable to get filesize: %s.\n"),
397 return GNUNET_SYSERR;
400 buffer = GNUNET_malloc (size + 1);
402 if (GNUNET_SYSERR == GNUNET_DISK_file_read (fh,
406 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
407 _ ("Unable to read file: %s.\n"),
409 GNUNET_DISK_file_close (fh);
410 GNUNET_free (buffer);
411 return GNUNET_SYSERR;
415 GNUNET_DISK_file_close (fh);
418 line = strtok (buffer, "\n");
421 sub_system = strtok (line, ",");
422 if (NULL == sub_system)
424 peer = strtok (NULL, ",");
427 key = strtok (NULL, ",");
430 value = strtok (NULL, ",");
433 expiry = strtok (NULL, ",");
436 entry = GNUNET_new (struct GNUNET_PEERSTORE_Record);
437 entry->sub_system = GNUNET_strdup (sub_system);
438 entry->key = GNUNET_strdup (key);
444 s = GNUNET_STRINGS_base64_decode (peer,
447 if (sizeof(struct GNUNET_PeerIdentity) == s)
448 GNUNET_memcpy (&entry->peer,
453 GNUNET_free_non_null (o);
455 entry->value_size = GNUNET_STRINGS_base64_decode (value,
457 (void **) &entry->value);
459 GNUNET_STRINGS_fancy_time_to_absolute (expiry,
462 GNUNET_free (entry->sub_system);
463 GNUNET_free (entry->key);
467 peer_id = GNUNET_i2s (&entry->peer);
468 GNUNET_CRYPTO_hash (peer_id,
472 GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (plugin->hm,
475 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
478 GNUNET_free (buffer);
484 store_and_free_entries (void *cls,
485 const struct GNUNET_HashCode *key,
488 struct GNUNET_DISK_FileHandle *fh = cls;
489 struct GNUNET_PEERSTORE_Record *entry = value;
495 GNUNET_STRINGS_base64_encode (entry->value,
498 expiry = GNUNET_STRINGS_absolute_time_to_string (entry->expiry);
499 GNUNET_STRINGS_base64_encode ((char *) &entry->peer,
500 sizeof(struct GNUNET_PeerIdentity),
502 GNUNET_asprintf (&line,
511 GNUNET_DISK_file_write (fh,
514 GNUNET_free (entry->sub_system);
515 GNUNET_free (entry->key);
516 GNUNET_free (entry->value);
524 * Shutdown database connection and associate data
526 * @param plugin the plugin context (state for this module)
529 database_shutdown (struct Plugin *plugin)
531 struct GNUNET_DISK_FileHandle *fh;
533 fh = GNUNET_DISK_file_open (plugin->fn,
534 GNUNET_DISK_OPEN_CREATE
535 | GNUNET_DISK_OPEN_TRUNCATE
536 | GNUNET_DISK_OPEN_READWRITE,
537 GNUNET_DISK_PERM_USER_WRITE
538 | GNUNET_DISK_PERM_USER_READ);
541 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
542 _ ("Unable to initialize file: %s.\n"),
546 GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
547 &store_and_free_entries,
549 GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
550 GNUNET_DISK_file_close (fh);
555 * Entry point for the plugin.
557 * @param cls The struct GNUNET_CONFIGURATION_Handle.
558 * @return NULL on error, otherwise the plugin context
561 libgnunet_plugin_peerstore_flat_init (void *cls)
563 static struct Plugin plugin;
564 const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
565 struct GNUNET_PEERSTORE_PluginFunctions *api;
567 if (NULL != plugin.cfg)
568 return NULL; /* can only initialize once! */
569 memset (&plugin, 0, sizeof(struct Plugin));
571 if (GNUNET_OK != database_setup (&plugin))
573 database_shutdown (&plugin);
576 api = GNUNET_new (struct GNUNET_PEERSTORE_PluginFunctions);
578 api->store_record = &peerstore_flat_store_record;
579 api->iterate_records = &peerstore_flat_iterate_records;
580 api->expire_records = &peerstore_flat_expire_records;
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is running\n");
587 * Exit point from the plugin.
589 * @param cls The plugin context (as returned by "init")
590 * @return Always NULL
593 libgnunet_plugin_peerstore_flat_done (void *cls)
595 struct GNUNET_PEERSTORE_PluginFunctions *api = cls;
596 struct Plugin *plugin = api->cls;
598 database_shutdown (plugin);
601 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Flat plugin is finished\n");
606 /* end of plugin_peerstore_sqlite.c */