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