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 dr download request to match against
468 * @param data plaintext data, starting from the beginning of the file
469 * @param data_len number of bytes in data
472 try_match_block (struct GNUNET_FS_DownloadContext *dc,
473 struct DownloadRequest *dr,
477 struct GNUNET_FS_ProgressInfo pi;
479 char enc[DBLOCK_SIZE];
480 struct ContentHashKey chks[CHK_PER_INODE];
481 struct ContentHashKey in_chk;
482 struct GNUNET_CRYPTO_AesSessionKey sk;
483 struct GNUNET_CRYPTO_AesInitializationVector iv;
485 struct DownloadRequest *drc;
486 struct GNUNET_DISK_FileHandle *fh;
493 odata_len = data_len;
494 if (BRS_DOWNLOAD_UP == dr->state)
498 complete = GNUNET_YES;
499 for (i=0;i<dr->num_children;i++)
501 drc = dr->children[i];
505 if (drc->state != BRS_RECONSTRUCT_META_UP)
506 complete = GNUNET_NO;
510 if (GNUNET_YES != complete)
512 data = (const char*) chks;
513 dlen = dr->num_children * sizeof (struct ContentHashKey);
517 if (dr->offset > data_len)
519 dlen = GNUNET_MIN (data_len - dr->offset,
522 GNUNET_CRYPTO_hash (&data[dr->offset],
525 GNUNET_CRYPTO_hash_to_aes_key (&in_chk.key, &sk, &iv);
526 if (-1 == GNUNET_CRYPTO_aes_encrypt (&data[dr->offset], dlen,
534 GNUNET_CRYPTO_hash (enc, dlen, &in_chk.query);
539 dr->state = BRS_RECONSTRUCT_META_UP;
542 if (0 != memcmp (&in_chk,
544 sizeof (struct ContentHashKey)))
546 /* other peer provided bogus meta data */
550 /* write block to disk */
551 fn = dc->filename != NULL
554 fh = GNUNET_DISK_file_open (fn,
555 GNUNET_DISK_OPEN_READWRITE |
556 GNUNET_DISK_OPEN_CREATE |
557 GNUNET_DISK_OPEN_TRUNCATE,
558 GNUNET_DISK_PERM_USER_READ |
559 GNUNET_DISK_PERM_USER_WRITE |
560 GNUNET_DISK_PERM_GROUP_READ |
561 GNUNET_DISK_PERM_OTHER_READ);
564 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
567 GNUNET_asprintf (&dc->emsg,
568 _("Failed to open file `%s' for writing"),
570 GNUNET_DISK_file_close (fh);
571 dr->state = BRS_ERROR;
572 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
573 pi.value.download.specifics.error.message = dc->emsg;
574 GNUNET_FS_download_make_status_ (&pi, dc);
578 GNUNET_DISK_file_write (fh,
582 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
585 GNUNET_asprintf (&dc->emsg,
586 _("Failed to open file `%s' for writing"),
588 GNUNET_DISK_file_close (fh);
589 dr->state = BRS_ERROR;
590 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
591 pi.value.download.specifics.error.message = dc->emsg;
592 GNUNET_FS_download_make_status_ (&pi, dc);
595 GNUNET_DISK_file_close (fh);
597 dr->state = BRS_DOWNLOAD_UP;
598 dc->completed = dc->length;
599 GNUNET_FS_download_sync_ (dc);
600 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
601 pi.value.download.specifics.progress.data = data;
602 pi.value.download.specifics.progress.offset = 0;
603 pi.value.download.specifics.progress.data_len = dlen;
604 pi.value.download.specifics.progress.depth = 0;
605 GNUNET_FS_download_make_status_ (&pi, dc);
606 if (0 != truncate (dc->filename,
607 GNUNET_ntohll (dc->uri->data.chk.file_length)))
608 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
611 check_completed (dc);
614 /* how did we get here? */
622 * Type of a function that libextractor calls for each
623 * meta data item found. If we find full data meta data,
624 * call 'try_match_block' on it.
626 * @param cls our 'struct GNUNET_FS_DownloadContext*'
627 * @param plugin_name name of the plugin that produced this value;
628 * special values can be used (i.e. '<zlib>' for zlib being
629 * used in the main libextractor library and yielding
631 * @param type libextractor-type describing the meta data
632 * @param format basic format information about data
633 * @param data_mime_type mime-type of data (not of the original file);
634 * can be NULL (if mime-type is not known)
635 * @param data actual meta-data found
636 * @param data_len number of bytes in data
637 * @return 0 to continue extracting, 1 to abort
640 match_full_data (void *cls,
641 const char *plugin_name,
642 enum EXTRACTOR_MetaType type,
643 enum EXTRACTOR_MetaFormat format,
644 const char *data_mime_type,
648 struct GNUNET_FS_DownloadContext *dc = cls;
650 if (type != EXTRACTOR_METATYPE_GNUNET_FULL_DATA)
653 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
654 "Found %u bytes of FD!\n",
655 (unsigned int) data_len);
657 if (GNUNET_FS_uri_chk_get_file_size (dc->uri) != data_len)
660 return 1; /* bogus meta data */
671 * Set the state of the given download request to
672 * BRS_DOWNLOAD_UP and propagate it up the tree.
674 * @param dr download request that is done
677 propagate_up (struct DownloadRequest *dr)
683 dr->state = BRS_DOWNLOAD_UP;
687 for (i=0;i<dr->num_children;i++)
688 if (dr->children[i]->state != BRS_DOWNLOAD_UP)
691 while (i == dr->num_children);
696 * Try top-down reconstruction. Before, the given request node
697 * must have the state BRS_CHK_SET. Afterwards, more nodes may
698 * have that state or advanced to BRS_DOWNLOAD_DOWN or even
699 * BRS_DOWNLOAD_UP. It is also possible to get BRS_ERROR on the
702 * @param dc overall download this block belongs to
703 * @param dr block to reconstruct
706 try_top_down_reconstruction (struct GNUNET_FS_DownloadContext *dc,
707 struct DownloadRequest *dr)
710 char block[DBLOCK_SIZE];
715 unsigned int chk_off;
716 struct DownloadRequest *drc;
717 uint64_t child_block_size;
718 const struct ContentHashKey *chks;
721 GNUNET_assert (dc->rfh != NULL);
722 GNUNET_assert (dr->state == BRS_CHK_SET);
723 total = GNUNET_FS_uri_chk_get_file_size (dc->uri);
724 GNUNET_assert (dr->depth < dc->treedepth);
725 len = GNUNET_FS_tree_calculate_block_size (total,
728 GNUNET_assert (len <= DBLOCK_SIZE);
729 off = compute_disk_offset (total,
732 if (dc->old_file_size < off + len)
733 return; /* failure */
735 GNUNET_DISK_file_seek (dc->rfh,
737 GNUNET_DISK_SEEK_SET) )
739 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
742 return; /* failure */
745 GNUNET_DISK_file_read (dc->rfh,
749 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
752 return; /* failure */
754 GNUNET_CRYPTO_hash (block, len, &key);
755 if (0 != memcmp (&key,
757 sizeof (GNUNET_HashCode)))
758 return; /* mismatch */
760 encrypt_existing_match (dc,
767 /* hash matches but encrypted block does not, really bad */
768 dr->state = BRS_ERROR;
770 while (dr->parent != NULL)
773 dr->state = BRS_ERROR;
778 dr->state = BRS_DOWNLOAD_DOWN;
780 /* set CHKs for children */
781 up_done = GNUNET_YES;
782 chks = (const struct ContentHashKey*) block;
783 for (i=0;i<dr->num_children;i++)
785 drc = dr->children[i];
786 GNUNET_assert (drc->offset >= dr->offset);
787 child_block_size = GNUNET_FS_tree_compute_tree_size (drc->depth);
788 GNUNET_assert (0 == (drc->offset - dr->offset) % child_block_size);
789 chk_off = (drc->offset - dr->offset) / child_block_size;
790 GNUNET_assert (drc->state == BRS_INIT);
791 drc->state = BRS_CHK_SET;
792 drc->chk = chks[chk_off];
793 try_top_down_reconstruction (dc, drc);
794 if (drc->state != BRS_DOWNLOAD_UP)
795 up_done = GNUNET_NO; /* children not all done */
797 if (up_done == GNUNET_YES)
798 propagate_up (dr); /* children all done (or no children...) */
803 * Schedule the download of the specified block in the tree.
805 * @param dc overall download this block belongs to
806 * @param dr request to schedule
809 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
810 struct DownloadRequest *dr)
819 case BRS_RECONSTRUCT_DOWN:
822 case BRS_RECONSTRUCT_META_UP:
825 case BRS_RECONSTRUCT_UP:
829 /* normal case, start download */
831 case BRS_DOWNLOAD_DOWN:
832 for (i=0;i<dr->num_children;i++)
833 schedule_block_download (dc, dr->children[i]);
835 case BRS_DOWNLOAD_UP:
843 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
844 "Scheduling download at offset %llu and depth %u for `%s'\n",
845 (unsigned long long) dr->offset,
847 GNUNET_h2s (&dr->chk.query));
849 GNUNET_CONTAINER_DLL_insert (dc->pending_head,
852 dr->is_pending = GNUNET_YES;
853 GNUNET_CONTAINER_multihashmap_put (dc->active,
856 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
857 if (dc->client == NULL)
858 return; /* download not active */
860 dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
861 sizeof (struct SearchMessage),
862 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
864 &transmit_download_request,
869 #define GNUNET_FS_URI_CHK_PREFIX GNUNET_FS_URI_PREFIX GNUNET_FS_URI_CHK_INFIX
872 * We found an entry in a directory. Check if the respective child
873 * already exists and if not create the respective child download.
875 * @param cls the parent download
876 * @param filename name of the file in the directory
877 * @param uri URI of the file (CHK or LOC)
878 * @param meta meta data of the file
879 * @param length number of bytes in data
880 * @param data contents of the file (or NULL if they were not inlined)
883 trigger_recursive_download (void *cls,
884 const char *filename,
885 const struct GNUNET_FS_Uri *uri,
886 const struct GNUNET_CONTAINER_MetaData *meta,
890 struct GNUNET_FS_DownloadContext *dc = cls;
891 struct GNUNET_FS_DownloadContext *cpos;
902 return; /* entry for the directory itself */
903 cpos = dc->child_head;
906 if ( (GNUNET_FS_uri_test_equal (uri,
908 ( (filename != NULL) &&
909 (0 == strcmp (cpos->filename,
915 return; /* already exists */
917 if (NULL == filename)
919 fn = GNUNET_FS_meta_data_suggest_filename (meta);
922 us = GNUNET_FS_uri_to_string (uri);
923 fn = GNUNET_strdup (&us [strlen (GNUNET_FS_URI_CHK_PREFIX)]);
926 else if (fn[0] == '.')
929 us = GNUNET_FS_uri_to_string (uri);
930 GNUNET_asprintf (&fn,
932 &us[strlen (GNUNET_FS_URI_CHK_PREFIX)], ext);
936 /* change '\' to '/' (this should have happened
937 during insertion, but malicious peers may
938 not have done this) */
939 while (NULL != (pos = strstr (fn, "\\")))
941 /* remove '../' everywhere (again, well-behaved
942 peers don't do this, but don't trust that
943 we did not get something nasty) */
944 while (NULL != (pos = strstr (fn, "../")))
952 if (dc->filename == NULL)
958 dn = GNUNET_strdup (dc->filename);
959 GNUNET_break ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
961 strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
962 GNUNET_FS_DIRECTORY_EXT)) );
963 sfn = GNUNET_strdup (filename);
964 while ( (strlen (sfn) > 0) &&
965 (filename[strlen(sfn)-1] == '/') )
966 sfn[strlen(sfn)-1] = '\0';
967 if ( (strlen (dn) >= strlen (GNUNET_FS_DIRECTORY_EXT)) &&
969 strstr (dn + strlen(dn) - strlen(GNUNET_FS_DIRECTORY_EXT),
970 GNUNET_FS_DIRECTORY_EXT)) )
971 dn[strlen(dn) - strlen (GNUNET_FS_DIRECTORY_EXT)] = '\0';
972 if ( (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (meta)) &&
973 ( (strlen (filename) < strlen (GNUNET_FS_DIRECTORY_EXT)) ||
975 strstr (filename + strlen(filename) - strlen(GNUNET_FS_DIRECTORY_EXT),
976 GNUNET_FS_DIRECTORY_EXT)) ) )
978 GNUNET_asprintf (&full_name,
983 GNUNET_FS_DIRECTORY_EXT);
987 GNUNET_asprintf (&full_name,
995 if ( (full_name != NULL) &&
997 GNUNET_DISK_directory_create_for_file (full_name)) )
999 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1000 _("Failed to create directory for recursive download of `%s'\n"),
1002 GNUNET_free (full_name);
1003 GNUNET_free_non_null (fn);
1009 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1010 "Triggering recursive download of size %llu with %u bytes MD\n",
1011 (unsigned long long) GNUNET_FS_uri_chk_get_file_size (uri),
1012 (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (meta));
1014 GNUNET_FS_download_start (dc->h,
1017 full_name, temp_name,
1019 GNUNET_FS_uri_chk_get_file_size (uri),
1024 GNUNET_free_non_null (full_name);
1025 GNUNET_free_non_null (temp_name);
1026 GNUNET_free_non_null (fn);
1031 * (recursively) free download request structure
1033 * @param dr request to free
1036 GNUNET_FS_free_download_request_ (struct DownloadRequest *dr)
1042 for (i=0;i<dr->num_children;i++)
1043 GNUNET_FS_free_download_request_ (dr->children[i]);
1044 GNUNET_free_non_null (dr->children);
1050 * Iterator over entries in the pending requests in the 'active' map for the
1051 * reply that we just got.
1053 * @param cls closure (our 'struct ProcessResultClosure')
1054 * @param key query for the given value / request
1055 * @param value value in the hash map (a 'struct DownloadRequest')
1056 * @return GNUNET_YES (we should continue to iterate); unless serious error
1059 process_result_with_request (void *cls,
1060 const GNUNET_HashCode *key,
1063 struct ProcessResultClosure *prc = cls;
1064 struct DownloadRequest *dr = value;
1065 struct GNUNET_FS_DownloadContext *dc = prc->dc;
1066 struct DownloadRequest *drc;
1067 struct GNUNET_DISK_FileHandle *fh = NULL;
1068 struct GNUNET_CRYPTO_AesSessionKey skey;
1069 struct GNUNET_CRYPTO_AesInitializationVector iv;
1071 struct GNUNET_FS_ProgressInfo pi;
1076 struct ContentHashKey *chk;
1079 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1080 "Received block `%s' matching pending request at depth %u and offset %llu/%llu\n",
1083 (unsigned long long) dr->offset,
1084 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1087 bs = GNUNET_FS_tree_calculate_block_size (GNUNET_ntohll (dc->uri->data.chk.file_length),
1090 if (prc->size != bs)
1092 GNUNET_asprintf (&dc->emsg,
1093 _("Internal error or bogus download URI (expected %u bytes at depth %u and offset %llu/%llu, got %u bytes)\n"),
1096 (unsigned long long) dr->offset,
1097 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length),
1099 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1102 while (dr->parent != NULL)
1104 dr->state = BRS_ERROR;
1107 dr->state = BRS_ERROR;
1111 (void) GNUNET_CONTAINER_multihashmap_remove (dc->active,
1114 if (GNUNET_YES == dr->is_pending)
1116 GNUNET_CONTAINER_DLL_remove (dc->pending_head,
1119 dr->is_pending = GNUNET_NO;
1123 GNUNET_CRYPTO_hash_to_aes_key (&dr->chk.key, &skey, &iv);
1124 if (-1 == GNUNET_CRYPTO_aes_decrypt (prc->data,
1131 dc->emsg = GNUNET_strdup (_("internal error decrypting content"));
1134 off = compute_disk_offset (GNUNET_ntohll (dc->uri->data.chk.file_length),
1138 if ( ( GNUNET_YES == prc->do_store) &&
1139 ( (dc->filename != NULL) ||
1140 (is_recursive_download (dc)) ) &&
1141 ( (dr->depth == dc->treedepth) ||
1142 (0 == (dc->options & GNUNET_FS_DOWNLOAD_NO_TEMPORARIES)) ) )
1144 fh = GNUNET_DISK_file_open (dc->filename != NULL
1146 : dc->temp_filename,
1147 GNUNET_DISK_OPEN_READWRITE |
1148 GNUNET_DISK_OPEN_CREATE,
1149 GNUNET_DISK_PERM_USER_READ |
1150 GNUNET_DISK_PERM_USER_WRITE |
1151 GNUNET_DISK_PERM_GROUP_READ |
1152 GNUNET_DISK_PERM_OTHER_READ);
1155 GNUNET_asprintf (&dc->emsg,
1156 _("Download failed: could not open file `%s': %s\n"),
1162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163 "Saving decrypted block to disk at offset %llu\n",
1164 (unsigned long long) off);
1167 GNUNET_DISK_file_seek (fh,
1169 GNUNET_DISK_SEEK_SET) ) )
1171 GNUNET_asprintf (&dc->emsg,
1172 _("Failed to seek to offset %llu in file `%s': %s\n"),
1173 (unsigned long long) off,
1179 GNUNET_DISK_file_write (fh,
1183 GNUNET_asprintf (&dc->emsg,
1184 _("Failed to write block of %u bytes at offset %llu in file `%s': %s\n"),
1185 (unsigned int) prc->size,
1186 (unsigned long long) off,
1191 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh));
1197 /* DBLOCK, update progress and try recursion if applicable */
1199 if (dr->offset < dc->offset)
1201 /* starting offset begins in the middle of pt,
1202 do not count first bytes as progress */
1203 GNUNET_assert (app > (dc->offset - dr->offset));
1204 app -= (dc->offset - dr->offset);
1206 if (dr->offset + prc->size > dc->offset + dc->length)
1208 /* end of block is after relevant range,
1209 do not count last bytes as progress */
1210 GNUNET_assert (app > (dr->offset + prc->size) - (dc->offset + dc->length));
1211 app -= (dr->offset + prc->size) - (dc->offset + dc->length);
1213 dc->completed += app;
1215 /* do recursive download if option is set and either meta data
1216 says it is a directory or if no meta data is given AND filename
1217 ends in '.gnd' (top-level case) */
1218 if (is_recursive_download (dc))
1219 GNUNET_FS_directory_list_contents (prc->size,
1222 &trigger_recursive_download,
1226 dr->state = BRS_DOWNLOAD_DOWN;
1227 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1228 pi.value.download.specifics.progress.data = pt;
1229 pi.value.download.specifics.progress.offset = dr->offset;
1230 pi.value.download.specifics.progress.data_len = prc->size;
1231 pi.value.download.specifics.progress.depth = dr->depth;
1232 GNUNET_FS_download_make_status_ (&pi, dc);
1233 GNUNET_assert (dc->completed <= dc->length);
1237 if (dc->completed == dc->length)
1239 /* download completed, signal */
1241 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1242 "Download completed, truncating file to desired length %llu\n",
1243 (unsigned long long) GNUNET_ntohll (dc->uri->data.chk.file_length));
1245 /* truncate file to size (since we store IBlocks at the end) */
1246 if (dc->filename != NULL)
1248 if (0 != truncate (dc->filename,
1249 GNUNET_ntohll (dc->uri->data.chk.file_length)))
1250 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
1254 GNUNET_assert (dr->depth == 0);
1255 check_completed (dc);
1259 /* bottom of the tree, no child downloads possible, just sync */
1260 GNUNET_FS_download_sync_ (dc);
1265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1266 "Triggering downloads of children (this block was at depth %u and offset %llu)\n",
1268 (unsigned long long) dr->offset);
1270 GNUNET_assert (0 == (prc->size % sizeof(struct ContentHashKey)));
1271 chk = (struct ContentHashKey*) pt;
1272 for (i=(prc->size / sizeof(struct ContentHashKey))-1;i>=0;i--)
1274 off = compute_dblock_offset (dr->offset,
1277 drc = dr->children[i];
1282 drc->state = BRS_CHK_SET;
1283 schedule_block_download (dc, drc);
1285 case BRS_RECONSTRUCT_DOWN:
1288 case BRS_RECONSTRUCT_META_UP:
1291 case BRS_RECONSTRUCT_UP:
1297 case BRS_DOWNLOAD_DOWN:
1300 case BRS_DOWNLOAD_UP:
1311 GNUNET_FS_download_sync_ (dc);
1316 GNUNET_DISK_file_close (fh);
1317 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
1318 pi.value.download.specifics.error.message = dc->emsg;
1319 GNUNET_FS_download_make_status_ (&pi, dc);
1320 /* abort all pending requests */
1323 GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1326 GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1328 GNUNET_FS_free_download_request_ (dc->top_request);
1329 dc->top_request = NULL;
1330 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
1332 dc->pending_head = NULL;
1333 dc->pending_tail = NULL;
1334 GNUNET_FS_download_sync_ (dc);
1340 * Process a download result.
1342 * @param dc our download context
1343 * @param type type of the result
1344 * @param data the (encrypted) response
1345 * @param size size of data
1348 process_result (struct GNUNET_FS_DownloadContext *dc,
1349 enum GNUNET_BLOCK_Type type,
1353 struct ProcessResultClosure prc;
1359 prc.do_store = GNUNET_YES;
1360 GNUNET_CRYPTO_hash (data, size, &prc.query);
1362 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1363 "Received result for query `%s' from `%s'-service\n",
1364 GNUNET_h2s (&prc.query),
1367 GNUNET_CONTAINER_multihashmap_get_multiple (dc->active,
1369 &process_result_with_request,
1375 * Type of a function to call when we receive a message
1378 * @param cls closure
1379 * @param msg message received, NULL on timeout or fatal error
1382 receive_results (void *cls,
1383 const struct GNUNET_MessageHeader * msg)
1385 struct GNUNET_FS_DownloadContext *dc = cls;
1386 const struct PutMessage *cm;
1389 if ( (NULL == msg) ||
1390 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_PUT) ||
1391 (sizeof (struct PutMessage) > ntohs(msg->size)) )
1393 GNUNET_break (msg == NULL);
1397 msize = ntohs(msg->size);
1398 cm = (const struct PutMessage*) msg;
1402 msize - sizeof (struct PutMessage));
1403 if (dc->client == NULL)
1404 return; /* fatal error */
1405 /* continue receiving */
1406 GNUNET_CLIENT_receive (dc->client,
1409 GNUNET_TIME_UNIT_FOREVER_REL);
1415 * We're ready to transmit a search request to the
1416 * file-sharing service. Do it. If there is
1417 * more than one request pending, try to send
1418 * multiple or request another transmission.
1420 * @param cls closure
1421 * @param size number of bytes available in buf
1422 * @param buf where the callee should write the message
1423 * @return number of bytes written to buf
1426 transmit_download_request (void *cls,
1430 struct GNUNET_FS_DownloadContext *dc = cls;
1432 struct SearchMessage *sm;
1433 struct DownloadRequest *dr;
1439 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1440 "Transmitting download request failed, trying to reconnect\n");
1445 GNUNET_assert (size >= sizeof (struct SearchMessage));
1448 while ( (NULL != (dr = dc->pending_head)) &&
1449 (size >= msize + sizeof (struct SearchMessage)) )
1452 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1453 "Transmitting download request for `%s' to `%s'-service\n",
1454 GNUNET_h2s (&dr->chk.query),
1457 memset (sm, 0, sizeof (struct SearchMessage));
1458 sm->header.size = htons (sizeof (struct SearchMessage));
1459 sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
1460 if (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY))
1461 sm->options = htonl (1);
1463 sm->options = htonl (0);
1465 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_DBLOCK);
1467 sm->type = htonl (GNUNET_BLOCK_TYPE_FS_IBLOCK);
1468 sm->anonymity_level = htonl (dc->anonymity);
1469 sm->target = dc->target.hashPubKey;
1470 sm->query = dr->chk.query;
1471 GNUNET_CONTAINER_DLL_remove (dc->pending_head,
1474 dr->is_pending = GNUNET_NO;
1475 msize += sizeof (struct SearchMessage);
1478 if (dc->pending_head != NULL)
1480 dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1481 sizeof (struct SearchMessage),
1482 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1484 &transmit_download_request,
1486 GNUNET_assert (dc->th != NULL);
1493 * Reconnect to the FS service and transmit our queries NOW.
1495 * @param cls our download context
1499 do_reconnect (void *cls,
1500 const struct GNUNET_SCHEDULER_TaskContext *tc)
1502 struct GNUNET_FS_DownloadContext *dc = cls;
1503 struct GNUNET_CLIENT_Connection *client;
1505 dc->task = GNUNET_SCHEDULER_NO_TASK;
1506 client = GNUNET_CLIENT_connect ("fs",
1510 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1511 "Connecting to `%s'-service failed, will try again.\n",
1516 dc->client = client;
1517 if (dc->pending_head != NULL)
1519 dc->th = GNUNET_CLIENT_notify_transmit_ready (client,
1520 sizeof (struct SearchMessage),
1521 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1523 &transmit_download_request,
1525 GNUNET_assert (dc->th != NULL);
1527 GNUNET_CLIENT_receive (client,
1530 GNUNET_TIME_UNIT_FOREVER_REL);
1535 * Add entries to the pending list.
1537 * @param cls our download context
1539 * @param entry entry of type "struct DownloadRequest"
1543 retry_entry (void *cls,
1544 const GNUNET_HashCode *key,
1547 struct GNUNET_FS_DownloadContext *dc = cls;
1548 struct DownloadRequest *dr = entry;
1550 GNUNET_CONTAINER_DLL_insert (dc->pending_head,
1553 dr->is_pending = GNUNET_YES;
1559 * We've lost our connection with the FS service.
1560 * Re-establish it and re-transmit all of our
1563 * @param dc download context that is having trouble
1566 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
1569 if (NULL != dc->client)
1572 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1573 "Moving all requests back to pending list\n");
1577 GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1580 /* full reset of the pending list */
1581 dc->pending_head = NULL;
1582 dc->pending_tail = NULL;
1583 GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1586 GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1590 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1591 "Will try to reconnect in 1s\n");
1594 = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
1601 * We're allowed to ask the FS service for our blocks. Start the download.
1603 * @param cls the 'struct GNUNET_FS_DownloadContext'
1604 * @param client handle to use for communcation with FS (we must destroy it!)
1607 activate_fs_download (void *cls,
1608 struct GNUNET_CLIENT_Connection *client)
1610 struct GNUNET_FS_DownloadContext *dc = cls;
1611 struct GNUNET_FS_ProgressInfo pi;
1614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1615 "Download activated\n");
1617 GNUNET_assert (NULL != client);
1618 GNUNET_assert (dc->client == NULL);
1619 GNUNET_assert (dc->th == NULL);
1620 dc->client = client;
1621 GNUNET_CLIENT_receive (client,
1624 GNUNET_TIME_UNIT_FOREVER_REL);
1625 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ACTIVE;
1626 GNUNET_FS_download_make_status_ (&pi, dc);
1627 GNUNET_CONTAINER_multihashmap_iterate (dc->active,
1631 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1632 "Asking for transmission to FS service\n");
1634 if (dc->pending_head != NULL)
1636 dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client,
1637 sizeof (struct SearchMessage),
1638 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
1640 &transmit_download_request,
1642 GNUNET_assert (dc->th != NULL);
1648 * We must stop to ask the FS service for our blocks. Pause the download.
1650 * @param cls the 'struct GNUNET_FS_DownloadContext'
1653 deactivate_fs_download (void *cls)
1655 struct GNUNET_FS_DownloadContext *dc = cls;
1656 struct GNUNET_FS_ProgressInfo pi;
1659 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1660 "Download deactivated\n");
1664 GNUNET_CLIENT_notify_transmit_ready_cancel (dc->th);
1667 if (NULL != dc->client)
1669 GNUNET_CLIENT_disconnect (dc->client, GNUNET_NO);
1672 pi.status = GNUNET_FS_STATUS_DOWNLOAD_INACTIVE;
1673 GNUNET_FS_download_make_status_ (&pi, dc);
1678 * (recursively) Create a download request structure.
1680 * @param parent parent of the current entry
1681 * @param depth depth of the current entry, 0 are the DBLOCKs,
1682 * top level block is 'dc->treedepth - 1'
1683 * @param dr_offset offset in the original file this block maps to
1684 * (as in, offset of the first byte of the first DBLOCK
1685 * in the subtree rooted in the returned download request tree)
1686 * @param file_start_offset desired starting offset for the download
1687 * in the original file; requesting tree should not contain
1688 * DBLOCKs prior to the file_start_offset
1689 * @param desired_length desired number of bytes the user wanted to access
1690 * (from file_start_offset). Resulting tree should not contain
1691 * DBLOCKs after file_start_offset + file_length.
1692 * @return download request tree for the given range of DBLOCKs at
1693 * the specified depth
1695 static struct DownloadRequest *
1696 create_download_request (struct DownloadRequest *parent,
1699 uint64_t file_start_offset,
1700 uint64_t desired_length)
1702 struct DownloadRequest *dr;
1704 unsigned int head_skip;
1705 uint64_t child_block_size;
1707 dr = GNUNET_malloc (sizeof (struct DownloadRequest));
1708 dr->parent = parent;
1710 dr->offset = dr_offset;
1713 child_block_size = GNUNET_FS_tree_compute_tree_size (depth - 1);
1715 /* calculate how many blocks at this level are not interesting
1716 from the start (rounded down), either because of the requested
1717 file offset or because this IBlock is further along */
1718 if (dr_offset < file_start_offset)
1719 head_skip = file_start_offset / child_block_size;
1721 head_skip = dr_offset / child_block_size;
1723 /* calculate index of last block at this level that is interesting (rounded up) */
1724 dr->num_children = file_start_offset + desired_length / child_block_size;
1725 if (dr->num_children * child_block_size < file_start_offset + desired_length)
1726 dr->num_children++; /* round up */
1728 /* now we can get the total number of children for this block */
1729 dr->num_children -= head_skip;
1730 if (dr->num_children > CHK_PER_INODE)
1731 dr->num_children = CHK_PER_INODE; /* cap at max */
1733 /* why else would we have gotten here to begin with? (that'd be a bad logic error) */
1734 GNUNET_assert (dr->num_children > 0);
1736 dr->children = GNUNET_malloc (dr->num_children *
1737 sizeof (struct DownloadRequest *));
1738 for (i=0;i<dr->num_children;i++)
1739 dr->children[i] = create_download_request (dr,
1741 dr_offset + i * child_block_size,
1750 * Continuation after a possible attempt to reconstruct
1751 * the current IBlock from the existing file.
1753 * @param cls the 'struct ReconstructContext'
1754 * @param tc scheduler context
1757 reconstruct_cont (void *cls,
1758 const struct GNUNET_SCHEDULER_TaskContext *tc)
1760 struct GNUNET_FS_DownloadContext *dc = cls;
1762 /* clean up state from tree encoder */
1765 GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
1768 if (dc->task != GNUNET_SCHEDULER_NO_TASK)
1770 GNUNET_SCHEDULER_cancel (dc->task);
1771 dc->task = GNUNET_SCHEDULER_NO_TASK;
1773 if (dc->rfh != NULL)
1775 GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (dc->rfh));
1778 /* start "normal" download */
1779 schedule_block_download (dc,
1785 * Task requesting the next block from the tree encoder.
1787 * @param cls the 'struct GNUJNET_FS_DownloadContext' we're processing
1788 * @param tc task context
1791 get_next_block (void *cls,
1792 const struct GNUNET_SCHEDULER_TaskContext *tc)
1794 struct GNUNET_FS_DownloadContext *dc = cls;
1796 dc->task = GNUNET_SCHEDULER_NO_TASK;
1797 GNUNET_FS_tree_encoder_next (dc->te);
1803 * Function called asking for the current (encoded)
1804 * block to be processed. After processing the
1805 * client should either call "GNUNET_FS_tree_encode_next"
1806 * or (on error) "GNUNET_FS_tree_encode_finish".
1808 * This function checks if the content on disk matches
1809 * the expected content based on the URI.
1811 * @param cls closure
1812 * @param chk content hash key for the block
1813 * @param offset offset of the block
1814 * @param depth depth of the block, 0 for DBLOCK
1815 * @param type type of the block (IBLOCK or DBLOCK)
1816 * @param block the (encrypted) block
1817 * @param block_size size of block (in bytes)
1820 reconstruct_cb (void *cls,
1821 const struct ContentHashKey *chk,
1824 enum GNUNET_BLOCK_Type type,
1826 uint16_t block_size)
1828 struct GNUNET_FS_DownloadContext *dc = cls;
1829 struct GNUNET_FS_ProgressInfo pi;
1830 struct DownloadRequest *dr;
1834 /* find corresponding request entry */
1835 dr = dc->top_request;
1836 while (dr->depth > depth)
1838 blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1839 chld = (offset - dr->offset) / blen;
1840 GNUNET_assert (chld < dr->num_children);
1841 dr = dr->children[chld];
1847 case BRS_RECONSTRUCT_DOWN:
1849 case BRS_RECONSTRUCT_META_UP:
1851 case BRS_RECONSTRUCT_UP:
1854 if (0 == memcmp (chk,
1856 sizeof (struct ContentHashKey)))
1858 /* block matches, hence tree below matches;
1859 this request is done! */
1860 dr->state = BRS_DOWNLOAD_UP;
1861 /* calculate how many bytes of payload this block
1863 blen = GNUNET_FS_tree_compute_tree_size (dr->depth);
1864 /* how many of those bytes are in the requested range? */
1865 blen = GNUNET_MIN (blen,
1866 dc->length + dc->offset - dr->offset);
1867 /* signal progress */
1868 dc->completed += blen;
1869 pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS;
1870 pi.value.download.specifics.progress.data = NULL;
1871 pi.value.download.specifics.progress.offset = offset;
1872 pi.value.download.specifics.progress.data_len = 0;
1873 pi.value.download.specifics.progress.depth = 0;
1874 GNUNET_FS_download_make_status_ (&pi, dc);
1880 case BRS_DOWNLOAD_DOWN:
1882 case BRS_DOWNLOAD_UP:
1890 if ( (dr == dc->top_request) &&
1891 (dr->state == BRS_DOWNLOAD_UP) )
1893 check_completed (dc);
1896 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
1902 * Function called by the tree encoder to obtain a block of plaintext
1903 * data (for the lowest level of the tree).
1905 * @param cls our 'struct ReconstructContext'
1906 * @param offset identifies which block to get
1907 * @param max (maximum) number of bytes to get; returning
1908 * fewer will also cause errors
1909 * @param buf where to copy the plaintext buffer
1910 * @param emsg location to store an error message (on error)
1911 * @return number of bytes copied to buf, 0 on error
1914 fh_reader (void *cls,
1920 struct GNUNET_FS_DownloadContext *dc = cls;
1921 struct GNUNET_DISK_FileHandle *fh = dc->rfh;
1926 GNUNET_DISK_file_seek (fh,
1928 GNUNET_DISK_SEEK_SET))
1930 *emsg = GNUNET_strdup (strerror (errno));
1933 ret = GNUNET_DISK_file_read (fh, buf, max);
1936 *emsg = GNUNET_strdup (strerror (errno));
1944 * Task that creates the initial (top-level) download
1945 * request for the file.
1947 * @param cls the 'struct GNUNET_FS_DownloadContext'
1948 * @param tc scheduler context
1951 GNUNET_FS_download_start_task_ (void *cls,
1952 const struct GNUNET_SCHEDULER_TaskContext *tc)
1954 struct GNUNET_FS_DownloadContext *dc = cls;
1955 struct GNUNET_FS_ProgressInfo pi;
1956 struct GNUNET_DISK_FileHandle *fh;
1959 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1960 "Start task running...\n");
1962 dc->task = GNUNET_SCHEDULER_NO_TASK;
1963 if (dc->length == 0)
1965 /* no bytes required! */
1966 if (dc->filename != NULL)
1968 fh = GNUNET_DISK_file_open (dc->filename != NULL
1970 : dc->temp_filename,
1971 GNUNET_DISK_OPEN_READWRITE |
1972 GNUNET_DISK_OPEN_CREATE |
1973 ( (0 == GNUNET_FS_uri_chk_get_file_size (dc->uri))
1974 ? GNUNET_DISK_OPEN_TRUNCATE : 0),
1975 GNUNET_DISK_PERM_USER_READ |
1976 GNUNET_DISK_PERM_USER_WRITE |
1977 GNUNET_DISK_PERM_GROUP_READ |
1978 GNUNET_DISK_PERM_OTHER_READ);
1979 GNUNET_DISK_file_close (fh);
1981 GNUNET_FS_download_sync_ (dc);
1982 check_completed (dc);
1985 if (dc->emsg != NULL)
1987 if (dc->top_request == NULL)
1989 dc->top_request = create_download_request (NULL, dc->treedepth - 1, 0,
1990 dc->offset, dc->length);
1991 dc->top_request->state = BRS_CHK_SET;
1992 dc->top_request->chk = (dc->uri->type == chk)
1993 ? dc->uri->data.chk.chk
1994 : dc->uri->data.loc.fi.chk;
1996 GNUNET_FS_download_sync_ (dc);
1997 pi.status = GNUNET_FS_STATUS_DOWNLOAD_START;
1998 pi.value.download.specifics.start.meta = dc->meta;
1999 GNUNET_FS_download_make_status_ (&pi, dc);
2001 GNUNET_FS_download_start_downloading_ (dc);
2002 /* attempt reconstruction from disk */
2003 if (GNUNET_YES == GNUNET_DISK_file_test (dc->filename))
2004 dc->rfh = GNUNET_DISK_file_open (dc->filename,
2005 GNUNET_DISK_OPEN_READ,
2006 GNUNET_DISK_PERM_NONE);
2007 if (dc->top_request->state == BRS_CHK_SET)
2009 if (dc->rfh != NULL)
2011 /* first, try top-down */
2013 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2014 "Trying top-down reconstruction for `%s'\n",
2017 try_top_down_reconstruction (dc, dc->top_request);
2018 switch (dc->top_request->state)
2022 case BRS_DOWNLOAD_DOWN:
2023 break; /* normal, some blocks already down */
2024 case BRS_DOWNLOAD_UP:
2025 /* already done entirely, party! */
2026 if (dc->rfh != NULL)
2028 /* avoid hanging on to file handle longer than
2030 GNUNET_DISK_file_close (dc->rfh);
2035 GNUNET_asprintf (&dc->emsg,
2037 GNUNET_FS_download_sync_ (dc);
2038 pi.status = GNUNET_FS_STATUS_DOWNLOAD_ERROR;
2039 pi.value.download.specifics.error.message = dc->emsg;
2040 GNUNET_FS_download_make_status_ (&pi, dc);
2048 /* attempt reconstruction from meta data */
2049 if ( (GNUNET_FS_uri_chk_get_file_size (dc->uri) <= MAX_INLINE_SIZE) &&
2050 (NULL != dc->meta) )
2053 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2054 "Trying to find embedded meta data for download of size %llu with %u bytes MD\n",
2055 (unsigned long long) GNUNET_FS_uri_chk_get_file_size (dc->uri),
2056 (unsigned int) GNUNET_CONTAINER_meta_data_get_serialized_size (dc->meta));
2058 GNUNET_CONTAINER_meta_data_iterate (dc->meta,
2061 if (dc->top_request->state == BRS_DOWNLOAD_UP)
2063 if (dc->rfh != NULL)
2065 /* avoid hanging on to file handle longer than
2067 GNUNET_DISK_file_close (dc->rfh);
2070 return; /* finished, status update was already done for us */
2073 if (dc->rfh != NULL)
2075 /* finally, try bottom-up */
2077 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2078 "Trying bottom-up reconstruction of file `%s'\n",
2081 dc->te = GNUNET_FS_tree_encoder_create (dc->h,
2088 dc->task = GNUNET_SCHEDULER_add_now (&get_next_block,
2093 /* simple, top-level download */
2094 schedule_block_download (dc,
2097 if (dc->top_request->state == BRS_DOWNLOAD_UP)
2098 check_completed (dc);
2103 * Create SUSPEND event for the given download operation
2104 * and then clean up our state (without stop signal).
2106 * @param cls the 'struct GNUNET_FS_DownloadContext' to signal for
2109 GNUNET_FS_download_signal_suspend_ (void *cls)
2111 struct GNUNET_FS_DownloadContext *dc = cls;
2112 struct GNUNET_FS_ProgressInfo pi;
2114 if (dc->top != NULL)
2115 GNUNET_FS_end_top (dc->h, dc->top);
2116 while (NULL != dc->child_head)
2117 GNUNET_FS_download_signal_suspend_ (dc->child_head);
2118 if (dc->search != NULL)
2120 dc->search->download = NULL;
2123 if (dc->job_queue != NULL)
2125 GNUNET_FS_dequeue_ (dc->job_queue);
2126 dc->job_queue = NULL;
2128 if (dc->parent != NULL)
2129 GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2130 dc->parent->child_tail,
2132 if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2134 GNUNET_SCHEDULER_cancel (dc->task);
2135 dc->task = GNUNET_SCHEDULER_NO_TASK;
2137 pi.status = GNUNET_FS_STATUS_DOWNLOAD_SUSPEND;
2138 GNUNET_FS_download_make_status_ (&pi, dc);
2141 GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
2144 if (dc->rfh != NULL)
2146 GNUNET_DISK_file_close (dc->rfh);
2149 GNUNET_FS_free_download_request_ (dc->top_request);
2150 if (dc->active != NULL)
2152 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2155 GNUNET_free_non_null (dc->filename);
2156 GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2157 GNUNET_FS_uri_destroy (dc->uri);
2158 GNUNET_free_non_null (dc->temp_filename);
2159 GNUNET_free_non_null (dc->serialization);
2165 * Download parts of a file. Note that this will store
2166 * the blocks at the respective offset in the given file. Also, the
2167 * download is still using the blocking of the underlying FS
2168 * encoding. As a result, the download may *write* outside of the
2169 * given boundaries (if offset and length do not match the 32k FS
2170 * block boundaries). <p>
2172 * This function should be used to focus a download towards a
2173 * particular portion of the file (optimization), not to strictly
2174 * limit the download to exactly those bytes.
2176 * @param h handle to the file sharing subsystem
2177 * @param uri the URI of the file (determines what to download); CHK or LOC URI
2178 * @param meta known metadata for the file (can be NULL)
2179 * @param filename where to store the file, maybe NULL (then no file is
2180 * created on disk and data must be grabbed from the callbacks)
2181 * @param tempname where to store temporary file data, not used if filename is non-NULL;
2182 * can be NULL (in which case we will pick a name if needed); the temporary file
2183 * may already exist, in which case we will try to use the data that is there and
2184 * if it is not what is desired, will overwrite it
2185 * @param offset at what offset should we start the download (typically 0)
2186 * @param length how many bytes should be downloaded starting at offset
2187 * @param anonymity anonymity level to use for the download
2188 * @param options various options
2189 * @param cctx initial value for the client context for this download
2190 * @param parent parent download to associate this download with (use NULL
2191 * for top-level downloads; useful for manually-triggered recursive downloads)
2192 * @return context that can be used to control this download
2194 struct GNUNET_FS_DownloadContext *
2195 GNUNET_FS_download_start (struct GNUNET_FS_Handle *h,
2196 const struct GNUNET_FS_Uri *uri,
2197 const struct GNUNET_CONTAINER_MetaData *meta,
2198 const char *filename,
2199 const char *tempname,
2203 enum GNUNET_FS_DownloadOptions options,
2205 struct GNUNET_FS_DownloadContext *parent)
2207 struct GNUNET_FS_DownloadContext *dc;
2209 GNUNET_assert (GNUNET_FS_uri_test_chk (uri) ||
2210 GNUNET_FS_uri_test_loc (uri) );
2212 if ( (offset + length < offset) ||
2213 (offset + length > GNUNET_FS_uri_chk_get_file_size (uri)) )
2219 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2220 "Starting download `%s' of %llu bytes\n",
2222 (unsigned long long) length);
2224 dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2226 dc->parent = parent;
2229 GNUNET_CONTAINER_DLL_insert (parent->child_head,
2233 dc->uri = GNUNET_FS_uri_dup (uri);
2234 dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta);
2235 dc->client_info = cctx;
2236 dc->start_time = GNUNET_TIME_absolute_get ();
2237 if (NULL != filename)
2239 dc->filename = GNUNET_strdup (filename);
2240 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2241 GNUNET_DISK_file_size (filename,
2245 if (GNUNET_FS_uri_test_loc (dc->uri))
2246 GNUNET_assert (GNUNET_OK ==
2247 GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2249 dc->offset = offset;
2250 dc->length = length;
2251 dc->anonymity = anonymity;
2252 dc->options = options;
2253 dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2254 dc->treedepth = GNUNET_FS_compute_depth (GNUNET_FS_uri_chk_get_file_size(dc->uri));
2255 if ( (filename == NULL) &&
2256 (is_recursive_download (dc) ) )
2258 if (tempname != NULL)
2259 dc->temp_filename = GNUNET_strdup (tempname);
2261 dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
2265 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2266 "Download tree has depth %u\n",
2271 dc->top = GNUNET_FS_make_top (dc->h,
2272 &GNUNET_FS_download_signal_suspend_,
2275 dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_,
2282 * Download parts of a file based on a search result. The download
2283 * will be associated with the search result (and the association
2284 * will be preserved when serializing/deserializing the state).
2285 * If the search is stopped, the download will not be aborted but
2286 * be 'promoted' to a stand-alone download.
2288 * As with the other download function, this will store
2289 * the blocks at the respective offset in the given file. Also, the
2290 * download is still using the blocking of the underlying FS
2291 * encoding. As a result, the download may *write* outside of the
2292 * given boundaries (if offset and length do not match the 32k FS
2293 * block boundaries). <p>
2295 * The given range can be used to focus a download towards a
2296 * particular portion of the file (optimization), not to strictly
2297 * limit the download to exactly those bytes.
2299 * @param h handle to the file sharing subsystem
2300 * @param sr the search result to use for the download (determines uri and
2301 * meta data and associations)
2302 * @param filename where to store the file, maybe NULL (then no file is
2303 * created on disk and data must be grabbed from the callbacks)
2304 * @param tempname where to store temporary file data, not used if filename is non-NULL;
2305 * can be NULL (in which case we will pick a name if needed); the temporary file
2306 * may already exist, in which case we will try to use the data that is there and
2307 * if it is not what is desired, will overwrite it
2308 * @param offset at what offset should we start the download (typically 0)
2309 * @param length how many bytes should be downloaded starting at offset
2310 * @param anonymity anonymity level to use for the download
2311 * @param options various download options
2312 * @param cctx initial value for the client context for this download
2313 * @return context that can be used to control this download
2315 struct GNUNET_FS_DownloadContext *
2316 GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h,
2317 struct GNUNET_FS_SearchResult *sr,
2318 const char *filename,
2319 const char *tempname,
2323 enum GNUNET_FS_DownloadOptions options,
2326 struct GNUNET_FS_DownloadContext *dc;
2328 if ( (sr == NULL) ||
2329 (sr->download != NULL) )
2334 GNUNET_assert (GNUNET_FS_uri_test_chk (sr->uri) ||
2335 GNUNET_FS_uri_test_loc (sr->uri) );
2336 if ( (offset + length < offset) ||
2337 (offset + length > sr->uri->data.chk.file_length) )
2343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2344 "Starting download `%s' of %llu bytes\n",
2346 (unsigned long long) length);
2348 dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
2352 if (sr->probe_ctx != NULL)
2354 GNUNET_FS_download_stop (sr->probe_ctx, GNUNET_YES);
2355 sr->probe_ctx = NULL;
2357 dc->uri = GNUNET_FS_uri_dup (sr->uri);
2358 dc->meta = GNUNET_CONTAINER_meta_data_duplicate (sr->meta);
2359 dc->client_info = cctx;
2360 dc->start_time = GNUNET_TIME_absolute_get ();
2361 if (NULL != filename)
2363 dc->filename = GNUNET_strdup (filename);
2364 if (GNUNET_YES == GNUNET_DISK_file_test (filename))
2365 GNUNET_DISK_file_size (filename,
2369 if (GNUNET_FS_uri_test_loc (dc->uri))
2370 GNUNET_assert (GNUNET_OK ==
2371 GNUNET_FS_uri_loc_get_peer_identity (dc->uri,
2373 dc->offset = offset;
2374 dc->length = length;
2375 dc->anonymity = anonymity;
2376 dc->options = options;
2377 dc->active = GNUNET_CONTAINER_multihashmap_create (1 + 2 * (length / DBLOCK_SIZE));
2378 dc->treedepth = GNUNET_FS_compute_depth (GNUNET_ntohll(dc->uri->data.chk.file_length));
2379 if ( (filename == NULL) &&
2380 (is_recursive_download (dc) ) )
2382 if (tempname != NULL)
2383 dc->temp_filename = GNUNET_strdup (tempname);
2385 dc->temp_filename = GNUNET_DISK_mktemp ("gnunet-directory-download-tmp");
2389 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2390 "Download tree has depth %u\n",
2393 dc->task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_,
2400 * Start the downloading process (by entering the queue).
2402 * @param dc our download context
2405 GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc)
2407 if (dc->completed == dc->length)
2409 GNUNET_assert (dc->job_queue == NULL);
2410 dc->job_queue = GNUNET_FS_queue_ (dc->h,
2411 &activate_fs_download,
2412 &deactivate_fs_download,
2414 (dc->length + DBLOCK_SIZE-1) / DBLOCK_SIZE);
2419 * Stop a download (aborts if download is incomplete).
2421 * @param dc handle for the download
2422 * @param do_delete delete files of incomplete downloads
2425 GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc,
2428 struct GNUNET_FS_ProgressInfo pi;
2431 if (dc->top != NULL)
2432 GNUNET_FS_end_top (dc->h, dc->top);
2435 if (dc->task != GNUNET_SCHEDULER_NO_TASK)
2437 GNUNET_SCHEDULER_cancel (dc->task);
2438 dc->task = GNUNET_SCHEDULER_NO_TASK;
2440 if (dc->search != NULL)
2442 dc->search->download = NULL;
2445 if (dc->job_queue != NULL)
2447 GNUNET_FS_dequeue_ (dc->job_queue);
2448 dc->job_queue = NULL;
2452 GNUNET_FS_tree_encoder_finish (dc->te, NULL, NULL);
2455 have_children = (NULL != dc->child_head) ? GNUNET_YES : GNUNET_NO;
2456 while (NULL != dc->child_head)
2457 GNUNET_FS_download_stop (dc->child_head,
2459 if (dc->parent != NULL)
2460 GNUNET_CONTAINER_DLL_remove (dc->parent->child_head,
2461 dc->parent->child_tail,
2463 if (dc->serialization != NULL)
2464 GNUNET_FS_remove_sync_file_ (dc->h,
2465 ( (dc->parent != NULL) || (dc->search != NULL) )
2466 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2467 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD ,
2469 if ( (GNUNET_YES == have_children) &&
2470 (dc->parent == NULL) )
2471 GNUNET_FS_remove_sync_dir_ (dc->h,
2472 (dc->search != NULL)
2473 ? GNUNET_FS_SYNC_PATH_CHILD_DOWNLOAD
2474 : GNUNET_FS_SYNC_PATH_MASTER_DOWNLOAD,
2476 pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED;
2477 GNUNET_FS_download_make_status_ (&pi, dc);
2478 GNUNET_FS_free_download_request_ (dc->top_request);
2479 dc->top_request = NULL;
2480 if (dc->active != NULL)
2482 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
2485 if (dc->filename != NULL)
2487 if ( (dc->completed != dc->length) &&
2488 (GNUNET_YES == do_delete) )
2490 if (0 != UNLINK (dc->filename))
2491 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
2495 GNUNET_free (dc->filename);
2497 GNUNET_CONTAINER_meta_data_destroy (dc->meta);
2498 GNUNET_FS_uri_destroy (dc->uri);
2499 if (NULL != dc->temp_filename)
2501 if (0 != UNLINK (dc->temp_filename))
2502 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
2505 GNUNET_free (dc->temp_filename);
2507 GNUNET_free_non_null (dc->serialization);
2511 /* end of fs_download.c */