error handling
[oweals/gnunet.git] / src / fs / fs_test_lib.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010, 2011, 2012 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 /**
22  * @file fs/fs_test_lib.c
23  * @brief library routines for testing FS publishing and downloading;
24  *        this code is limited to flat files
25  *        and no keywords (those functions can be tested with
26  *        single-peer setups; this is for testing routing).
27  * @author Christian Grothoff
28  */
29 #include "platform.h"
30 #include "fs_api.h"
31 #include "fs_test_lib.h"
32
33
34 #define CONTENT_LIFETIME GNUNET_TIME_UNIT_HOURS
35
36
37 /**
38  * Handle for a publishing operation started for testing FS.
39  */
40 struct TestPublishOperation
41 {
42   /**
43    * Handle for the operation to connect to the peer's 'fs' service.
44    */
45   struct GNUNET_TESTBED_Operation *fs_op;
46
47   /**
48    * Handle to the file sharing context using this daemon.
49    */
50   struct GNUNET_FS_Handle *fs;
51
52   /**
53    * Function to call when upload is done.
54    */
55   GNUNET_FS_TEST_UriContinuation publish_cont;
56
57   /**
58    * Closure for publish_cont.
59    */
60   void *publish_cont_cls;
61
62   /**
63    * Task to abort publishing (timeout).
64    */
65   struct GNUNET_SCHEDULER_Task *publish_timeout_task;
66
67   /**
68    * Seed for file generation.
69    */
70   uint32_t publish_seed;
71
72   /**
73    * Context for current publishing operation.
74    */
75   struct GNUNET_FS_PublishContext *publish_context;
76
77   /**
78    * Result URI.
79    */
80   struct GNUNET_FS_Uri *publish_uri;
81
82   /**
83    * Name of the temporary file used, or NULL for none.
84    */
85   char *publish_tmp_file;
86
87   /**
88    * Size of the file.
89    */
90   uint64_t size;
91
92   /**
93    * Anonymity level used.
94    */
95   uint32_t anonymity;
96
97   /**
98    * Verbosity level of the current operation.
99    */
100   unsigned int verbose;
101
102   /**
103    * Are we testing indexing? (YES: index, NO: insert, SYSERR: simulate)
104    */
105   int do_index;
106 };
107
108
109 /**
110  * Handle for a download operation started for testing FS.
111  */
112 struct TestDownloadOperation
113 {
114   /**
115    * Handle for the operation to connect to the peer's 'fs' service.
116    */
117   struct GNUNET_TESTBED_Operation *fs_op;
118
119   /**
120    * Handle to the file sharing context using this daemon.
121    */
122   struct GNUNET_FS_Handle *fs;
123
124   /**
125    * Handle to the daemon via testing.
126    */
127   struct GNUNET_TESTING_Daemon *daemon;
128
129   /**
130    * Function to call when download is done.
131    */
132   GNUNET_SCHEDULER_TaskCallback download_cont;
133
134   /**
135    * Closure for download_cont.
136    */
137   void *download_cont_cls;
138
139   /**
140    * URI to download.
141    */
142   struct GNUNET_FS_Uri *uri;
143
144   /**
145    * Task to abort downloading (timeout).
146    */
147   struct GNUNET_SCHEDULER_Task *download_timeout_task;
148
149   /**
150    * Context for current download operation.
151    */
152   struct GNUNET_FS_DownloadContext *download_context;
153
154   /**
155    * Size of the file.
156    */
157   uint64_t size;
158
159   /**
160    * Anonymity level used.
161    */
162   uint32_t anonymity;
163
164   /**
165    * Seed for download verification.
166    */
167   uint32_t download_seed;
168
169   /**
170    * Verbosity level of the current operation.
171    */
172   unsigned int verbose;
173 };
174
175
176 /**
177  * Task scheduled to report on the completion of our publish operation.
178  *
179  * @param cls the publish operation context
180  * @param tc scheduler context (unused)
181  */
182 static void
183 report_uri (void *cls)
184 {
185   struct TestPublishOperation *po = cls;
186
187   GNUNET_FS_publish_stop (po->publish_context);
188   GNUNET_TESTBED_operation_done (po->fs_op);
189   po->publish_cont (po->publish_cont_cls,
190                     po->publish_uri,
191                     (GNUNET_YES == po->do_index)
192                     ? po->publish_tmp_file
193                     : NULL);
194   GNUNET_FS_uri_destroy (po->publish_uri);
195   if ((GNUNET_YES != po->do_index) &&
196       (NULL != po->publish_tmp_file))
197     (void) GNUNET_DISK_directory_remove (po->publish_tmp_file);
198   GNUNET_free_non_null (po->publish_tmp_file);
199   GNUNET_free (po);
200 }
201
202
203 /**
204  * Task scheduled to run when publish operation times out.
205  *
206  * @param cls the publish operation context
207  */
208 static void
209 publish_timeout (void *cls)
210 {
211   struct TestPublishOperation *po = cls;
212
213   po->publish_timeout_task = NULL;
214   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
215               "Timeout while trying to publish data\n");
216   GNUNET_TESTBED_operation_done (po->fs_op);
217   GNUNET_FS_publish_stop (po->publish_context);
218   po->publish_cont (po->publish_cont_cls, NULL, NULL);
219   (void) GNUNET_DISK_directory_remove (po->publish_tmp_file);
220   GNUNET_free_non_null (po->publish_tmp_file);
221   GNUNET_free (po);
222 }
223
224
225 /**
226  * Progress callback for file-sharing events while publishing.
227  *
228  * @param cls the publish operation context
229  * @param info information about the event
230  */
231 static void *
232 publish_progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
233 {
234   struct TestPublishOperation *po = cls;
235
236   switch (info->status)
237   {
238   case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
239     GNUNET_SCHEDULER_cancel (po->publish_timeout_task);
240     po->publish_timeout_task = NULL;
241     po->publish_uri =
242       GNUNET_FS_uri_dup (info->value.publish.specifics.completed.chk_uri);
243     GNUNET_SCHEDULER_add_now (&report_uri,
244                               po);
245     break;
246
247   case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
248     if (po->verbose)
249       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Publishing at %llu/%llu bytes\n",
250                   (unsigned long long) info->value.publish.completed,
251                   (unsigned long long) info->value.publish.size);
252     break;
253
254   case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
255     break;
256
257   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
258     if (po->verbose)
259       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Download at %llu/%llu bytes\n",
260                   (unsigned long long) info->value.download.completed,
261                   (unsigned long long) info->value.download.size);
262     break;
263
264   default:
265     break;
266   }
267   return NULL;
268 }
269
270
271 /**
272  * Generate test data for publishing test.
273  *
274  * @param cls pointer to uint32_t with publishing seed
275  * @param offset offset to generate data for
276  * @param max maximum number of bytes to generate
277  * @param buf where to write generated data
278  * @param emsg where to store error message (unused)
279  * @return number of bytes written to buf
280  */
281 static size_t
282 file_generator (void *cls,
283                 uint64_t offset,
284                 size_t max,
285                 void *buf,
286                 char **emsg)
287 {
288   uint32_t *publish_seed = cls;
289   uint64_t pos;
290   uint8_t *cbuf = buf;
291   int mod;
292
293   if (emsg != NULL)
294     *emsg = NULL;
295   if (buf == NULL)
296     return 0;
297   for (pos = 0; pos < 8; pos++)
298     cbuf[pos] = (uint8_t) (offset >> pos * 8);
299   for (pos = 8; pos < max; pos++)
300   {
301     mod = (255 - (offset / 1024 / 32));
302     if (mod == 0)
303       mod = 1;
304     cbuf[pos] = (uint8_t) ((offset * (*publish_seed)) % mod);
305   }
306   return max;
307 }
308
309
310 /**
311  * Connect adapter for publishing operation.
312  *
313  * @param cls the 'struct TestPublishOperation'
314  * @param cfg configuration of the peer to connect to; will be available until
315  *          GNUNET_TESTBED_operation_done() is called on the operation returned
316  *          from GNUNET_TESTBED_service_connect()
317  * @return service handle to return in 'op_result', NULL on error
318  */
319 static void *
320 publish_connect_adapter (void *cls,
321                          const struct GNUNET_CONFIGURATION_Handle *cfg)
322 {
323   struct TestPublishOperation *po = cls;
324
325   return GNUNET_FS_start (cfg,
326                           "fs-test-publish",
327                           &publish_progress_cb, po,
328                           GNUNET_FS_FLAGS_NONE,
329                           GNUNET_FS_OPTIONS_END);
330 }
331
332
333 /**
334  * Adapter function called to destroy connection to file-sharing service.
335  *
336  * @param cls the 'struct GNUNET_FS_Handle'
337  * @param op_result unused (different for publish/download!)
338  */
339 static void
340 fs_disconnect_adapter (void *cls,
341                        void *op_result)
342 {
343   struct GNUNET_FS_Handle *fs = op_result;
344
345   GNUNET_FS_stop (fs);
346 }
347
348
349 /**
350  * Callback to be called when testbed has connected to the fs service
351  *
352  * @param cls the 'struct TestPublishOperation'
353  * @param op the operation that has been finished
354  * @param ca_result the 'struct GNUNET_FS_Handle ' (NULL on error)
355  * @param emsg error message in case the operation has failed; will be NULL if
356  *          operation has executed successfully.
357  */
358 static void
359 publish_fs_connect_complete_cb (void *cls,
360                                 struct GNUNET_TESTBED_Operation *op,
361                                 void *ca_result,
362                                 const char *emsg)
363 {
364   struct TestPublishOperation *po = cls;
365   struct GNUNET_FS_FileInformation *fi;
366   struct GNUNET_DISK_FileHandle *fh;
367   char *em;
368   uint64_t off;
369   char buf[DBLOCK_SIZE];
370   size_t bsize;
371   struct GNUNET_FS_BlockOptions bo;
372
373   if (NULL == ca_result)
374   {
375     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
376                 "Failed to connect to FS for publishing: %s\n", emsg);
377     po->publish_cont (po->publish_cont_cls,
378                       NULL, NULL);
379     GNUNET_TESTBED_operation_done (po->fs_op);
380     GNUNET_free (po);
381     return;
382   }
383   po->fs = ca_result;
384
385   bo.expiration_time = GNUNET_TIME_relative_to_absolute (CONTENT_LIFETIME);
386   bo.anonymity_level = po->anonymity;
387   bo.content_priority = 42;
388   bo.replication_level = 1;
389   if (GNUNET_YES == po->do_index)
390   {
391     po->publish_tmp_file = GNUNET_DISK_mktemp ("fs-test-publish-index");
392     GNUNET_assert (po->publish_tmp_file != NULL);
393     fh = GNUNET_DISK_file_open (po->publish_tmp_file,
394                                 GNUNET_DISK_OPEN_WRITE
395                                 | GNUNET_DISK_OPEN_CREATE,
396                                 GNUNET_DISK_PERM_USER_READ
397                                 | GNUNET_DISK_PERM_USER_WRITE);
398     GNUNET_assert (NULL != fh);
399     off = 0;
400     while (off < po->size)
401     {
402       bsize = GNUNET_MIN (sizeof(buf), po->size - off);
403       emsg = NULL;
404       GNUNET_assert (bsize == file_generator (&po->publish_seed, off, bsize,
405                                               buf, &em));
406       GNUNET_assert (em == NULL);
407       GNUNET_assert (bsize == GNUNET_DISK_file_write (fh, buf, bsize));
408       off += bsize;
409     }
410     GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
411     fi = GNUNET_FS_file_information_create_from_file (po->fs, po,
412                                                       po->publish_tmp_file,
413                                                       NULL, NULL, po->do_index,
414                                                       &bo);
415     GNUNET_assert (NULL != fi);
416   }
417   else
418   {
419     fi = GNUNET_FS_file_information_create_from_reader (po->fs, po,
420                                                         po->size,
421                                                         &file_generator,
422                                                         &po->publish_seed,
423                                                         NULL, NULL,
424                                                         po->do_index, &bo);
425     GNUNET_assert (NULL != fi);
426   }
427   po->publish_context =
428     GNUNET_FS_publish_start (po->fs, fi, NULL, NULL, NULL,
429                              GNUNET_FS_PUBLISH_OPTION_NONE);
430 }
431
432
433 /**
434  * Publish a file at the given peer.
435  *
436  * @param peer where to publish
437  * @param timeout if this operation cannot be completed within the
438  *                given period, call the continuation with an error code
439  * @param anonymity option for publication
440  * @param do_index GNUNET_YES for index, GNUNET_NO for insertion,
441  *                GNUNET_SYSERR for simulation
442  * @param size size of the file to publish
443  * @param seed seed to use for file generation
444  * @param verbose how verbose to be in reporting
445  * @param cont function to call when done
446  * @param cont_cls closure for cont
447  */
448 void
449 GNUNET_FS_TEST_publish (struct GNUNET_TESTBED_Peer *peer,
450                         struct GNUNET_TIME_Relative timeout, uint32_t anonymity,
451                         int do_index, uint64_t size, uint32_t seed,
452                         unsigned int verbose,
453                         GNUNET_FS_TEST_UriContinuation cont, void *cont_cls)
454 {
455   struct TestPublishOperation *po;
456
457   po = GNUNET_new (struct TestPublishOperation);
458   po->publish_cont = cont;
459   po->publish_cont_cls = cont_cls;
460   po->publish_seed = seed;
461   po->anonymity = anonymity;
462   po->size = size;
463   po->verbose = verbose;
464   po->do_index = do_index;
465   po->fs_op = GNUNET_TESTBED_service_connect (po,
466                                               peer,
467                                               "fs",
468                                               &publish_fs_connect_complete_cb,
469                                               po,
470                                               &publish_connect_adapter,
471                                               &fs_disconnect_adapter,
472                                               po);
473   po->publish_timeout_task =
474     GNUNET_SCHEDULER_add_delayed (timeout, &publish_timeout, po);
475 }
476
477
478 /* ************************** download ************************ */
479
480
481 /**
482  * Task scheduled to run when download operation times out.
483  *
484  * @param cls the download operation context
485  */
486 static void
487 download_timeout (void *cls)
488 {
489   struct TestDownloadOperation *dop = cls;
490
491   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
492               "Timeout while trying to download file\n");
493   dop->download_timeout_task = NULL;
494   GNUNET_FS_download_stop (dop->download_context,
495                            GNUNET_YES);
496   GNUNET_SCHEDULER_add_now (dop->download_cont,
497                             dop->download_cont_cls);
498   GNUNET_TESTBED_operation_done (dop->fs_op);
499   GNUNET_FS_uri_destroy (dop->uri);
500   GNUNET_free (dop);
501 }
502
503
504 /**
505  * Task scheduled to report on the completion of our download operation.
506  *
507  * @param cls the download operation context
508  */
509 static void
510 report_success (void *cls)
511 {
512   struct TestDownloadOperation *dop = cls;
513
514   GNUNET_FS_download_stop (dop->download_context,
515                            GNUNET_YES);
516   GNUNET_SCHEDULER_add_now (dop->download_cont,
517                             dop->download_cont_cls);
518   GNUNET_TESTBED_operation_done (dop->fs_op);
519   GNUNET_FS_uri_destroy (dop->uri);
520   GNUNET_free (dop);
521 }
522
523
524 /**
525  * Progress callback for file-sharing events while downloading.
526  *
527  * @param cls the download operation context
528  * @param info information about the event
529  */
530 static void *
531 download_progress_cb (void *cls,
532                       const struct GNUNET_FS_ProgressInfo *info)
533 {
534   struct TestDownloadOperation *dop = cls;
535
536   switch (info->status)
537   {
538   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
539     if (dop->verbose)
540       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
541                   "Download at %llu/%llu bytes\n",
542                   (unsigned long long) info->value.download.completed,
543                   (unsigned long long) info->value.download.size);
544     break;
545
546   case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
547     GNUNET_SCHEDULER_cancel (dop->download_timeout_task);
548     dop->download_timeout_task = NULL;
549     GNUNET_SCHEDULER_add_now (&report_success, dop);
550     break;
551
552   case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
553   case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
554     break;
555
556   /* FIXME: monitor data correctness during download progress */
557   /* FIXME: do performance reports given sufficient verbosity */
558   /* FIXME: advance timeout task to "immediate" on error */
559   default:
560     break;
561   }
562   return NULL;
563 }
564
565
566 /**
567  * Connect adapter for download operation.
568  *
569  * @param cls the 'struct TestDownloadOperation'
570  * @param cfg configuration of the peer to connect to; will be available until
571  *          GNUNET_TESTBED_operation_done() is called on the operation returned
572  *          from GNUNET_TESTBED_service_connect()
573  * @return service handle to return in 'op_result', NULL on error
574  */
575 static void *
576 download_connect_adapter (void *cls,
577                           const struct GNUNET_CONFIGURATION_Handle *cfg)
578 {
579   struct TestPublishOperation *po = cls;
580
581   return GNUNET_FS_start (cfg,
582                           "fs-test-download",
583                           &download_progress_cb, po,
584                           GNUNET_FS_FLAGS_NONE,
585                           GNUNET_FS_OPTIONS_END);
586 }
587
588
589 /**
590  * Callback to be called when testbed has connected to the fs service
591  *
592  * @param cls the 'struct TestPublishOperation'
593  * @param op the operation that has been finished
594  * @param ca_result the 'struct GNUNET_FS_Handle ' (NULL on error)
595  * @param emsg error message in case the operation has failed; will be NULL if
596  *          operation has executed successfully.
597  */
598 static void
599 download_fs_connect_complete_cb (void *cls,
600                                  struct GNUNET_TESTBED_Operation *op,
601                                  void *ca_result,
602                                  const char *emsg)
603 {
604   struct TestDownloadOperation *dop = cls;
605
606   dop->fs = ca_result;
607   GNUNET_assert (NULL != dop->fs);
608   dop->download_context =
609     GNUNET_FS_download_start (dop->fs, dop->uri, NULL, NULL, NULL, 0, dop->size,
610                               dop->anonymity, GNUNET_FS_DOWNLOAD_OPTION_NONE,
611                               NULL, NULL);
612 }
613
614
615 /**
616  * Perform test download.
617  *
618  * @param peer which peer to download from
619  * @param timeout if this operation cannot be completed within the
620  *                given period, call the continuation with an error code
621  * @param anonymity option for download
622  * @param seed used for file validation
623  * @param uri URI of file to download (CHK/LOC only)
624  * @param verbose how verbose to be in reporting
625  * @param cont function to call when done
626  * @param cont_cls closure for cont
627  */
628 void
629 GNUNET_FS_TEST_download (struct GNUNET_TESTBED_Peer *peer,
630                          struct GNUNET_TIME_Relative timeout,
631                          uint32_t anonymity, uint32_t seed,
632                          const struct GNUNET_FS_Uri *uri, unsigned int verbose,
633                          GNUNET_SCHEDULER_TaskCallback cont, void *cont_cls)
634 {
635   struct TestDownloadOperation *dop;
636
637   dop = GNUNET_new (struct TestDownloadOperation);
638   dop->uri = GNUNET_FS_uri_dup (uri);
639   dop->size = GNUNET_FS_uri_chk_get_file_size (uri);
640   dop->verbose = verbose;
641   dop->anonymity = anonymity;
642   dop->download_cont = cont;
643   dop->download_cont_cls = cont_cls;
644   dop->download_seed = seed;
645
646   dop->fs_op = GNUNET_TESTBED_service_connect (dop,
647                                                peer,
648                                                "fs",
649                                                &download_fs_connect_complete_cb,
650                                                dop,
651                                                &download_connect_adapter,
652                                                &fs_disconnect_adapter,
653                                                dop);
654   dop->download_timeout_task =
655     GNUNET_SCHEDULER_add_delayed (timeout, &download_timeout, dop);
656 }
657
658
659 /* end of fs_test_lib.c */