GNUnet  0.19.3
gnunet-service-messenger_message_store.c
Go to the documentation of this file.
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2020--2021 GNUnet e.V.
4 
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.
9 
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.
14 
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/>.
17 
18  SPDX-License-Identifier: AGPL3.0-or-later
19  */
26 #include "platform.h"
28 #include "messenger_api_message.h"
29 
30 void
32 {
33  GNUNET_assert(store);
34 
35  store->storage_messages = NULL;
36 
40 
41  store->rewrite_entries = GNUNET_NO;
42  store->write_links = GNUNET_NO;
43 }
44 
45 static int
47  const struct GNUNET_HashCode *key,
48  void *value)
49 {
50  struct GNUNET_MESSENGER_MessageEntry *entry = value;
51 
52  GNUNET_free(entry);
53 
54  return GNUNET_YES;
55 }
56 
57 static int
59  const struct GNUNET_HashCode *key,
60  void *value)
61 {
62  struct GNUNET_MESSENGER_Message *message = value;
63 
64  destroy_message (message);
65 
66  return GNUNET_YES;
67 }
68 
69 static int
71  const struct GNUNET_HashCode *key,
72  void *value)
73 {
74  struct GNUNET_HashCode *previous = value;
75 
76  GNUNET_free(previous);
77 
78  return GNUNET_YES;
79 }
80 
81 void
83 {
84  GNUNET_assert(store);
85 
86  if (store->storage_messages)
87  {
89 
90  store->storage_messages = NULL;
91  }
92 
96 
100 }
101 
103 {
104  struct GNUNET_HashCode hash;
106 };
107 
108 #define load_message_store_attribute_failed(file, attribute) \
109  sizeof(attribute) != GNUNET_DISK_file_read(file, &(attribute), sizeof(attribute))
110 
111 #define save_message_store_attribute_failed(file, attribute) \
112  sizeof(attribute) != GNUNET_DISK_file_write(file, &(attribute), sizeof(attribute))
113 
114 static void
116  const char *filename)
117 {
119 
121 
122  if (!entries)
123  return;
124 
126  struct GNUNET_MESSENGER_MessageEntry *entry;
127 
128  memset(&storage, 0, sizeof(storage));
129 
130  do
131  {
132  entry = NULL;
133 
137  break;
138 
140 
141  GNUNET_memcpy(entry, &(storage.entry), sizeof(*entry));
142 
143  if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->entries, &(storage.hash))) ||
144  (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, &(storage.hash), entry,
146  {
147  store->rewrite_entries = GNUNET_YES;
148  break;
149  }
150  }
151  while (entry);
152 
153  if (entry)
154  GNUNET_free(entry);
155 
157 }
158 
160 {
161  struct GNUNET_HashCode hash;
163 };
164 
165 static void
167  const char *filename)
168 {
170 
172 
173  if (!entries)
174  return;
175 
177  struct GNUNET_MESSENGER_MessageLink *link;
178 
179  memset(&storage, 0, sizeof(storage));
180 
181  do
182  {
183  link = NULL;
184 
188  ((GNUNET_YES == storage.link.multiple) &&
190  break;
191 
193 
194  GNUNET_memcpy(link, &(storage.link), sizeof(*link));
195 
196  if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->links, &(storage.hash))) ||
197  (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->links, &(storage.hash), link,
199  break;
200  }
201  while (link);
202 
203  if (link)
204  GNUNET_free(link);
205 
207 }
208 
209 void
211  const char *directory)
212 {
213  GNUNET_assert((store) && (directory));
214 
216 
217  if (store->storage_messages)
219 
220  char *filename;
221  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
222 
225  else
226  store->storage_messages = NULL;
227 
229 
230  if (!store->storage_messages)
231  return;
232 
233  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
234 
237 
239 
240  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
241 
244 
246 }
247 
249 {
251 
253 };
254 
255 static int
257  const struct GNUNET_HashCode *key,
258  void *value)
259 {
261  struct GNUNET_MESSENGER_MessageEntry *entry = value;
262 
263  GNUNET_DISK_file_write (save->storage, key, sizeof(*key));
264  GNUNET_DISK_file_write (save->storage, &(entry->offset), sizeof(entry->offset));
265  GNUNET_DISK_file_write (save->storage, &(entry->length), sizeof(entry->length));
266 
267  return GNUNET_YES;
268 }
269 
270 static int
272  const struct GNUNET_HashCode *key,
273  void *value)
274 {
276 
277  if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (save->store->entries, key))
278  return GNUNET_YES;
279 
280  struct GNUNET_MESSENGER_Message *message = value;
282 
283  GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
284 
285  storage.entry.length = get_message_size (message, GNUNET_YES);
286  storage.entry.offset = GNUNET_DISK_file_seek (save->store->storage_messages, 0, GNUNET_DISK_SEEK_END);
287 
288  if ((GNUNET_SYSERR == storage.entry.offset) ||
289  (save_message_store_attribute_failed(save->storage, storage.hash)) ||
290  (save_message_store_attribute_failed(save->storage, storage.entry.offset)) ||
292  return GNUNET_YES;
293 
294  char *buffer = GNUNET_malloc(storage.entry.length);
295 
296  encode_message (message, storage.entry.length, buffer, GNUNET_YES);
297 
298  GNUNET_DISK_file_write (save->store->storage_messages, buffer, storage.entry.length);
299 
300  GNUNET_free(buffer);
301  return GNUNET_YES;
302 }
303 
304 static int
306  const struct GNUNET_HashCode *key,
307  void *value)
308 {
310  struct GNUNET_MESSENGER_MessageLink *link = value;
311 
312  GNUNET_DISK_file_write (save->storage, key, sizeof(*key));
313  GNUNET_DISK_file_write (save->storage, &(link->multiple), sizeof(link->multiple));
314  GNUNET_DISK_file_write (save->storage, &(link->first), sizeof(link->first));
315 
316  if (GNUNET_YES == link->multiple)
317  GNUNET_DISK_file_write (save->storage, &(link->second), sizeof(link->second));
318 
319  return GNUNET_YES;
320 }
321 
322 void
324  const char *directory)
325 {
326  GNUNET_assert((store) && (directory));
327 
329 
331 
332  char *filename;
333 
334  if (GNUNET_YES != store->write_links)
335  goto save_entries;
336 
337  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
338 
339  save.store = store;
341 
343 
344  if (!save.storage)
345  goto save_entries;
346 
348  goto close_links;
349 
352 
353 close_links:
354  GNUNET_DISK_file_close (save.storage);
355 
356 save_entries:
357  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
358 
359  save.store = store;
361 
363 
364  if (!save.storage)
365  return;
366 
368  {
370  goto close_entries;
371 
374  }
376  goto close_entries;
377 
378  if (store->storage_messages)
380 
381  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
382 
384  permission);
385 
387 
388  if (store->storage_messages)
389  {
391 
393  GNUNET_DISK_file_sync (save.storage);
394  }
395 
396 close_entries:
397  GNUNET_DISK_file_close (save.storage);
398 }
399 
400 int
402  const struct GNUNET_HashCode *hash)
403 {
404  GNUNET_assert((store) && (hash));
405 
407  return GNUNET_YES;
408 
410 }
411 
412 const struct GNUNET_MESSENGER_Message*
414  const struct GNUNET_HashCode *hash)
415 {
416  GNUNET_assert((store) && (hash));
417 
419 
420  if (message)
421  return message;
422 
423  if (!store->storage_messages)
424  return NULL;
425 
426  const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
427 
428  if (!entry)
429  return NULL;
430 
432  return message;
433 
434  char *buffer = GNUNET_malloc(entry->length);
435 
436  if (!buffer)
437  return NULL;
438 
439  if ((GNUNET_DISK_file_read (store->storage_messages, buffer, entry->length) != entry->length) ||
441  goto free_buffer;
442 
444 
445  const int decoding = decode_message (message, entry->length, buffer, GNUNET_YES, NULL);
446 
447  struct GNUNET_HashCode check;
448  hash_message (message, entry->length, buffer, &check);
449 
450  if ((GNUNET_YES != decoding) || (GNUNET_CRYPTO_hash_cmp (hash, &check) != 0))
451  {
452  if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry))
453  GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Corrupted entry could not be removed from store: %s\n",
454  GNUNET_h2s(hash));
455 
456  store->rewrite_entries = GNUNET_YES;
457 
458  goto free_message;
459  }
460 
461  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
463  goto free_buffer;
464 
465 free_message: destroy_message (message);
466  message = NULL;
467 
468 free_buffer:
469  GNUNET_free(buffer);
470 
471  return message;
472 }
473 
474 const struct GNUNET_MESSENGER_MessageLink*
476  const struct GNUNET_HashCode *hash,
477  int deleted_only)
478 {
479  if (deleted_only)
480  goto get_link;
481 
482  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash);
483 
484  if (!message)
485  goto get_link;
486 
487  static struct GNUNET_MESSENGER_MessageLink link;
488 
489  GNUNET_memcpy(&(link.first), &(message->header.previous), sizeof(link.first));
490 
492 
493  if (GNUNET_YES == link.multiple)
494  GNUNET_memcpy(&(link.second), &(message->body.merge.previous), sizeof(link.second));
495  else
496  GNUNET_memcpy(&(link.second), &(message->header.previous), sizeof(link.second));
497 
498  return &link;
499 
500 get_link:
501  return GNUNET_CONTAINER_multihashmap_get (store->links, hash);
502 }
503 
504 int
506  const struct GNUNET_HashCode *hash,
507  struct GNUNET_MESSENGER_Message *message)
508 {
509  GNUNET_assert((store) && (hash) && (message));
510 
511  return GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
513 }
514 
515 static void
517  const struct GNUNET_HashCode *hash,
518  const struct GNUNET_MESSENGER_Message *message)
519 {
521 
522  GNUNET_memcpy(&(link->first), &(message->header.previous), sizeof(link->first));
523 
525 
526  if (GNUNET_YES == link->multiple)
527  GNUNET_memcpy(&(link->second), &(message->body.merge.previous), sizeof(link->second));
528  else
529  GNUNET_memcpy(&(link->second), &(message->header.previous), sizeof(link->second));
530 
531  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(store->links, hash, link,
533  GNUNET_free(link);
534 }
535 
536 int
538  const struct GNUNET_HashCode *hash)
539 {
540  GNUNET_assert((store) && (hash));
541 
542  const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
543 
544  if (!entry)
545  goto clear_memory;
546 
547  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash);
548 
549  if (message)
550  add_link (store, hash, message);
551 
552  if (!store->storage_messages)
553  goto clear_entry;
554 
556  return GNUNET_SYSERR;
557 
558  char *clear_buffer = GNUNET_malloc(entry->length);
559 
560  if (!clear_buffer)
561  return GNUNET_SYSERR;
562 
563  GNUNET_CRYPTO_zero_keys (clear_buffer, entry->length);
564 
565  if ((entry->length != GNUNET_DISK_file_write (store->storage_messages, clear_buffer, entry->length)) || (GNUNET_OK
567  {
568  GNUNET_free(clear_buffer);
569  return GNUNET_SYSERR;
570  }
571 
572  GNUNET_free(clear_buffer);
573 
574 clear_entry:
575  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry))
576  store->rewrite_entries = GNUNET_YES;
577 
578 clear_memory:
580  return GNUNET_OK;
581 }
struct GNUNET_HashCode key
The key used in the DHT.
static char * filename
static char * value
Value of the record to add/remove.
static int iterate_destroy_messages(void *cls, const struct GNUNET_HashCode *key, void *value)
static int iterate_save_messages(void *cls, const struct GNUNET_HashCode *key, void *value)
static int iterate_save_entries(void *cls, const struct GNUNET_HashCode *key, void *value)
void save_message_store(struct GNUNET_MESSENGER_MessageStore *store, const char *directory)
Saves messages from a message store into a directory.
static int iterate_save_links(void *cls, const struct GNUNET_HashCode *key, void *value)
const struct GNUNET_MESSENGER_Message * get_store_message(struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash)
Returns the message from a message store matching a given hash.
static void load_message_store_entries(struct GNUNET_MESSENGER_MessageStore *store, const char *filename)
void clear_message_store(struct GNUNET_MESSENGER_MessageStore *store)
Clears a message store, wipes its content and deallocates its memory.
#define load_message_store_attribute_failed(file, attribute)
void init_message_store(struct GNUNET_MESSENGER_MessageStore *store)
Initializes a message store as fully empty.
#define save_message_store_attribute_failed(file, attribute)
int contains_store_message(const struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash)
Checks if a message matching a given hash is stored in a message store.
static void load_message_store_links(struct GNUNET_MESSENGER_MessageStore *store, const char *filename)
int delete_store_message(struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash)
Deletes a message in the message store.
void load_message_store(struct GNUNET_MESSENGER_MessageStore *store, const char *directory)
Loads messages from a directory into a message store.
int put_store_message(struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, struct GNUNET_MESSENGER_Message *message)
Stores a message into the message store.
static int iterate_destroy_entries(void *cls, const struct GNUNET_HashCode *key, void *value)
static void add_link(struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, const struct GNUNET_MESSENGER_Message *message)
static int iterate_destroy_links(void *cls, const struct GNUNET_HashCode *key, void *value)
const struct GNUNET_MESSENGER_MessageLink * get_store_message_link(struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash, int deleted_only)
Returns the message link from a message store matching a given hash.
static void save()
Write persistent statistics to disk.
void GNUNET_CRYPTO_zero_keys(void *buffer, size_t length)
Zero out buffer, securely against compiler optimizations.
struct GNUNET_DISK_FileHandle * GNUNET_DISK_file_open(const char *fn, enum GNUNET_DISK_OpenFlags flags, enum GNUNET_DISK_AccessPermissions perm)
Open a file.
Definition: disk.c:1234
enum GNUNET_GenericReturnValue GNUNET_DISK_file_test(const char *fil)
Check that fil corresponds to a filename (of a file that exists and that is not a directory).
Definition: disk.c:481
ssize_t GNUNET_DISK_file_write(const struct GNUNET_DISK_FileHandle *h, const void *buffer, size_t n)
Write a buffer to a file.
Definition: disk.c:685
off_t GNUNET_DISK_file_seek(const struct GNUNET_DISK_FileHandle *h, off_t offset, enum GNUNET_DISK_Seek whence)
Move the read/write pointer in a file.
Definition: disk.c:205
GNUNET_DISK_AccessPermissions
File access permissions, UNIX-style.
enum GNUNET_GenericReturnValue GNUNET_DISK_file_sync(const struct GNUNET_DISK_FileHandle *h)
Write file changes to disk.
Definition: disk.c:1424
enum GNUNET_GenericReturnValue GNUNET_DISK_file_close(struct GNUNET_DISK_FileHandle *h)
Close an open file.
Definition: disk.c:1305
ssize_t GNUNET_DISK_file_read(const struct GNUNET_DISK_FileHandle *h, void *result, size_t len)
Read the contents of a binary file into a buffer.
Definition: disk.c:621
@ GNUNET_DISK_OPEN_READ
Open the file for reading.
@ GNUNET_DISK_OPEN_WRITE
Open the file for writing.
@ GNUNET_DISK_OPEN_CREATE
Create file if it doesn't exist.
@ GNUNET_DISK_OPEN_READWRITE
Open the file for both reading and writing.
@ GNUNET_DISK_PERM_USER_READ
Owner can read.
@ GNUNET_DISK_PERM_USER_WRITE
Owner can write.
@ GNUNET_DISK_SEEK_SET
Seek an absolute position (from the start of the file).
@ GNUNET_DISK_SEEK_END
Seek an absolute position from the end of the file.
int GNUNET_CRYPTO_hash_cmp(const struct GNUNET_HashCode *h1, const struct GNUNET_HashCode *h2)
Compare function for HashCodes, producing a total ordering of all hashcodes.
Definition: crypto_hash.c:220
enum GNUNET_GenericReturnValue GNUNET_CONTAINER_multihashmap_contains(const struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key)
Check if the map contains any value under the given key (including values that are NULL).
int GNUNET_CONTAINER_multihashmap_remove_all(struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key)
Remove all entries for the given key from the map.
int GNUNET_CONTAINER_multihashmap_iterate(struct GNUNET_CONTAINER_MultiHashMap *map, GNUNET_CONTAINER_MultiHashMapIteratorCallback it, void *it_cls)
Iterate over all entries in the map.
enum GNUNET_GenericReturnValue GNUNET_CONTAINER_multihashmap_remove(struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key, const void *value)
Remove the given key-value pair from the map.
enum GNUNET_GenericReturnValue GNUNET_CONTAINER_multihashmap_put(struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key, void *value, enum GNUNET_CONTAINER_MultiHashMapOption opt)
Store a key-value pair in the map.
struct GNUNET_CONTAINER_MultiHashMap * GNUNET_CONTAINER_multihashmap_create(unsigned int len, int do_not_copy_keys)
Create a multi hash map.
void GNUNET_CONTAINER_multihashmap_destroy(struct GNUNET_CONTAINER_MultiHashMap *map)
Destroy a hash map.
void * GNUNET_CONTAINER_multihashmap_get(const struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key)
Given a key find a value in the map matching the key.
@ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_FAST
, ' bother checking if a value already exists (faster than GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE...
#define GNUNET_log(kind,...)
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
@ GNUNET_OK
@ GNUNET_YES
@ GNUNET_NO
@ GNUNET_SYSERR
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
const char * GNUNET_h2s(const struct GNUNET_HashCode *hc)
Convert a hash value to a string (for printing debug messages).
@ GNUNET_ERROR_TYPE_WARNING
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
#define GNUNET_malloc(size)
Wrapper around malloc.
#define GNUNET_free(ptr)
Wrapper around free.
@ GNUNET_MESSENGER_KIND_UNKNOWN
The unknown kind.
@ GNUNET_MESSENGER_KIND_MERGE
The merge kind.
void hash_message(const struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer, struct GNUNET_HashCode *hash)
Calculates a hash of a given buffer with a length in bytes from a message.
void destroy_message(struct GNUNET_MESSENGER_Message *message)
Destroys a message and frees its memory fully.
void encode_message(const struct GNUNET_MESSENGER_Message *message, uint16_t length, char *buffer, int include_header)
Encodes a given message into a buffer of a maximal length in bytes.
int decode_message(struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer, int include_header, uint16_t *padding)
Decodes a message from a given buffer of a maximal length in bytes.
uint16_t get_message_kind_size(enum GNUNET_MESSENGER_MessageKind kind, int include_header)
Returns the minimal size in bytes to encode a message of a specific kind.
struct GNUNET_MESSENGER_Message * create_message(enum GNUNET_MESSENGER_MessageKind kind)
Creates and allocates a new message with a specific kind.
uint16_t get_message_size(const struct GNUNET_MESSENGER_Message *message, int include_header)
Returns the exact size in bytes to encode a given message.
messenger api: client and service implementation of GNUnet MESSENGER service
Handle used to access files (and pipes).
A 512-bit hashcode.
struct GNUNET_MESSENGER_MessageMerge merge
struct GNUNET_HashCode previous
The hash of the previous message from the senders perspective.
enum GNUNET_MESSENGER_MessageKind kind
The kind of the message.
struct GNUNET_HashCode previous
The hash of a second previous message.
struct GNUNET_CONTAINER_MultiHashMap * links
struct GNUNET_DISK_FileHandle * storage_messages
struct GNUNET_CONTAINER_MultiHashMap * messages
struct GNUNET_CONTAINER_MultiHashMap * entries
struct GNUNET_MESSENGER_MessageHeader header
Header.
struct GNUNET_MESSENGER_MessageBody body
Body.
struct ListEntry * entries
List of peers in the list.