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