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