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
46  const struct GNUNET_HashCode *key,
47  void *value)
48 {
49  struct GNUNET_MESSENGER_MessageEntry *entry = value;
50 
51  GNUNET_free(entry);
52 
53  return GNUNET_YES;
54 }
55 
56 static int
58  const struct GNUNET_HashCode *key,
59  void *value)
60 {
61  struct GNUNET_MESSENGER_Message *message = value;
62 
63  destroy_message (message);
64 
65  return GNUNET_YES;
66 }
67 
68 static int
70  const struct GNUNET_HashCode *key,
71  void *value)
72 {
73  struct GNUNET_HashCode *previous = value;
74 
75  GNUNET_free(previous);
76 
77  return GNUNET_YES;
78 }
79 
80 void
82 {
83  GNUNET_assert(store);
84 
85  if (store->storage_messages)
86  {
88 
89  store->storage_messages = NULL;
90  }
91 
95 
99 }
100 
102 {
103  struct GNUNET_HashCode hash;
105 };
106 
107 #define load_message_store_attribute_failed(file, attribute) \
108  sizeof(attribute) != GNUNET_DISK_file_read(file, &(attribute), sizeof(attribute))
109 
110 #define save_message_store_attribute_failed(file, attribute) \
111  sizeof(attribute) != GNUNET_DISK_file_write(file, &(attribute), sizeof(attribute))
112 
113 static void
115  const char *filename)
116 {
118 
120 
121  if (!entries)
122  return;
123 
125  struct GNUNET_MESSENGER_MessageEntry *entry;
126 
127  memset(&storage, 0, sizeof(storage));
128 
129  do
130  {
131  entry = NULL;
132 
136  break;
137 
139 
140  GNUNET_memcpy(entry, &(storage.entry), sizeof(*entry));
141 
142  if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->entries, &(storage.hash))) ||
143  (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->entries, &(storage.hash), entry,
145  {
146  store->rewrite_entries = GNUNET_YES;
147  break;
148  }
149  }
150  while (entry);
151 
152  if (entry)
153  GNUNET_free(entry);
154 
156 }
157 
159 {
160  struct GNUNET_HashCode hash;
162 };
163 
164 static void
166  const char *filename)
167 {
169 
171 
172  if (!entries)
173  return;
174 
176  struct GNUNET_MESSENGER_MessageLink *link;
177 
178  memset(&storage, 0, sizeof(storage));
179 
180  do
181  {
182  link = NULL;
183 
187  ((GNUNET_YES == storage.link.multiple) &&
189  break;
190 
192 
193  GNUNET_memcpy(link, &(storage.link), sizeof(*link));
194 
195  if ((GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (store->links, &(storage.hash))) ||
196  (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put (store->links, &(storage.hash), link,
198  break;
199  }
200  while (link);
201 
202  if (link)
203  GNUNET_free(link);
204 
206 }
207 
208 void
210  const char *directory)
211 {
212  GNUNET_assert((store) && (directory));
213 
215 
216  if (store->storage_messages)
218 
219  char *filename;
220  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
221 
224  else
225  store->storage_messages = NULL;
226 
228 
229  if (!store->storage_messages)
230  return;
231 
232  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
233 
236 
238 
239  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
240 
243 
245 }
246 
248 {
250 
252 };
253 
254 static int
256  const struct GNUNET_HashCode *key,
257  void *value)
258 {
260  struct GNUNET_MESSENGER_MessageEntry *entry = value;
261 
263 
264  GNUNET_DISK_file_write (save->storage, key, sizeof(*key));
265  GNUNET_DISK_file_write (save->storage, &(entry->offset), sizeof(entry->offset));
266  GNUNET_DISK_file_write (save->storage, &(entry->length), sizeof(entry->length));
267 
268  return GNUNET_YES;
269 }
270 
271 static int
273  const struct GNUNET_HashCode *key,
274  void *value)
275 {
277 
278  if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains (save->store->entries, key))
279  return GNUNET_YES;
280 
281  struct GNUNET_MESSENGER_Message *message = value;
283 
284  GNUNET_memcpy(&(storage.hash), key, sizeof(storage.hash));
285 
286  storage.entry.length = get_message_size (message, GNUNET_YES);
287  storage.entry.offset = GNUNET_DISK_file_seek (save->store->storage_messages, 0, GNUNET_DISK_SEEK_END);
288 
289  if ((GNUNET_SYSERR == storage.entry.offset) ||
290  (save_message_store_attribute_failed(save->storage, storage.hash)) ||
291  (save_message_store_attribute_failed(save->storage, storage.entry.offset)) ||
293  return GNUNET_YES;
294 
295  char *buffer = GNUNET_malloc(storage.entry.length);
296 
297  encode_message (message, storage.entry.length, buffer, GNUNET_YES);
298 
299  GNUNET_DISK_file_write (save->store->storage_messages, buffer, storage.entry.length);
300 
301  GNUNET_free(buffer);
302  return GNUNET_YES;
303 }
304 
305 static int
307  const struct GNUNET_HashCode *key,
308  void *value)
309 {
311  struct GNUNET_MESSENGER_MessageLink *link = value;
312 
313  GNUNET_DISK_file_write (save->storage, key, sizeof(*key));
314  GNUNET_DISK_file_write (save->storage, &(link->multiple), sizeof(link->multiple));
315  GNUNET_DISK_file_write (save->storage, &(link->first), sizeof(link->first));
316 
317  if (GNUNET_YES == link->multiple)
318  GNUNET_DISK_file_write (save->storage, &(link->second), sizeof(link->second));
319 
320  return GNUNET_YES;
321 }
322 
323 void
325  const char *directory)
326 {
327  GNUNET_assert((store) && (directory));
328 
330 
332 
333  char *filename;
334 
335  if (GNUNET_YES != store->write_links)
336  goto save_entries;
337 
338  GNUNET_asprintf (&filename, "%s%s", directory, "links.store");
339 
340  save.store = store;
342 
344 
345  if (!save.storage)
346  goto save_entries;
347 
349  goto close_links;
350 
353 
354 close_links:
355  GNUNET_DISK_file_close (save.storage);
356 
357 save_entries:
358  GNUNET_asprintf (&filename, "%s%s", directory, "entries.store");
359 
360  save.store = store;
362 
364 
365  if (!save.storage)
366  return;
367 
369  {
371  goto close_entries;
372 
375  }
377  goto close_entries;
378 
379  if (store->storage_messages)
381 
382  GNUNET_asprintf (&filename, "%s%s", directory, "messages.store");
383 
385  permission);
386 
388 
389  if (store->storage_messages)
390  {
392 
394  GNUNET_DISK_file_sync (save.storage);
395  }
396 
397 close_entries:
398  GNUNET_DISK_file_close (save.storage);
399 }
400 
401 int
403  const struct GNUNET_HashCode *hash)
404 {
405  GNUNET_assert((store) && (hash));
406 
408  return GNUNET_YES;
409 
411 }
412 
413 const struct GNUNET_MESSENGER_Message*
415  const struct GNUNET_HashCode *hash)
416 {
417  GNUNET_assert((store) && (hash));
418 
420 
421  if (message)
422  return message;
423 
424  if (!store->storage_messages)
425  return NULL;
426 
427  const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
428 
429  if (!entry)
430  return NULL;
431 
433  return message;
434 
435  char *buffer = GNUNET_malloc(entry->length);
436 
437  if (!buffer)
438  return NULL;
439 
440  if ((GNUNET_DISK_file_read (store->storage_messages, buffer, entry->length) != entry->length) ||
442  goto free_buffer;
443 
445 
446  const int decoding = decode_message (message, entry->length, buffer, GNUNET_YES, NULL);
447 
448  struct GNUNET_HashCode check;
449  hash_message (message, entry->length, buffer, &check);
450 
451  if ((GNUNET_YES != decoding) || (GNUNET_CRYPTO_hash_cmp (hash, &check) != 0))
452  {
453  if (GNUNET_YES != GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry))
454  GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Corrupted entry could not be removed from store: %s\n",
455  GNUNET_h2s(hash));
456 
457  store->rewrite_entries = GNUNET_YES;
458 
459  goto free_message;
460  }
461 
462  if (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
464  goto free_buffer;
465 
466 free_message: destroy_message (message);
467  message = NULL;
468 
469 free_buffer:
470  GNUNET_free(buffer);
471 
472  return message;
473 }
474 
475 const struct GNUNET_MESSENGER_MessageLink*
477  const struct GNUNET_HashCode *hash,
478  int deleted_only)
479 {
480  if (deleted_only)
481  goto get_link;
482 
483  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash);
484 
485  if (!message)
486  goto get_link;
487 
488  static struct GNUNET_MESSENGER_MessageLink link;
489 
490  GNUNET_memcpy(&(link.first), &(message->header.previous), sizeof(link.first));
491 
493 
494  if (GNUNET_YES == link.multiple)
495  GNUNET_memcpy(&(link.second), &(message->body.merge.previous), sizeof(link.second));
496  else
497  GNUNET_memcpy(&(link.second), &(message->header.previous), sizeof(link.second));
498 
499  return &link;
500 
501 get_link:
502  return GNUNET_CONTAINER_multihashmap_get (store->links, hash);
503 }
504 
505 int
507  const struct GNUNET_HashCode *hash,
508  struct GNUNET_MESSENGER_Message *message)
509 {
510  GNUNET_assert((store) && (hash) && (message));
511 
512  return GNUNET_CONTAINER_multihashmap_put (store->messages, hash, message,
514 }
515 
516 static void
518  const struct GNUNET_HashCode *hash,
519  const struct GNUNET_MESSENGER_Message *message)
520 {
522 
523  GNUNET_memcpy(&(link->first), &(message->header.previous), sizeof(link->first));
524 
526 
527  if (GNUNET_YES == link->multiple)
528  GNUNET_memcpy(&(link->second), &(message->body.merge.previous), sizeof(link->second));
529  else
530  GNUNET_memcpy(&(link->second), &(message->header.previous), sizeof(link->second));
531 
532  if (GNUNET_OK != GNUNET_CONTAINER_multihashmap_put(store->links, hash, link,
534  GNUNET_free(link);
535 }
536 
537 int
539  const struct GNUNET_HashCode *hash)
540 {
541  GNUNET_assert((store) && (hash));
542 
543  const struct GNUNET_MESSENGER_MessageEntry *entry = GNUNET_CONTAINER_multihashmap_get (store->entries, hash);
544 
545  if (!entry)
546  goto clear_memory;
547 
548  const struct GNUNET_MESSENGER_Message *message = get_store_message(store, hash);
549 
550  if (message)
551  add_link (store, hash, message);
552 
553  if (!store->storage_messages)
554  goto clear_entry;
555 
557  return GNUNET_SYSERR;
558 
559  char *clear_buffer = GNUNET_malloc(entry->length);
560 
561  if (!clear_buffer)
562  return GNUNET_SYSERR;
563 
564  GNUNET_CRYPTO_zero_keys (clear_buffer, entry->length);
565 
566  if ((entry->length != GNUNET_DISK_file_write (store->storage_messages, clear_buffer, entry->length)) || (GNUNET_OK
568  {
569  GNUNET_free(clear_buffer);
570  return GNUNET_SYSERR;
571  }
572 
573  GNUNET_free(clear_buffer);
574 
575 clear_entry:
576  if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove (store->entries, hash, entry))
577  store->rewrite_entries = GNUNET_YES;
578 
579 clear_memory:
581  return GNUNET_OK;
582 }
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.
#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:219
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.
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.