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 {
99 };
100 
101 static void
103 {
105 
107 
108  if (!entries)
109  return;
110 
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 
139  GNUNET_DISK_file_close (entries);
140 }
141 
143 {
146 };
147 
148 static void
150 {
152 
154 
155  if (!entries)
156  return;
157 
159  struct GNUNET_MESSENGER_MessageLink *link = NULL;
160 
161  do
162  {
163  if ((sizeof(storage.hash) != GNUNET_DISK_file_read (entries, &(storage.hash), sizeof(storage.hash))) ||
164  (sizeof(storage.link.multiple) != GNUNET_DISK_file_read (entries, &(storage.link.multiple), sizeof(storage.link.multiple))) ||
165  (sizeof(storage.link.first) != GNUNET_DISK_file_read (entries, &(storage.link.first), sizeof(storage.link.first))) ||
166  ((GNUNET_YES == storage.link.multiple) &&
167  (sizeof(storage.link.second) != GNUNET_DISK_file_read (entries, &(storage.link.second), sizeof(storage.link.second)))))
168  break;
169 
171 
172  GNUNET_memcpy(link, &(storage.link), sizeof(*link));
173 
174  if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->links, &(storage.hash))) ||
175  (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->links, &(storage.hash), link,
177  break;
178  }
179  while (link);
180 
181  if (link)
182  GNUNET_free(link);
183 
184  GNUNET_DISK_file_close (entries);
185 }
186 
187 void
188 load_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory)
189 {
190  GNUNET_assert((store) && (directory));
191 
193 
194  if (store->storage_messages)
196 
197  char *filename;
198  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
199 
200  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
201  store->storage_messages = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READWRITE, permission);
202  else
203  store->storage_messages = NULL;
204 
205  GNUNET_free(filename);
206 
207  if (!store->storage_messages)
208  return;
209 
210  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
211 
212  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
213  load_message_store_entries(store, filename);
214 
215  GNUNET_free(filename);
216 
217  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
218 
219  if (GNUNET_YES == GNUNET_DISK_file_test (filename))
220  load_message_store_links(store, filename);
221 
222  GNUNET_free(filename);
223 }
224 
226 {
228 
230 };
231 
232 static int
233 iterate_save_entries (void *cls, const struct GNUNET_HashCode *key, void *value)
234 {
236  struct GNUNET_MESSENGER_MessageEntry *entry = value;
237 
239 
240  GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
241  GNUNET_memcpy(&(storage.entry), entry, sizeof(*entry));
242 
243  GNUNET_DISK_file_write (save->storage, &storage, sizeof(storage));
244 
245  return GNUNET_YES;
246 }
247 
248 static int
249 iterate_save_messages (void *cls, const struct GNUNET_HashCode *key, void *value)
250 {
252 
254  return GNUNET_YES;
255 
256  struct GNUNET_MESSENGER_Message *message = value;
258 
259  GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
260 
261  storage.entry.length = get_message_size (message, GNUNET_YES);
263 
264  if ((GNUNET_SYSERR == storage.entry.offset) || (sizeof(storage)
265  != GNUNET_DISK_file_write (save->storage, &storage, sizeof(storage))))
266  return GNUNET_YES;
267 
268  char *buffer = GNUNET_malloc(storage.entry.length);
269 
270  encode_message (message, storage.entry.length, buffer, GNUNET_YES);
271 
272  GNUNET_DISK_file_write (save->store->storage_messages, buffer, storage.entry.length);
273 
274  GNUNET_free(buffer);
275 
276  return GNUNET_YES;
277 }
278 
279 static int
280 iterate_save_links (void *cls, const struct GNUNET_HashCode *key, void *value)
281 {
283  struct GNUNET_MESSENGER_MessageLink *link = value;
284 
285  GNUNET_DISK_file_write (save->storage, key, sizeof(*key));
286  GNUNET_DISK_file_write (save->storage, &(link->multiple), sizeof(link->multiple));
287  GNUNET_DISK_file_write (save->storage, &(link->first), sizeof(link->first));
288 
289  if (GNUNET_YES == link->multiple)
290  GNUNET_DISK_file_write (save->storage, &(link->second), sizeof(link->second));
291 
292  return GNUNET_YES;
293 }
294 
295 void
296 save_message_store (struct GNUNET_MESSENGER_MessageStore *store, const char *directory)
297 {
298  GNUNET_assert((store) && (directory));
299 
301 
303 
304  char *filename;
305 
306  if (GNUNET_YES != store->write_links)
307  goto save_entries;
308 
309  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
310 
311  save.store = store;
313 
314  if (!save.storage)
315  goto save_entries;
316 
318  goto close_links;
319 
321  store->write_links = GNUNET_NO;
322 
323 close_links:
325 
326 save_entries:
327  GNUNET_free(filename);
328  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
329 
330  save.store = store;
332 
333  GNUNET_free(filename);
334 
335  if (!save.storage)
336  return;
337 
338  if (GNUNET_YES == store->rewrite_entries)
339  {
341  goto close_entries;
342 
344  store->rewrite_entries = GNUNET_NO;
345  }
347  goto close_entries;
348 
349  if (store->storage_messages)
351 
352  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
353 
355  permission);
356 
357  GNUNET_free(filename);
358 
359  if (store->storage_messages)
360  {
362 
365  }
366 
367 close_entries:
369 }
370 
371 int
373 {
374  GNUNET_assert((store) && (hash));
375 
377  return GNUNET_YES;
378 
379  return GNUNET_CONTAINER_multihashmap_contains (store->entries, hash);
380 }
381 
382 const struct GNUNET_MESSENGER_Message*
384 {
385  GNUNET_assert((store) && (hash));
386 
388 
389  if (message)
390  return message;
391 
392  if (!store->storage_messages)
393  return NULL;
394 
395  const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
396 
397  if (!entry)
398  return NULL;
399 
401  return message;
402 
403  char *buffer = GNUNET_malloc(entry->length);
404 
405  if (!buffer)
406  return NULL;
407 
408  if (GNUNET_DISK_file_read (store->storage_messages, buffer, entry->length) != entry->length)
409  goto free_buffer;
410 
412 
413  const int decoding = decode_message (message, entry->length, buffer, GNUNET_YES, NULL);
414 
415  struct GNUNET_HashCode check;
416  hash_message (message, entry->length, buffer, &check);
417 
418  if ((GNUNET_YES != decoding) || (GNUNET_CRYPTO_hash_cmp (hash, &check) != 0))
419  {
420  GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry);
421  store->rewrite_entries = GNUNET_YES;
422 
423  goto free_message;
424  }
425 
426  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
428  goto free_buffer;
429 
430 free_message: destroy_message (message);
431  message = NULL;
432 
433 free_buffer:
434  GNUNET_free(buffer);
435 
436  return message;
437 }
438 
439 const struct GNUNET_MESSENGER_MessageLink*
441  int deleted_only)
442 {
443  if (deleted_only)
444  goto get_link;
445 
446  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash);
447 
448  if (!message)
449  goto get_link;
450 
451  static struct GNUNET_MESSENGER_MessageLink link;
452 
453  GNUNET_memcpy(&(link.first), &(message->header.previous), sizeof(link.first));
454 
456 
457  if (GNUNET_YES == link.multiple)
458  GNUNET_memcpy(&(link.second), &(message->body.merge.previous), sizeof(link.second));
459  else
460  GNUNET_memcpy(&(link.second), &(message->header.previous), sizeof(link.second));
461 
462  return &link;
463 
464 get_link:
465  return GNUNET_CONTAINER_multihashmap_get (store->links, hash);
466 }
467 
468 int
470  struct GNUNET_MESSENGER_Message *message)
471 {
472  GNUNET_assert((store) && (hash) && (message));
473 
474  return GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
476 }
477 
478 static void
480  const struct GNUNET_MESSENGER_Message *message)
481 {
483 
484  GNUNET_memcpy(&(link->first), &(message->header.previous), sizeof(link->first));
485 
487 
488  if (GNUNET_YES == link->multiple)
489  GNUNET_memcpy(&(link->second), &(message->body.merge.previous), sizeof(link->second));
490  else
491  GNUNET_memcpy(&(link->second), &(message->header.previous), sizeof(link->second));
492 
493  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(store->links, hash, link,
495  GNUNET_free(link);
496 }
497 
498 int
500 {
501  GNUNET_assert((store) && (hash));
502 
503  const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
504 
505  if (!entry)
506  goto clear_memory;
507 
508  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash);
509 
510  if (message)
511  add_link (store, hash, message);
512 
513  if (!store->storage_messages)
514  goto clear_entry;
515 
517  return GNUNET_SYSERR;
518 
519  char *clear_buffer = GNUNET_malloc(entry->length);
520 
521  if (!clear_buffer)
522  return GNUNET_SYSERR;
523 
524  GNUNET_CRYPTO_zero_keys (clear_buffer, entry->length);
525 
526  if ((entry->length != GNUNET_DISK_file_write (store->storage_messages, clear_buffer, entry->length)) || (GNUNET_OK
528  {
529  GNUNET_free(clear_buffer);
530  return GNUNET_SYSERR;
531  }
532 
533  GNUNET_free(clear_buffer);
534 
535 clear_entry:
536  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry))
537  store->rewrite_entries = GNUNET_YES;
538 
539 clear_memory:
541  return GNUNET_OK;
542 }
Open the file for reading.
struct GNUNET_MESSENGER_MessageHeader header
Header.
Create file if it doesn&#39;t exist.
void destroy_message(struct GNUNET_MESSENGER_Message *message)
Destroys a message and frees its memory fully.
void save_message_store(struct GNUNET_MESSENGER_MessageStore *store, const char *directory)
Saves messages from a message store into a directory.
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:602
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.
#define GNUNET_assert(cond)
Use this for fatal errors that cannot be handled.
enum GNUNET_GenericReturnValue GNUNET_DISK_file_close(struct GNUNET_DISK_FileHandle *h)
Close an open file.
Definition: disk.c:1126
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.
struct GNUNET_DISK_FileHandle * storage_messages
struct GNUNET_CONTAINER_MultiHashMap * messages
int delete_store_message(struct GNUNET_MESSENGER_MessageStore *store, const struct GNUNET_HashCode *hash)
Deletes a message in the message store.
#define GNUNET_memcpy(dst, src, n)
Call memcpy() but check for n being 0 first.
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.
#define GNUNET_new(type)
Allocate a struct or union of the given type.
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)...
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
struct GNUNET_MESSENGER_Message * create_message(enum GNUNET_MESSENGER_MessageKind kind)
Creates and allocates a new message with a specific kind.
static void save()
Write persistent statistics to disk.
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.
void GNUNET_CRYPTO_zero_keys(void *buffer, size_t length)
Zero out buffer, securely against compiler optimizations.
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.
struct GNUNET_CONTAINER_MultiHashMap * links
static int iterate_save_messages(void *cls, const struct GNUNET_HashCode *key, void *value)
, &#39; bother checking if a value already exists (faster than GNUNET_CONTAINER_MULTIHASHMAPOPTION_...
static char * value
Value of the record to add/remove.
static int iterate_save_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_save_links(void *cls, const struct GNUNET_HashCode *key, void *value)
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:666
void GNUNET_CONTAINER_multihashmap_destroy(struct GNUNET_CONTAINER_MultiHashMap *map)
Destroy a hash map.
struct GNUNET_HashCode previous
The hash of a second previous message.
void load_message_store(struct GNUNET_MESSENGER_MessageStore *store, const char *directory)
Loads messages from a directory into a message store.
struct ListEntry * entries
List of peers in the list.
struct GNUNET_MESSENGER_MessageBody body
Body.
static char * filename
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.
GNUNET_DISK_AccessPermissions
File access permissions, UNIX-style.
struct GNUNET_MESSENGER_MessageMerge merge
A 512-bit hashcode.
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.
int int GNUNET_asprintf(char **buf, const char *format,...) __attribute__((format(printf
Like asprintf, just portable.
struct GNUNET_HashCode previous
The hash of the previous message from the senders perspective.
static int iterate_destroy_messages(void *cls, const struct GNUNET_HashCode *key, void *value)
struct GNUNET_HashCode key
The key used in the DHT.
Seek an absolute position from the end of the file.
Open the file for writing.
static void load_message_store_links(struct GNUNET_MESSENGER_MessageStore *store, const char *filename)
static int iterate_destroy_links(void *cls, const struct GNUNET_HashCode *key, void *value)
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.
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.
static int iterate_destroy_entries(void *cls, const struct GNUNET_HashCode *key, void *value)
Seek an absolute position (from the start 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
enum GNUNET_GenericReturnValue GNUNET_DISK_file_sync(const struct GNUNET_DISK_FileHandle *h)
Write file changes to disk.
Definition: disk.c:1245
Open the file for both reading and writing.
struct GNUNET_CONTAINER_MultiHashMap * GNUNET_CONTAINER_multihashmap_create(unsigned int len, int do_not_copy_keys)
Create a multi hash map.
void init_message_store(struct GNUNET_MESSENGER_MessageStore *store)
Initializes a message store as fully empty.
int GNUNET_CONTAINER_multihashmap_iterate(struct GNUNET_CONTAINER_MultiHashMap *map, GNUNET_CONTAINER_MulitHashMapIteratorCallback it, void *it_cls)
Iterate over all entries in the map.
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.
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:1055
Handle used to access files (and pipes).
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:437
#define GNUNET_malloc(size)
Wrapper around malloc.
static void load_message_store_entries(struct GNUNET_MESSENGER_MessageStore *store, const char *filename)
struct GNUNET_CONTAINER_MultiHashMap * entries
#define GNUNET_free(ptr)
Wrapper around free.
messenger api: client and service implementation of GNUnet MESSENGER service
enum GNUNET_MESSENGER_MessageKind kind
The kind of the message.
void clear_message_store(struct GNUNET_MESSENGER_MessageStore *store)
Clears a message store, wipes its content and deallocates its memory.
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.