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