2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 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 2, 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
27 * - callback signaling
28 * - location URI suppport (can wait)
29 * - persistence (can wait)
32 #include "gnunet_constants.h"
33 #include "gnunet_fs_service.h"
36 #define DEBUG_DOWNLOAD GNUNET_YES
40 * Schedule the download of the specified
43 * @param dc overall download this block belongs to
44 * @param chk content-hash-key of the block
45 * @param offset offset of the block in the file
46 * (for IBlocks, the offset is the lowest
47 * offset of any DBlock in the subtree under
49 * @param depth depth of the block, 0 is the root of the tree
52 schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
53 const struct ContentHashKey *chk,
57 struct DownloadRequest *sm;
59 sm = GNUNET_malloc (sizeof (struct DownloadRequest));
63 sm->is_pending = GNUNET_YES;
64 sm->next = dc->pending;
66 GNUNET_CONTAINER_multihashmap_put (dc->active,
69 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
74 * We've lost our connection with the FS service.
75 * Re-establish it and re-transmit all of our
78 * @param dc download context that is having trouble
81 try_reconnect (struct GNUNET_FS_DownloadContext *dc);
85 * Process a search result.
87 * @param sc our search context
88 * @param type type of the result
89 * @param data the (encrypted) response
90 * @param size size of data
93 process_result (struct GNUNET_FS_DownloadContext *dc,
98 GNUNET_HashCode query;
99 struct DownloadRequest *sm;
100 struct GNUNET_CRYPTO_AesSessionKey skey;
101 struct GNUNET_CRYPTO_AesInitializationVector iv;
104 GNUNET_CRYPTO_hash (data, size, &query);
105 sm = GNUNET_CONTAINER_multihashmap_get (dc->active,
112 GNUNET_assert (GNUNET_YES ==
113 GNUNET_CONTAINER_multihashmap_remove (dc->active,
116 GNUNET_CRYPTO_hash_to_aes_key (&sm->chk.key, &skey, &iv);
117 GNUNET_CRYPTO_aes_decrypt (data,
122 // FIXME: save to disk
123 // FIXME: make persistent
124 // FIXME: call progress callback
125 // FIXME: trigger next block (if applicable)
130 * Type of a function to call when we receive a message
134 * @param msg message received, NULL on timeout or fatal error
137 receive_results (void *cls,
138 const struct GNUNET_MessageHeader * msg)
140 struct GNUNET_FS_DownloadContext *dc = cls;
141 const struct ContentMessage *cm;
144 if ( (NULL == msg) ||
145 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_CONTENT) ||
146 (ntohs (msg->size) <= sizeof (struct ContentMessage)) )
151 msize = ntohs (msg->size);
152 cm = (const struct ContentMessage*) msg;
156 msize - sizeof (struct ContentMessage));
157 /* continue receiving */
158 GNUNET_CLIENT_receive (dc->client,
161 GNUNET_TIME_UNIT_FOREVER_REL);
167 * We're ready to transmit a search request to the
168 * file-sharing service. Do it. If there is
169 * more than one request pending, try to send
170 * multiple or request another transmission.
173 * @param size number of bytes available in buf
174 * @param buf where the callee should write the message
175 * @return number of bytes written to buf
178 transmit_download_request (void *cls,
182 struct GNUNET_FS_DownloadContext *dc = cls;
184 struct SearchMessage *sm;
191 GNUNET_assert (size >= sizeof (struct SearchMessage));
194 while ( (dc->pending == NULL) &&
195 (size > msize + sizeof (struct SearchMessage)) )
197 memset (sm, 0, sizeof (struct SearchMessage));
198 sm->header.size = htons (sizeof (struct SearchMessage));
199 sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH);
200 sm->anonymity_level = htonl (dc->anonymity);
201 // FIXME: support 'loc' URIs (set sm->target)
202 sm->query = dc->pending->chk.query;
203 dc->pending->is_pending = GNUNET_NO;
204 dc->pending = dc->pending->next;
205 msize += sizeof (struct SearchMessage);
213 * Reconnect to the FS service and transmit
216 * @param cls our download context
220 do_reconnect (void *cls,
221 const struct GNUNET_SCHEDULER_TaskContext *tc)
223 struct GNUNET_FS_DownloadContext *dc = cls;
224 struct GNUNET_CLIENT_Connection *client;
226 dc->task = GNUNET_SCHEDULER_NO_TASK;
227 client = GNUNET_CLIENT_connect (dc->h->sched,
236 GNUNET_CLIENT_notify_transmit_ready (client,
237 sizeof (struct SearchMessage),
238 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
239 &transmit_download_request,
241 GNUNET_CLIENT_receive (client,
244 GNUNET_TIME_UNIT_FOREVER_REL);
249 * Add entries that are not yet pending back to
252 * @param cls our download context
254 * @param entry entry of type "struct DownloadRequest"
258 retry_entry (void *cls,
259 const GNUNET_HashCode *key,
262 struct GNUNET_FS_DownloadContext *dc = cls;
263 struct DownloadRequest *dr = entry;
265 if (! dr->is_pending)
267 dr->next = dc->pending;
268 dr->is_pending = GNUNET_YES;
276 * We've lost our connection with the FS service.
277 * Re-establish it and re-transmit all of our
280 * @param dc download context that is having trouble
283 try_reconnect (struct GNUNET_FS_DownloadContext *dc)
286 if (NULL != dc->client)
288 GNUNET_CONTAINER_multihashmap_iterate (dc->active,
291 GNUNET_CLIENT_disconnect (dc->client);
295 = GNUNET_SCHEDULER_add_delayed (dc->h->sched,
297 GNUNET_SCHEDULER_PRIORITY_IDLE,
298 GNUNET_SCHEDULER_NO_TASK,
299 GNUNET_TIME_UNIT_SECONDS,
306 * Download parts of a file. Note that this will store
307 * the blocks at the respective offset in the given file. Also, the
308 * download is still using the blocking of the underlying FS
309 * encoding. As a result, the download may *write* outside of the
310 * given boundaries (if offset and length do not match the 32k FS
311 * block boundaries). <p>
313 * This function should be used to focus a download towards a
314 * particular portion of the file (optimization), not to strictly
315 * limit the download to exactly those bytes.
317 * @param h handle to the file sharing subsystem
318 * @param uri the URI of the file (determines what to download); CHK or LOC URI
319 * @param filename where to store the file, maybe NULL (then no file is
320 * created on disk and data must be grabbed from the callbacks)
321 * @param offset at what offset should we start the download (typically 0)
322 * @param length how many bytes should be downloaded starting at offset
323 * @param anonymity anonymity level to use for the download
324 * @param options various options
325 * @param parent parent download to associate this download with (use NULL
326 * for top-level downloads; useful for manually-triggered recursive downloads)
327 * @return context that can be used to control this download
329 struct GNUNET_FS_DownloadContext *
330 GNUNET_FS_file_download_start (struct GNUNET_FS_Handle *h,
331 const struct GNUNET_FS_Uri *uri,
332 const char *filename,
336 enum GNUNET_FS_DownloadOptions options,
337 struct GNUNET_FS_DownloadContext *parent)
339 struct GNUNET_FS_DownloadContext *dc;
340 struct GNUNET_CLIENT_Connection *client;
342 client = GNUNET_CLIENT_connect (h->sched,
347 // FIXME: add support for "loc" URIs!
348 GNUNET_assert (GNUNET_FS_uri_test_chk (uri));
349 if ( (dc->offset + dc->length < dc->offset) ||
350 (dc->offset + dc->length > uri->data.chk.file_length) )
355 dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
359 dc->uri = GNUNET_FS_uri_dup (uri);
360 dc->filename = (NULL == filename) ? NULL : GNUNET_strdup (filename);
363 dc->anonymity = anonymity;
364 dc->options = options;
365 dc->active = GNUNET_CONTAINER_multihashmap_create (1 + (length / DBLOCK_SIZE));
366 // FIXME: make persistent
367 schedule_block_download (dc,
368 &dc->uri->data.chk.chk,
371 GNUNET_CLIENT_notify_transmit_ready (client,
372 sizeof (struct SearchMessage),
373 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
374 &transmit_download_request,
376 GNUNET_CLIENT_receive (client,
379 GNUNET_TIME_UNIT_FOREVER_REL);
380 // FIXME: signal download start
386 * Free entries in the map.
388 * @param cls unused (NULL)
390 * @param entry entry of type "struct DownloadRequest" which is freed
394 free_entry (void *cls,
395 const GNUNET_HashCode *key,
404 * Stop a download (aborts if download is incomplete).
406 * @param dc handle for the download
407 * @param do_delete delete files of incomplete downloads
410 GNUNET_FS_file_download_stop (struct GNUNET_FS_DownloadContext *dc,
413 // FIXME: make unpersistent
414 // FIXME: signal download end
416 if (GNUNET_SCHEDULER_NO_TASK != dc->task)
417 GNUNET_SCHEDULER_cancel (dc->h->sched,
419 if (NULL != dc->client)
420 GNUNET_CLIENT_disconnect (dc->client);
421 GNUNET_CONTAINER_multihashmap_iterate (dc->active,
424 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
425 GNUNET_FS_uri_destroy (dc->uri);
426 GNUNET_free_non_null (dc->filename);
450 * Node-specific data (not shared, keep small!). 152 bytes.
451 * Nodes are kept in a doubly-linked list.
456 * Pointer to shared data between all nodes (request manager,
457 * progress data, etc.).
459 struct GNUNET_ECRS_DownloadContext *ctx;
462 * Previous entry in DLL.
472 * What is the GNUNET_EC_ContentHashKey for this block?
474 GNUNET_EC_ContentHashKey chk;
477 * At what offset (on the respective level!) is this
480 unsigned long long offset;
483 * 0 for dblocks, >0 for iblocks.
490 * @brief structure that keeps track of currently pending requests for
493 * Handle to the state of a request manager. Here we keep track of
494 * which queries went out with which priorities and which nodes in
495 * the merkle-tree are waiting for the replies.
497 struct GNUNET_ECRS_DownloadContext
501 * Total number of bytes in the file.
503 unsigned long long total;
506 * Number of bytes already obtained
508 unsigned long long completed;
511 * Starting-offset in file (for partial download)
513 unsigned long long offset;
516 * Length of the download (starting at offset).
518 unsigned long long length;
521 * Time download was started.
523 GNUNET_CronTime startTime;
526 * Doubly linked list of all pending requests (head)
531 * Doubly linked list of all pending requests (tail)
536 * FSLIB context for issuing requests.
538 struct GNUNET_FS_SearchContext *sctx;
541 * Context for error reporting.
543 struct GNUNET_GE_Context *ectx;
546 * Configuration information.
548 struct GNUNET_GC_Configuration *cfg;
556 * Do we exclusively own this sctx?
566 * Main thread running the operation.
568 struct GNUNET_ThreadHandle *main;
571 * Function to call when we make progress.
573 GNUNET_ECRS_DownloadProgressCallback dpcb;
576 * Extra argument to dpcb.
581 * Identity of the peer having the content, or all-zeros
582 * if we don't know of such a peer.
584 GNUNET_PeerIdentity target;
587 * Abort? Flag that can be set at any time
588 * to abort the RM as soon as possible. Set
589 * to GNUNET_YES during orderly shutdown,
590 * set to GNUNET_SYSERR on error.
595 * Do we have a specific peer from which we download
601 * Desired anonymity level for the download.
603 unsigned int anonymityLevel;
606 * The depth of the file-tree.
608 unsigned int treedepth;
613 content_receive_callback (const GNUNET_HashCode * query,
614 const GNUNET_DatastoreValue * reply, void *cls,
615 unsigned long long uid);
619 * Close the files and free the associated resources.
621 * @param self reference to the download context
624 free_request_manager (struct GNUNET_ECRS_DownloadContext *rm)
628 if (rm->abortFlag == GNUNET_NO)
629 rm->abortFlag = GNUNET_YES;
630 if (rm->my_sctx == GNUNET_YES)
631 GNUNET_FS_destroy_search_context (rm->sctx);
633 GNUNET_FS_suspend_search_context (rm->sctx);
634 while (rm->head != NULL)
637 GNUNET_DLL_remove (rm->head, rm->tail, pos);
638 if (rm->my_sctx != GNUNET_YES)
639 GNUNET_FS_stop_search (rm->sctx, &content_receive_callback, pos);
642 if (rm->my_sctx != GNUNET_YES)
643 GNUNET_FS_resume_search_context (rm->sctx);
644 GNUNET_GE_ASSERT (NULL, rm->tail == NULL);
647 if (rm->main != NULL)
648 GNUNET_thread_release_self (rm->main);
649 GNUNET_free_non_null (rm->filename);
657 * @param self reference to the download context
658 * @param level level in the tree to read/write at
659 * @param pos position where to read or write
660 * @param buf where to read from or write to
661 * @param len how many bytes to read or write
662 * @return number of bytes read, GNUNET_SYSERR on error
665 read_from_files (struct GNUNET_ECRS_DownloadContext *self,
667 unsigned long long pos, void *buf, unsigned int len)
669 if ((level > 0) || (self->handle == -1))
670 return GNUNET_SYSERR;
671 LSEEK (self->handle, pos, SEEK_SET);
672 return READ (self->handle, buf, len);
678 * @param self reference to the download context
679 * @param level level in the tree to write to
680 * @param pos position where to write
681 * @param buf where to write to
682 * @param len how many bytes to write
683 * @return number of bytes written, GNUNET_SYSERR on error
686 write_to_files (struct GNUNET_ECRS_DownloadContext *self,
688 unsigned long long pos, void *buf, unsigned int len)
693 return len; /* lie -- no more temps */
694 if (self->handle == -1)
696 LSEEK (self->handle, pos, SEEK_SET);
697 ret = WRITE (self->handle, buf, len);
699 GNUNET_GE_LOG_STRERROR_FILE (self->ectx,
700 GNUNET_GE_ERROR | GNUNET_GE_BULK |
701 GNUNET_GE_USER, "write", self->filename);
706 * Queue a request for execution.
708 * @param rm the request manager struct from createRequestManager
709 * @param node the node to call once a reply is received
712 add_request (struct Node *node)
714 struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
716 GNUNET_DLL_insert (rm->head, rm->tail, node);
717 GNUNET_FS_start_search (rm->sctx,
718 rm->have_target == GNUNET_NO ? NULL : &rm->target,
719 GNUNET_ECRS_BLOCKTYPE_DATA, 1,
722 &content_receive_callback, node);
726 signal_abort (struct GNUNET_ECRS_DownloadContext *rm, const char *msg)
728 rm->abortFlag = GNUNET_SYSERR;
729 if ((rm->head != NULL) && (rm->dpcb != NULL))
730 rm->dpcb (rm->length + 1, 0, 0, 0, msg, 0, rm->dpcbClosure);
731 GNUNET_thread_stop_sleep (rm->main);
737 * @param self the request manager struct from createRequestManager
738 * @param node the block for which the request is canceled
741 delete_node (struct Node *node)
743 struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
745 GNUNET_DLL_remove (rm->head, rm->tail, node);
747 if (rm->head == NULL)
748 GNUNET_thread_stop_sleep (rm->main);
752 * Compute how many bytes of data are stored in
756 get_node_size (const struct Node *node)
760 unsigned long long rsize;
761 unsigned long long spos;
762 unsigned long long epos;
764 GNUNET_GE_ASSERT (node->ctx->ectx, node->offset < node->ctx->total);
765 if (node->level == 0)
767 ret = GNUNET_ECRS_DBLOCK_SIZE;
768 if (node->offset + (unsigned long long) ret > node->ctx->total)
769 ret = (unsigned int) (node->ctx->total - node->offset);
771 GNUNET_GE_LOG (node->ctx->rm->ectx,
772 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
773 "Node at offset %llu and level %d has size %u\n",
774 node->offset, node->level, ret);
778 rsize = GNUNET_ECRS_DBLOCK_SIZE;
779 for (i = 0; i < node->level - 1; i++)
780 rsize *= GNUNET_ECRS_CHK_PER_INODE;
781 spos = rsize * (node->offset / sizeof (GNUNET_EC_ContentHashKey));
782 epos = spos + rsize * GNUNET_ECRS_CHK_PER_INODE;
783 if (epos > node->ctx->total)
784 epos = node->ctx->total;
785 ret = (epos - spos) / rsize;
786 if (ret * rsize < epos - spos)
787 ret++; /* need to round up! */
789 GNUNET_GE_LOG (node->ctx->rm->ectx,
790 GNUNET_GE_DEBUG | GNUNET_GE_REQUEST | GNUNET_GE_USER,
791 "Node at offset %llu and level %d has size %u\n",
792 node->offset, node->level,
793 ret * sizeof (GNUNET_EC_ContentHashKey));
795 return ret * sizeof (GNUNET_EC_ContentHashKey);
799 * Notify client about progress.
802 notify_client_about_progress (const struct Node *node,
803 const char *data, unsigned int size)
805 struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
808 if ((rm->abortFlag != GNUNET_NO) || (node->level != 0))
810 rm->completed += size;
811 eta = GNUNET_get_time ();
812 if (rm->completed > 0)
813 eta = (GNUNET_CronTime) (rm->startTime +
814 (((double) (eta - rm->startTime) /
815 (double) rm->completed)) *
816 (double) rm->length);
817 if (rm->dpcb != NULL)
818 rm->dpcb (rm->length,
819 rm->completed, eta, node->offset, data, size, rm->dpcbClosure);
824 * DOWNLOAD children of this GNUNET_EC_IBlock.
826 * @param node the node for which the children should be downloaded
827 * @param data data for the node
828 * @param size size of data
830 static void iblock_download_children (const struct Node *node,
831 const char *data, unsigned int size);
834 * Check if self block is already present on the drive. If the block
835 * is a dblock and present, the ProgressModel is notified. If the
836 * block is present and it is an iblock, downloading the children is
839 * Also checks if the block is within the range of blocks
840 * that we are supposed to download. If not, the method
841 * returns as if the block is present but does NOT signal
844 * @param node that is checked for presence
845 * @return GNUNET_YES if present, GNUNET_NO if not.
848 check_node_present (const struct Node *node)
856 size = get_node_size (node);
857 /* first check if node is within range.
858 For now, keeping it simple, we only do
859 this for level-0 nodes */
860 if ((node->level == 0) &&
861 ((node->offset + size < node->ctx->offset) ||
862 (node->offset >= node->ctx->offset + node->ctx->length)))
864 data = GNUNET_malloc (size);
866 res = read_from_files (node->ctx, node->level, node->offset, data, size);
869 GNUNET_hash (data, size, &hc);
870 if (0 == memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode)))
872 notify_client_about_progress (node, data, size);
874 iblock_download_children (node, data, size);
883 * DOWNLOAD children of this GNUNET_EC_IBlock.
885 * @param node the node that should be downloaded
888 iblock_download_children (const struct Node *node,
889 const char *data, unsigned int size)
891 struct GNUNET_GE_Context *ectx = node->ctx->ectx;
894 unsigned int childcount;
895 const GNUNET_EC_ContentHashKey *chks;
896 unsigned int levelSize;
897 unsigned long long baseOffset;
899 GNUNET_GE_ASSERT (ectx, node->level > 0);
900 childcount = size / sizeof (GNUNET_EC_ContentHashKey);
901 if (size != childcount * sizeof (GNUNET_EC_ContentHashKey))
903 GNUNET_GE_BREAK (ectx, 0);
906 if (node->level == 1)
908 levelSize = GNUNET_ECRS_DBLOCK_SIZE;
910 node->offset / sizeof (GNUNET_EC_ContentHashKey) *
911 GNUNET_ECRS_DBLOCK_SIZE;
916 sizeof (GNUNET_EC_ContentHashKey) * GNUNET_ECRS_CHK_PER_INODE;
917 baseOffset = node->offset * GNUNET_ECRS_CHK_PER_INODE;
919 chks = (const GNUNET_EC_ContentHashKey *) data;
920 for (i = 0; i < childcount; i++)
922 child = GNUNET_malloc (sizeof (struct Node));
923 child->ctx = node->ctx;
924 child->chk = chks[i];
925 child->offset = baseOffset + i * levelSize;
926 GNUNET_GE_ASSERT (ectx, child->offset < node->ctx->total);
927 child->level = node->level - 1;
928 GNUNET_GE_ASSERT (ectx, (child->level != 0) ||
929 ((child->offset % GNUNET_ECRS_DBLOCK_SIZE) == 0));
930 if (GNUNET_NO == check_node_present (child))
933 GNUNET_free (child); /* done already! */
939 * Decrypts a given data block
941 * @param data represents the data block
942 * @param hashcode represents the key concatenated with the initial
943 * value used in the alg
944 * @param result where to store the result (encrypted block)
945 * @returns GNUNET_OK on success, GNUNET_SYSERR on error
948 decrypt_content (const char *data,
949 unsigned int size, const GNUNET_HashCode * hashcode,
952 GNUNET_AES_InitializationVector iv;
953 GNUNET_AES_SessionKey skey;
955 /* get key and init value from the GNUNET_HashCode */
956 GNUNET_hash_to_AES_key (hashcode, &skey, &iv);
957 return GNUNET_AES_decrypt (&skey, data, size, &iv, result);
961 * We received a GNUNET_EC_ContentHashKey reply for a block. Decrypt. Note
962 * that the caller (fslib) has already aquired the
963 * RM lock (we sometimes aquire it again in callees,
964 * mostly because our callees could be also be theoretically
965 * called from elsewhere).
967 * @param cls the node for which the reply is given, freed in
969 * @param query the query for which reply is the answer
970 * @param reply the reply
971 * @return GNUNET_OK if the reply was valid, GNUNET_SYSERR on error
974 content_receive_callback (const GNUNET_HashCode * query,
975 const GNUNET_DatastoreValue * reply, void *cls,
976 unsigned long long uid)
978 struct Node *node = cls;
979 struct GNUNET_ECRS_DownloadContext *rm = node->ctx;
980 struct GNUNET_GE_Context *ectx = rm->ectx;
985 if (rm->abortFlag != GNUNET_NO)
986 return GNUNET_SYSERR;
987 GNUNET_GE_ASSERT (ectx,
988 0 == memcmp (query, &node->chk.query,
989 sizeof (GNUNET_HashCode)));
990 size = ntohl (reply->size) - sizeof (GNUNET_DatastoreValue);
991 if ((size <= sizeof (GNUNET_EC_DBlock)) ||
992 (size - sizeof (GNUNET_EC_DBlock) != get_node_size (node)))
994 GNUNET_GE_BREAK (ectx, 0);
995 return GNUNET_SYSERR; /* invalid size! */
997 size -= sizeof (GNUNET_EC_DBlock);
998 data = GNUNET_malloc (size);
1000 decrypt_content ((const char *)
1001 &((const GNUNET_EC_DBlock *) &reply[1])[1], size,
1002 &node->chk.key, data))
1003 GNUNET_GE_ASSERT (ectx, 0);
1004 GNUNET_hash (data, size, &hc);
1005 if (0 != memcmp (&hc, &node->chk.key, sizeof (GNUNET_HashCode)))
1008 GNUNET_GE_BREAK (ectx, 0);
1010 _("Decrypted content does not match key. "
1011 "This is either a bug or a maliciously inserted "
1012 "file. Download aborted.\n"));
1013 return GNUNET_SYSERR;
1015 if (size != write_to_files (rm, node->level, node->offset, data, size))
1017 GNUNET_GE_LOG_STRERROR (ectx,
1018 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
1019 GNUNET_GE_USER | GNUNET_GE_BULK, "WRITE");
1020 signal_abort (rm, _("IO error."));
1021 return GNUNET_SYSERR;
1023 notify_client_about_progress (node, data, size);
1024 if (node->level > 0)
1025 iblock_download_children (node, data, size);
1027 /* request satisfied, stop requesting! */
1034 * Helper function to sanitize filename
1035 * and create necessary directories.
1038 get_real_download_filename (struct GNUNET_GE_Context *ectx,
1039 const char *filename)
1046 if ((filename[strlen (filename) - 1] == '/') ||
1047 (filename[strlen (filename) - 1] == '\\'))
1050 GNUNET_malloc (strlen (filename) + strlen (GNUNET_DIRECTORY_EXT));
1051 strcpy (realFN, filename);
1052 realFN[strlen (filename) - 1] = '\0';
1053 strcat (realFN, GNUNET_DIRECTORY_EXT);
1057 realFN = GNUNET_strdup (filename);
1059 path = GNUNET_malloc (strlen (realFN) * strlen (GNUNET_DIRECTORY_EXT) + 1);
1060 strcpy (path, realFN);
1062 while (*pos != '\0')
1064 if (*pos == DIR_SEPARATOR)
1067 if ((0 == STAT (path, &buf)) && (!S_ISDIR (buf.st_mode)))
1069 *pos = DIR_SEPARATOR;
1070 memmove (pos + strlen (GNUNET_DIRECTORY_EXT),
1073 GNUNET_DIRECTORY_EXT, strlen (GNUNET_DIRECTORY_EXT));
1074 pos += strlen (GNUNET_DIRECTORY_EXT);
1078 *pos = DIR_SEPARATOR;
1083 GNUNET_free (realFN);
1087 /* ***************** main method **************** */
1091 * Download parts of a file. Note that this will store
1092 * the blocks at the respective offset in the given file.
1093 * Also, the download is still using the blocking of the
1094 * underlying ECRS encoding. As a result, the download
1095 * may *write* outside of the given boundaries (if offset
1096 * and length do not match the 32k ECRS block boundaries).
1099 * This function should be used to focus a download towards a
1100 * particular portion of the file (optimization), not to strictly
1101 * limit the download to exactly those bytes.
1103 * @param uri the URI of the file (determines what to download)
1104 * @param filename where to store the file
1105 * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files
1106 * @param start starting offset
1107 * @param length length of the download (starting at offset)
1109 struct GNUNET_ECRS_DownloadContext *
1110 GNUNET_ECRS_file_download_partial_start (struct GNUNET_GE_Context *ectx,
1111 struct GNUNET_GC_Configuration *cfg,
1112 struct GNUNET_FS_SearchContext *sc,
1113 const struct GNUNET_ECRS_URI *uri,
1114 const char *filename,
1115 unsigned long long offset,
1116 unsigned long long length,
1117 unsigned int anonymityLevel,
1119 GNUNET_ECRS_DownloadProgressCallback
1120 dpcb, void *dpcbClosure)
1122 struct GNUNET_ECRS_DownloadContext *rm;
1127 if ((!GNUNET_ECRS_uri_test_chk (uri)) && (!GNUNET_ECRS_uri_test_loc (uri)))
1129 GNUNET_GE_BREAK (ectx, 0);
1132 rm = GNUNET_malloc (sizeof (struct GNUNET_ECRS_DownloadContext));
1133 memset (rm, 0, sizeof (struct GNUNET_ECRS_DownloadContext));
1136 rm->sctx = GNUNET_FS_create_search_context (ectx, cfg);
1137 if (rm->sctx == NULL)
1142 rm->my_sctx = GNUNET_YES;
1147 rm->my_sctx = GNUNET_NO;
1151 rm->startTime = GNUNET_get_time ();
1152 rm->anonymityLevel = anonymityLevel;
1153 rm->offset = offset;
1154 rm->length = length;
1156 rm->dpcbClosure = dpcbClosure;
1157 rm->main = GNUNET_thread_get_self ();
1158 rm->total = GNUNET_ntohll (uri->data.fi.file_length);
1160 filename != NULL ? get_real_download_filename (ectx, filename) : NULL;
1162 if ((rm->filename != NULL) &&
1164 GNUNET_disk_directory_create_for_file (ectx, rm->filename)))
1166 free_request_manager (rm);
1171 if (rm->filename != NULL)
1173 ret = GNUNET_disk_file_open (ectx,
1175 O_CREAT | O_WRONLY | O_TRUNC,
1179 free_request_manager (rm);
1184 dpcb (0, 0, rm->startTime, 0, NULL, 0, dpcbClosure);
1185 free_request_manager (rm);
1188 rm->treedepth = GNUNET_ECRS_compute_depth (rm->total);
1189 if ((NULL != rm->filename) &&
1190 ((0 == STAT (rm->filename, &buf))
1191 && ((size_t) buf.st_size > rm->total)))
1193 /* if exists and oversized, truncate */
1194 if (truncate (rm->filename, rm->total) != 0)
1196 GNUNET_GE_LOG_STRERROR_FILE (ectx,
1197 GNUNET_GE_ERROR | GNUNET_GE_ADMIN |
1198 GNUNET_GE_BULK, "truncate",
1200 free_request_manager (rm);
1204 if (rm->filename != NULL)
1206 rm->handle = GNUNET_disk_file_open (ectx,
1212 free_request_manager (rm);
1218 if (GNUNET_ECRS_uri_test_loc (uri))
1220 GNUNET_hash (&uri->data.loc.peer, sizeof (GNUNET_RSA_PublicKey),
1221 &rm->target.hashPubKey);
1222 rm->have_target = GNUNET_YES;
1224 top = GNUNET_malloc (sizeof (struct Node));
1225 memset (top, 0, sizeof (struct Node));
1227 top->chk = uri->data.fi.chk;
1229 top->level = rm->treedepth;
1230 if (GNUNET_NO == check_node_present (top))
1238 GNUNET_ECRS_file_download_partial_stop (struct GNUNET_ECRS_DownloadContext
1243 ret = rm->abortFlag;
1244 free_request_manager (rm);
1245 if (ret == GNUNET_NO)
1246 ret = GNUNET_OK; /* normal termination */
1251 * Download parts of a file. Note that this will store
1252 * the blocks at the respective offset in the given file.
1253 * Also, the download is still using the blocking of the
1254 * underlying ECRS encoding. As a result, the download
1255 * may *write* outside of the given boundaries (if offset
1256 * and length do not match the 32k ECRS block boundaries).
1259 * This function should be used to focus a download towards a
1260 * particular portion of the file (optimization), not to strictly
1261 * limit the download to exactly those bytes.
1263 * @param uri the URI of the file (determines what to download)
1264 * @param filename where to store the file
1265 * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files
1266 * @param start starting offset
1267 * @param length length of the download (starting at offset)
1270 GNUNET_ECRS_file_download_partial (struct GNUNET_GE_Context *ectx,
1271 struct GNUNET_GC_Configuration *cfg,
1272 const struct GNUNET_ECRS_URI *uri,
1273 const char *filename,
1274 unsigned long long offset,
1275 unsigned long long length,
1276 unsigned int anonymityLevel,
1278 GNUNET_ECRS_DownloadProgressCallback dpcb,
1280 GNUNET_ECRS_TestTerminate tt,
1283 struct GNUNET_ECRS_DownloadContext *rm;
1288 rm = GNUNET_ECRS_file_download_partial_start (ectx,
1299 return GNUNET_SYSERR;
1300 while ((GNUNET_OK == tt (ttClosure)) &&
1301 (GNUNET_YES != GNUNET_shutdown_test ()) &&
1302 (rm->abortFlag == GNUNET_NO) && (rm->head != NULL))
1303 GNUNET_thread_sleep (5 * GNUNET_CRON_SECONDS);
1304 ret = GNUNET_ECRS_file_download_partial_stop (rm);
1309 * Download a file (simplified API).
1311 * @param uri the URI of the file (determines what to download)
1312 * @param filename where to store the file
1315 GNUNET_ECRS_file_download (struct GNUNET_GE_Context *ectx,
1316 struct GNUNET_GC_Configuration *cfg,
1317 const struct GNUNET_ECRS_URI *uri,
1318 const char *filename,
1319 unsigned int anonymityLevel,
1320 GNUNET_ECRS_DownloadProgressCallback dpcb,
1321 void *dpcbClosure, GNUNET_ECRS_TestTerminate tt,
1324 return GNUNET_ECRS_file_download_partial (ectx,
1329 GNUNET_ECRS_uri_get_file_size
1330 (uri), anonymityLevel, GNUNET_NO,
1331 dpcb, dpcbClosure, tt, ttClosure);
1336 /* end of fs_download.c */