2 This file is part of GNUnet.
3 (C) 2001-2011 Christian Grothoff (and other contributing authors)
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.
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.
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.
21 * @file fs/fs_download.c
22 * @brief download methods
23 * @author Christian Grothoff
26 * - different priority for scheduling probe downloads?
29 #include "gnunet_constants.h"
30 #include "gnunet_fs_service.h"
34 #define DEBUG_DOWNLOAD GNUNET_NO
37 * Determine if the given download (options and meta data) should cause
38 * use to try to do a recursive download.
41 is_recursive_download (struct GNUNET_FS_DownloadContext *dc)
43 return (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) &&
44 ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ||
45 ( (dc->meta == NULL) &&
46 ( (NULL == dc->filename) ||
47 ( (strlen (dc->filename) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
49 strstr (dc->filename + strlen(dc->filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
50 GNUNET_FS_DIRECTORY_EXT)) ) ) ) );
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).
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
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
71 compute_disk_offset (uint64_t fsize,
76 uint64_t lsize; /* what is the size of all IBlocks for depth "i"? */
77 uint64_t loff; /* where do IBlocks for depth "i" start? */
78 unsigned int ioff; /* which IBlock corresponds to "off" at depth "i"? */
82 /* first IBlocks start at the end of file, rounded up
83 to full DBLOCK_SIZE */
84 loff = ((fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * DBLOCK_SIZE;
85 lsize = ( (fsize + DBLOCK_SIZE - 1) / DBLOCK_SIZE) * sizeof (struct ContentHashKey);
86 GNUNET_assert (0 == (off % DBLOCK_SIZE));
87 ioff = (off / DBLOCK_SIZE);
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;
96 return loff + ioff * sizeof (struct ContentHashKey);
101 * Given a block at the given offset and depth, calculate the offset
102 * for the CHK at the given index.
104 * @param offset the offset of the first
105 * DBLOCK in the subtree of the
107 * @param depth the depth of the IBLOCK in the tree, 0 for DBLOCK
108 * @param k which CHK in the IBLOCK are we
110 * @return offset if k=0, otherwise an appropriately
111 * larger value (i.e., if depth = 1,
112 * the returned value should be offset+k*DBLOCK_SIZE)
115 compute_dblock_offset (uint64_t offset,
120 uint64_t lsize; /* what is the size of the sum of all DBlocks
121 that a CHK at depth i corresponds to? */
126 for (i=1;i<depth;i++)
127 lsize *= CHK_PER_INODE;
128 return offset + k * lsize;
133 * Fill in all of the generic fields for a download event and call the
136 * @param pi structure to fill in
137 * @param dc overall download context
140 GNUNET_FS_download_make_status_ (struct GNUNET_FS_ProgressInfo *pi,
141 struct GNUNET_FS_DownloadContext *dc)
143 pi->value.download.dc = dc;
144 pi->value.download.cctx
146 pi->value.download.pctx
147 = (dc->parent == NULL) ? NULL : dc->parent->client_info;
148 pi->value.download.sctx
149 = (dc->search == NULL) ? NULL : dc->search->client_info;
150 pi->value.download.uri
152 pi->value.download.filename
154 pi->value.download.size
156 pi->value.download.duration
157 = GNUNET_TIME_absolute_get_duration (dc->start_time);
158 pi->value.download.completed
160 pi->value.download.anonymity
162 pi->value.download.eta
163 = GNUNET_TIME_calculate_eta (dc->start_time,
166 pi->value.download.is_active = (dc->client == NULL) ? GNUNET_NO : GNUNET_YES;
167 if (0 == (dc->options & GNUNET_FS_DOWNLOAD_IS_PROBE))
168 dc->client_info = dc->h->upcb (dc->h->upcb_cls,
171 dc->client_info = GNUNET_FS_search_probe_progress_ (NULL,
177 * We're ready to transmit a search request to the
178 * file-sharing service. Do it. If there is
179 * more than one request pending, try to send
180 * multiple or request another transmission.
183 * @param size number of bytes available in buf
184 * @param buf where the callee should write the message
185 * @return number of bytes written to buf
188 transmit_download_request (void *cls,
194 * Closure for iterator processing results.
196 struct ProcessResultClosure
202 GNUNET_HashCode query;
205 * Data found in P2P network.
210 * Our download context.
212 struct GNUNET_FS_DownloadContext *dc;
215 * Number of bytes in data.
222 enum GNUNET_BLOCK_Type type;
225 * Flag to indicate if this block should be stored on disk.
233 * Iterator over entries in the pending requests in the 'active' map for the
234 * reply that we just got.
236 * @param cls closure (our 'struct ProcessResultClosure')
237 * @param key query for the given value / request
238 * @param value value in the hash map (a 'struct DownloadRequest')
239 * @return GNUNET_YES (we should continue to iterate); unless serious error
242 process_result_with_request (void *cls,
243 const GNUNET_HashCode * key,
248 * We've found a matching block without downloading it.
249 * Encrypt it and pass it to our "receive" function as
250 * if we had received it from the network.
252 * @param dc download in question
253 * @param chk request this relates to
254 * @param dr request details
255 * @param block plaintext data matching request
256 * @param len number of bytes in block
257 * @param do_store should we still store the block on disk?
258 * @return GNUNET_OK on success
261 encrypt_existing_match (struct GNUNET_FS_DownloadContext *dc,
262 const struct ContentHashKey *chk,
263 struct DownloadRequest *dr,
268 struct ProcessResultClosure prc;
270 struct GNUNET_CRYPTO_AesSessionKey sk;
271 struct GNUNET_CRYPTO_AesInitializationVector iv;
272 GNUNET_HashCode query;
274 GNUNET_CRYPTO_hash_to_aes_key (&chk->key, &sk, &iv);
275 if (-1 == GNUNET_CRYPTO_aes_encrypt (block, len,
281 return GNUNET_SYSERR;
283 GNUNET_CRYPTO_hash (enc, len, &query);
284 if (0 != memcmp (&query,
286 sizeof (GNUNET_HashCode)))
289 return GNUNET_SYSERR;
292 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293 "Matching block for `%s' at offset %llu already present, no need for download!\n",
295 (unsigned long long) dr->offset);
297 /* already got it! */
301 prc.type = (0 == dr->depth)
302 ? GNUNET_BLOCK_TYPE_FS_DBLOCK
303 : GNUNET_BLOCK_TYPE_FS_IBLOCK;
304 prc.query = chk->query;
305 prc.do_store = do_store;
306 process_result_with_request (&prc,
314 * We've lost our connection with the FS service.
315 * Re-establish it and re-transmit all of our
318 * @param dc download context that is having trouble
321 try_reconnect (struct GNUNET_FS_DownloadContext *dc);
325 * We found an entry in a directory. Check if the respective child
326 * already exists and if not create the respective child download.
328 * @param cls the parent download
329 * @param filename name of the file in the directory
330 * @param uri URI of the file (CHK or LOC)
331 * @param meta meta data of the file
332 * @param length number of bytes in data
333 * @param data contents of the file (or NULL if they were not inlined)
336 trigger_recursive_download (void *cls,
337 const char *filename,
338 const struct GNUNET_FS_Uri *uri,
339 const struct GNUNET_CONTAINER_MetaData *meta,
345 * We're done downloading a directory. Open the file and
346 * trigger all of the (remaining) child downloads.
348 * @param dc context of download that just completed
351 full_recursive_download (struct GNUNET_FS_DownloadContext *dc)
356 struct GNUNET_DISK_FileHandle *h;
357 struct GNUNET_DISK_MapHandle *m;
359 size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri);
360 size = (size_t) size64;
361 if (size64 != (uint64_t) size)
363 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364 _("Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n"));
367 if (dc->filename != NULL)
369 h = GNUNET_DISK_file_open (dc->filename,
370 GNUNET_DISK_OPEN_READ,
371 GNUNET_DISK_PERM_NONE);
375 GNUNET_assert (dc->temp_filename != NULL);
376 h = GNUNET_DISK_file_open (dc->temp_filename,
377 GNUNET_DISK_OPEN_READ,
378 GNUNET_DISK_PERM_NONE);
382 data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size);
385 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
386 _("Directory too large for system address space\n"));
390 GNUNET_FS_directory_list_contents (size,
393 &trigger_recursive_download,
395 GNUNET_DISK_file_unmap (m);
397 GNUNET_DISK_file_close (h);
398 if (dc->filename == NULL)
400 if (0 != UNLINK (dc->temp_filename))
401 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
404 GNUNET_free (dc->temp_filename);
405 dc->temp_filename = NULL;
411 * Check if all child-downloads have completed (or trigger them if
412 * necessary) and once we're completely done, signal completion (and
413 * possibly recurse to parent). This function MUST be called when the
414 * download of a file itself is done or when the download of a file is
415 * done and then later a direct child download has completed (and
416 * hence this download may complete itself).
418 * @param dc download to check for completion of children
421 check_completed (struct GNUNET_FS_DownloadContext *dc)
423 struct GNUNET_FS_ProgressInfo pi;
424 struct GNUNET_FS_DownloadContext *pos;
426 /* first, check if we need to download children */
427 if ( (dc->child_head == NULL) &&
428 (is_recursive_download (dc)) )
429 full_recursive_download (dc);
430 /* then, check if children are done already */
431 pos = dc->child_head;
434 if ( (pos->emsg == NULL) &&
435 (pos->completed < pos->length) )
436 return; /* not done yet */
437 if ( (pos->child_head != NULL) &&
438 (pos->has_finished != GNUNET_YES) )
439 return; /* not transitively done yet */
442 /* All of our children are done, so mark this download done */
443 dc->has_finished = GNUNET_YES;
444 if (dc->job_queue != NULL)
446 GNUNET_FS_dequeue_ (dc->job_queue);
447 dc->job_queue = NULL;
449 GNUNET_FS_download_sync_ (dc);
451 /* signal completion */
452 pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED;
453 GNUNET_FS_download_make_status_ (&pi, dc);
455 /* let parent know */
456 if (dc->parent != NULL)
457 check_completed (dc->parent);
462 * We got a block of plaintext data (from the meta data).
463 * Try it for upward reconstruction of the data. On success,
464 * the top-level block will move to state BRS_DOWNLOAD_UP.
466 * @param dr one of our request entries
467 * @param data plaintext data, starting from the beginning of the file
468 * @param data_len number of bytes in data
471 try_match_block (struct GNUNET_FS_DownloadContext *dc,
472 struct DownloadRequest *dr,
476 struct GNUNET_FS_ProgressInfo pi;
478 char enc[DBLOCK_SIZE];
479 struct ContentHashKey chks[CHK_PER_INODE];
480 struct ContentHashKey in_chk;
481 struct GNUNET_CRYPTO_AesSessionKey sk;
482 struct GNUNET_CRYPTO_AesInitializationVector iv;
484 struct DownloadRequest *drc;
485 struct GNUNET_DISK_FileHandle *fh;
492 odata_len = data_len;
493 if (BRS_DOWNLOAD_UP == dr->state)
497 complete = GNUNET_YES;
498 for (i=0;i<dr->num_children;i++)
500 drc = dr->children[i];
504 if (drc->state != BRS_RECONSTRUCT_META_UP)
505 complete = GNUNET_NO;
509 if (GNUNET_YES != complete)
511 data = (const char*) chks;
512 dlen = dr->num_children * sizeof (struct ContentHashKey);
516 if (dr->offset > data_len)
518 dlen = GNUNET_MIN (data_len - dr->offset,
521 GNUNET_CRYPTO_hash (&data[dr->offset],
524 GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv);
525 if (-1 == GNUNET_CRYPTO_aes_encrypt (&data[dr->offset], dlen,
533 GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query);
538 dr->state = BRS_RECONSTRUCT_META_UP;
541 if (0 != memcmp (&in_chk,
543 sizeof (struct ContentHashKey)))
545 /* other peer provided bogus meta data */
549 /* write block to disk */
550 fn = dc->filename != NULL
553 fh = GNUNET_DISK_file_open (fn,
554 GNUNET_DISK_OPEN_READWRITE |
555 GNUNET_DISK_OPEN_CREATE |
556 GNUNET_DISK_OPEN_TRUNCATE,
557 GNUNET_DISK_PERM_USER_READ |
558 GNUNET_DISK_PERM_USER_WRITE |
559 GNUNET_DISK_PERM_GROUP_READ |
560 GNUNET_DISK_PERM_OTHER_READ);
563 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
566 GNUNET_asprintf (&dc->emsg,
567 _("Failed to open file `%s' for writing"),
569 GNUNET_DISK_file_close (fh);
570 dr->state = BRS_ERROR;
571 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
572 pi.value.download.specifics.error.message = dc->emsg;
573 GNUNET_FS_download_make_status_ (&pi, dc);
577 GNUNET_DISK_file_write (fh,
581 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
584 GNUNET_asprintf (&dc->emsg,
585 _("Failed to open file `%s' for writing"),
587 GNUNET_DISK_file_close (fh);
588 dr->state = BRS_ERROR;
589 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
590 pi.value.download.specifics.error.message = dc->emsg;
591 GNUNET_FS_download_make_status_ (&pi, dc);
594 GNUNET_DISK_file_close (fh);
596 dr->state = BRS_DOWNLOAD_UP;
597 dc->completed = dc->length;
598 GNUNET_FS_download_sync_ (dc);
599 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
600 pi.value.download.specifics.progress.data = data;
601 pi.value.download.specifics.progress.offset = 0;
602 pi.value.download.specifics.progress.data_len = dlen;
603 pi.value.download.specifics.progress.depth = 0;
604 GNUNET_FS_download_make_status_ (&pi, dc);
605 if (0 != truncate (dc->filename,
606 GNUNET_ntohll (dc->uri->data.chk.file_length)))
607 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
610 check_completed (dc);
613 /* how did we get here? */
621 * Type of a function that libextractor calls for each
622 * meta data item found. If we find full data meta data,
623 * call 'try_match_block' on it.
625 * @param cls our 'struct GNUNET_FS_DownloadContext*'
626 * @param plugin_name name of the plugin that produced this value;
627 * special values can be used (i.e. '<zlib>' for zlib being
628 * used in the main libextractor library and yielding
630 * @param type libextractor-type describing the meta data
631 * @param format basic format information about data
632 * @param data_mime_type mime-type of data (not of the original file);
633 * can be NULL (if mime-type is not known)
634 * @param data actual meta-data found
635 * @param data_len number of bytes in data
636 * @return 0 to continue extracting, 1 to abort
639 match_full_data (void *cls,
640 const char *plugin_name,
641 enum EXTRACTOR_MetaType type,
642 enum EXTRACTOR_MetaFormat format,
643 const char *data_mime_type,
647 struct GNUNET_FS_DownloadContext *dc = cls;
649 if (type != EXTRACTOR_METATYPE_GNUNET_FULL_DATA)
652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653 "Found %u bytes of FD!\n",
654 (unsigned int) data_len);
656 if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len)
659 return 1; /* bogus meta data */
670 * Set the state of the given download request to
671 * BRS_DOWNLOAD_UP and propagate it up the tree.
673 * @param dr download request that is done
676 propagate_up (struct DownloadRequest *dr)
682 dr->state = BRS_DOWNLOAD_UP;
686 for (i=0;i<dr->num_children;i++)
687 if (dr->children[i]->state != BRS_DOWNLOAD_UP)
690 while (i == dr->num_children);
695 * Try top-down reconstruction. Before, the given request node
696 * must have the state BRS_CHK_SET. Afterwards, more nodes may
697 * have that state or advanced to BRS_DOWNLOAD_DOWN or even
698 * BRS_DOWNLOAD_UP. It is also possible to get BRS_ERROR on the
701 * @param dc overall download this block belongs to
702 * @param dr block to reconstruct
705 try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
706 struct DownloadRequest *dr)
709 char block[DBLOCK_SIZE];
714 unsigned int chk_off;
715 struct DownloadRequest *drc;
716 uint64_t child_block_size;
717 const struct ContentHashKey *chks;
720 GNUNET_assert (dc->rfh != NULL);
721 GNUNET_assert (dr->state == BRS_CHK_SET);
722 total = GNUNET_FS_uri_chk_get_file_size (dc->uri);
723 GNUNET_assert (dr->depth < dc->treedepth);
724 len = GNUNET_FS_tree_calculate_block_size (total,
727 GNUNET_assert (len <= DBLOCK_SIZE);
728 off = compute_disk_offset (total,
731 if (dc->old_file_size < off + len)
732 return; /* failure */
734 GNUNET_DISK_file_seek (dc->rfh,
736 GNUNET_DISK_SEEK_SET) )
738 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
741 return; /* failure */
744 GNUNET_DISK_file_read (dc->rfh,
748 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
751 return; /* failure */
753 GNUNET_CRYPTO_hash (block, len, &key);
754 if (0 != memcmp (&key,
756 sizeof (GNUNET_HashCode)))
757 return; /* mismatch */
759 encrypt_existing_match (dc,
766 /* hash matches but encrypted block does not, really bad */
767 dr->state = BRS_ERROR;
769 while (dr->parent != NULL)
772 dr->state = BRS_ERROR;
777 dr->state = BRS_DOWNLOAD_DOWN;
779 /* set CHKs for children */
780 up_done = GNUNET_YES;
781 chks = (const struct ContentHashKey*) block;
782 for (i=0;i<dr->num_children;i++)
784 drc = dr->children[i];
785 GNUNET_assert (drc->offset >= dr->offset);
786 child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth);
787 GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);
788 chk_off = (drc->offset - dr->offset) / child_block_size;
789 GNUNET_assert (drc->state == BRS_INIT);
790 drc->state = BRS_CHK_SET;
791 drc->chk = chks[chk_off];
792 try_top_down_reconstruction (dc, drc);
793 if (drc->state != BRS_DOWNLOAD_UP)
794 up_done = GNUNET_NO; /* children not all done */
796 if (up_done == GNUNET_YES)
797 propagate_up (dr); /* children all done (or no children...) */
802 * Schedule the download of the specified block in the tree.
804 * @param dc overall download this block belongs to
805 * @param chk content-hash-key of the block
806 * @param offset offset of the block in the file
807 * (for IBlocks, the offset is the lowest
808 * offset of any DBlock in the subtree under
810 * @param depth depth of the block, 0 is the root of the tree
813 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
814 struct DownloadRequest *dr)
823 case BRS_RECONSTRUCT_DOWN:
826 case BRS_RECONSTRUCT_META_UP:
829 case BRS_RECONSTRUCT_UP:
833 /* normal case, start download */
835 case BRS_DOWNLOAD_DOWN:
836 for (i=0;i<dr->num_children;i++)
837 schedule_block_download (dc, dr->children[i]);
839 case BRS_DOWNLOAD_UP:
847 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
848 "Scheduling download at offset %llu and depth %u for `%s'\n",
849 (unsigned long long) dr->offset,
851 GNUNET_h2s (&dr->chk.query));
853 GNUNET_CONTAINER_DLL_insert (dc->pending_head,
856 dr->is_pending = GNUNET_YES;
857 GNUNET_CONTAINER_multihashmap_put (dc->active,
860 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
861 if (dc->client == NULL)
862 return; /* download not active */
864 dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
865 sizeof (struct SearchMessage),
866 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
868 &transmit_download_request,
873 #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
876 * We found an entry in a directory. Check if the respective child
877 * already exists and if not create the respective child download.
879 * @param cls the parent download
880 * @param filename name of the file in the directory
881 * @param uri URI of the file (CHK or LOC)
882 * @param meta meta data of the file
883 * @param length number of bytes in data
884 * @param data contents of the file (or NULL if they were not inlined)
887 trigger_recursive_download (void *cls,
888 const char *filename,
889 const struct GNUNET_FS_Uri *uri,
890 const struct GNUNET_CONTAINER_MetaData *meta,
894 struct GNUNET_FS_DownloadContext *dc = cls;
895 struct GNUNET_FS_DownloadContext *cpos;
896 // struct GNUNET_DISK_FileHandle *fh;
898 // const char *real_name;
907 return; /* entry for the directory itself */
908 cpos = dc->child_head;
911 if ( (GNUNET_FS_uri_test_equal (uri,
913 ( (filename != NULL) &&
914 (0 == strcmp (cpos->filename,
920 return; /* already exists */
922 if (NULL == filename)
924 fn = GNUNET_FS_meta_data_suggest_filename (meta);
927 us = GNUNET_FS_uri_to_string (uri);
928 fn = GNUNET_strdup (&us [strlen (GNUNET_FS_URI_CHK_PREFIX)]);
931 else if (fn[0] == '.')
934 us = GNUNET_FS_uri_to_string (uri);
935 GNUNET_asprintf (&fn,
937 &us[strlen (GNUNET_FS_URI_CHK_PREFIX)], ext);
941 /* change '\' to '/' (this should have happened
942 during insertion, but malicious peers may
943 not have done this) */
944 while (NULL != (pos = strstr (fn, "\\")))
946 /* remove '../' everywhere (again, well-behaved
947 peers don't do this, but don't trust that
948 we did not get something nasty) */
949 while (NULL != (pos = strstr (fn, "../")))
957 if (dc->filename == NULL)
963 dn = GNUNET_strdup (dc->filename);
964 GNUNET_break ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
966 strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
967 GNUNET_FS_DIRECTORY_EXT)) );
968 if ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
970 strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
971 GNUNET_FS_DIRECTORY_EXT)) )
972 dn[strlen(dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0';
973 if ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) &&
974 ( (strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) ||
976 strstr (filename + strlen(filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
977 GNUNET_FS_DIRECTORY_EXT)) ) )
979 GNUNET_asprintf (&full_name,
984 GNUNET_FS_DIRECTORY_EXT);
988 GNUNET_asprintf (&full_name,
996 if ( (full_name != NULL) &&
998 GNUNET_DISK_directory_create_for_file (full_name)) )
1000 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1001 _("Failed to create directory for recursive download of `%s'\n"),
1003 GNUNET_free (full_name);
1004 GNUNET_free_non_null (fn);
1012 if (GNUNET_FS_uri_chk_get_file_size (uri) == length)
1014 if (full_name == NULL)
1016 temp_name = GNUNET_DISK_mktemp ("gnunet-download-trd");
1017 real_name = temp_name;
1021 real_name = full_name;
1023 /* write to disk, then trigger normal download which will instantly progress to completion */
1024 fh = GNUNET_DISK_file_open (real_name,
1025 GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE | GNUNET_DISK_OPEN_CREATE,
1026 GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE);
1029 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1032 GNUNET_free (full_name);
1033 GNUNET_free_non_null (fn);
1037 GNUNET_DISK_file_write (fh,
1041 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
1046 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1047 "Wrote %llu bytes of plaintext from meta data to `%s' for validation\n",
1048 (unsigned long long) length,
1051 GNUNET_DISK_file_close (fh);
1055 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1056 _("Length mismatch between supplied plaintext and expected file size (%llu != %llu)\n"),
1057 (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri),
1058 (unsigned long long) length);
1063 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1064 "Triggering recursive download of size %llu with %u bytes MD\n",
1065 (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri),
1066 (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (meta));
1068 GNUNET_FS_download_start (dc->h,
1071 full_name, temp_name,
1073 GNUNET_FS_uri_chk_get_file_size (uri),
1078 GNUNET_free_non_null (full_name);
1079 GNUNET_free_non_null (temp_name);
1080 GNUNET_free_non_null (fn);
1085 * (recursively) free download request structure
1087 * @param dr request to free
1090 GNUNET_FS_free_download_request_ (struct DownloadRequest *dr)
1096 for (i=0;i<dr->num_children;i++)
1097 GNUNET_FS_free_download_request_ (dr->children[i]);
1098 GNUNET_free_non_null (dr->children);
1104 * Iterator over entries in the pending requests in the 'active' map for the
1105 * reply that we just got.
1107 * @param cls closure (our 'struct ProcessResultClosure')
1108 * @param key query for the given value / request
1109 * @param value value in the hash map (a 'struct DownloadRequest')
1110 * @return GNUNET_YES (we should continue to iterate); unless serious error
1113 process_result_with_request (void *cls,
1114 const GNUNET_HashCode *key,
1117 struct ProcessResultClosure *prc = cls;
1118 struct DownloadRequest *dr = value;
1119 struct GNUNET_FS_DownloadContext *dc = prc->dc;
1120 struct DownloadRequest *drc;
1121 struct GNUNET_DISK_FileHandle *fh = NULL;
1122 struct GNUNET_CRYPTO_AesSessionKey skey;
1123 struct GNUNET_CRYPTO_AesInitializationVector iv;
1125 struct GNUNET_FS_ProgressInfo pi;
1130 struct ContentHashKey *chk;
1133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1134 "Received block `%s' matching pending request at depth %u and offset %llu/%llu\n",
1137 (unsigned long long) dr->offset,
1138 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1141 bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll (dc->uri->data.chk.file_length),
1144 if (prc->size != bs)
1146 GNUNET_asprintf (&dc->emsg,
1147 _("Internal error or bogus download URI (expected %u bytes at depth %u and offset %llu/%llu, got %u bytes)\n"),
1150 (unsigned long long) dr->offset,
1151 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length),
1153 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1156 while (dr->parent != NULL)
1158 dr->state = BRS_ERROR;
1161 dr->state = BRS_ERROR;
1165 (void) GNUNET_CONTAINER_multihashmap_remove (dc->active,
1168 if (GNUNET_YES == dr->is_pending)
1170 GNUNET_CONTAINER_DLL_remove (dc->pending_head,
1173 dr->is_pending = GNUNET_NO;
1177 GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv);
1178 if (-1 == GNUNET_CRYPTO_aes_decrypt (prc->data,
1185 dc->emsg = GNUNET_strdup (_("internal error decrypting content"));
1188 off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
1192 if ( ( GNUNET_YES == prc->do_store) &&
1193 ( (dc->filename != NULL) ||
1194 (is_recursive_download (dc)) ) &&
1195 ( (dr->depth == dc->treedepth) ||
1196 (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES)) ) )
1198 fh = GNUNET_DISK_file_open (dc->filename != NULL
1200 : dc->temp_filename,
1201 GNUNET_DISK_OPEN_READWRITE |
1202 GNUNET_DISK_OPEN_CREATE,
1203 GNUNET_DISK_PERM_USER_READ |
1204 GNUNET_DISK_PERM_USER_WRITE |
1205 GNUNET_DISK_PERM_GROUP_READ |
1206 GNUNET_DISK_PERM_OTHER_READ);
1209 GNUNET_asprintf (&dc->emsg,
1210 _("Download failed: could not open file `%s': %s\n"),
1216 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1217 "Saving decrypted block to disk at offset %llu\n",
1218 (unsigned long long) off);
1221 GNUNET_DISK_file_seek (fh,
1223 GNUNET_DISK_SEEK_SET) ) )
1225 GNUNET_asprintf (&dc->emsg,
1226 _("Failed to seek to offset %llu in file `%s': %s\n"),
1227 (unsigned long long) off,
1233 GNUNET_DISK_file_write (fh,
1237 GNUNET_asprintf (&dc->emsg,
1238 _("Failed to write block of %u bytes at offset %llu in file `%s': %s\n"),
1239 (unsigned int) prc->size,
1240 (unsigned long long) off,
1245 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
1251 /* DBLOCK, update progress and try recursion if applicable */
1253 if (dr->offset < dc->offset)
1255 /* starting offset begins in the middle of pt,
1256 do not count first bytes as progress */
1257 GNUNET_assert (app > (dc->offset - dr->offset));
1258 app -= (dc->offset - dr->offset);
1260 if (dr->offset + prc->size > dc->offset + dc->length)
1262 /* end of block is after relevant range,
1263 do not count last bytes as progress */
1264 GNUNET_assert (app > (dr->offset + prc->size) - (dc->offset + dc->length));
1265 app -= (dr->offset + prc->size) - (dc->offset + dc->length);
1267 dc->completed += app;
1269 /* do recursive download if option is set and either meta data
1270 says it is a directory or if no meta data is given AND filename
1271 ends in '.gnd' (top-level case) */
1272 if (is_recursive_download (dc))
1273 GNUNET_FS_directory_list_contents (prc->size,
1276 &trigger_recursive_download,
1280 dr->state = BRS_DOWNLOAD_DOWN;
1281 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1282 pi.value.download.specifics.progress.data = pt;
1283 pi.value.download.specifics.progress.offset = dr->offset;
1284 pi.value.download.specifics.progress.data_len = prc->size;
1285 pi.value.download.specifics.progress.depth = dr->depth;
1286 GNUNET_FS_download_make_status_ (&pi, dc);
1287 GNUNET_assert (dc->completed <= dc->length);
1291 if (dc->completed == dc->length)
1293 /* download completed, signal */
1295 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1296 "Download completed, truncating file to desired length %llu\n",
1297 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1299 /* truncate file to size (since we store IBlocks at the end) */
1300 if (dc->filename != NULL)
1302 if (0 != truncate (dc->filename,
1303 GNUNET_ntohll (dc->uri->data.chk.file_length)))
1304 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1308 GNUNET_assert (dr->depth == 0);
1309 check_completed (dc);
1313 /* bottom of the tree, no child downloads possible, just sync */
1314 GNUNET_FS_download_sync_ (dc);
1319 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1320 "Triggering downloads of children (this block was at depth %u and offset %llu)\n",
1322 (unsigned long long) dr->offset);
1324 GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey)));
1325 chk = (struct ContentHashKey*) pt;
1326 for (i=(prc->size / sizeof(struct ContentHashKey))-1;i>=0;i--)
1328 off = compute_dblock_offset (dr->offset,
1331 drc = dr->children[i];
1336 drc->state = BRS_CHK_SET;
1337 schedule_block_download (dc, drc);
1339 case BRS_RECONSTRUCT_DOWN:
1342 case BRS_RECONSTRUCT_META_UP:
1345 case BRS_RECONSTRUCT_UP:
1351 case BRS_DOWNLOAD_DOWN:
1354 case BRS_DOWNLOAD_UP:
1365 GNUNET_FS_download_sync_ (dc);
1370 GNUNET_DISK_file_close (fh);
1371 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1372 pi.value.download.specifics.error.message = dc->emsg;
1373 GNUNET_FS_download_make_status_ (&pi, dc);
1374 /* abort all pending requests */
1377 GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1380 GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1382 GNUNET_FS_free_download_request_ (dc->top_request);
1383 dc->top_request = NULL;
1384 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
1386 dc->pending_head = NULL;
1387 dc->pending_tail = NULL;
1388 GNUNET_FS_download_sync_ (dc);
1394 * Process a download result.
1396 * @param dc our download context
1397 * @param type type of the result
1398 * @param data the (encrypted) response
1399 * @param size size of data
1402 process_result (struct GNUNET_FS_DownloadContext *dc,
1403 enum GNUNET_BLOCK_Type type,
1407 struct ProcessResultClosure prc;
1413 prc.do_store = GNUNET_YES;
1414 GNUNET_CRYPTO_hash (data, size, &prc.query);
1416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1417 "Received result for query `%s' from `%s'-service\n",
1418 GNUNET_h2s (&prc.query),
1421 GNUNET_CONTAINER_multihashmap_get_multiple (dc->active,
1423 &process_result_with_request,
1429 * Type of a function to call when we receive a message
1432 * @param cls closure
1433 * @param msg message received, NULL on timeout or fatal error
1436 receive_results (void *cls,
1437 const struct GNUNET_MessageHeader * msg)
1439 struct GNUNET_FS_DownloadContext *dc = cls;
1440 const struct PutMessage *cm;
1443 if ( (NULL == msg) ||
1444 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
1445 (sizeof (struct PutMessage) > ntohs(msg->size)) )
1447 GNUNET_break (msg == NULL);
1451 msize = ntohs(msg->size);
1452 cm = (const struct PutMessage*) msg;
1456 msize - sizeof (struct PutMessage));
1457 if (dc->client == NULL)
1458 return; /* fatal error */
1459 /* continue receiving */
1460 GNUNET_CLIENT_receive (dc->client,
1463 GNUNET_TIME_UNIT_FOREVER_REL);
1469 * We're ready to transmit a search request to the
1470 * file-sharing service. Do it. If there is
1471 * more than one request pending, try to send
1472 * multiple or request another transmission.
1474 * @param cls closure
1475 * @param size number of bytes available in buf
1476 * @param buf where the callee should write the message
1477 * @return number of bytes written to buf
1480 transmit_download_request (void *cls,
1484 struct GNUNET_FS_DownloadContext *dc = cls;
1486 struct SearchMessage *sm;
1487 struct DownloadRequest *dr;
1493 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1494 "Transmitting download request failed, trying to reconnect\n");
1499 GNUNET_assert (size >= sizeof (struct SearchMessage));
1502 while ( (NULL != (dr = dc->pending_head)) &&
1503 (size >= msize + sizeof (struct SearchMessage)) )
1506 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1507 "Transmitting download request for `%s' to `%s'-service\n",
1508 GNUNET_h2s (&dr->chk.query),
1511 memset (sm, 0, sizeof (struct SearchMessage));
1512 sm->header.size = htons (sizeof (struct SearchMessage));
1513 sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1514 if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY))
1515 sm->options = htonl (1);
1517 sm->options = htonl (0);
1519 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK);
1521 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK);
1522 sm->anonymity_level = htonl (dc->anonymity);
1523 sm->target = dc->target.hashPubKey;
1524 sm->query = dr->chk.query;
1525 GNUNET_CONTAINER_DLL_remove (dc->pending_head,
1528 dr->is_pending = GNUNET_NO;
1529 msize += sizeof (struct SearchMessage);
1532 if (dc->pending_head != NULL)
1534 dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1535 sizeof (struct SearchMessage),
1536 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1538 &transmit_download_request,
1540 GNUNET_assert (dc->th != NULL);
1547 * Reconnect to the FS service and transmit our queries NOW.
1549 * @param cls our download context
1553 do_reconnect (void *cls,
1554 const struct GNUNET_SCHEDULER_TaskContext *tc)
1556 struct GNUNET_FS_DownloadContext *dc = cls;
1557 struct GNUNET_CLIENT_Connection *client;
1559 dc->task = GNUNET_SCHEDULER_NO_TASK;
1560 client = GNUNET_CLIENT_connect ("fs",
1564 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1565 "Connecting to `%s'-service failed, will try again.\n",
1570 dc->client = client;
1571 if (dc->pending_head != NULL)
1573 dc->th = GNUNET_CLIENT_notify_transmit_ready (client,
1574 sizeof (struct SearchMessage),
1575 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1577 &transmit_download_request,
1579 GNUNET_assert (dc->th != NULL);
1581 GNUNET_CLIENT_receive (client,
1584 GNUNET_TIME_UNIT_FOREVER_REL);
1589 * Add entries to the pending list.
1591 * @param cls our download context
1593 * @param entry entry of type "struct DownloadRequest"
1597 retry_entry (void *cls,
1598 const GNUNET_HashCode *key,
1601 struct GNUNET_FS_DownloadContext *dc = cls;
1602 struct DownloadRequest *dr = entry;
1604 GNUNET_CONTAINER_DLL_insert (dc->pending_head,
1607 dr->is_pending = GNUNET_YES;
1613 * We've lost our connection with the FS service.
1614 * Re-establish it and re-transmit all of our
1617 * @param dc download context that is having trouble
1620 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1623 if (NULL != dc->client)
1626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1627 "Moving all requests back to pending list\n");
1631 GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1634 /* full reset of the pending list */
1635 dc->pending_head = NULL;
1636 dc->pending_tail = NULL;
1637 GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1640 GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1644 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1645 "Will try to reconnect in 1s\n");
1648 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
1655 * We're allowed to ask the FS service for our blocks. Start the download.
1657 * @param cls the 'struct GNUNET_FS_DownloadContext'
1658 * @param client handle to use for communcation with FS (we must destroy it!)
1661 activate_fs_download (void *cls,
1662 struct GNUNET_CLIENT_Connection *client)
1664 struct GNUNET_FS_DownloadContext *dc = cls;
1665 struct GNUNET_FS_ProgressInfo pi;
1668 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1669 "Download activated\n");
1671 GNUNET_assert (NULL != client);
1672 GNUNET_assert (dc->client == NULL);
1673 GNUNET_assert (dc->th == NULL);
1674 dc->client = client;
1675 GNUNET_CLIENT_receive (client,
1678 GNUNET_TIME_UNIT_FOREVER_REL);
1679 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1680 GNUNET_FS_download_make_status_ (&pi, dc);
1681 GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1685 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1686 "Asking for transmission to FS service\n");
1688 if (dc->pending_head != NULL)
1690 dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1691 sizeof (struct SearchMessage),
1692 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1694 &transmit_download_request,
1696 GNUNET_assert (dc->th != NULL);
1702 * We must stop to ask the FS service for our blocks. Pause the download.
1704 * @param cls the 'struct GNUNET_FS_DownloadContext'
1707 deactivate_fs_download (void *cls)
1709 struct GNUNET_FS_DownloadContext *dc = cls;
1710 struct GNUNET_FS_ProgressInfo pi;
1713 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1714 "Download deactivated\n");
1718 GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1721 if (NULL != dc->client)
1723 GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1726 pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1727 GNUNET_FS_download_make_status_ (&pi, dc);
1732 * (recursively) Create a download request structure.
1734 * @param parent parent of the current entry
1735 * @param depth depth of the current entry, 0 are the DBLOCKs,
1736 * top level block is 'dc->treedepth - 1'
1737 * @param dr_offset offset in the original file this block maps to
1738 * (as in, offset of the first byte of the first DBLOCK
1739 * in the subtree rooted in the returned download request tree)
1740 * @param file_start_offset desired starting offset for the download
1741 * in the original file; requesting tree should not contain
1742 * DBLOCKs prior to the file_start_offset
1743 * @param file_length desired number of bytes the user wanted to access
1744 * (from file_start_offset). Resulting tree should not contain
1745 * DBLOCKs after file_start_offset + file_length.
1746 * @return download request tree for the given range of DBLOCKs at
1747 * the specified depth
1749 static struct DownloadRequest *
1750 create_download_request (struct DownloadRequest *parent,
1753 uint64_t file_start_offset,
1754 uint64_t desired_length)
1756 struct DownloadRequest *dr;
1758 unsigned int head_skip;
1759 uint64_t child_block_size;
1761 dr = GNUNET_malloc (sizeof (struct DownloadRequest));
1762 dr->parent = parent;
1764 dr->offset = dr_offset;
1767 child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
1769 /* calculate how many blocks at this level are not interesting
1770 from the start (rounded down), either because of the requested
1771 file offset or because this IBlock is further along */
1772 if (dr_offset < file_start_offset)
1773 head_skip = file_start_offset / child_block_size;
1775 head_skip = dr_offset / child_block_size;
1777 /* calculate index of last block at this level that is interesting (rounded up) */
1778 dr->num_children = file_start_offset + desired_length / child_block_size;
1779 if (dr->num_children * child_block_size < file_start_offset + desired_length)
1780 dr->num_children++; /* round up */
1782 /* now we can get the total number of children for this block */
1783 dr->num_children -= head_skip;
1784 if (dr->num_children > CHK_PER_INODE)
1785 dr->num_children = CHK_PER_INODE; /* cap at max */
1787 /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
1788 GNUNET_assert (dr->num_children > 0);
1790 dr->children = GNUNET_malloc (dr->num_children *
1791 sizeof (struct DownloadRequest *));
1792 for (i=0;i<dr->num_children;i++)
1793 dr->children[i] = create_download_request (dr,
1795 dr_offset + i * child_block_size,
1804 * Continuation after a possible attempt to reconstruct
1805 * the current IBlock from the existing file.
1807 * @param cls the 'struct ReconstructContext'
1808 * @param tc scheduler context
1811 reconstruct_cont (void *cls,
1812 const struct GNUNET_SCHEDULER_TaskContext *tc)
1814 struct GNUNET_FS_DownloadContext *dc = cls;
1816 /* clean up state from tree encoder */
1819 GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
1822 if (dc->task != GNUNET_SCHEDULER_NO_TASK)
1824 GNUNET_SCHEDULER_cancel (dc->task);
1825 dc->task = GNUNET_SCHEDULER_NO_TASK;
1827 if (dc->rfh != NULL)
1829 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
1832 /* start "normal" download */
1833 schedule_block_download (dc,
1839 * Task requesting the next block from the tree encoder.
1841 * @param tc task context
1844 get_next_block (void *cls,
1845 const struct GNUNET_SCHEDULER_TaskContext *tc)
1847 struct GNUNET_FS_DownloadContext *dc = cls;
1849 dc->task = GNUNET_SCHEDULER_NO_TASK;
1850 GNUNET_FS_tree_encoder_next (dc->te);
1856 * Function called asking for the current (encoded)
1857 * block to be processed. After processing the
1858 * client should either call "GNUNET_FS_tree_encode_next"
1859 * or (on error) "GNUNET_FS_tree_encode_finish".
1861 * This function checks if the content on disk matches
1862 * the expected content based on the URI.
1864 * @param cls closure
1865 * @param chk content hash key for the block
1866 * @param offset offset of the block
1867 * @param depth depth of the block, 0 for DBLOCK
1868 * @param type type of the block (IBLOCK or DBLOCK)
1869 * @param block the (encrypted) block
1870 * @param block_size size of block (in bytes)
1873 reconstruct_cb (void *cls,
1874 const struct ContentHashKey *chk,
1877 enum GNUNET_BLOCK_Type type,
1879 uint16_t block_size)
1881 struct GNUNET_FS_DownloadContext *dc = cls;
1882 struct GNUNET_FS_ProgressInfo pi;
1883 struct DownloadRequest *dr;
1887 /* find corresponding request entry */
1888 dr = dc->top_request;
1889 while (dr->depth > depth)
1891 blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1892 chld = (offset - dr->offset) / blen;
1893 GNUNET_assert (chld < dr->num_children);
1894 dr = dr->children[chld];
1900 case BRS_RECONSTRUCT_DOWN:
1902 case BRS_RECONSTRUCT_META_UP:
1904 case BRS_RECONSTRUCT_UP:
1907 if (0 == memcmp (chk,
1909 sizeof (struct ContentHashKey)))
1911 /* block matches, hence tree below matches;
1912 this request is done! */
1913 dr->state = BRS_DOWNLOAD_UP;
1914 /* calculate how many bytes of payload this block
1916 blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1917 /* how many of those bytes are in the requested range? */
1918 blen = GNUNET_MIN (blen,
1919 dc->length + dc->offset - dr->offset);
1920 /* signal progress */
1921 dc->completed += blen;
1922 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1923 pi.value.download.specifics.progress.data = NULL;
1924 pi.value.download.specifics.progress.offset = offset;
1925 pi.value.download.specifics.progress.data_len = 0;
1926 pi.value.download.specifics.progress.depth = 0;
1927 GNUNET_FS_download_make_status_ (&pi, dc);
1933 case BRS_DOWNLOAD_DOWN:
1935 case BRS_DOWNLOAD_UP:
1943 if ( (dr == dc->top_request) &&
1944 (dr->state == BRS_DOWNLOAD_UP) )
1946 check_completed (dc);
1949 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
1955 * Function called by the tree encoder to obtain a block of plaintext
1956 * data (for the lowest level of the tree).
1958 * @param cls our 'struct ReconstructContext'
1959 * @param offset identifies which block to get
1960 * @param max (maximum) number of bytes to get; returning
1961 * fewer will also cause errors
1962 * @param buf where to copy the plaintext buffer
1963 * @param emsg location to store an error message (on error)
1964 * @return number of bytes copied to buf, 0 on error
1967 fh_reader (void *cls,
1973 struct GNUNET_FS_DownloadContext *dc = cls;
1974 struct GNUNET_DISK_FileHandle *fh = dc->rfh;
1979 GNUNET_DISK_file_seek (fh,
1981 GNUNET_DISK_SEEK_SET))
1983 *emsg = GNUNET_strdup (strerror (errno));
1986 ret = GNUNET_DISK_file_read (fh, buf, max);
1989 *emsg = GNUNET_strdup (strerror (errno));
1997 * Task that creates the initial (top-level) download
1998 * request for the file.
2000 * @param cls the 'struct GNUNET_FS_DownloadContext'
2001 * @param tc scheduler context
2004 GNUNET_FS_download_start_task_ (void *cls,
2005 const struct GNUNET_SCHEDULER_TaskContext *tc)
2007 struct GNUNET_FS_DownloadContext *dc = cls;
2008 struct GNUNET_FS_ProgressInfo pi;
2009 struct GNUNET_DISK_FileHandle *fh;
2012 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2013 "Start task running...\n");
2015 dc->task = GNUNET_SCHEDULER_NO_TASK;
2016 if (dc->length == 0)
2018 /* no bytes required! */
2019 if (dc->filename != NULL)
2021 fh = GNUNET_DISK_file_open (dc->filename != NULL
2023 : dc->temp_filename,
2024 GNUNET_DISK_OPEN_READWRITE |
2025 GNUNET_DISK_OPEN_CREATE |
2026 ( (0 == GNUNET_FS_uri_chk_get_file_size (dc->uri))
2027 ? GNUNET_DISK_OPEN_TRUNCATE : 0),
2028 GNUNET_DISK_PERM_USER_READ |
2029 GNUNET_DISK_PERM_USER_WRITE |
2030 GNUNET_DISK_PERM_GROUP_READ |
2031 GNUNET_DISK_PERM_OTHER_READ);
2032 GNUNET_DISK_file_close (fh);
2034 GNUNET_FS_download_sync_ (dc);
2035 check_completed (dc);
2038 if (dc->emsg != NULL)
2040 if (dc->top_request == NULL)
2042 dc->top_request = create_download_request (NULL, dc->treedepth - 1, 0,
2043 dc->offset, dc->length);
2044 dc->top_request->state = BRS_CHK_SET;
2045 dc->top_request->chk = (dc->uri->type == chk)
2046 ? dc->uri->data.chk.chk
2047 : dc->uri->data.loc.fi.chk;
2049 GNUNET_FS_download_sync_ (dc);
2050 pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
2051 pi.value.download.specifics.start.meta = dc->meta;
2052 GNUNET_FS_download_make_status_ (&pi, dc);
2054 GNUNET_FS_download_start_downloading_ (dc);
2055 /* attempt reconstruction from disk */
2056 if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))
2057 dc->rfh = GNUNET_DISK_file_open (dc->filename,
2058 GNUNET_DISK_OPEN_READ,
2059 GNUNET_DISK_PERM_NONE);
2060 if (dc->top_request->state == BRS_CHK_SET)
2062 if (dc->rfh != NULL)
2064 /* first, try top-down */
2066 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2067 "Trying top-down reconstruction for `%s'\n",
2070 try_top_down_reconstruction (dc, dc->top_request);
2071 switch (dc->top_request->state)
2075 case BRS_DOWNLOAD_DOWN:
2076 break; /* normal, some blocks already down */
2077 case BRS_DOWNLOAD_UP:
2078 /* already done entirely, party! */
2079 if (dc->rfh != NULL)
2081 /* avoid hanging on to file handle longer than
2083 GNUNET_DISK_file_close (dc->rfh);
2088 GNUNET_asprintf (&dc->emsg,
2090 GNUNET_FS_download_sync_ (dc);
2091 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
2092 pi.value.download.specifics.error.message = dc->emsg;
2093 GNUNET_FS_download_make_status_ (&pi, dc);
2101 /* attempt reconstruction from meta data */
2102 if ( (GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
2103 (NULL != dc->meta) )
2106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2107 "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
2108 (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
2109 (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta));
2111 GNUNET_CONTAINER_meta_data_iterate (dc->meta,
2114 if (dc->top_request->state == BRS_DOWNLOAD_UP)
2116 if (dc->rfh != NULL)
2118 /* avoid hanging on to file handle longer than
2120 GNUNET_DISK_file_close (dc->rfh);
2123 return; /* finished, status update was already done for us */
2126 if (dc->rfh != NULL)
2128 /* finally, try bottom-up */
2130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2131 "Trying bottom-up reconstruction of file `%s'\n",
2134 dc->te = GNUNET_FS_tree_encoder_create (dc->h,
2141 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
2146 /* simple, top-level download */
2147 schedule_block_download (dc,
2150 if (dc->top_request->state == BRS_DOWNLOAD_UP)
2151 check_completed (dc);
2156 * Create SUSPEND event for the given download operation
2157 * and then clean up our state (without stop signal).
2159 * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
2162 GNUNET_FS_download_signal_suspend_ (void *cls)
2164 struct GNUNET_FS_DownloadContext *dc = cls;
2165 struct GNUNET_FS_ProgressInfo pi;
2167 if (dc->top != NULL)
2168 GNUNET_FS_end_top (dc->h, dc->top);
2169 while (NULL != dc->child_head)
2170 GNUNET_FS_download_signal_suspend_ (dc->child_head);
2171 if (dc->search != NULL)
2173 dc->search->download = NULL;
2176 if (dc->job_queue != NULL)
2178 GNUNET_FS_dequeue_ (dc->job_queue);
2179 dc->job_queue = NULL;
2181 if (dc->parent != NULL)
2182 GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2183 dc->parent->child_tail,
2185 if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2187 GNUNET_SCHEDULER_cancel (dc->task);
2188 dc->task = GNUNET_SCHEDULER_NO_TASK;
2190 pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
2191 GNUNET_FS_download_make_status_ (&pi, dc);
2194 GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
2197 if (dc->rfh != NULL)
2199 GNUNET_DISK_file_close (dc->rfh);
2202 GNUNET_FS_free_download_request_ (dc->top_request);
2203 if (dc->active != NULL)
2205 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2208 GNUNET_free_non_null (dc->filename);
2209 GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2210 GNUNET_FS_uri_destroy (dc->uri);
2211 GNUNET_free_non_null (dc->temp_filename);
2212 GNUNET_free_non_null (dc->serialization);
2218 * Download parts of a file. Note that this will store
2219 * the blocks at the respective offset in the given file. Also, the
2220 * download is still using the blocking of the underlying FS
2221 * encoding. As a result, the download may *write* outside of the
2222 * given boundaries (if offset and length do not match the 32k FS
2223 * block boundaries). <p>
2225 * This function should be used to focus a download towards a
2226 * particular portion of the file (optimization), not to strictly
2227 * limit the download to exactly those bytes.
2229 * @param h handle to the file sharing subsystem
2230 * @param uri the URI of the file (determines what to download); CHK or LOC URI
2231 * @param meta known metadata for the file (can be NULL)
2232 * @param filename where to store the file, maybe NULL (then no file is
2233 * created on disk and data must be grabbed from the callbacks)
2234 * @param tempname where to store temporary file data, not used if filename is non-NULL;
2235 * can be NULL (in which case we will pick a name if needed); the temporary file
2236 * may already exist, in which case we will try to use the data that is there and
2237 * if it is not what is desired, will overwrite it
2238 * @param offset at what offset should we start the download (typically 0)
2239 * @param length how many bytes should be downloaded starting at offset
2240 * @param anonymity anonymity level to use for the download
2241 * @param options various options
2242 * @param cctx initial value for the client context for this download
2243 * @param parent parent download to associate this download with (use NULL
2244 * for top-level downloads; useful for manually-triggered recursive downloads)
2245 * @return context that can be used to control this download
2247 struct GNUNET_FS_DownloadContext *
2248 GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
2249 const struct GNUNET_FS_Uri *uri,
2250 const struct GNUNET_CONTAINER_MetaData *meta,
2251 const char *filename,
2252 const char *tempname,
2256 enum GNUNET_FS_DownloadOptions options,
2258 struct GNUNET_FS_DownloadContext *parent)
2260 struct GNUNET_FS_DownloadContext *dc;
2262 GNUNET_assert (GNUNET_FS_uri_test_chk (uri) ||
2263 GNUNET_FS_uri_test_loc (uri) );
2265 if ( (offset + length < offset) ||
2266 (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)) )
2272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2273 "Starting download `%s' of %llu bytes\n",
2275 (unsigned long long) length);
2277 dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2279 dc->parent = parent;
2282 GNUNET_CONTAINER_DLL_insert (parent->child_head,
2286 dc->uri = GNUNET_FS_uri_dup (uri);
2287 dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
2288 dc->client_info = cctx;
2289 dc->start_time = GNUNET_TIME_absolute_get ();
2290 if (NULL != filename)
2292 dc->filename = GNUNET_strdup (filename);
2293 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2294 GNUNET_DISK_file_size (filename,
2298 if (GNUNET_FS_uri_test_loc (dc->uri))
2299 GNUNET_assert (GNUNET_OK ==
2300 GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2302 dc->offset = offset;
2303 dc->length = length;
2304 dc->anonymity = anonymity;
2305 dc->options = options;
2306 dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2307 dc->treedepth = GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size(dc->uri));
2308 if ( (filename == NULL) &&
2309 (is_recursive_download (dc) ) )
2311 if (tempname != NULL)
2312 dc->temp_filename = GNUNET_strdup (tempname);
2314 dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
2318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2319 "Download tree has depth %u\n",
2324 dc->top = GNUNET_FS_make_top (dc->h,
2325 &GNUNET_FS_download_signal_suspend_,
2328 dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_,
2335 * Download parts of a file based on a search result. The download
2336 * will be associated with the search result (and the association
2337 * will be preserved when serializing/deserializing the state).
2338 * If the search is stopped, the download will not be aborted but
2339 * be 'promoted' to a stand-alone download.
2341 * As with the other download function, this will store
2342 * the blocks at the respective offset in the given file. Also, the
2343 * download is still using the blocking of the underlying FS
2344 * encoding. As a result, the download may *write* outside of the
2345 * given boundaries (if offset and length do not match the 32k FS
2346 * block boundaries). <p>
2348 * The given range can be used to focus a download towards a
2349 * particular portion of the file (optimization), not to strictly
2350 * limit the download to exactly those bytes.
2352 * @param h handle to the file sharing subsystem
2353 * @param sr the search result to use for the download (determines uri and
2354 * meta data and associations)
2355 * @param filename where to store the file, maybe NULL (then no file is
2356 * created on disk and data must be grabbed from the callbacks)
2357 * @param tempname where to store temporary file data, not used if filename is non-NULL;
2358 * can be NULL (in which case we will pick a name if needed); the temporary file
2359 * may already exist, in which case we will try to use the data that is there and
2360 * if it is not what is desired, will overwrite it
2361 * @param offset at what offset should we start the download (typically 0)
2362 * @param length how many bytes should be downloaded starting at offset
2363 * @param anonymity anonymity level to use for the download
2364 * @param options various download options
2365 * @param cctx initial value for the client context for this download
2366 * @return context that can be used to control this download
2368 struct GNUNET_FS_DownloadContext *
2369 GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
2370 struct GNUNET_FS_SearchResult *sr,
2371 const char *filename,
2372 const char *tempname,
2376 enum GNUNET_FS_DownloadOptions options,
2379 struct GNUNET_FS_DownloadContext *dc;
2381 if ( (sr == NULL) ||
2382 (sr->download != NULL) )
2387 GNUNET_assert (GNUNET_FS_uri_test_chk (sr->uri) ||
2388 GNUNET_FS_uri_test_loc (sr->uri) );
2389 if ( (offset + length < offset) ||
2390 (offset + length > sr->uri->data.chk.file_length) )
2396 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2397 "Starting download `%s' of %llu bytes\n",
2399 (unsigned long long) length);
2401 dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2405 if (sr->probe_ctx != NULL)
2407 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
2408 sr->probe_ctx = NULL;
2410 dc->uri = GNUNET_FS_uri_dup (sr->uri);
2411 dc->meta = GNUNET_CONTAINER_meta_data_duplicate (sr->meta);
2412 dc->client_info = cctx;
2413 dc->start_time = GNUNET_TIME_absolute_get ();
2414 if (NULL != filename)
2416 dc->filename = GNUNET_strdup (filename);
2417 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2418 GNUNET_DISK_file_size (filename,
2422 if (GNUNET_FS_uri_test_loc (dc->uri))
2423 GNUNET_assert (GNUNET_OK ==
2424 GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2426 dc->offset = offset;
2427 dc->length = length;
2428 dc->anonymity = anonymity;
2429 dc->options = options;
2430 dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2431 dc->treedepth = GNUNET_FS_compute_depth (GNUNET_ntohll(dc->uri->data.chk.file_length));
2432 if ( (filename == NULL) &&
2433 (is_recursive_download (dc) ) )
2435 if (tempname != NULL)
2436 dc->temp_filename = GNUNET_strdup (tempname);
2438 dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
2442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2443 "Download tree has depth %u\n",
2446 dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_,
2453 * Start the downloading process (by entering the queue).
2455 * @param dc our download context
2458 GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
2460 if (dc->completed == dc->length)
2462 GNUNET_assert (dc->job_queue == NULL);
2463 dc->job_queue = GNUNET_FS_queue_ (dc->h,
2464 &activate_fs_download,
2465 &deactivate_fs_download,
2467 (dc->length + DBLOCK_SIZE-1) / DBLOCK_SIZE);
2472 * Stop a download (aborts if download is incomplete).
2474 * @param dc handle for the download
2475 * @param do_delete delete files of incomplete downloads
2478 GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc,
2481 struct GNUNET_FS_ProgressInfo pi;
2484 if (dc->top != NULL)
2485 GNUNET_FS_end_top (dc->h, dc->top);
2488 if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2490 GNUNET_SCHEDULER_cancel (dc->task);
2491 dc->task = GNUNET_SCHEDULER_NO_TASK;
2493 if (dc->search != NULL)
2495 dc->search->download = NULL;
2498 if (dc->job_queue != NULL)
2500 GNUNET_FS_dequeue_ (dc->job_queue);
2501 dc->job_queue = NULL;
2505 GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
2508 have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
2509 while (NULL != dc->child_head)
2510 GNUNET_FS_download_stop (dc->child_head,
2512 if (dc->parent != NULL)
2513 GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2514 dc->parent->child_tail,
2516 if (dc->serialization != NULL)
2517 GNUNET_FS_remove_sync_file_ (dc->h,
2518 ( (dc->parent != NULL) || (dc->search != NULL) )
2519 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2520 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD ,
2522 if ( (GNUNET_YES == have_children) &&
2523 (dc->parent == NULL) )
2524 GNUNET_FS_remove_sync_dir_ (dc->h,
2525 (dc->search != NULL)
2526 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2527 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2529 pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
2530 GNUNET_FS_download_make_status_ (&pi, dc);
2531 GNUNET_FS_free_download_request_ (dc->top_request);
2532 dc->top_request = NULL;
2533 if (dc->active != NULL)
2535 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2538 if (dc->filename != NULL)
2540 if ( (dc->completed != dc->length) &&
2541 (GNUNET_YES == do_delete) )
2543 if (0 != UNLINK (dc->filename))
2544 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2548 GNUNET_free (dc->filename);
2550 GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2551 GNUNET_FS_uri_destroy (dc->uri);
2552 if (NULL != dc->temp_filename)
2554 if (0 != UNLINK (dc->temp_filename))
2555 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2558 GNUNET_free (dc->temp_filename);
2560 GNUNET_free_non_null (dc->serialization);
2564 /* end of fs_download.c */