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