first step to remove plibc
[oweals/gnunet.git] / src / fs / fs_download.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-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  * @file fs/fs_download.c
22  * @brief download methods
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_constants.h"
27 #include "gnunet_fs_service.h"
28 #include "fs_api.h"
29 #include "fs_tree.h"
30
31
32 /**
33  * Determine if the given download (options and meta data) should cause
34  * use to try to do a recursive download.
35  */
36 static int
37 is_recursive_download (struct GNUNET_FS_DownloadContext *dc)
38 {
39   return (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) &&
40          ((GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ||
41           ((NULL == dc->meta) &&
42            ((NULL == dc->filename) ||
43             ((strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
44              (NULL != strstr (dc->filename + strlen (dc->filename) -
45                                 strlen (GNUNET_FS_DIRECTORY_EXT),
46                               GNUNET_FS_DIRECTORY_EXT))))));
47 }
48
49
50 /**
51  * We're storing the IBLOCKS after the DBLOCKS on disk (so that we
52  * only have to truncate the file once we're done).
53  *
54  * Given the offset of a block (with respect to the DBLOCKS) and its
55  * depth, return the offset where we would store this block in the
56  * file.
57  *
58  * @param fsize overall file size
59  * @param off offset of the block in the file
60  * @param depth depth of the block in the tree, 0 for DBLOCK
61  * @return off for DBLOCKS (depth == treedepth),
62  *         otherwise an offset past the end
63  *         of the file that does not overlap
64  *         with the range for any other block
65  */
66 static uint64_t
67 compute_disk_offset (uint64_t fsize, uint64_t off, unsigned int depth)
68 {
69   unsigned int i;
70   uint64_t lsize; /* what is the size of all IBlocks for depth "i"? */
71   uint64_t loff; /* where do IBlocks for depth "i" start? */
72   unsigned int ioff; /* which IBlock corresponds to "off" at depth "i"? */
73
74   if (0 == depth)
75     return off;
76   /* first IBlocks start at the end of file, rounded up
77    * to full DBLOCK_SIZE */
78   loff = ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * DBLOCK_SIZE;
79   lsize =
80     ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * sizeof (struct ContentHashKey);
81   GNUNET_assert (0 == (off % DBLOCK_SIZE));
82   ioff = (off / DBLOCK_SIZE);
83   for (i = 1; i < depth; i++)
84   {
85     loff += lsize;
86     lsize = (lsize + CHK_PER_INODE - 1) / CHK_PER_INODE;
87     GNUNET_assert (lsize > 0);
88     GNUNET_assert (0 == (ioff % CHK_PER_INODE));
89     ioff /= CHK_PER_INODE;
90   }
91   return loff + ioff * sizeof (struct ContentHashKey);
92 }
93
94
95 /**
96  * Fill in all of the generic fields for a download event and call the
97  * callback.
98  *
99  * @param pi structure to fill in
100  * @param dc overall download context
101  */
102 void
103 GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
104                                  struct GNUNET_FS_DownloadContext *dc)
105 {
106   pi->value.download.dc = dc;
107   pi->value.download.cctx = dc->client_info;
108   pi->value.download.pctx =
109     (NULL == dc->parent) ? NULL : dc->parent->client_info;
110   pi->value.download.sctx =
111     (NULL == dc->search) ? NULL : dc->search->client_info;
112   pi->value.download.uri = dc->uri;
113   pi->value.download.filename = dc->filename;
114   pi->value.download.size = dc->length;
115   /* FIXME: Fix duration calculation to account for pauses */
116   pi->value.download.duration =
117     GNUNET_TIME_absolute_get_duration (dc->start_time);
118   pi->value.download.completed = dc->completed;
119   pi->value.download.anonymity = dc->anonymity;
120   pi->value.download.eta =
121     GNUNET_TIME_calculate_eta (dc->start_time, dc->completed, dc->length);
122   pi->value.download.is_active = (NULL == dc->mq) ? GNUNET_NO : GNUNET_YES;
123   pi->fsh = dc->h;
124   if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
125     dc->client_info = dc->h->upcb (dc->h->upcb_cls, pi);
126   else
127     dc->client_info = GNUNET_FS_search_probe_progress_ (NULL, pi);
128 }
129
130
131 /**
132  * Closure for iterator processing results.
133  */
134 struct ProcessResultClosure
135 {
136
137   /**
138    * Hash of data.
139    */
140   struct GNUNET_HashCode query;
141
142   /**
143    * Data found in P2P network.
144    */
145   const void *data;
146
147   /**
148    * Our download context.
149    */
150   struct GNUNET_FS_DownloadContext *dc;
151
152   /**
153    * When did we last transmit the request?
154    */
155   struct GNUNET_TIME_Absolute last_transmission;
156
157   /**
158    * Number of bytes in data.
159    */
160   size_t size;
161
162   /**
163    * Type of data.
164    */
165   enum GNUNET_BLOCK_Type type;
166
167   /**
168    * Flag to indicate if this block should be stored on disk.
169    */
170   int do_store;
171
172   /**
173    * how much respect did we offer to get this reply?
174    */
175   uint32_t respect_offered;
176
177   /**
178    * how often did we transmit the query?
179    */
180   uint32_t num_transmissions;
181 };
182
183
184 /**
185  * Iterator over entries in the pending requests in the 'active' map for the
186  * reply that we just got.
187  *
188  * @param cls closure (our 'struct ProcessResultClosure')
189  * @param key query for the given value / request
190  * @param value value in the hash map (a 'struct DownloadRequest')
191  * @return #GNUNET_YES (we should continue to iterate); unless serious error
192  */
193 static int
194 process_result_with_request (void *cls,
195                              const struct GNUNET_HashCode *key,
196                              void *value);
197
198
199 /**
200  * We've found a matching block without downloading it.
201  * Encrypt it and pass it to our "receive" function as
202  * if we had received it from the network.
203  *
204  * @param dc download in question
205  * @param chk request this relates to
206  * @param dr request details
207  * @param block plaintext data matching request
208  * @param len number of bytes in block
209  * @param do_store should we still store the block on disk?
210  * @return GNUNET_OK on success
211  */
212 static int
213 encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc,
214                         const struct ContentHashKey *chk,
215                         struct DownloadRequest *dr,
216                         const char *block,
217                         size_t len,
218                         int do_store)
219 {
220   struct ProcessResultClosure prc;
221   char enc[len];
222   struct GNUNET_CRYPTO_SymmetricSessionKey sk;
223   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
224   struct GNUNET_HashCode query;
225
226   GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv);
227   if (-1 == GNUNET_CRYPTO_symmetric_encrypt (block, len, &sk, &iv, enc))
228   {
229     GNUNET_break (0);
230     return GNUNET_SYSERR;
231   }
232   GNUNET_CRYPTO_hash (enc, len, &query);
233   if (0 != memcmp (&query, &chk->query, sizeof (struct GNUNET_HashCode)))
234   {
235     GNUNET_break_op (0);
236     return GNUNET_SYSERR;
237   }
238   GNUNET_log (
239     GNUNET_ERROR_TYPE_DEBUG,
240     "Matching %u byte block for `%s' at offset %llu already present, no need for download!\n",
241     (unsigned int) len,
242     dc->filename,
243     (unsigned long long) dr->offset);
244   /* already got it! */
245   prc.dc = dc;
246   prc.data = enc;
247   prc.size = len;
248   prc.type = (0 == dr->depth) ? GNUNET_BLOCK_TYPE_FS_DBLOCK
249                               : GNUNET_BLOCK_TYPE_FS_IBLOCK;
250   prc.query = chk->query;
251   prc.do_store = do_store;
252   prc.last_transmission = GNUNET_TIME_UNIT_FOREVER_ABS;
253   process_result_with_request (&prc, &chk->key, dr);
254   return GNUNET_OK;
255 }
256
257
258 /**
259  * We've lost our connection with the FS service.
260  * Re-establish it and re-transmit all of our
261  * pending requests.
262  *
263  * @param dc download context that is having trouble
264  */
265 static void
266 try_reconnect (struct GNUNET_FS_DownloadContext *dc);
267
268
269 /**
270  * We found an entry in a directory.  Check if the respective child
271  * already exists and if not create the respective child download.
272  *
273  * @param cls the parent download
274  * @param filename name of the file in the directory
275  * @param uri URI of the file (CHK or LOC)
276  * @param meta meta data of the file
277  * @param length number of bytes in data
278  * @param data contents of the file (or NULL if they were not inlined)
279  */
280 static void
281 trigger_recursive_download (void *cls,
282                             const char *filename,
283                             const struct GNUNET_FS_Uri *uri,
284                             const struct GNUNET_CONTAINER_MetaData *meta,
285                             size_t length,
286                             const void *data);
287
288
289 /**
290  * We're done downloading a directory.  Open the file and
291  * trigger all of the (remaining) child downloads.
292  *
293  * @param dc context of download that just completed
294  */
295 static void
296 full_recursive_download (struct GNUNET_FS_DownloadContext *dc)
297 {
298   size_t size;
299   uint64_t size64;
300   void *data;
301   struct GNUNET_DISK_FileHandle *h;
302   struct GNUNET_DISK_MapHandle *m;
303
304   size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri);
305   size = (size_t) size64;
306   if (size64 != (uint64_t) size)
307   {
308     GNUNET_log (
309       GNUNET_ERROR_TYPE_ERROR,
310       _ (
311         "Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n"));
312     return;
313   }
314   if (NULL != dc->filename)
315   {
316     h = GNUNET_DISK_file_open (dc->filename,
317                                GNUNET_DISK_OPEN_READ,
318                                GNUNET_DISK_PERM_NONE);
319   }
320   else
321   {
322     GNUNET_assert (NULL != dc->temp_filename);
323     h = GNUNET_DISK_file_open (dc->temp_filename,
324                                GNUNET_DISK_OPEN_READ,
325                                GNUNET_DISK_PERM_NONE);
326   }
327   if (NULL == h)
328     return; /* oops */
329   data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size);
330   if (NULL == data)
331   {
332     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
333                 _ ("Directory too large for system address space\n"));
334   }
335   else
336   {
337     if (GNUNET_OK !=
338         GNUNET_FS_directory_list_contents (size,
339                                            data,
340                                            0,
341                                            &trigger_recursive_download,
342                                            dc))
343     {
344       GNUNET_log (
345         GNUNET_ERROR_TYPE_WARNING,
346         _ (
347           "Failed to access full directroy contents of `%s' for recursive download\n"),
348         dc->filename);
349     }
350     GNUNET_DISK_file_unmap (m);
351   }
352   GNUNET_DISK_file_close (h);
353   if (NULL == dc->filename)
354   {
355     if (0 != unlink (dc->temp_filename))
356       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
357                                 "unlink",
358                                 dc->temp_filename);
359     GNUNET_free (dc->temp_filename);
360     dc->temp_filename = NULL;
361   }
362 }
363
364
365 /**
366  * Check if all child-downloads have completed (or trigger them if
367  * necessary) and once we're completely done, signal completion (and
368  * possibly recurse to parent).  This function MUST be called when the
369  * download of a file itself is done or when the download of a file is
370  * done and then later a direct child download has completed (and
371  * hence this download may complete itself).
372  *
373  * @param dc download to check for completion of children
374  */
375 static void
376 check_completed (struct GNUNET_FS_DownloadContext *dc)
377 {
378   struct GNUNET_FS_ProgressInfo pi;
379   struct GNUNET_FS_DownloadContext *pos;
380
381   /* first, check if we need to download children */
382   if (is_recursive_download (dc))
383     full_recursive_download (dc);
384   /* then, check if children are done already */
385   for (pos = dc->child_head; NULL != pos; pos = pos->next)
386   {
387     if ((NULL == pos->emsg) && (pos->completed < pos->length))
388       return; /* not done yet */
389     if ((NULL != pos->child_head) && (pos->has_finished != GNUNET_YES))
390       return; /* not transitively done yet */
391   }
392   /* All of our children are done, so mark this download done */
393   dc->has_finished = GNUNET_YES;
394   if (NULL != dc->job_queue)
395   {
396     GNUNET_FS_dequeue_ (dc->job_queue);
397     dc->job_queue = NULL;
398   }
399   if (NULL != dc->task)
400   {
401     GNUNET_SCHEDULER_cancel (dc->task);
402     dc->task = NULL;
403   }
404   if (NULL != dc->rfh)
405   {
406     GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
407     dc->rfh = NULL;
408   }
409   GNUNET_FS_download_sync_ (dc);
410
411   /* signal completion */
412   pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
413   GNUNET_FS_download_make_status_ (&pi, dc);
414
415   /* let parent know */
416   if (NULL != dc->parent)
417     check_completed (dc->parent);
418 }
419
420
421 /**
422  * We got a block of plaintext data (from the meta data).
423  * Try it for upward reconstruction of the data.  On success,
424  * the top-level block will move to state BRS_DOWNLOAD_UP.
425  *
426  * @param dc context for the download
427  * @param dr download request to match against
428  * @param data plaintext data, starting from the beginning of the file
429  * @param data_len number of bytes in data
430  */
431 static void
432 try_match_block (struct GNUNET_FS_DownloadContext *dc,
433                  struct DownloadRequest *dr,
434                  const char *data,
435                  size_t data_len)
436 {
437   struct GNUNET_FS_ProgressInfo pi;
438   unsigned int i;
439   char enc[DBLOCK_SIZE];
440   struct ContentHashKey chks[CHK_PER_INODE];
441   struct ContentHashKey in_chk;
442   struct GNUNET_CRYPTO_SymmetricSessionKey sk;
443   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
444   size_t dlen;
445   struct DownloadRequest *drc;
446   struct GNUNET_DISK_FileHandle *fh;
447   int complete;
448   const char *fn;
449   const char *odata;
450   size_t odata_len;
451
452   odata = data;
453   odata_len = data_len;
454   if (BRS_DOWNLOAD_UP == dr->state)
455     return;
456   if (dr->depth > 0)
457   {
458     if ((dc->offset > 0) ||
459         (dc->length < GNUNET_ntohll (dc->uri->data.chk.file_length)))
460     {
461       /* NOTE: this test is not tight, but should suffice; the issue
462          here is that 'dr->num_children' may inherently only specify a
463          smaller range than what is in the original file;
464          thus, reconstruction of (some) inner blocks will fail.
465          FIXME: we might eventually want to write a tighter test to
466          maximize the circumstances under which we do succeed with
467          IBlock reconstruction. (need good tests though). */
468       return;
469     }
470     complete = GNUNET_YES;
471     for (i = 0; i < dr->num_children; i++)
472     {
473       drc = dr->children[i];
474       try_match_block (dc, drc, data, data_len);
475       if (drc->state != BRS_RECONSTRUCT_META_UP)
476         complete = GNUNET_NO;
477       else
478         chks[i] = drc->chk;
479     }
480     if (GNUNET_YES != complete)
481       return;
482     data = (const char *) chks;
483     dlen = dr->num_children * sizeof (struct ContentHashKey);
484   }
485   else
486   {
487     if (dr->offset > data_len)
488       return; /* oops */
489     dlen = GNUNET_MIN (data_len - dr->offset, DBLOCK_SIZE);
490   }
491   GNUNET_CRYPTO_hash (&data[dr->offset], dlen, &in_chk.key);
492   GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv);
493   if (-1 ==
494       GNUNET_CRYPTO_symmetric_encrypt (&data[dr->offset], dlen, &sk, &iv, enc))
495   {
496     GNUNET_break (0);
497     return;
498   }
499   GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query);
500   switch (dr->state)
501   {
502   case BRS_INIT:
503     dr->chk = in_chk;
504     dr->state = BRS_RECONSTRUCT_META_UP;
505     break;
506   case BRS_CHK_SET:
507     if (0 != memcmp (&in_chk, &dr->chk, sizeof (struct ContentHashKey)))
508     {
509       /* other peer provided bogus meta data */
510       GNUNET_break_op (0);
511       break;
512     }
513     /* write block to disk */
514     fn = (NULL != dc->filename) ? dc->filename : dc->temp_filename;
515     if (NULL != fn)
516     {
517       fh = GNUNET_DISK_file_open (fn,
518                                   GNUNET_DISK_OPEN_READWRITE |
519                                     GNUNET_DISK_OPEN_CREATE |
520                                     GNUNET_DISK_OPEN_TRUNCATE,
521                                   GNUNET_DISK_PERM_USER_READ |
522                                     GNUNET_DISK_PERM_USER_WRITE |
523                                     GNUNET_DISK_PERM_GROUP_READ |
524                                     GNUNET_DISK_PERM_OTHER_READ);
525       if (NULL == fh)
526       {
527         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
528         GNUNET_asprintf (&dc->emsg,
529                          _ ("Failed to open file `%s' for writing"),
530                          fn);
531         GNUNET_DISK_file_close (fh);
532         dr->state = BRS_ERROR;
533         pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
534         pi.value.download.specifics.error.message = dc->emsg;
535         GNUNET_FS_download_make_status_ (&pi, dc);
536         return;
537       }
538       if (data_len != GNUNET_DISK_file_write (fh, odata, odata_len))
539       {
540         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "write", fn);
541         GNUNET_asprintf (&dc->emsg,
542                          _ ("Failed to open file `%s' for writing"),
543                          fn);
544         GNUNET_DISK_file_close (fh);
545         dr->state = BRS_ERROR;
546         pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
547         pi.value.download.specifics.error.message = dc->emsg;
548         GNUNET_FS_download_make_status_ (&pi, dc);
549         return;
550       }
551       GNUNET_DISK_file_close (fh);
552     }
553     /* signal success */
554     dr->state = BRS_DOWNLOAD_UP;
555     dc->completed = dc->length;
556     GNUNET_FS_download_sync_ (dc);
557     pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
558     pi.value.download.specifics.progress.data = data;
559     pi.value.download.specifics.progress.offset = 0;
560     pi.value.download.specifics.progress.data_len = dlen;
561     pi.value.download.specifics.progress.depth = 0;
562     pi.value.download.specifics.progress.respect_offered = 0;
563     pi.value.download.specifics.progress.block_download_duration =
564       GNUNET_TIME_UNIT_ZERO;
565     GNUNET_FS_download_make_status_ (&pi, dc);
566     if ((NULL != dc->filename) &&
567         (0 != truncate (dc->filename,
568                         GNUNET_ntohll (dc->uri->data.chk.file_length))))
569       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
570                                 "truncate",
571                                 dc->filename);
572     check_completed (dc);
573     break;
574   default:
575     /* how did we get here? */
576     GNUNET_break (0);
577     break;
578   }
579 }
580
581
582 /**
583  * Type of a function that libextractor calls for each
584  * meta data item found.  If we find full data meta data,
585  * call 'try_match_block' on it.
586  *
587  * @param cls our 'struct GNUNET_FS_DownloadContext*'
588  * @param plugin_name name of the plugin that produced this value;
589  *        special values can be used (i.e. '&lt;zlib&gt;' for zlib being
590  *        used in the main libextractor library and yielding
591  *        meta data).
592  * @param type libextractor-type describing the meta data
593  * @param format basic format information about data
594  * @param data_mime_type mime-type of data (not of the original file);
595  *        can be NULL (if mime-type is not known)
596  * @param data actual meta-data found
597  * @param data_len number of bytes in data
598  * @return 0 to continue extracting, 1 to abort
599  */
600 static int
601 match_full_data (void *cls,
602                  const char *plugin_name,
603                  enum EXTRACTOR_MetaType type,
604                  enum EXTRACTOR_MetaFormat format,
605                  const char *data_mime_type,
606                  const char *data,
607                  size_t data_len)
608 {
609   struct GNUNET_FS_DownloadContext *dc = cls;
610
611   if (EXTRACTOR_METATYPE_GNUNET_FULL_DATA != type)
612     return 0;
613   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
614               "Found %u bytes of FD!\n",
615               (unsigned int) data_len);
616   if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len)
617   {
618     GNUNET_break_op (0);
619     return 1; /* bogus meta data */
620   }
621   try_match_block (dc, dc->top_request, data, data_len);
622   return 1;
623 }
624
625
626 /**
627  * Set the state of the given download request to
628  * BRS_DOWNLOAD_UP and propagate it up the tree.
629  *
630  * @param dr download request that is done
631  */
632 static void
633 propagate_up (struct DownloadRequest *dr)
634 {
635   unsigned int i;
636
637   do
638   {
639     dr->state = BRS_DOWNLOAD_UP;
640     dr = dr->parent;
641     if (NULL == dr)
642       break;
643     for (i = 0; i < dr->num_children; i++)
644       if (dr->children[i]->state != BRS_DOWNLOAD_UP)
645         break;
646   } while (i == dr->num_children);
647 }
648
649
650 /**
651  * Try top-down reconstruction.  Before, the given request node
652  * must have the state BRS_CHK_SET.  Afterwards, more nodes may
653  * have that state or advanced to BRS_DOWNLOAD_DOWN or even
654  * BRS_DOWNLOAD_UP.  It is also possible to get BRS_ERROR on the
655  * top level.
656  *
657  * @param dc overall download this block belongs to
658  * @param dr block to reconstruct
659  */
660 static void
661 try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
662                              struct DownloadRequest *dr)
663 {
664   uint64_t off;
665   char block[DBLOCK_SIZE];
666   struct GNUNET_HashCode key;
667   uint64_t total;
668   size_t len;
669   unsigned int i;
670   struct DownloadRequest *drc;
671   uint64_t child_block_size;
672   const struct ContentHashKey *chks;
673   int up_done;
674
675   GNUNET_assert (NULL != dc->rfh);
676   GNUNET_assert (BRS_CHK_SET == dr->state);
677   total = GNUNET_FS_uri_chk_get_file_size (dc->uri);
678   GNUNET_assert (dr->depth < dc->treedepth);
679   len = GNUNET_FS_tree_calculate_block_size (total, dr->offset, dr->depth);
680   GNUNET_assert (len <= DBLOCK_SIZE);
681   off = compute_disk_offset (total, dr->offset, dr->depth);
682   if (dc->old_file_size < off + len)
683     return; /* failure */
684   if (off != GNUNET_DISK_file_seek (dc->rfh, off, GNUNET_DISK_SEEK_SET))
685   {
686     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "seek", dc->filename);
687     return; /* failure */
688   }
689   if (len != GNUNET_DISK_file_read (dc->rfh, block, len))
690   {
691     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "read", dc->filename);
692     return; /* failure */
693   }
694   GNUNET_CRYPTO_hash (block, len, &key);
695   if (0 != memcmp (&key, &dr->chk.key, sizeof (struct GNUNET_HashCode)))
696     return; /* mismatch */
697   if (GNUNET_OK !=
698       encrypt_existing_match (dc, &dr->chk, dr, block, len, GNUNET_NO))
699   {
700     /* hash matches but encrypted block does not, really bad */
701     dr->state = BRS_ERROR;
702     /* propagate up */
703     while (NULL != dr->parent)
704     {
705       dr = dr->parent;
706       dr->state = BRS_ERROR;
707     }
708     return;
709   }
710   /* block matches */
711   dr->state = BRS_DOWNLOAD_DOWN;
712
713   /* set CHKs for children */
714   up_done = GNUNET_YES;
715   chks = (const struct ContentHashKey *) block;
716   for (i = 0; i < dr->num_children; i++)
717   {
718     drc = dr->children[i];
719     GNUNET_assert (drc->offset >= dr->offset);
720     child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth);
721     GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);
722     if (BRS_INIT == drc->state)
723     {
724       drc->state = BRS_CHK_SET;
725       drc->chk = chks[drc->chk_idx];
726       try_top_down_reconstruction (dc, drc);
727     }
728     if (BRS_DOWNLOAD_UP != drc->state)
729       up_done = GNUNET_NO; /* children not all done */
730   }
731   if (GNUNET_YES == up_done)
732     propagate_up (dr); /* children all done (or no children...) */
733 }
734
735
736 /**
737  * Add entries to the message queue.
738  *
739  * @param cls our download context
740  * @param key unused
741  * @param entry entry of type `struct DownloadRequest`
742  * @return #GNUNET_OK
743  */
744 static int
745 retry_entry (void *cls, const struct GNUNET_HashCode *key, void *entry)
746 {
747   struct GNUNET_FS_DownloadContext *dc = cls;
748   struct DownloadRequest *dr = entry;
749   struct SearchMessage *sm;
750   struct GNUNET_MQ_Envelope *env;
751
752   env = GNUNET_MQ_msg (sm, GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
753   if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY))
754     sm->options = htonl (GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY);
755   else
756     sm->options = htonl (GNUNET_FS_SEARCH_OPTION_NONE);
757   if (0 == dr->depth)
758     sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK);
759   else
760     sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK);
761   sm->anonymity_level = htonl (dc->anonymity);
762   sm->target = dc->target;
763   sm->query = dr->chk.query;
764   GNUNET_MQ_send (dc->mq, env);
765   return GNUNET_OK;
766 }
767
768
769 /**
770  * Schedule the download of the specified block in the tree.
771  *
772  * @param dc overall download this block belongs to
773  * @param dr request to schedule
774  */
775 static void
776 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
777                          struct DownloadRequest *dr)
778 {
779   unsigned int i;
780
781   switch (dr->state)
782   {
783   case BRS_INIT:
784     GNUNET_assert (0);
785     break;
786   case BRS_RECONSTRUCT_DOWN:
787     GNUNET_assert (0);
788     break;
789   case BRS_RECONSTRUCT_META_UP:
790     GNUNET_assert (0);
791     break;
792   case BRS_RECONSTRUCT_UP:
793     GNUNET_assert (0);
794     break;
795   case BRS_CHK_SET:
796     /* normal case, start download */
797     break;
798   case BRS_DOWNLOAD_DOWN:
799     for (i = 0; i < dr->num_children; i++)
800       schedule_block_download (dc, dr->children[i]);
801     return;
802   case BRS_DOWNLOAD_UP:
803     /* We're done! */
804     return;
805   case BRS_ERROR:
806     GNUNET_break (0);
807     return;
808   }
809   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
810               "Scheduling download at offset %llu and depth %u for `%s'\n",
811               (unsigned long long) dr->offset,
812               dr->depth,
813               GNUNET_h2s (&dr->chk.query));
814   if (GNUNET_NO != GNUNET_CONTAINER_multihashmap_contains_value (dc->active,
815                                                                  &dr->chk.query,
816                                                                  dr))
817     return; /* already active */
818   GNUNET_CONTAINER_multihashmap_put (dc->active,
819                                      &dr->chk.query,
820                                      dr,
821                                      GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
822   if (NULL == dc->mq)
823     return; /* download not active */
824   retry_entry (dc, &dr->chk.query, dr);
825 }
826
827
828 #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
829
830 /**
831  * We found an entry in a directory.  Check if the respective child
832  * already exists and if not create the respective child download.
833  *
834  * @param cls the parent download
835  * @param filename name of the file in the directory
836  * @param uri URI of the file (CHK or LOC)
837  * @param meta meta data of the file
838  * @param length number of bytes in data
839  * @param data contents of the file (or NULL if they were not inlined)
840  */
841 static void
842 trigger_recursive_download (void *cls,
843                             const char *filename,
844                             const struct GNUNET_FS_Uri *uri,
845                             const struct GNUNET_CONTAINER_MetaData *meta,
846                             size_t length,
847                             const void *data)
848 {
849   struct GNUNET_FS_DownloadContext *dc = cls;
850   struct GNUNET_FS_DownloadContext *cpos;
851   char *temp_name;
852   char *fn;
853   char *us;
854   char *ext;
855   char *dn;
856   char *pos;
857   char *full_name;
858   char *sfn;
859
860   if (NULL == uri)
861     return; /* entry for the directory itself */
862   cpos = dc->child_head;
863   while (NULL != cpos)
864   {
865     if ((GNUNET_FS_uri_test_equal (uri, cpos->uri)) ||
866         ((NULL != filename) && (0 == strcmp (cpos->filename, filename))))
867       break;
868     cpos = cpos->next;
869   }
870   if (NULL != cpos)
871     return; /* already exists */
872   fn = NULL;
873   if (NULL == filename)
874   {
875     fn = GNUNET_FS_meta_data_suggest_filename (meta);
876     if (NULL == fn)
877     {
878       us = GNUNET_FS_uri_to_string (uri);
879       fn = GNUNET_strdup (&us[strlen (GNUNET_FS_URI_CHK_PREFIX)]);
880       GNUNET_free (us);
881     }
882     else if ('.' == fn[0])
883     {
884       ext = fn;
885       us = GNUNET_FS_uri_to_string (uri);
886       GNUNET_asprintf (&fn,
887                        "%s%s",
888                        &us[strlen (GNUNET_FS_URI_CHK_PREFIX)],
889                        ext);
890       GNUNET_free (ext);
891       GNUNET_free (us);
892     }
893     /* change '\' to '/' (this should have happened
894      * during insertion, but malicious peers may
895      * not have done this) */
896     while (NULL != (pos = strstr (fn, "\\")))
897       *pos = '/';
898     /* remove '../' everywhere (again, well-behaved
899      * peers don't do this, but don't trust that
900      * we did not get something nasty) */
901     while (NULL != (pos = strstr (fn, "../")))
902     {
903       pos[0] = '_';
904       pos[1] = '_';
905       pos[2] = '_';
906     }
907     filename = fn;
908   }
909   if (NULL == dc->filename)
910   {
911     full_name = NULL;
912   }
913   else
914   {
915     dn = GNUNET_strdup (dc->filename);
916     GNUNET_break (
917       (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
918       (NULL != strstr (dn + strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT),
919                        GNUNET_FS_DIRECTORY_EXT)));
920     sfn = GNUNET_strdup (filename);
921     while ((strlen (sfn) > 0) && ('/' == filename[strlen (sfn) - 1]))
922       sfn[strlen (sfn) - 1] = '\0';
923     if ((strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
924         (NULL != strstr (dn + strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT),
925                          GNUNET_FS_DIRECTORY_EXT)))
926       dn[strlen (dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0';
927     if ((GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) &&
928         ((strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) ||
929          (NULL == strstr (filename + strlen (filename) -
930                             strlen (GNUNET_FS_DIRECTORY_EXT),
931                           GNUNET_FS_DIRECTORY_EXT))))
932     {
933       GNUNET_asprintf (&full_name,
934                        "%s%s%s%s",
935                        dn,
936                        DIR_SEPARATOR_STR,
937                        sfn,
938                        GNUNET_FS_DIRECTORY_EXT);
939     }
940     else
941     {
942       GNUNET_asprintf (&full_name, "%s%s%s", dn, DIR_SEPARATOR_STR, sfn);
943     }
944     GNUNET_free (sfn);
945     GNUNET_free (dn);
946   }
947   if ((NULL != full_name) &&
948       (GNUNET_OK != GNUNET_DISK_directory_create_for_file (full_name)))
949   {
950     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
951                 _ (
952                   "Failed to create directory for recursive download of `%s'\n"),
953                 full_name);
954     GNUNET_free (full_name);
955     GNUNET_free_non_null (fn);
956     return;
957   }
958
959   temp_name = NULL;
960   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
961               "Triggering recursive download of size %llu with %u bytes MD\n",
962               (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri),
963               (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (
964                 meta));
965   GNUNET_FS_download_start (dc->h,
966                             uri,
967                             meta,
968                             full_name,
969                             temp_name,
970                             0,
971                             GNUNET_FS_uri_chk_get_file_size (uri),
972                             dc->anonymity,
973                             dc->options,
974                             NULL,
975                             dc);
976   GNUNET_free_non_null (full_name);
977   GNUNET_free_non_null (temp_name);
978   GNUNET_free_non_null (fn);
979 }
980
981
982 /**
983  * (recursively) free download request structure
984  *
985  * @param dr request to free
986  */
987 void
988 GNUNET_FS_free_download_request_ (struct DownloadRequest *dr)
989 {
990   if (NULL == dr)
991     return;
992   for (unsigned int i = 0; i < dr->num_children; i++)
993     GNUNET_FS_free_download_request_ (dr->children[i]);
994   GNUNET_free_non_null (dr->children);
995   GNUNET_free (dr);
996 }
997
998
999 /**
1000  * Iterator over entries in the pending requests in the 'active' map for the
1001  * reply that we just got.
1002  *
1003  * @param cls closure (our `struct ProcessResultClosure`)
1004  * @param key query for the given value / request
1005  * @param value value in the hash map (a `struct DownloadRequest`)
1006  * @return #GNUNET_YES (we should continue to iterate); unless serious error
1007  */
1008 static int
1009 process_result_with_request (void *cls,
1010                              const struct GNUNET_HashCode *key,
1011                              void *value)
1012 {
1013   struct ProcessResultClosure *prc = cls;
1014   struct DownloadRequest *dr = value;
1015   struct GNUNET_FS_DownloadContext *dc = prc->dc;
1016   struct DownloadRequest *drc;
1017   struct GNUNET_DISK_FileHandle *fh = NULL;
1018   struct GNUNET_CRYPTO_SymmetricSessionKey skey;
1019   struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
1020   char pt[prc->size];
1021   struct GNUNET_FS_ProgressInfo pi;
1022   uint64_t off;
1023   size_t bs;
1024   size_t app;
1025   int i;
1026   struct ContentHashKey *chkarr;
1027
1028   GNUNET_log (
1029     GNUNET_ERROR_TYPE_DEBUG,
1030     "Received %u byte block `%s' matching pending request at depth %u and offset %llu/%llu\n",
1031     (unsigned int) prc->size,
1032     GNUNET_h2s (key),
1033     dr->depth,
1034     (unsigned long long) dr->offset,
1035     (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1036   bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll (
1037                                               dc->uri->data.chk.file_length),
1038                                             dr->offset,
1039                                             dr->depth);
1040   if (prc->size != bs)
1041   {
1042     GNUNET_asprintf (
1043       &dc->emsg,
1044       _ (
1045         "Internal error or bogus download URI (expected %u bytes at depth %u and offset %llu/%llu, got %u bytes)"),
1046       bs,
1047       dr->depth,
1048       (unsigned long long) dr->offset,
1049       (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length),
1050       prc->size);
1051     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "%s\n", dc->emsg);
1052     while (NULL != dr->parent)
1053     {
1054       dr->state = BRS_ERROR;
1055       dr = dr->parent;
1056     }
1057     dr->state = BRS_ERROR;
1058     goto signal_error;
1059   }
1060
1061   (void) GNUNET_CONTAINER_multihashmap_remove (dc->active, &prc->query, dr);
1062   GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv);
1063   if (-1 ==
1064       GNUNET_CRYPTO_symmetric_decrypt (prc->data, prc->size, &skey, &iv, pt))
1065   {
1066     GNUNET_break (0);
1067     dc->emsg = GNUNET_strdup (_ ("internal error decrypting content"));
1068     goto signal_error;
1069   }
1070   off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
1071                              dr->offset,
1072                              dr->depth);
1073   /* save to disk */
1074   if ((GNUNET_YES == prc->do_store) &&
1075       ((NULL != dc->filename) || (is_recursive_download (dc))) &&
1076       ((dr->depth == dc->treedepth) ||
1077        (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES))))
1078   {
1079     fh = GNUNET_DISK_file_open (NULL != dc->filename ? dc->filename
1080                                                      : dc->temp_filename,
1081                                 GNUNET_DISK_OPEN_READWRITE |
1082                                   GNUNET_DISK_OPEN_CREATE,
1083                                 GNUNET_DISK_PERM_USER_READ |
1084                                   GNUNET_DISK_PERM_USER_WRITE |
1085                                   GNUNET_DISK_PERM_GROUP_READ |
1086                                   GNUNET_DISK_PERM_OTHER_READ);
1087     if (NULL == fh)
1088     {
1089       GNUNET_asprintf (&dc->emsg,
1090                        _ ("Download failed: could not open file `%s': %s"),
1091                        dc->filename,
1092                        strerror (errno));
1093       goto signal_error;
1094     }
1095     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1096                 "Saving decrypted block to disk at offset %llu\n",
1097                 (unsigned long long) off);
1098     if ((off != GNUNET_DISK_file_seek (fh, off, GNUNET_DISK_SEEK_SET)))
1099     {
1100       GNUNET_asprintf (&dc->emsg,
1101                        _ ("Failed to seek to offset %llu in file `%s': %s"),
1102                        (unsigned long long) off,
1103                        dc->filename,
1104                        strerror (errno));
1105       goto signal_error;
1106     }
1107     if (prc->size != GNUNET_DISK_file_write (fh, pt, prc->size))
1108     {
1109       GNUNET_asprintf (
1110         &dc->emsg,
1111         _ ("Failed to write block of %u bytes at offset %llu in file `%s': %s"),
1112         (unsigned int) prc->size,
1113         (unsigned long long) off,
1114         dc->filename,
1115         strerror (errno));
1116       goto signal_error;
1117     }
1118     GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
1119     fh = NULL;
1120   }
1121
1122   if (0 == dr->depth)
1123   {
1124     /* DBLOCK, update progress and try recursion if applicable */
1125     app = prc->size;
1126     if (dr->offset < dc->offset)
1127     {
1128       /* starting offset begins in the middle of pt,
1129        * do not count first bytes as progress */
1130       GNUNET_assert (app > (dc->offset - dr->offset));
1131       app -= (dc->offset - dr->offset);
1132     }
1133     if (dr->offset + prc->size > dc->offset + dc->length)
1134     {
1135       /* end of block is after relevant range,
1136        * do not count last bytes as progress */
1137       GNUNET_assert (app >
1138                      (dr->offset + prc->size) - (dc->offset + dc->length));
1139       app -= (dr->offset + prc->size) - (dc->offset + dc->length);
1140     }
1141     dc->completed += app;
1142
1143     /* do recursive download if option is set and either meta data
1144      * says it is a directory or if no meta data is given AND filename
1145      * ends in '.gnd' (top-level case) */
1146     if (is_recursive_download (dc))
1147       GNUNET_FS_directory_list_contents (prc->size,
1148                                          pt,
1149                                          off,
1150                                          &trigger_recursive_download,
1151                                          dc);
1152   }
1153   GNUNET_assert (dc->completed <= dc->length);
1154   dr->state = BRS_DOWNLOAD_DOWN;
1155   pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1156   pi.value.download.specifics.progress.data = pt;
1157   pi.value.download.specifics.progress.offset = dr->offset;
1158   pi.value.download.specifics.progress.data_len = prc->size;
1159   pi.value.download.specifics.progress.depth = dr->depth;
1160   pi.value.download.specifics.progress.respect_offered = prc->respect_offered;
1161   pi.value.download.specifics.progress.num_transmissions =
1162     prc->num_transmissions;
1163   if (prc->last_transmission.abs_value_us !=
1164       GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
1165     pi.value.download.specifics.progress.block_download_duration =
1166       GNUNET_TIME_absolute_get_duration (prc->last_transmission);
1167   else
1168     pi.value.download.specifics.progress.block_download_duration =
1169       GNUNET_TIME_UNIT_ZERO; /* found locally */
1170   GNUNET_FS_download_make_status_ (&pi, dc);
1171   if (0 == dr->depth)
1172     propagate_up (dr);
1173
1174   if (dc->completed == dc->length)
1175   {
1176     /* download completed, signal */
1177     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1178                 "Download completed, truncating file to desired length %llu\n",
1179                 (unsigned long long) GNUNET_ntohll (
1180                   dc->uri->data.chk.file_length));
1181     /* truncate file to size (since we store IBlocks at the end) */
1182     if (NULL != dc->filename)
1183     {
1184       if (0 != truncate (dc->filename,
1185                          GNUNET_ntohll (dc->uri->data.chk.file_length)))
1186         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1187                                   "truncate",
1188                                   dc->filename);
1189     }
1190     GNUNET_assert (0 == dr->depth);
1191     check_completed (dc);
1192   }
1193   if (0 == dr->depth)
1194   {
1195     /* bottom of the tree, no child downloads possible, just sync */
1196     GNUNET_FS_download_sync_ (dc);
1197     return GNUNET_YES;
1198   }
1199
1200   GNUNET_log (
1201     GNUNET_ERROR_TYPE_DEBUG,
1202     "Triggering downloads of children (this block was at depth %u and offset %llu)\n",
1203     dr->depth,
1204     (unsigned long long) dr->offset);
1205   GNUNET_assert (0 == (prc->size % sizeof (struct ContentHashKey)));
1206   chkarr = (struct ContentHashKey *) pt;
1207   for (i = dr->num_children - 1; i >= 0; i--)
1208   {
1209     drc = dr->children[i];
1210     switch (drc->state)
1211     {
1212     case BRS_INIT:
1213       if ((drc->chk_idx + 1) * sizeof (struct ContentHashKey) > prc->size)
1214       {
1215         /* 'chkarr' does not have enough space for this chk_idx;
1216            internal error! */
1217         GNUNET_break (0);
1218         GNUNET_assert (0);
1219         dc->emsg = GNUNET_strdup (_ ("internal error decoding tree"));
1220         goto signal_error;
1221       }
1222       drc->chk = chkarr[drc->chk_idx];
1223       drc->state = BRS_CHK_SET;
1224       if (GNUNET_YES == dc->issue_requests)
1225         schedule_block_download (dc, drc);
1226       break;
1227     case BRS_RECONSTRUCT_DOWN:
1228       GNUNET_assert (0);
1229       break;
1230     case BRS_RECONSTRUCT_META_UP:
1231       GNUNET_assert (0);
1232       break;
1233     case BRS_RECONSTRUCT_UP:
1234       GNUNET_assert (0);
1235       break;
1236     case BRS_CHK_SET:
1237       GNUNET_assert (0);
1238       break;
1239     case BRS_DOWNLOAD_DOWN:
1240       GNUNET_assert (0);
1241       break;
1242     case BRS_DOWNLOAD_UP:
1243       GNUNET_assert (0);
1244       break;
1245     case BRS_ERROR:
1246       GNUNET_assert (0);
1247       break;
1248     default:
1249       GNUNET_assert (0);
1250       break;
1251     }
1252   }
1253   GNUNET_FS_download_sync_ (dc);
1254   return GNUNET_YES;
1255
1256 signal_error:
1257   if (NULL != fh)
1258     GNUNET_DISK_file_close (fh);
1259   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1260   pi.value.download.specifics.error.message = dc->emsg;
1261   GNUNET_FS_download_make_status_ (&pi, dc);
1262   GNUNET_MQ_destroy (dc->mq);
1263   dc->mq = NULL;
1264   GNUNET_FS_free_download_request_ (dc->top_request);
1265   dc->top_request = NULL;
1266   if (NULL != dc->job_queue)
1267   {
1268     GNUNET_FS_dequeue_ (dc->job_queue);
1269     dc->job_queue = NULL;
1270   }
1271   GNUNET_FS_download_sync_ (dc);
1272   return GNUNET_NO;
1273 }
1274
1275
1276 /**
1277  * Type of a function to call when we check the PUT message
1278  * from the service.
1279  *
1280  * @param cls closure
1281  * @param msg message received
1282  */
1283 static int
1284 check_put (void *cls, const struct ClientPutMessage *cm)
1285 {
1286   /* any varsize length is OK */
1287   return GNUNET_OK;
1288 }
1289
1290
1291 /**
1292  * Type of a function to call when we receive a message
1293  * from the service.
1294  *
1295  * @param cls closure
1296  * @param msg message received
1297  */
1298 static void
1299 handle_put (void *cls, const struct ClientPutMessage *cm)
1300 {
1301   struct GNUNET_FS_DownloadContext *dc = cls;
1302   uint16_t msize = ntohs (cm->header.size) - sizeof (*cm);
1303   struct ProcessResultClosure prc;
1304
1305   prc.dc = dc;
1306   prc.data = &cm[1];
1307   prc.last_transmission = GNUNET_TIME_absolute_ntoh (cm->last_transmission);
1308   prc.size = msize;
1309   prc.type = ntohl (cm->type);
1310   prc.do_store = GNUNET_YES;
1311   prc.respect_offered = ntohl (cm->respect_offered);
1312   prc.num_transmissions = ntohl (cm->num_transmissions);
1313   GNUNET_CRYPTO_hash (prc.data, msize, &prc.query);
1314   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1315               "Received result for query `%s' from FS service\n",
1316               GNUNET_h2s (&prc.query));
1317   GNUNET_CONTAINER_multihashmap_get_multiple (dc->active,
1318                                               &prc.query,
1319                                               &process_result_with_request,
1320                                               &prc);
1321 }
1322
1323
1324 /**
1325  * Generic error handler, called with the appropriate error code and
1326  * the same closure specified at the creation of the message queue.
1327  * Not every message queue implementation supports an error handler.
1328  *
1329  * @param cls closure with the `struct GNUNET_FS_DownloadContext *`
1330  * @param error error code
1331  */
1332 static void
1333 download_mq_error_handler (void *cls, enum GNUNET_MQ_Error error)
1334 {
1335   struct GNUNET_FS_DownloadContext *dc = cls;
1336
1337   if (NULL != dc->mq)
1338   {
1339     GNUNET_MQ_destroy (dc->mq);
1340     dc->mq = NULL;
1341   }
1342   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1343               "Transmitting download request failed, trying to reconnect\n");
1344   try_reconnect (dc);
1345 }
1346
1347
1348 /**
1349  * Reconnect to the FS service and transmit our queries NOW.
1350  *
1351  * @param cls our download context
1352  */
1353 static void
1354 do_reconnect (void *cls)
1355 {
1356   struct GNUNET_FS_DownloadContext *dc = cls;
1357   struct GNUNET_MQ_MessageHandler handlers[] =
1358     {GNUNET_MQ_hd_var_size (put,
1359                             GNUNET_MESSAGE_TYPE_FS_PUT,
1360                             struct ClientPutMessage,
1361                             dc),
1362      GNUNET_MQ_handler_end ()};
1363
1364   dc->task = NULL;
1365   dc->mq = GNUNET_CLIENT_connect (dc->h->cfg,
1366                                   "fs",
1367                                   handlers,
1368                                   &download_mq_error_handler,
1369                                   dc);
1370   if (NULL == dc->mq)
1371   {
1372     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1373                 "Connecting to `%s'-service failed, will try again.\n",
1374                 "FS");
1375     try_reconnect (dc);
1376     return;
1377   }
1378   GNUNET_CONTAINER_multihashmap_iterate (dc->active, &retry_entry, dc);
1379 }
1380
1381
1382 /**
1383  * We've lost our connection with the FS service.
1384  * Re-establish it and re-transmit all of our
1385  * pending requests.
1386  *
1387  * @param dc download context that is having trouble
1388  */
1389 static void
1390 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1391 {
1392   if (NULL != dc->mq)
1393   {
1394     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1395                 "Moving all requests back to pending list\n");
1396     GNUNET_MQ_destroy (dc->mq);
1397     dc->mq = NULL;
1398   }
1399   if (0 == dc->reconnect_backoff.rel_value_us)
1400     dc->reconnect_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
1401   else
1402     dc->reconnect_backoff = GNUNET_TIME_STD_BACKOFF (dc->reconnect_backoff);
1403
1404   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1405               "Will try to reconnect in %s\n",
1406               GNUNET_STRINGS_relative_time_to_string (dc->reconnect_backoff,
1407                                                       GNUNET_YES));
1408   GNUNET_break (NULL != dc->job_queue);
1409   dc->task =
1410     GNUNET_SCHEDULER_add_delayed (dc->reconnect_backoff, &do_reconnect, dc);
1411 }
1412
1413
1414 /**
1415  * We're allowed to ask the FS service for our blocks.  Start the download.
1416  *
1417  * @param cls the 'struct GNUNET_FS_DownloadContext'
1418  * @param mq handle to use for communcation with FS (we must destroy it!)
1419  */
1420 static void
1421 activate_fs_download (void *cls)
1422 {
1423   struct GNUNET_FS_DownloadContext *dc = cls;
1424   struct GNUNET_FS_ProgressInfo pi;
1425
1426   GNUNET_assert (NULL == dc->mq);
1427   GNUNET_assert (NULL != dc->active);
1428   do_reconnect (dc);
1429   if (NULL != dc->mq)
1430     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download activated\n");
1431   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1432   GNUNET_FS_download_make_status_ (&pi, dc);
1433 }
1434
1435
1436 /**
1437  * We must stop to ask the FS service for our blocks.  Pause the download.
1438  *
1439  * @param cls the `struct GNUNET_FS_DownloadContext`
1440  */
1441 static void
1442 deactivate_fs_download (void *cls)
1443 {
1444   struct GNUNET_FS_DownloadContext *dc = cls;
1445   struct GNUNET_FS_ProgressInfo pi;
1446
1447   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Download deactivated\n");
1448   if (NULL != dc->mq)
1449   {
1450     GNUNET_MQ_destroy (dc->mq);
1451     dc->mq = NULL;
1452   }
1453   pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1454   GNUNET_FS_download_make_status_ (&pi, dc);
1455 }
1456
1457
1458 /**
1459  * (recursively) Create a download request structure.
1460  *
1461  * @param parent parent of the current entry
1462  * @param chk_idx index of the chk for this block in the parent block
1463  * @param depth depth of the current entry, 0 are the DBLOCKs,
1464  *              top level block is 'dc->treedepth - 1'
1465  * @param dr_offset offset in the original file this block maps to
1466  *              (as in, offset of the first byte of the first DBLOCK
1467  *               in the subtree rooted in the returned download request tree)
1468  * @param file_start_offset desired starting offset for the download
1469  *             in the original file; requesting tree should not contain
1470  *             DBLOCKs prior to the file_start_offset
1471  * @param desired_length desired number of bytes the user wanted to access
1472  *        (from file_start_offset).  Resulting tree should not contain
1473  *        DBLOCKs after file_start_offset + file_length.
1474  * @return download request tree for the given range of DBLOCKs at
1475  *         the specified depth
1476  */
1477 static struct DownloadRequest *
1478 create_download_request (struct DownloadRequest *parent,
1479                          unsigned int chk_idx,
1480                          unsigned int depth,
1481                          uint64_t dr_offset,
1482                          uint64_t file_start_offset,
1483                          uint64_t desired_length)
1484 {
1485   struct DownloadRequest *dr;
1486   unsigned int i;
1487   unsigned int head_skip;
1488   uint64_t child_block_size;
1489
1490   dr = GNUNET_new (struct DownloadRequest);
1491   dr->parent = parent;
1492   dr->depth = depth;
1493   dr->offset = dr_offset;
1494   dr->chk_idx = chk_idx;
1495   if (0 == depth)
1496     return dr;
1497   child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
1498
1499   /* calculate how many blocks at this level are not interesting
1500    * from the start (rounded down), either because of the requested
1501    * file offset or because this IBlock is further along */
1502   if (dr_offset < file_start_offset)
1503   {
1504     head_skip = (file_start_offset - dr_offset) / child_block_size;
1505   }
1506   else
1507   {
1508     head_skip = 0;
1509   }
1510
1511   /* calculate index of last block at this level that is interesting (rounded up) */
1512   dr->num_children =
1513     (file_start_offset + desired_length - dr_offset) / child_block_size;
1514   if (dr->num_children * child_block_size <
1515       file_start_offset + desired_length - dr_offset)
1516     dr->num_children++; /* round up */
1517   GNUNET_assert (dr->num_children > head_skip);
1518   dr->num_children -= head_skip;
1519   if (dr->num_children > CHK_PER_INODE)
1520     dr->num_children = CHK_PER_INODE; /* cap at max */
1521   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1522               "Block at offset %llu and depth %u has %u children\n",
1523               (unsigned long long) dr_offset,
1524               depth,
1525               dr->num_children);
1526
1527   /* now we can get the total number of *interesting* children for this block */
1528
1529   /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
1530   GNUNET_assert (dr->num_children > 0);
1531
1532   dr->children = GNUNET_new_array (dr->num_children, struct DownloadRequest *);
1533   for (i = 0; i < dr->num_children; i++)
1534   {
1535     dr->children[i] =
1536       create_download_request (dr,
1537                                i + head_skip,
1538                                depth - 1,
1539                                dr_offset + (i + head_skip) * child_block_size,
1540                                file_start_offset,
1541                                desired_length);
1542   }
1543   return dr;
1544 }
1545
1546
1547 /**
1548  * Continuation after a possible attempt to reconstruct
1549  * the current IBlock from the existing file.
1550  *
1551  * @param cls the 'struct ReconstructContext'
1552  */
1553 static void
1554 reconstruct_cont (void *cls)
1555 {
1556   struct GNUNET_FS_DownloadContext *dc = cls;
1557
1558   /* clean up state from tree encoder */
1559   if (NULL != dc->task)
1560   {
1561     GNUNET_SCHEDULER_cancel (dc->task);
1562     dc->task = NULL;
1563   }
1564   if (NULL != dc->rfh)
1565   {
1566     GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
1567     dc->rfh = NULL;
1568   }
1569   /* start "normal" download */
1570   dc->issue_requests = GNUNET_YES;
1571   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting normal download\n");
1572   schedule_block_download (dc, dc->top_request);
1573 }
1574
1575
1576 /**
1577  * Task requesting the next block from the tree encoder.
1578  *
1579  * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
1580  */
1581 static void
1582 get_next_block (void *cls)
1583 {
1584   struct GNUNET_FS_DownloadContext *dc = cls;
1585
1586   dc->task = NULL;
1587   GNUNET_FS_tree_encoder_next (dc->te);
1588 }
1589
1590
1591 /**
1592  * Function called asking for the current (encoded)
1593  * block to be processed.  After processing the
1594  * client should either call "GNUNET_FS_tree_encode_next"
1595  * or (on error) "GNUNET_FS_tree_encode_finish".
1596  *
1597  * This function checks if the content on disk matches
1598  * the expected content based on the URI.
1599  *
1600  * @param cls closure
1601  * @param chk content hash key for the block
1602  * @param offset offset of the block
1603  * @param depth depth of the block, 0 for DBLOCK
1604  * @param type type of the block (IBLOCK or DBLOCK)
1605  * @param block the (encrypted) block
1606  * @param block_size size of block (in bytes)
1607  */
1608 static void
1609 reconstruct_cb (void *cls,
1610                 const struct ContentHashKey *chk,
1611                 uint64_t offset,
1612                 unsigned int depth,
1613                 enum GNUNET_BLOCK_Type type,
1614                 const void *block,
1615                 uint16_t block_size)
1616 {
1617   struct GNUNET_FS_DownloadContext *dc = cls;
1618   struct GNUNET_FS_ProgressInfo pi;
1619   struct DownloadRequest *dr;
1620   uint64_t blen;
1621   unsigned int chld;
1622
1623   /* find corresponding request entry */
1624   dr = dc->top_request;
1625   while (dr->depth > depth)
1626   {
1627     GNUNET_assert (dr->num_children > 0);
1628     blen = GNUNET_FS_tree_compute_tree_size (dr->depth - 1);
1629     chld = (offset - dr->offset) / blen;
1630     if (chld < dr->children[0]->chk_idx)
1631     {
1632       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1633                   "Block %u < %u irrelevant for our range\n",
1634                   chld,
1635                   dr->children[0]->chk_idx);
1636       dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1637       return; /* irrelevant block */
1638     }
1639     if (chld > dr->children[dr->num_children - 1]->chk_idx)
1640     {
1641       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1642                   "Block %u > %u irrelevant for our range\n",
1643                   chld,
1644                   dr->children[dr->num_children - 1]->chk_idx);
1645       dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1646       return; /* irrelevant block */
1647     }
1648     dr = dr->children[chld - dr->children[0]->chk_idx];
1649   }
1650   GNUNET_log (
1651     GNUNET_ERROR_TYPE_DEBUG,
1652     "Matched TE block with request at offset %llu and depth %u in state %d\n",
1653     (unsigned long long) dr->offset,
1654     dr->depth,
1655     dr->state);
1656   /* FIXME: this code needs more testing and might
1657      need to handle more states... */
1658   switch (dr->state)
1659   {
1660   case BRS_INIT:
1661     break;
1662   case BRS_RECONSTRUCT_DOWN:
1663     break;
1664   case BRS_RECONSTRUCT_META_UP:
1665     break;
1666   case BRS_RECONSTRUCT_UP:
1667     break;
1668   case BRS_CHK_SET:
1669     if (0 == memcmp (chk, &dr->chk, sizeof (struct ContentHashKey)))
1670     {
1671       GNUNET_log (
1672         GNUNET_ERROR_TYPE_DEBUG,
1673         "Reconstruction succeeded, can use block at offset %llu, depth %u\n",
1674         (unsigned long long) offset,
1675         depth);
1676       /* block matches, hence tree below matches;
1677        * this request is done! */
1678       dr->state = BRS_DOWNLOAD_UP;
1679       (void) GNUNET_CONTAINER_multihashmap_remove (dc->active,
1680                                                    &dr->chk.query,
1681                                                    dr);
1682       /* calculate how many bytes of payload this block
1683        * corresponds to */
1684       blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1685       /* how many of those bytes are in the requested range? */
1686       blen = GNUNET_MIN (blen, dc->length + dc->offset - dr->offset);
1687       /* signal progress */
1688       dc->completed += blen;
1689       pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1690       pi.value.download.specifics.progress.data = NULL;
1691       pi.value.download.specifics.progress.offset = offset;
1692       pi.value.download.specifics.progress.data_len = 0;
1693       pi.value.download.specifics.progress.depth = 0;
1694       pi.value.download.specifics.progress.respect_offered = 0;
1695       pi.value.download.specifics.progress.block_download_duration =
1696         GNUNET_TIME_UNIT_ZERO;
1697       GNUNET_FS_download_make_status_ (&pi, dc);
1698       /* FIXME: duplicated code from 'process_result_with_request - refactor */
1699       if (dc->completed == dc->length)
1700       {
1701         /* download completed, signal */
1702         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1703                     "Download completed, truncating file to desired length %llu\n",
1704                     (unsigned long long) GNUNET_ntohll (
1705                       dc->uri->data.chk.file_length));
1706         /* truncate file to size (since we store IBlocks at the end) */
1707         if (NULL != dc->filename)
1708         {
1709           if (0 != truncate (dc->filename,
1710                              GNUNET_ntohll (dc->uri->data.chk.file_length)))
1711             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1712                                       "truncate",
1713                                       dc->filename);
1714         }
1715       }
1716     }
1717     else
1718       GNUNET_log (
1719         GNUNET_ERROR_TYPE_DEBUG,
1720         "Reconstruction failed, need to download block at offset %llu, depth %u\n",
1721         (unsigned long long) offset,
1722         depth);
1723     break;
1724   case BRS_DOWNLOAD_DOWN:
1725     break;
1726   case BRS_DOWNLOAD_UP:
1727     break;
1728   case BRS_ERROR:
1729     break;
1730   default:
1731     GNUNET_assert (0);
1732     break;
1733   }
1734   dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1735   if ((dr == dc->top_request) && (dr->state == BRS_DOWNLOAD_UP))
1736     check_completed (dc);
1737 }
1738
1739
1740 /**
1741  * Function called by the tree encoder to obtain a block of plaintext
1742  * data (for the lowest level of the tree).
1743  *
1744  * @param cls our 'struct ReconstructContext'
1745  * @param offset identifies which block to get
1746  * @param max (maximum) number of bytes to get; returning
1747  *        fewer will also cause errors
1748  * @param buf where to copy the plaintext buffer
1749  * @param emsg location to store an error message (on error)
1750  * @return number of bytes copied to buf, 0 on error
1751  */
1752 static size_t
1753 fh_reader (void *cls, uint64_t offset, size_t max, void *buf, char **emsg)
1754 {
1755   struct GNUNET_FS_DownloadContext *dc = cls;
1756   struct GNUNET_DISK_FileHandle *fh = dc->rfh;
1757   ssize_t ret;
1758
1759   if (NULL != emsg)
1760     *emsg = NULL;
1761   if (offset != GNUNET_DISK_file_seek (fh, offset, GNUNET_DISK_SEEK_SET))
1762   {
1763     if (NULL != emsg)
1764       *emsg = GNUNET_strdup (strerror (errno));
1765     return 0;
1766   }
1767   ret = GNUNET_DISK_file_read (fh, buf, max);
1768   if (ret < 0)
1769   {
1770     if (NULL != emsg)
1771       *emsg = GNUNET_strdup (strerror (errno));
1772     return 0;
1773   }
1774   return ret;
1775 }
1776
1777
1778 /**
1779  * Task that creates the initial (top-level) download
1780  * request for the file.
1781  *
1782  * @param cls the 'struct GNUNET_FS_DownloadContext'
1783  */
1784 void
1785 GNUNET_FS_download_start_task_ (void *cls)
1786 {
1787   struct GNUNET_FS_DownloadContext *dc = cls;
1788   struct GNUNET_FS_ProgressInfo pi;
1789   struct GNUNET_DISK_FileHandle *fh;
1790
1791   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start task running...\n");
1792   dc->task = NULL;
1793   if (0 == dc->length)
1794   {
1795     /* no bytes required! */
1796     if (NULL != dc->filename)
1797     {
1798       fh = GNUNET_DISK_file_open (dc->filename,
1799                                   GNUNET_DISK_OPEN_READWRITE |
1800                                     GNUNET_DISK_OPEN_CREATE |
1801                                     ((0 ==
1802                                       GNUNET_FS_uri_chk_get_file_size (dc->uri))
1803                                        ? GNUNET_DISK_OPEN_TRUNCATE
1804                                        : 0),
1805                                   GNUNET_DISK_PERM_USER_READ |
1806                                     GNUNET_DISK_PERM_USER_WRITE |
1807                                     GNUNET_DISK_PERM_GROUP_READ |
1808                                     GNUNET_DISK_PERM_OTHER_READ);
1809       GNUNET_DISK_file_close (fh);
1810     }
1811     GNUNET_FS_download_sync_ (dc);
1812     pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1813     pi.value.download.specifics.start.meta = dc->meta;
1814     GNUNET_FS_download_make_status_ (&pi, dc);
1815     check_completed (dc);
1816     return;
1817   }
1818   if (NULL != dc->emsg)
1819     return;
1820   if (NULL == dc->top_request)
1821   {
1822     dc->top_request = create_download_request (NULL,
1823                                                0,
1824                                                dc->treedepth - 1,
1825                                                0,
1826                                                dc->offset,
1827                                                dc->length);
1828     dc->top_request->state = BRS_CHK_SET;
1829     dc->top_request->chk = (dc->uri->type == GNUNET_FS_URI_CHK)
1830                              ? dc->uri->data.chk.chk
1831                              : dc->uri->data.loc.fi.chk;
1832     /* signal start */
1833     GNUNET_FS_download_sync_ (dc);
1834     if (NULL != dc->search)
1835       GNUNET_FS_search_result_sync_ (dc->search);
1836     pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1837     pi.value.download.specifics.start.meta = dc->meta;
1838     GNUNET_FS_download_make_status_ (&pi, dc);
1839   }
1840   GNUNET_FS_download_start_downloading_ (dc);
1841   /* attempt reconstruction from disk */
1842   if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))
1843     dc->rfh = GNUNET_DISK_file_open (dc->filename,
1844                                      GNUNET_DISK_OPEN_READ,
1845                                      GNUNET_DISK_PERM_NONE);
1846   if (dc->top_request->state == BRS_CHK_SET)
1847   {
1848     if (NULL != dc->rfh)
1849     {
1850       /* first, try top-down */
1851       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1852                   "Trying top-down reconstruction for `%s'\n",
1853                   dc->filename);
1854       try_top_down_reconstruction (dc, dc->top_request);
1855       switch (dc->top_request->state)
1856       {
1857       case BRS_CHK_SET:
1858         break; /* normal */
1859       case BRS_DOWNLOAD_DOWN:
1860         break; /* normal, some blocks already down */
1861       case BRS_DOWNLOAD_UP:
1862         /* already done entirely, party! */
1863         if (NULL != dc->rfh)
1864         {
1865           /* avoid hanging on to file handle longer than
1866            * necessary */
1867           GNUNET_DISK_file_close (dc->rfh);
1868           dc->rfh = NULL;
1869         }
1870         return;
1871       case BRS_ERROR:
1872         GNUNET_asprintf (&dc->emsg, _ ("Invalid URI"));
1873         GNUNET_FS_download_sync_ (dc);
1874         pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1875         pi.value.download.specifics.error.message = dc->emsg;
1876         GNUNET_FS_download_make_status_ (&pi, dc);
1877         return;
1878       default:
1879         GNUNET_assert (0);
1880         break;
1881       }
1882     }
1883   }
1884   /* attempt reconstruction from meta data */
1885   if ((GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
1886       (NULL != dc->meta))
1887   {
1888     GNUNET_log (
1889       GNUNET_ERROR_TYPE_DEBUG,
1890       "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
1891       (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
1892       (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta));
1893     GNUNET_CONTAINER_meta_data_iterate (dc->meta, &match_full_data, dc);
1894     if (BRS_DOWNLOAD_UP == dc->top_request->state)
1895     {
1896       if (NULL != dc->rfh)
1897       {
1898         /* avoid hanging on to file handle longer than
1899          * necessary */
1900         GNUNET_DISK_file_close (dc->rfh);
1901         dc->rfh = NULL;
1902       }
1903       return; /* finished, status update was already done for us */
1904     }
1905   }
1906   if (NULL != dc->rfh)
1907   {
1908     /* finally, actually run bottom-up */
1909     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1910                 "Trying bottom-up reconstruction of file `%s'\n",
1911                 dc->filename);
1912     dc->te =
1913       GNUNET_FS_tree_encoder_create (dc->h,
1914                                      GNUNET_FS_uri_chk_get_file_size (dc->uri),
1915                                      dc,
1916                                      &fh_reader,
1917                                      &reconstruct_cb,
1918                                      NULL,
1919                                      &reconstruct_cont);
1920     dc->task = GNUNET_SCHEDULER_add_now (&get_next_block, dc);
1921   }
1922   else
1923   {
1924     /* simple, top-level download */
1925     dc->issue_requests = GNUNET_YES;
1926     schedule_block_download (dc, dc->top_request);
1927   }
1928   if (BRS_DOWNLOAD_UP == dc->top_request->state)
1929     check_completed (dc);
1930 }
1931
1932
1933 /**
1934  * Create SUSPEND event for the given download operation
1935  * and then clean up our state (without stop signal).
1936  *
1937  * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
1938  */
1939 void
1940 GNUNET_FS_download_signal_suspend_ (void *cls)
1941 {
1942   struct GNUNET_FS_DownloadContext *dc = cls;
1943   struct GNUNET_FS_ProgressInfo pi;
1944
1945   if (NULL != dc->top)
1946     GNUNET_FS_end_top (dc->h, dc->top);
1947   while (NULL != dc->child_head)
1948     GNUNET_FS_download_signal_suspend_ (dc->child_head);
1949   if (NULL != dc->search)
1950   {
1951     dc->search->download = NULL;
1952     dc->search = NULL;
1953   }
1954   if (NULL != dc->job_queue)
1955   {
1956     GNUNET_FS_dequeue_ (dc->job_queue);
1957     dc->job_queue = NULL;
1958   }
1959   if (NULL != dc->parent)
1960     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
1961                                  dc->parent->child_tail,
1962                                  dc);
1963   if (NULL != dc->task)
1964   {
1965     GNUNET_SCHEDULER_cancel (dc->task);
1966     dc->task = NULL;
1967   }
1968   pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
1969   GNUNET_FS_download_make_status_ (&pi, dc);
1970   if (NULL != dc->te)
1971   {
1972     GNUNET_FS_tree_encoder_finish (dc->te, NULL);
1973     dc->te = NULL;
1974   }
1975   if (NULL != dc->rfh)
1976   {
1977     GNUNET_DISK_file_close (dc->rfh);
1978     dc->rfh = NULL;
1979   }
1980   GNUNET_FS_free_download_request_ (dc->top_request);
1981   if (NULL != dc->active)
1982   {
1983     GNUNET_CONTAINER_multihashmap_destroy (dc->active);
1984     dc->active = NULL;
1985   }
1986   GNUNET_free_non_null (dc->filename);
1987   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
1988   GNUNET_FS_uri_destroy (dc->uri);
1989   GNUNET_free_non_null (dc->temp_filename);
1990   GNUNET_free_non_null (dc->serialization);
1991   GNUNET_assert (NULL == dc->job_queue);
1992   GNUNET_free (dc);
1993 }
1994
1995
1996 /**
1997  * Helper function to setup the download context.
1998  *
1999  * @param h handle to the file sharing subsystem
2000  * @param uri the URI of the file (determines what to download); CHK or LOC URI
2001  * @param meta known metadata for the file (can be NULL)
2002  * @param filename where to store the file, maybe NULL (then no file is
2003  *        created on disk and data must be grabbed from the callbacks)
2004  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2005  *        can be NULL (in which case we will pick a name if needed); the temporary file
2006  *        may already exist, in which case we will try to use the data that is there and
2007  *        if it is not what is desired, will overwrite it
2008  * @param offset at what offset should we start the download (typically 0)
2009  * @param length how many bytes should be downloaded starting at offset
2010  * @param anonymity anonymity level to use for the download
2011  * @param options various options
2012  * @param cctx initial value for the client context for this download
2013  * @return context that can be used to control this download
2014  */
2015 struct GNUNET_FS_DownloadContext *
2016 create_download_context (struct GNUNET_FS_Handle *h,
2017                          const struct GNUNET_FS_Uri *uri,
2018                          const struct GNUNET_CONTAINER_MetaData *meta,
2019                          const char *filename,
2020                          const char *tempname,
2021                          uint64_t offset,
2022                          uint64_t length,
2023                          uint32_t anonymity,
2024                          enum GNUNET_FS_DownloadOptions options,
2025                          void *cctx)
2026 {
2027   struct GNUNET_FS_DownloadContext *dc;
2028
2029   GNUNET_assert (GNUNET_FS_uri_test_chk (uri) || GNUNET_FS_uri_test_loc (uri));
2030   if ((offset + length < offset) ||
2031       (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)))
2032   {
2033     GNUNET_break (0);
2034     return NULL;
2035   }
2036   dc = GNUNET_new (struct GNUNET_FS_DownloadContext);
2037   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2038               "Starting download %p, %u bytes at offset %llu\n",
2039               dc,
2040               (unsigned int) length,
2041               (unsigned long long) offset);
2042   dc->h = h;
2043   dc->uri = GNUNET_FS_uri_dup (uri);
2044   dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
2045   dc->client_info = cctx;
2046   dc->start_time = GNUNET_TIME_absolute_get ();
2047   if (NULL != filename)
2048   {
2049     dc->filename = GNUNET_strdup (filename);
2050     if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2051       GNUNET_break (GNUNET_OK == GNUNET_DISK_file_size (filename,
2052                                                         &dc->old_file_size,
2053                                                         GNUNET_YES,
2054                                                         GNUNET_YES));
2055   }
2056   if (GNUNET_FS_uri_test_loc (dc->uri))
2057     GNUNET_assert (GNUNET_OK ==
2058                    GNUNET_FS_uri_loc_get_peer_identity (dc->uri, &dc->target));
2059   dc->offset = offset;
2060   dc->length = length;
2061   dc->anonymity = anonymity;
2062   dc->options = options;
2063   dc->active =
2064     GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE),
2065                                           GNUNET_NO);
2066   dc->treedepth =
2067     GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size (dc->uri));
2068   if ((NULL == filename) && (is_recursive_download (dc)))
2069   {
2070     if (NULL != tempname)
2071       dc->temp_filename = GNUNET_strdup (tempname);
2072     else
2073       dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
2074   }
2075   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2076               "Starting download `%s' of %llu bytes with tree depth %u\n",
2077               filename,
2078               (unsigned long long) length,
2079               dc->treedepth);
2080   GNUNET_assert (NULL == dc->job_queue);
2081   dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc);
2082   return dc;
2083 }
2084
2085
2086 /**
2087  * Download parts of a file.  Note that this will store
2088  * the blocks at the respective offset in the given file.  Also, the
2089  * download is still using the blocking of the underlying FS
2090  * encoding.  As a result, the download may *write* outside of the
2091  * given boundaries (if offset and length do not match the 32k FS
2092  * block boundaries). <p>
2093  *
2094  * This function should be used to focus a download towards a
2095  * particular portion of the file (optimization), not to strictly
2096  * limit the download to exactly those bytes.
2097  *
2098  * @param h handle to the file sharing subsystem
2099  * @param uri the URI of the file (determines what to download); CHK or LOC URI
2100  * @param meta known metadata for the file (can be NULL)
2101  * @param filename where to store the file, maybe NULL (then no file is
2102  *        created on disk and data must be grabbed from the callbacks)
2103  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2104  *        can be NULL (in which case we will pick a name if needed); the temporary file
2105  *        may already exist, in which case we will try to use the data that is there and
2106  *        if it is not what is desired, will overwrite it
2107  * @param offset at what offset should we start the download (typically 0)
2108  * @param length how many bytes should be downloaded starting at offset
2109  * @param anonymity anonymity level to use for the download
2110  * @param options various options
2111  * @param cctx initial value for the client context for this download
2112  * @param parent parent download to associate this download with (use NULL
2113  *        for top-level downloads; useful for manually-triggered recursive downloads)
2114  * @return context that can be used to control this download
2115  */
2116 struct GNUNET_FS_DownloadContext *
2117 GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
2118                           const struct GNUNET_FS_Uri *uri,
2119                           const struct GNUNET_CONTAINER_MetaData *meta,
2120                           const char *filename,
2121                           const char *tempname,
2122                           uint64_t offset,
2123                           uint64_t length,
2124                           uint32_t anonymity,
2125                           enum GNUNET_FS_DownloadOptions options,
2126                           void *cctx,
2127                           struct GNUNET_FS_DownloadContext *parent)
2128 {
2129   struct GNUNET_FS_DownloadContext *dc;
2130
2131   dc = create_download_context (h,
2132                                 uri,
2133                                 meta,
2134                                 filename,
2135                                 tempname,
2136                                 offset,
2137                                 length,
2138                                 anonymity,
2139                                 options,
2140                                 cctx);
2141   if (NULL == dc)
2142     return NULL;
2143   dc->parent = parent;
2144   if (NULL != parent)
2145     GNUNET_CONTAINER_DLL_insert (parent->child_head, parent->child_tail, dc);
2146   else if (0 == (GNUNET_FS_DOWNLOAD_IS_PROBE & options))
2147     dc->top =
2148       GNUNET_FS_make_top (dc->h, &GNUNET_FS_download_signal_suspend_, dc);
2149   return dc;
2150 }
2151
2152
2153 /**
2154  * Download parts of a file based on a search result.  The download
2155  * will be associated with the search result (and the association
2156  * will be preserved when serializing/deserializing the state).
2157  * If the search is stopped, the download will not be aborted but
2158  * be 'promoted' to a stand-alone download.
2159  *
2160  * As with the other download function, this will store
2161  * the blocks at the respective offset in the given file.  Also, the
2162  * download is still using the blocking of the underlying FS
2163  * encoding.  As a result, the download may *write* outside of the
2164  * given boundaries (if offset and length do not match the 32k FS
2165  * block boundaries). <p>
2166  *
2167  * The given range can be used to focus a download towards a
2168  * particular portion of the file (optimization), not to strictly
2169  * limit the download to exactly those bytes.
2170  *
2171  * @param h handle to the file sharing subsystem
2172  * @param sr the search result to use for the download (determines uri and
2173  *        meta data and associations)
2174  * @param filename where to store the file, maybe NULL (then no file is
2175  *        created on disk and data must be grabbed from the callbacks)
2176  * @param tempname where to store temporary file data, not used if filename is non-NULL;
2177  *        can be NULL (in which case we will pick a name if needed); the temporary file
2178  *        may already exist, in which case we will try to use the data that is there and
2179  *        if it is not what is desired, will overwrite it
2180  * @param offset at what offset should we start the download (typically 0)
2181  * @param length how many bytes should be downloaded starting at offset
2182  * @param anonymity anonymity level to use for the download
2183  * @param options various download options
2184  * @param cctx initial value for the client context for this download
2185  * @return context that can be used to control this download
2186  */
2187 struct GNUNET_FS_DownloadContext *
2188 GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
2189                                       struct GNUNET_FS_SearchResult *sr,
2190                                       const char *filename,
2191                                       const char *tempname,
2192                                       uint64_t offset,
2193                                       uint64_t length,
2194                                       uint32_t anonymity,
2195                                       enum GNUNET_FS_DownloadOptions options,
2196                                       void *cctx)
2197 {
2198   struct GNUNET_FS_DownloadContext *dc;
2199
2200   if ((NULL == sr) || (NULL != sr->download))
2201   {
2202     GNUNET_break (0);
2203     return NULL;
2204   }
2205   dc = create_download_context (h,
2206                                 sr->uri,
2207                                 sr->meta,
2208                                 filename,
2209                                 tempname,
2210                                 offset,
2211                                 length,
2212                                 anonymity,
2213                                 options,
2214                                 cctx);
2215   if (NULL == dc)
2216     return NULL;
2217   dc->search = sr;
2218   sr->download = dc;
2219   if (NULL != sr->probe_ctx)
2220   {
2221     GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
2222     sr->probe_ctx = NULL;
2223     GNUNET_FS_stop_probe_ping_task_ (sr);
2224   }
2225   return dc;
2226 }
2227
2228
2229 /**
2230  * Start the downloading process (by entering the queue).
2231  *
2232  * @param dc our download context
2233  */
2234 void
2235 GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
2236 {
2237   if (dc->completed == dc->length)
2238     return;
2239   if (NULL != dc->mq)
2240     return; /* already running */
2241   GNUNET_assert (NULL == dc->job_queue);
2242   GNUNET_assert (NULL == dc->task);
2243   GNUNET_assert (NULL != dc->active);
2244   dc->job_queue =
2245     GNUNET_FS_queue_ (dc->h,
2246                       &activate_fs_download,
2247                       &deactivate_fs_download,
2248                       dc,
2249                       (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE,
2250                       (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
2251                         ? GNUNET_FS_QUEUE_PRIORITY_NORMAL
2252                         : GNUNET_FS_QUEUE_PRIORITY_PROBE);
2253   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2254               "Download %p put into queue as job %p\n",
2255               dc,
2256               dc->job_queue);
2257 }
2258
2259 /**
2260  * Suspend a download.
2261  *
2262  * @param dc handle for the download
2263  */
2264 void
2265 GNUNET_FS_download_suspend (struct GNUNET_FS_DownloadContext *dc)
2266 {
2267   deactivate_fs_download (dc);
2268 }
2269
2270
2271 /**
2272  * Resume a suspended download.
2273  *
2274  * @param dc handle for the download
2275  */
2276 void
2277 GNUNET_FS_download_resume (struct GNUNET_FS_DownloadContext *dc)
2278 {
2279   struct GNUNET_FS_ProgressInfo pi;
2280
2281   pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
2282   GNUNET_FS_download_make_status_ (&pi, dc);
2283
2284   GNUNET_assert (NULL == dc->task);
2285   dc->job_queue =
2286     GNUNET_FS_queue_ (dc->h,
2287                       &activate_fs_download,
2288                       &deactivate_fs_download,
2289                       dc,
2290                       (dc->length + DBLOCK_SIZE - 1) / DBLOCK_SIZE,
2291                       (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
2292                         ? GNUNET_FS_QUEUE_PRIORITY_NORMAL
2293                         : GNUNET_FS_QUEUE_PRIORITY_PROBE);
2294 }
2295
2296
2297 /**
2298  * Stop a download (aborts if download is incomplete).
2299  *
2300  * @param dc handle for the download
2301  * @param do_delete delete files of incomplete downloads
2302  */
2303 void
2304 GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc, int do_delete)
2305 {
2306   struct GNUNET_FS_ProgressInfo pi;
2307   int have_children;
2308   int search_was_null;
2309
2310   if (NULL != dc->top)
2311     GNUNET_FS_end_top (dc->h, dc->top);
2312   if (NULL != dc->task)
2313   {
2314     GNUNET_SCHEDULER_cancel (dc->task);
2315     dc->task = NULL;
2316   }
2317   search_was_null = (NULL == dc->search);
2318   if (NULL != dc->search)
2319   {
2320     dc->search->download = NULL;
2321     GNUNET_FS_search_result_sync_ (dc->search);
2322     dc->search = NULL;
2323   }
2324   if (NULL != dc->job_queue)
2325   {
2326     GNUNET_FS_dequeue_ (dc->job_queue);
2327     dc->job_queue = NULL;
2328   }
2329   if (NULL != dc->te)
2330   {
2331     GNUNET_FS_tree_encoder_finish (dc->te, NULL);
2332     dc->te = NULL;
2333   }
2334   have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
2335   while (NULL != dc->child_head)
2336     GNUNET_FS_download_stop (dc->child_head, do_delete);
2337   if (NULL != dc->parent)
2338     GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2339                                  dc->parent->child_tail,
2340                                  dc);
2341   if (NULL != dc->serialization)
2342     GNUNET_FS_remove_sync_file_ (dc->h,
2343                                  ((NULL != dc->parent) || (! search_was_null))
2344                                    ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2345                                    : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2346                                  dc->serialization);
2347   if ((GNUNET_YES == have_children) && (NULL == dc->parent))
2348     GNUNET_FS_remove_sync_dir_ (dc->h,
2349                                 (! search_was_null)
2350                                   ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2351                                   : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2352                                 dc->serialization);
2353   pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
2354   GNUNET_FS_download_make_status_ (&pi, dc);
2355   GNUNET_FS_free_download_request_ (dc->top_request);
2356   dc->top_request = NULL;
2357   if (NULL != dc->active)
2358   {
2359     GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2360     dc->active = NULL;
2361   }
2362   if (NULL != dc->filename)
2363   {
2364     if ((dc->completed != dc->length) && (GNUNET_YES == do_delete))
2365     {
2366       if ((0 != unlink (dc->filename)) && (ENOENT != errno))
2367         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2368                                   "unlink",
2369                                   dc->filename);
2370     }
2371     GNUNET_free (dc->filename);
2372   }
2373   GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2374   GNUNET_FS_uri_destroy (dc->uri);
2375   if (NULL != dc->temp_filename)
2376   {
2377     if (0 != unlink (dc->temp_filename))
2378       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2379                                 "unlink",
2380                                 dc->temp_filename);
2381     GNUNET_free (dc->temp_filename);
2382   }
2383   GNUNET_free_non_null (dc->serialization);
2384   GNUNET_assert (NULL == dc->job_queue);
2385   GNUNET_free (dc);
2386 }
2387
2388 /* end of fs_download.c */