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