fix
[oweals/gnunet.git] / src / datacache / datacache.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2015 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  * @file datacache/datacache.c
20  * @brief datacache API implementation
21  * @author Christian Grothoff
22  */
23 #include "platform.h"
24 #include "gnunet_util_lib.h"
25 #include "gnunet_datacache_lib.h"
26 #include "gnunet_statistics_service.h"
27 #include "gnunet_datacache_plugin.h"
28
29
30 #define LOG(kind,...) GNUNET_log_from (kind, "datacache", __VA_ARGS__)
31
32 #define LOG_STRERROR_FILE(kind,op,fn) GNUNET_log_from_strerror_file (kind, "datacache", op, fn)
33
34 /**
35  * Internal state of the datacache library.
36  */
37 struct GNUNET_DATACACHE_Handle
38 {
39
40   /**
41    * Bloomfilter to quickly tell if we don't have the content.
42    */
43   struct GNUNET_CONTAINER_BloomFilter *filter;
44
45   /**
46    * Our configuration.
47    */
48   const struct GNUNET_CONFIGURATION_Handle *cfg;
49
50   /**
51    * Opaque handle for the statistics service.
52    */
53   struct GNUNET_STATISTICS_Handle *stats;
54
55   /**
56    * Configuration section to use.
57    */
58   char *section;
59
60   /**
61    * API of the transport as returned by the plugin's
62    * initialization function.
63    */
64   struct GNUNET_DATACACHE_PluginFunctions *api;
65
66   /**
67    * Short name for the plugin (i.e. "sqlite").
68    */
69   char *short_name;
70
71   /**
72    * Name of the library (i.e. "gnunet_plugin_datacache_sqlite").
73    */
74   char *lib_name;
75
76   /**
77    * Name for the bloom filter file.
78    */
79   char *bloom_name;
80
81   /**
82    * Environment provided to our plugin.
83    */
84   struct GNUNET_DATACACHE_PluginEnvironment env;
85
86   /**
87    * How much space is in use right now?
88    */
89   unsigned long long utilization;
90
91 };
92
93
94 /**
95  * Function called by plugins to notify the datacache
96  * about content deletions.
97  *
98  * @param cls closure
99  * @param key key of the content that was deleted
100  * @param size number of bytes that were made available
101  */
102 static void
103 env_delete_notify (void *cls,
104                    const struct GNUNET_HashCode *key,
105                    size_t size)
106 {
107   struct GNUNET_DATACACHE_Handle *h = cls;
108
109   LOG (GNUNET_ERROR_TYPE_DEBUG,
110        "Content under key `%s' discarded\n",
111        GNUNET_h2s (key));
112   GNUNET_assert (h->utilization >= size);
113   h->utilization -= size;
114   GNUNET_CONTAINER_bloomfilter_remove (h->filter,
115                                        key);
116   GNUNET_STATISTICS_update (h->stats,
117                             gettext_noop ("# bytes stored"),
118                             - (long long) size,
119                             GNUNET_NO);
120   GNUNET_STATISTICS_update (h->stats,
121                             gettext_noop ("# items stored"),
122                             -1,
123                             GNUNET_NO);
124 }
125
126
127 /**
128  * Create a data cache.
129  *
130  * @param cfg configuration to use
131  * @param section section in the configuration that contains our options
132  * @return handle to use to access the service
133  */
134 struct GNUNET_DATACACHE_Handle *
135 GNUNET_DATACACHE_create (const struct GNUNET_CONFIGURATION_Handle *cfg,
136                          const char *section)
137 {
138   unsigned int bf_size;
139   unsigned long long quota;
140   struct GNUNET_DATACACHE_Handle *ret;
141   char *libname;
142   char *name;
143
144   if (GNUNET_OK !=
145       GNUNET_CONFIGURATION_get_value_size (cfg,
146                                            section,
147                                            "QUOTA",
148                                            &quota))
149   {
150     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
151                                section,
152                                "QUOTA");
153     return NULL;
154   }
155   if (GNUNET_OK !=
156       GNUNET_CONFIGURATION_get_value_string (cfg,
157                                              section,
158                                              "DATABASE",
159                                              &name))
160   {
161     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
162                                section,
163                                "DATABASE");
164     return NULL;
165   }
166   bf_size = quota / 32;         /* 8 bit per entry, 1 bit per 32 kb in DB */
167
168   ret = GNUNET_new (struct GNUNET_DATACACHE_Handle);
169
170   if (GNUNET_YES !=
171       GNUNET_CONFIGURATION_get_value_yesno (cfg,
172                                             section,
173                                             "DISABLE_BF"))
174   {
175     if (GNUNET_YES !=
176         GNUNET_CONFIGURATION_get_value_yesno (cfg,
177                                               section,
178                                               "DISABLE_BF_RC"))
179     {
180       ret->bloom_name = GNUNET_DISK_mktemp ("gnunet-datacachebloom");
181     }
182     if (NULL != ret->bloom_name)
183     {
184       ret->filter = GNUNET_CONTAINER_bloomfilter_load (ret->bloom_name,
185                                                        quota / 1024,     /* 8 bit per entry in DB, expect 1k entries */
186                                                        5);
187     }
188     if (NULL == ret->filter)
189     {
190       ret->filter = GNUNET_CONTAINER_bloomfilter_init (NULL,
191                                                        bf_size,
192                                                        5); /* approx. 3% false positives at max use */
193     }
194   }
195   ret->stats = GNUNET_STATISTICS_create ("datacache", cfg);
196   ret->section = GNUNET_strdup (section);
197   ret->env.cfg = cfg;
198   ret->env.delete_notify = &env_delete_notify;
199   ret->env.section = ret->section;
200   ret->env.cls = ret;
201   ret->env.delete_notify = &env_delete_notify;
202   ret->env.quota = quota;
203   LOG (GNUNET_ERROR_TYPE_INFO,
204        _("Loading `%s' datacache plugin\n"),
205        name);
206   GNUNET_asprintf (&libname,
207                    "libgnunet_plugin_datacache_%s",
208                    name);
209   ret->short_name = name;
210   ret->lib_name = libname;
211   ret->api = GNUNET_PLUGIN_load (libname, &ret->env);
212   if (ret->api == NULL)
213   {
214     LOG (GNUNET_ERROR_TYPE_ERROR,
215          _("Failed to load datacache plugin for `%s'\n"),
216          name);
217     GNUNET_DATACACHE_destroy (ret);
218     return NULL;
219   }
220   return ret;
221 }
222
223
224 /**
225  * Destroy a data cache (and free associated resources).
226  *
227  * @param h handle to the datastore
228  */
229 void
230 GNUNET_DATACACHE_destroy (struct GNUNET_DATACACHE_Handle *h)
231 {
232   if (NULL != h->filter)
233     GNUNET_CONTAINER_bloomfilter_free (h->filter);
234   if (NULL != h->api)
235     GNUNET_break (NULL ==
236                   GNUNET_PLUGIN_unload (h->lib_name,
237                                         h->api));
238   GNUNET_free (h->lib_name);
239   GNUNET_free (h->short_name);
240   GNUNET_free (h->section);
241   if (NULL != h->bloom_name)
242   {
243     if (0 != UNLINK (h->bloom_name))
244       GNUNET_log_from_strerror_file (GNUNET_ERROR_TYPE_WARNING,
245                                      "datacache",
246                                      "unlink",
247                                      h->bloom_name);
248     GNUNET_free (h->bloom_name);
249   }
250   GNUNET_STATISTICS_destroy (h->stats,
251                              GNUNET_NO);
252   GNUNET_free (h);
253 }
254
255
256 /**
257  * Store an item in the datastore.
258  *
259  * @param h handle to the datacache
260  * @param key key to store data under
261  * @param xor_distance distance of @a key to our PID
262  * @param data_size number of bytes in @a data
263  * @param data data to store
264  * @param type type of the value
265  * @param discard_time when to discard the value in any case
266  * @param path_info_len number of entries in @a path_info
267  * @param path_info a path through the network
268  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error, #GNUNET_NO if duplicate
269  */
270 int
271 GNUNET_DATACACHE_put (struct GNUNET_DATACACHE_Handle *h,
272                       const struct GNUNET_HashCode *key,
273                       uint32_t xor_distance,
274                       size_t data_size,
275                       const char *data,
276                       enum GNUNET_BLOCK_Type type,
277                       struct GNUNET_TIME_Absolute discard_time,
278                       unsigned int path_info_len,
279                       const struct GNUNET_PeerIdentity *path_info)
280 {
281   ssize_t used;
282
283   used = h->api->put (h->api->cls,
284                       key,
285                       xor_distance,
286                       data_size,
287                       data,
288                       type,
289                       discard_time,
290                       path_info_len,
291                       path_info);
292   if (-1 == used)
293   {
294     GNUNET_break (0);
295     return GNUNET_SYSERR;
296   }
297   if (0 == used)
298   {
299     /* duplicate */
300     return GNUNET_NO;
301   }
302   LOG (GNUNET_ERROR_TYPE_DEBUG,
303        "Stored data under key `%s' in cache\n",
304        GNUNET_h2s (key));
305   if (NULL != h->filter)
306     GNUNET_CONTAINER_bloomfilter_add (h->filter,
307                                       key);
308   GNUNET_STATISTICS_update (h->stats,
309                             gettext_noop ("# bytes stored"),
310                             used,
311                             GNUNET_NO);
312   GNUNET_STATISTICS_update (h->stats,
313                             gettext_noop ("# items stored"),
314                             1,
315                             GNUNET_NO);
316   while (h->utilization + used > h->env.quota)
317     GNUNET_assert (GNUNET_OK ==
318                    h->api->del (h->api->cls));
319   h->utilization += used;
320   return GNUNET_OK;
321 }
322
323
324 /**
325  * Iterate over the results for a particular key
326  * in the datacache.
327  *
328  * @param h handle to the datacache
329  * @param key what to look up
330  * @param type entries of which type are relevant?
331  * @param iter maybe NULL (to just count)
332  * @param iter_cls closure for @a iter
333  * @return the number of results found
334  */
335 unsigned int
336 GNUNET_DATACACHE_get (struct GNUNET_DATACACHE_Handle *h,
337                       const struct GNUNET_HashCode *key,
338                       enum GNUNET_BLOCK_Type type,
339                       GNUNET_DATACACHE_Iterator iter,
340                       void *iter_cls)
341 {
342   GNUNET_STATISTICS_update (h->stats,
343                             gettext_noop ("# requests received"),
344                             1,
345                             GNUNET_NO);
346   LOG (GNUNET_ERROR_TYPE_DEBUG,
347        "Processing request for key `%s'\n",
348        GNUNET_h2s (key));
349   if ( (NULL != h->filter) &&
350        (GNUNET_OK != GNUNET_CONTAINER_bloomfilter_test (h->filter, key)) )
351   {
352     GNUNET_STATISTICS_update (h->stats,
353                               gettext_noop ("# requests filtered by bloom filter"),
354                               1,
355                               GNUNET_NO);
356     LOG (GNUNET_ERROR_TYPE_DEBUG,
357          "Bloomfilter filters request for key `%s'\n",
358          GNUNET_h2s (key));
359     return 0;                   /* can not be present */
360   }
361   return h->api->get (h->api->cls,
362                       key,
363                       type,
364                       iter,
365                       iter_cls);
366 }
367
368
369 /**
370  * Obtain a random element from the datacache.
371  *
372  * @param h handle to the datacache
373  * @param iter maybe NULL (to just count)
374  * @param iter_cls closure for @a iter
375  * @return the number of results found (zero or 1)
376  */
377 unsigned int
378 GNUNET_DATACACHE_get_random (struct GNUNET_DATACACHE_Handle *h,
379                              GNUNET_DATACACHE_Iterator iter,
380                              void *iter_cls)
381 {
382   GNUNET_STATISTICS_update (h->stats,
383                             gettext_noop ("# requests for random value received"),
384                             1,
385                             GNUNET_NO);
386   LOG (GNUNET_ERROR_TYPE_DEBUG,
387        "Processing request for random value\n");
388   return h->api->get_random (h->api->cls,
389                              iter,
390                              iter_cls);
391 }
392
393
394 /**
395  * Iterate over the results that are "close" to a particular key in
396  * the datacache.  "close" is defined as numerically larger than @a
397  * key (when interpreted as a circular address space), with small
398  * distance.
399  *
400  * @param h handle to the datacache
401  * @param key area of the keyspace to look into
402  * @param num_results number of results that should be returned to @a iter
403  * @param iter maybe NULL (to just count)
404  * @param iter_cls closure for @a iter
405  * @return the number of results found
406  */
407 unsigned int
408 GNUNET_DATACACHE_get_closest (struct GNUNET_DATACACHE_Handle *h,
409                               const struct GNUNET_HashCode *key,
410                               unsigned int num_results,
411                               GNUNET_DATACACHE_Iterator iter,
412                               void *iter_cls)
413 {
414   GNUNET_STATISTICS_update (h->stats,
415                             gettext_noop ("# proximity search requests received"),
416                             1,
417                             GNUNET_NO);
418   LOG (GNUNET_ERROR_TYPE_DEBUG,
419        "Processing proximity search at `%s'\n",
420        GNUNET_h2s (key));
421   return h->api->get_closest (h->api->cls,
422                               key,
423                               num_results,
424                               iter,
425                               iter_cls);
426 }
427
428
429 /* end of datacache.c */