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