GNUnet  0.11.x
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  */
27 #include "messenger_api_message.h"
28 
29 void
31 {
32  GNUNET_assert(store);
33 
34  store->storage_messages = NULL;
35 
39 
40  store->rewrite_entries = GNUNET_NO;
41  store->write_links = GNUNET_NO;
42 }
43 
44 static int
45 iterate_destroy_entries (void *cls, const struct GNUNET_HashCode *key, void *value)
46 {
47  struct GNUNET_MESSENGER_MessageEntry *entry = value;
48 
49  GNUNET_free(entry);
50 
51  return GNUNET_YES;
52 }
53 
54 static int
55 iterate_destroy_messages (void *cls, const struct GNUNET_HashCode *key, void *value)
56 {
57  struct GNUNET_MESSENGER_Message *message = value;
58 
59  destroy_message (message);
60 
61  return GNUNET_YES;
62 }
63 
64 static int
65 iterate_destroy_links (void *cls, const struct GNUNET_HashCode *key, void *value)
66 {
67  struct GNUNET_HashCode *previous = value;
68 
69  GNUNET_free(previous);
70 
71  return GNUNET_YES;
72 }
73 
74 void
76 {
77  GNUNET_assert(store);
78 
79  if (store->storage_messages)
80  {
82 
83  store->storage_messages = NULL;
84  }
85 
89 
93 }
94 
96 {
97  struct GNUNET_HashCode hash;
99 };
100 
101 static void
103 {
105 
107 
108  if (!entries)
109  return;
110 
112  struct GNUNET_MESSENGER_MessageEntry *entry;
113 
114  do
115  {
117 
118  if (GNUNET_DISK_file_read (entries, &storage, sizeof(storage)) == sizeof(storage))
119  {
120  GNUNET_memcpy(entry, &(storage.entry), sizeof(*entry));
121 
122  if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->entries, &(storage.hash))) ||
123  (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, &(storage.hash), entry,
125  {
126  store->rewrite_entries = GNUNET_YES;
127  GNUNET_free(entry);
128  }
129  }
130  else
131  {
132  GNUNET_free(entry);
133 
134  entry = NULL;
135  }
136  }
137  while (entry);
138 
140 }
141 
143 {
144  struct GNUNET_HashCode hash;
146 };
147 
148 static void
150 {
152 
154 
155  if (!entries)
156  return;
157 
159  struct GNUNET_MESSENGER_MessageLink *link = NULL;
160 
161  memset(&storage, 0, sizeof(storage));
162 
163  do
164  {
165  if ((sizeof(storage.hash) != GNUNET_DISK_file_read (entries, &(storage.hash), sizeof(storage.hash))) ||
166  (sizeof(storage.link.multiple) != GNUNET_DISK_file_read (entries, &(storage.link.multiple), sizeof(storage.link.multiple))) ||
167  (sizeof(storage.link.first) != GNUNET_DISK_file_read (entries, &(storage.link.first), sizeof(storage.link.first))) ||
168  ((GNUNET_YES == storage.link.multiple) &&
169  (sizeof(storage.link.second) != GNUNET_DISK_file_read (entries, &(storage.link.second), sizeof(storage.link.second)))))
170  break;
171 
173 
174  GNUNET_memcpy(link, &(storage.link), sizeof(*link));
175 
176  if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->links, &(storage.hash))) ||
177  (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->links, &(storage.hash), link,
179  break;
180  }
181  while (link);
182 
183  if (link)
184  GNUNET_free(link);
185 
187 }
188 
189 void
190 load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory)
191 {
192  GNUNET_assert((store) && (directory));
193 
195 
196  if (store->storage_messages)
198 
199  char *filename;
200  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
201 
204  else
205  store->storage_messages = NULL;
206 
208 
209  if (!store->storage_messages)
210  return;
211 
212  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
213 
216 
218 
219  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
220 
223 
225 }
226 
228 {
230 
232 };
233 
234 static int
235 iterate_save_entries (void *cls, const struct GNUNET_HashCode *key, void *value)
236 {
238  struct GNUNET_MESSENGER_MessageEntry *entry = value;
239 
241 
242  GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
243  GNUNET_memcpy(&(storage.entry), entry, sizeof(*entry));
244 
245  GNUNET_DISK_file_write (save->storage, &storage, sizeof(storage));
246 
247  return GNUNET_YES;
248 }
249 
250 static int
251 iterate_save_messages (void *cls, const struct GNUNET_HashCode *key, void *value)
252 {
254 
255  if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (save->store->entries, key))
256  return GNUNET_YES;
257 
258  struct GNUNET_MESSENGER_Message *message = value;
260 
261  GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
262 
263  storage.entry.length = get_message_size (message, GNUNET_YES);
264  storage.entry.offset = GNUNET_DISK_file_seek (save->store->storage_messages, 0, GNUNET_DISK_SEEK_END);
265 
266  if ((GNUNET_SYSERR == storage.entry.offset) || (sizeof(storage)
267  != GNUNET_DISK_file_write (save->storage, &storage, sizeof(storage))))
268  return GNUNET_YES;
269 
270  char *buffer = GNUNET_malloc(storage.entry.length);
271 
272  encode_message (message, storage.entry.length, buffer, GNUNET_YES);
273 
274  GNUNET_DISK_file_write (save->store->storage_messages, buffer, storage.entry.length);
275 
276  GNUNET_free(buffer);
277 
278  return GNUNET_YES;
279 }
280 
281 static int
282 iterate_save_links (void *cls, const struct GNUNET_HashCode *key, void *value)
283 {
285  struct GNUNET_MESSENGER_MessageLink *link = value;
286 
287  GNUNET_DISK_file_write (save->storage, key, sizeof(*key));
288  GNUNET_DISK_file_write (save->storage, &(link->multiple), sizeof(link->multiple));
289  GNUNET_DISK_file_write (save->storage, &(link->first), sizeof(link->first));
290 
291  if (GNUNET_YES == link->multiple)
292  GNUNET_DISK_file_write (save->storage, &(link->second), sizeof(link->second));
293 
294  return GNUNET_YES;
295 }
296 
297 void
298 save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory)
299 {
300  GNUNET_assert((store) && (directory));
301 
303 
305 
306  char *filename;
307 
308  if (GNUNET_YES != store->write_links)
309  goto save_entries;
310 
311  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
312 
313  save.store = store;
315 
317 
318  if (!save.storage)
319  goto save_entries;
320 
322  goto close_links;
323 
326 
327 close_links:
328  GNUNET_DISK_file_close (save.storage);
329 
330 save_entries:
331  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
332 
333  save.store = store;
335 
337 
338  if (!save.storage)
339  return;
340 
342  {
344  goto close_entries;
345 
348  }
350  goto close_entries;
351 
352  if (store->storage_messages)
354 
355  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
356 
358  permission);
359 
361 
362  if (store->storage_messages)
363  {
365 
367  GNUNET_DISK_file_sync (save.storage);
368  }
369 
370 close_entries:
371  GNUNET_DISK_file_close (save.storage);
372 }
373 
374 int
376 {
377  GNUNET_assert((store) && (hash));
378 
380  return GNUNET_YES;
381 
383 }
384 
385 const struct GNUNET_MESSENGER_Message*
387 {
388  GNUNET_assert((store) && (hash));
389 
391 
392  if (message)
393  return message;
394 
395  if (!store->storage_messages)
396  return NULL;
397 
398  const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
399 
400  if (!entry)
401  return NULL;
402 
404  return message;
405 
406  char *buffer = GNUNET_malloc(entry->length);
407 
408  if (!buffer)
409  return NULL;
410 
411  if ((GNUNET_DISK_file_read (store->storage_messages, buffer, entry->length) != entry->length) ||
413  goto free_buffer;
414 
416 
417  const int decoding = decode_message (message, entry->length, buffer, GNUNET_YES, NULL);
418 
419  struct GNUNET_HashCode check;
420  hash_message (message, entry->length, buffer, &check);
421 
422  if ((GNUNET_YES != decoding) || (GNUNET_CRYPTO_hash_cmp (hash, &check) != 0))
423  {
424  if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry))
425  GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Corrupted entry could not be removed from store: %s\n",
426  GNUNET_h2s(hash));
427 
428  store->rewrite_entries = GNUNET_YES;
429 
430  goto free_message;
431  }
432 
433  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
435  goto free_buffer;
436 
437 free_message: destroy_message (message);
438  message = NULL;
439 
440 free_buffer:
441  GNUNET_free(buffer);
442 
443  return message;
444 }
445 
446 const struct GNUNET_MESSENGER_MessageLink*
448  int deleted_only)
449 {
450  if (deleted_only)
451  goto get_link;
452 
453  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash);
454 
455  if (!message)
456  goto get_link;
457 
458  static struct GNUNET_MESSENGER_MessageLink link;
459 
460  GNUNET_memcpy(&(link.first), &(message->header.previous), sizeof(link.first));
461 
463 
464  if (GNUNET_YES == link.multiple)
465  GNUNET_memcpy(&(link.second), &(message->body.merge.previous), sizeof(link.second));
466  else
467  GNUNET_memcpy(&(link.second), &(message->header.previous), sizeof(link.second));
468 
469  return &link;
470 
471 get_link:
472  return GNUNET_CONTAINER_multihashmap_get (store->links, hash);
473 }
474 
475 int
477  struct GNUNET_MESSENGER_Message *message)
478 {
479  GNUNET_assert((store) && (hash) && (message));
480 
481  return GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
483 }
484 
485 static void
486 add_link (struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash,
487  const struct GNUNET_MESSENGER_Message *message)
488 {
490 
491  GNUNET_memcpy(&(link->first), &(message->header.previous), sizeof(link->first));
492 
494 
495  if (GNUNET_YES == link->multiple)
496  GNUNET_memcpy(&(link->second), &(message->body.merge.previous), sizeof(link->second));
497  else
498  GNUNET_memcpy(&(link->second), &(message->header.previous), sizeof(link->second));
499 
500  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(store->links, hash, link,
502  GNUNET_free(link);
503 }
504 
505 int
507 {
508  GNUNET_assert((store) && (hash));
509 
510  const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
511 
512  if (!entry)
513  goto clear_memory;
514 
515  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash);
516 
517  if (message)
518  add_link (store, hash, message);
519 
520  if (!store->storage_messages)
521  goto clear_entry;
522 
524  return GNUNET_SYSERR;
525 
526  char *clear_buffer = GNUNET_malloc(entry->length);
527 
528  if (!clear_buffer)
529  return GNUNET_SYSERR;
530 
531  GNUNET_CRYPTO_zero_keys (clear_buffer, entry->length);
532 
533  if ((entry->length != GNUNET_DISK_file_write (store->storage_messages, clear_buffer, entry->length)) || (GNUNET_OK
535  {
536  GNUNET_free(clear_buffer);
537  return GNUNET_SYSERR;
538  }
539 
540  GNUNET_free(clear_buffer);
541 
542 clear_entry:
543  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry))
544  store->rewrite_entries = GNUNET_YES;
545 
546 clear_memory:
548  return GNUNET_OK;
549 }
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.
void init_message_store(struct GNUNET_MESSENGER_MessageStore *store)
Initializes a message store as fully empty.
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.
#define GNUNET_log(kind,...)
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
@ GNUNET_OK
Definition: gnunet_common.h:95
@ GNUNET_YES
Definition: gnunet_common.h:97
@ GNUNET_NO
Definition: gnunet_common.h:94
@ GNUNET_SYSERR
Definition: gnunet_common.h:93
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:1235
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:482
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:686
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:206
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:1425
enum GNUNET_GenericReturnValue GNUNET_DISK_file_close(struct GNUNET_DISK_FileHandle *h)
Close an open file.
Definition: disk.c:1306
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:622
@ 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:201
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_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(struct GNUNET_CONTAINER_MultiHashMap *map, const struct GNUNET_HashCode *key, const void *value)
Remove the given key-value pair from the map.
struct GNUNET_CONTAINER_MultiHashMap * GNUNET_CONTAINER_multihashmap_create(unsigned int len, int do_not_copy_keys)
Create a multi hash map.
int 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.
int GNUNET_CONTAINER_multihashmap_iterate(struct GNUNET_CONTAINER_MultiHashMap *map, GNUNET_CONTAINER_MulitHashMapIteratorCallback it, void *it_cls)
Iterate over all entries in the 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_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.
int decode_message(struct GNUNET_MESSENGER_Message *message, uint16_t length, const char *buffer, int include_signature, uint16_t *padding)
Decodes a message from a given buffer of a maximal length in bytes.
uint16_t get_message_size(const struct GNUNET_MESSENGER_Message *message, int include_signature)
Returns the exact size in bytes to encode a given message.
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.
uint16_t get_message_kind_size(enum GNUNET_MESSENGER_MessageKind kind)
Returns the minimal size in bytes to encode a message of a specific kind.
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_signature)
Encodes a given message into a buffer of a maximal length in bytes.
struct GNUNET_MESSENGER_Message * create_message(enum GNUNET_MESSENGER_MessageKind kind)
Creates and allocates a new message with a specific kind.
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.