check return values from GNSRECORD_record_serialize/size always
[oweals/gnunet.git] / src / namestore / plugin_namestore_flat.c
1  /*
2   * This file is part of GNUnet
3   * Copyright (C) 2009-2015, 2018 GNUnet e.V.
4   *
5   * GNUnet is free software; you can redistribute it and/or modify
6   * it under the terms of the GNU General Public License as published
7   * by the Free Software Foundation; either version 3, or (at your
8   * 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   * General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with GNUnet; see the file COPYING.  If not, write to the
17   * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18   * Boston, MA 02110-1301, USA.
19   */
20
21 /**
22  * @file namestore/plugin_namestore_flat.c
23  * @brief file-based namestore backend
24  * @author Martin Schanzenbach
25  * @author Christian Grothoff
26  */
27
28 #include "platform.h"
29 #include "gnunet_namestore_plugin.h"
30 #include "gnunet_namestore_service.h"
31 #include "gnunet_gnsrecord_lib.h"
32 #include "namestore.h"
33
34 /**
35  * Context for all functions in this plugin.
36  */
37 struct Plugin
38 {
39
40   const struct GNUNET_CONFIGURATION_Handle *cfg;
41
42   /**
43    * Database filename.
44    */
45   char *fn;
46
47   /**
48    * HashMap
49    */
50   struct GNUNET_CONTAINER_MultiHashMap *hm;
51
52 };
53
54
55 struct FlatFileEntry
56 {
57   /**
58    * Entry zone
59    */
60   struct GNUNET_CRYPTO_EcdsaPrivateKey *private_key;
61
62   /**
63    * Record cound
64    */
65   uint32_t record_count;
66
67   /**
68    * Rvalue
69    */
70   uint64_t rvalue;
71
72   /**
73    * Record data
74    */
75   struct GNUNET_GNSRECORD_Data *record_data;
76
77   /**
78    * Label
79    */
80   char *label;
81
82
83 };
84
85
86 /**
87  * Initialize the database connections and associated
88  * data structures (create tables and indices
89  * as needed as well).
90  *
91  * @param plugin the plugin context (state for this module)
92  * @return #GNUNET_OK on success
93  */
94 static int
95 database_setup (struct Plugin *plugin)
96 {
97   char *afsdir;
98   char *key;
99   char *record_data;
100   char *zone_private_key;
101   char *record_data_b64;
102   char *buffer;
103   char *line;
104   char *label;
105   char *rvalue;
106   char *record_count;
107   size_t record_data_size;
108   uint64_t size;
109   size_t key_len;
110   struct GNUNET_HashCode hkey;
111   struct GNUNET_DISK_FileHandle *fh;
112   struct FlatFileEntry *entry;
113
114   if (GNUNET_OK !=
115       GNUNET_CONFIGURATION_get_value_filename (plugin->cfg,
116                                                "namestore-flat",
117                                                "FILENAME",
118                                                &afsdir))
119   {
120     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
121                                "namestore-flat",
122                                "FILENAME");
123     return GNUNET_SYSERR;
124   }
125   if (GNUNET_OK !=
126       GNUNET_DISK_file_test (afsdir))
127   {
128     if (GNUNET_OK !=
129         GNUNET_DISK_directory_create_for_file (afsdir))
130     {
131       GNUNET_break (0);
132       GNUNET_free (afsdir);
133       return GNUNET_SYSERR;
134     }
135   }
136   /* afsdir should be UTF-8-encoded. If it isn't, it's a bug */
137   plugin->fn = afsdir;
138
139   /* Load data from file into hashmap */
140   plugin->hm = GNUNET_CONTAINER_multihashmap_create (10,
141                                                      GNUNET_NO);
142   fh = GNUNET_DISK_file_open (afsdir,
143                               GNUNET_DISK_OPEN_CREATE |
144                               GNUNET_DISK_OPEN_READWRITE,
145                               GNUNET_DISK_PERM_USER_WRITE |
146                               GNUNET_DISK_PERM_USER_READ);
147   if (NULL == fh)
148   {
149     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
150          _("Unable to initialize file: %s.\n"),
151          afsdir);
152     return GNUNET_SYSERR;
153   }
154   if (GNUNET_SYSERR ==
155       GNUNET_DISK_file_size (afsdir,
156                              &size,
157                              GNUNET_YES,
158                              GNUNET_YES))
159   {
160     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
161          _("Unable to get filesize: %s.\n"),
162          afsdir);
163     GNUNET_DISK_file_close (fh);
164     return GNUNET_SYSERR;
165   }
166
167   buffer = GNUNET_malloc (size + 1);
168   if (GNUNET_SYSERR ==
169       GNUNET_DISK_file_read (fh,
170                              buffer,
171                              size))
172   {
173     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
174          _("Unable to read file: %s.\n"),
175          afsdir);
176     GNUNET_free (buffer);
177     GNUNET_DISK_file_close (fh);
178     return GNUNET_SYSERR;
179   }
180   buffer[size] = '\0';
181   GNUNET_DISK_file_close (fh);
182
183   if (0 < size)
184   {
185     line = strtok (buffer, "\n");
186     while (line != NULL)
187     {
188       zone_private_key = strtok (line, ",");
189       if (NULL == zone_private_key)
190         break;
191       rvalue = strtok (NULL, ",");
192       if (NULL == rvalue)
193         break;
194       record_count = strtok (NULL, ",");
195       if (NULL == record_count)
196         break;
197       record_data_b64 = strtok (NULL, ",");
198       if (NULL == record_data_b64)
199         break;
200       label = strtok (NULL, ",");
201       if (NULL == label)
202         break;
203       line = strtok (NULL, "\n");
204       entry = GNUNET_new (struct FlatFileEntry);
205       {
206         unsigned long long ll;
207
208         if (1 != sscanf (rvalue,
209                          "%llu",
210                          &ll))
211         {
212           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
213                       "Error parsing entry\n");
214           GNUNET_free (entry);
215           break;
216         }
217         entry->rvalue = (uint64_t) ll;
218       }
219       {
220         unsigned int ui;
221
222         if (1 != sscanf (record_count,
223                          "%u",
224                          &ui))
225         {
226           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
227                       "Error parsing entry\n");
228           GNUNET_free (entry);
229           break;
230         }
231         entry->record_count = (uint32_t) ui;
232       }
233       entry->label = GNUNET_strdup (label);
234       record_data_size
235         = GNUNET_STRINGS_base64_decode (record_data_b64,
236                                         strlen (record_data_b64),
237                                         &record_data);
238       entry->record_data =
239         GNUNET_new_array (entry->record_count,
240                           struct GNUNET_GNSRECORD_Data);
241       if (GNUNET_OK !=
242           GNUNET_GNSRECORD_records_deserialize (record_data_size,
243                                                 record_data,
244                                                 entry->record_count,
245                                                 entry->record_data))
246       {
247         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
248                     "Unable to deserialize record %s\n",
249                     label);
250         GNUNET_free (entry->label);
251         GNUNET_free (entry);
252         GNUNET_free (record_data);
253         break;
254       }
255       GNUNET_free (record_data);
256       GNUNET_STRINGS_base64_decode (zone_private_key,
257                                     strlen (zone_private_key),
258                                     (char**)&entry->private_key);
259       key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
260       key = GNUNET_malloc (strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
261       GNUNET_memcpy (key,
262                      label,
263                      strlen (label));
264       GNUNET_memcpy (key+strlen(label),
265                      entry->private_key,
266                      sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
267       GNUNET_CRYPTO_hash (key,
268                           key_len,
269                           &hkey);
270       GNUNET_free (key);
271       if (GNUNET_OK !=
272           GNUNET_CONTAINER_multihashmap_put (plugin->hm,
273                                              &hkey,
274                                              entry,
275                                              GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
276       {
277         GNUNET_free (entry);
278         GNUNET_break (0);
279       }
280     }
281   }
282   GNUNET_free (buffer);
283   return GNUNET_OK;
284 }
285
286
287 /**
288  * Store values in hashmap in file and free data
289  *
290  * @param plugin the plugin context
291  * @param key key in the map
292  * @param value a `struct FlatFileEntry`
293  */
294 static int
295 store_and_free_entries (void *cls,
296                         const struct GNUNET_HashCode *key,
297                         void *value)
298 {
299   struct GNUNET_DISK_FileHandle *fh = cls;
300   struct FlatFileEntry *entry = value;
301   char *line;
302   char *zone_private_key;
303   char *record_data_b64;
304   ssize_t data_size;
305
306   (void) key;
307   GNUNET_STRINGS_base64_encode ((char*)entry->private_key,
308                                 sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey),
309                                 &zone_private_key);
310   data_size = GNUNET_GNSRECORD_records_get_size (entry->record_count,
311                                                  entry->record_data);
312   if (data_size < 0)
313   {
314     GNUNET_break (0);
315     GNUNET_free (zone_private_key);
316     return GNUNET_SYSERR;
317   }
318   if (data_size >= UINT16_MAX)
319   {
320     GNUNET_break (0);
321     GNUNET_free (zone_private_key);
322     return GNUNET_SYSERR;
323   }
324   {
325     char data[data_size];
326     ssize_t ret;
327
328     ret = GNUNET_GNSRECORD_records_serialize (entry->record_count,
329                                               entry->record_data,
330                                               data_size,
331                                               data);
332     if ( (ret < 0) ||
333          (data_size != ret) )
334     {
335       GNUNET_break (0);
336       GNUNET_free (zone_private_key);
337       return GNUNET_SYSERR;
338     }
339     GNUNET_STRINGS_base64_encode (data,
340                                   data_size,
341                                   &record_data_b64);
342   }
343   GNUNET_asprintf (&line,
344                    "%s,%llu,%u,%s,%s\n",
345                    zone_private_key,
346                    (unsigned long long) entry->rvalue,
347                    (unsigned int) entry->record_count,
348                    record_data_b64,
349                    entry->label);
350   GNUNET_free (record_data_b64);
351   GNUNET_free (zone_private_key);
352
353   GNUNET_DISK_file_write (fh,
354                           line,
355                           strlen (line));
356
357   GNUNET_free (line);
358   GNUNET_free (entry->private_key);
359   GNUNET_free (entry->label);
360   GNUNET_free (entry->record_data);
361   GNUNET_free (entry);
362   return GNUNET_YES;
363 }
364
365
366 /**
367  * Shutdown database connection and associate data
368  * structures.
369  * @param plugin the plugin context (state for this module)
370  */
371 static void
372 database_shutdown (struct Plugin *plugin)
373 {
374   struct GNUNET_DISK_FileHandle *fh;
375
376   fh = GNUNET_DISK_file_open (plugin->fn,
377                               GNUNET_DISK_OPEN_CREATE |
378                               GNUNET_DISK_OPEN_TRUNCATE |
379                               GNUNET_DISK_OPEN_READWRITE,
380                               GNUNET_DISK_PERM_USER_WRITE |
381                               GNUNET_DISK_PERM_USER_READ);
382   if (NULL == fh)
383   {
384     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
385                 _("Unable to initialize file: %s.\n"),
386                 plugin->fn);
387     return;
388   }
389
390   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
391                                          &store_and_free_entries,
392                                          fh);
393   GNUNET_CONTAINER_multihashmap_destroy (plugin->hm);
394   GNUNET_DISK_file_close (fh);
395 }
396
397
398 /**
399  * Store a record in the datastore.  Removes any existing record in the
400  * same zone with the same name.
401  *
402  * @param cls closure (internal context for the plugin)
403  * @param zone_key private key of the zone
404  * @param label name that is being mapped (at most 255 characters long)
405  * @param rd_count number of entries in @a rd array
406  * @param rd array of records with data to store
407  * @return #GNUNET_OK on success, else #GNUNET_SYSERR
408  */
409 static int
410 namestore_flat_store_records (void *cls,
411                               const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
412                               const char *label,
413                               unsigned int rd_count,
414                               const struct GNUNET_GNSRECORD_Data *rd)
415 {
416   struct Plugin *plugin = cls;
417   uint64_t rvalue;
418   size_t key_len;
419   char *key;
420   struct GNUNET_HashCode hkey;
421   struct FlatFileEntry *entry;
422
423   rvalue = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
424                                      UINT64_MAX);
425   key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
426   key = GNUNET_malloc (key_len);
427   GNUNET_memcpy (key,
428                  label,
429                  strlen (label));
430   GNUNET_memcpy (key + strlen(label),
431                  zone_key,
432                  sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
433   GNUNET_CRYPTO_hash (key,
434                       key_len,
435                       &hkey);
436   GNUNET_CONTAINER_multihashmap_remove_all (plugin->hm,
437                                             &hkey);
438   if (0 == rd_count)
439   {
440     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
441                      "sqlite",
442                      "Record deleted\n");
443     return GNUNET_OK;
444   }
445   entry = GNUNET_new (struct FlatFileEntry);
446   entry->private_key = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey);
447   GNUNET_asprintf (&entry->label,
448                    label,
449                    strlen (label));
450   GNUNET_memcpy (entry->private_key,
451                  zone_key,
452                  sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
453   entry->rvalue = rvalue;
454   entry->record_count = rd_count;
455   entry->record_data = GNUNET_new_array (rd_count,
456                                          struct GNUNET_GNSRECORD_Data);
457   for (unsigned int i = 0; i < rd_count; i++)
458   {
459     entry->record_data[i].expiration_time = rd[i].expiration_time;
460     entry->record_data[i].record_type = rd[i].record_type;
461     entry->record_data[i].flags = rd[i].flags;
462     entry->record_data[i].data_size = rd[i].data_size;
463     entry->record_data[i].data = GNUNET_malloc (rd[i].data_size);
464     GNUNET_memcpy ((char*)entry->record_data[i].data,
465                    rd[i].data,
466                    rd[i].data_size);
467   }
468   return GNUNET_CONTAINER_multihashmap_put (plugin->hm,
469                                             &hkey,
470                                             entry,
471                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
472 }
473
474
475 /**
476  * Lookup records in the datastore for which we are the authority.
477  *
478  * @param cls closure (internal context for the plugin)
479  * @param zone private key of the zone
480  * @param label name of the record in the zone
481  * @param iter function to call with the result
482  * @param iter_cls closure for @a iter
483  * @return #GNUNET_OK on success, #GNUNET_NO for no results, else #GNUNET_SYSERR
484  */
485 static int
486 namestore_flat_lookup_records (void *cls,
487                                const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
488                                const char *label,
489                                GNUNET_NAMESTORE_RecordIterator iter,
490                                void *iter_cls)
491 {
492   struct Plugin *plugin = cls;
493   struct FlatFileEntry *entry;
494   struct GNUNET_HashCode hkey;
495   char *key;
496   size_t key_len;
497
498   if (NULL == zone)
499   {
500     GNUNET_break (0);
501     return GNUNET_SYSERR;
502   }
503   key_len = strlen (label) + sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey);
504   key = GNUNET_malloc (key_len);
505   GNUNET_memcpy (key,
506                  label,
507                  strlen (label));
508   GNUNET_memcpy (key+strlen(label),
509                  zone,
510                  sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey));
511   GNUNET_CRYPTO_hash (key,
512                       key_len,
513                       &hkey);
514   GNUNET_free (key);
515
516   entry = GNUNET_CONTAINER_multihashmap_get (plugin->hm,
517                                              &hkey);
518
519   if (NULL == entry)
520     return GNUNET_NO;
521   if (NULL != iter)
522     iter (iter_cls,
523           0,
524           entry->private_key,
525           entry->label,
526           entry->record_count,
527           entry->record_data);
528   return GNUNET_YES;
529 }
530
531
532 /**
533  * Closure for #iterate_zones.
534  */
535 struct IterateContext
536 {
537   /**
538    * How many more records should we skip before returning results?
539    */
540   uint64_t offset;
541
542   /**
543    * How many more records should we return?
544    */
545   uint64_t limit;
546
547   /**
548    * What is the position of the current entry, counting
549    * starts from 1.
550    */
551   uint64_t pos;
552
553   /**
554    * Target zone.
555    */
556   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone;
557
558   /**
559    * Function to call on each record.
560    */
561   GNUNET_NAMESTORE_RecordIterator iter;
562
563   /**
564    * Closure for @e iter.
565    */
566   void *iter_cls;
567
568 };
569
570
571 /**
572  * Helper function for #namestore_flat_iterate_records().
573  *
574  * @param cls a `struct IterateContext`
575  * @param key unused
576  * @param value a `struct FlatFileEntry`
577  * @return #GNUNET_YES to continue the iteration
578  */
579 static int
580 iterate_zones (void *cls,
581                const struct GNUNET_HashCode *key,
582                void *value)
583 {
584   struct IterateContext *ic = cls;
585   struct FlatFileEntry *entry = value;
586
587   (void) key;
588   if (0 == ic->limit)
589     return GNUNET_NO;
590   if ( (NULL != ic->zone) &&
591        (0 != memcmp (entry->private_key,
592                      ic->zone,
593                      sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey))) )
594     return GNUNET_YES;
595   ic->pos++;
596   if (ic->offset > 0)
597   {
598     ic->offset--;
599     return GNUNET_YES;
600   }
601   ic->iter (ic->iter_cls,
602             ic->pos,
603             entry->private_key,
604             entry->label,
605             entry->record_count,
606             entry->record_data);
607   ic->limit--;
608   if (0 == ic->limit)
609     return GNUNET_NO;
610   return GNUNET_YES;
611 }
612
613
614 /**
615  * Iterate over the results for a particular key and zone in the
616  * datastore.  Will return at most one result to the iterator.
617  *
618  * @param cls closure (internal context for the plugin)
619  * @param zone hash of public key of the zone, NULL to iterate over all zones
620  * @param serial serial number to exclude in the list of all matching records
621  * @param limit maximum number of results to return to @a iter
622  * @param iter function to call with the result
623  * @param iter_cls closure for @a iter
624  * @return #GNUNET_OK on success, #GNUNET_NO if there were no more results, #GNUNET_SYSERR on error
625  */
626 static int
627 namestore_flat_iterate_records (void *cls,
628                                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
629                                 uint64_t serial,
630                                 uint64_t limit,
631                                 GNUNET_NAMESTORE_RecordIterator iter,
632                                 void *iter_cls)
633 {
634   struct Plugin *plugin = cls;
635   struct IterateContext ic;
636
637   ic.offset = serial;
638   ic.pos = 0;
639   ic.limit = limit;
640   ic.iter = iter;
641   ic.iter_cls = iter_cls;
642   ic.zone = zone;
643   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
644                                          &iterate_zones,
645                                          &ic);
646   return (0 == ic.limit) ? GNUNET_OK : GNUNET_NO;
647 }
648
649
650 /**
651  * Closure for #zone_to_name.
652  */
653 struct ZoneToNameContext
654 {
655   const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone;
656   const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone;
657   GNUNET_NAMESTORE_RecordIterator iter;
658   void *iter_cls;
659
660   int result_found;
661 };
662
663
664 static int
665 zone_to_name (void *cls,
666               const struct GNUNET_HashCode *key,
667               void *value)
668 {
669   struct ZoneToNameContext *ztn = cls;
670   struct FlatFileEntry *entry = value;
671
672   (void) key;
673   if (0 != memcmp (entry->private_key,
674                    ztn->zone,
675                    sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)))
676     return GNUNET_YES;
677
678   for (unsigned int i = 0; i < entry->record_count; i++)
679   {
680     if (GNUNET_GNSRECORD_TYPE_PKEY != entry->record_data[i].record_type)
681       continue;
682     if (0 == memcmp (ztn->value_zone,
683                      entry->record_data[i].data,
684                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)))
685     {
686       ztn->iter (ztn->iter_cls,
687                  0,
688                  entry->private_key,
689                  entry->label,
690                  entry->record_count,
691                  entry->record_data);
692       ztn->result_found = GNUNET_YES;
693     }
694   }
695   return GNUNET_YES;
696 }
697
698
699 /**
700  * Look for an existing PKEY delegation record for a given public key.
701  * Returns at most one result to the iterator.
702  *
703  * @param cls closure (internal context for the plugin)
704  * @param zone private key of the zone to look up in, never NULL
705  * @param value_zone public key of the target zone (value), never NULL
706  * @param iter function to call with the result
707  * @param iter_cls closure for @a iter
708  * @return #GNUNET_OK on success, #GNUNET_NO if there were no results, #GNUNET_SYSERR on error
709  */
710 static int
711 namestore_flat_zone_to_name (void *cls,
712                              const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
713                              const struct GNUNET_CRYPTO_EcdsaPublicKey *value_zone,
714                              GNUNET_NAMESTORE_RecordIterator iter,
715                              void *iter_cls)
716 {
717   struct Plugin *plugin = cls;
718   struct ZoneToNameContext ztn = {
719     .iter = iter,
720     .iter_cls = iter_cls,
721     .zone = zone,
722     .value_zone = value_zone,
723     .result_found = GNUNET_NO
724   };
725
726   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
727               "Performing reverse lookup for `%s'\n",
728               GNUNET_GNSRECORD_z2s (value_zone));
729   GNUNET_CONTAINER_multihashmap_iterate (plugin->hm,
730                                          &zone_to_name,
731                                          &ztn);
732   return ztn.result_found;
733 }
734
735
736 /**
737  * Entry point for the plugin.
738  *
739  * @param cls the "struct GNUNET_NAMESTORE_PluginEnvironment*"
740  * @return NULL on error, otherwise the plugin context
741  */
742 void *
743 libgnunet_plugin_namestore_flat_init (void *cls)
744 {
745   static struct Plugin plugin;
746   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
747   struct GNUNET_NAMESTORE_PluginFunctions *api;
748
749   if (NULL != plugin.cfg)
750     return NULL;                /* can only initialize once! */
751   memset (&plugin,
752           0,
753           sizeof (struct Plugin));
754   plugin.cfg = cfg;
755   if (GNUNET_OK != database_setup (&plugin))
756   {
757     database_shutdown (&plugin);
758     return NULL;
759   }
760   api = GNUNET_new (struct GNUNET_NAMESTORE_PluginFunctions);
761   api->cls = &plugin;
762   api->store_records = &namestore_flat_store_records;
763   api->iterate_records = &namestore_flat_iterate_records;
764   api->zone_to_name = &namestore_flat_zone_to_name;
765   api->lookup_records = &namestore_flat_lookup_records;
766   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
767               _("flat file database running\n"));
768   return api;
769 }
770
771
772 /**
773  * Exit point from the plugin.
774  *
775  * @param cls the plugin context (as returned by "init")
776  * @return always NULL
777  */
778 void *
779 libgnunet_plugin_namestore_flat_done (void *cls)
780 {
781   struct GNUNET_NAMESTORE_PluginFunctions *api = cls;
782   struct Plugin *plugin = api->cls;
783
784   database_shutdown (plugin);
785   plugin->cfg = NULL;
786   GNUNET_free (api);
787   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
788               "flat file plugin is finished\n");
789   return NULL;
790 }
791
792 /* end of plugin_namestore_flat.c */