aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-09-05 20:40:29 +0000
committerChristian Grothoff <christian@grothoff.org>2009-09-05 20:40:29 +0000
commit0b90310c9a26ffa0e195759b7a5d5c07fa33f642 (patch)
treede64994c81c9b7490b7fb4a6a7f64b664896227f /src
parentba388145e2a982d25fb27af1ea80231625f56ded (diff)
downloadgnunet-0b90310c9a26ffa0e195759b7a5d5c07fa33f642.tar.gz
gnunet-0b90310c9a26ffa0e195759b7a5d5c07fa33f642.zip
towards having download
Diffstat (limited to 'src')
-rw-r--r--src/fs/fs.h155
-rw-r--r--src/fs/fs_download.c386
2 files changed, 508 insertions, 33 deletions
diff --git a/src/fs/fs.h b/src/fs/fs.h
index ed925d9c8..3dc60a5dc 100644
--- a/src/fs/fs.h
+++ b/src/fs/fs.h
@@ -337,12 +337,12 @@ struct GNUNET_FS_FileInformation
337 /** 337 /**
338 * Desired anonymity level. 338 * Desired anonymity level.
339 */ 339 */
340 unsigned int anonymity; 340 uint32_t anonymity;
341 341
342 /** 342 /**
343 * Desired priority (for keeping the content in the DB). 343 * Desired priority (for keeping the content in the DB).
344 */ 344 */
345 unsigned int priority; 345 uint32_t priority;
346 346
347}; 347};
348 348
@@ -428,12 +428,6 @@ struct GNUNET_FS_PublishContext
428 char *nuid; 428 char *nuid;
429 429
430 /** 430 /**
431 * ID of the task performing the upload. NO_TASK
432 * if the upload has completed.
433 */
434 GNUNET_SCHEDULER_TaskIdentifier upload_task;
435
436 /**
437 * Our own client handle for the FS service; 431 * Our own client handle for the FS service;
438 * only briefly used when we start to index a 432 * only briefly used when we start to index a
439 * file, otherwise NULL. 433 * file, otherwise NULL.
@@ -441,6 +435,23 @@ struct GNUNET_FS_PublishContext
441 struct GNUNET_CLIENT_Connection *client; 435 struct GNUNET_CLIENT_Connection *client;
442 436
443 /** 437 /**
438 * Current position in the file-tree for the
439 * upload.
440 */
441 struct GNUNET_FS_FileInformation *fi_pos;
442
443 /**
444 * Connection to the datastore service.
445 */
446 struct GNUNET_DATASTORE_Handle *dsh;
447
448 /**
449 * ID of the task performing the upload. NO_TASK
450 * if the upload has completed.
451 */
452 GNUNET_SCHEDULER_TaskIdentifier upload_task;
453
454 /**
444 * Typically GNUNET_NO. Set to GNUNET_YES if 455 * Typically GNUNET_NO. Set to GNUNET_YES if
445 * "upload_task" is GNUNET_SCHEDULER_NO_TASK 456 * "upload_task" is GNUNET_SCHEDULER_NO_TASK
446 * and we're waiting for a response from the 457 * and we're waiting for a response from the
@@ -461,17 +472,6 @@ struct GNUNET_FS_PublishContext
461 enum GNUNET_FS_PublishOptions options; 472 enum GNUNET_FS_PublishOptions options;
462 473
463 /** 474 /**
464 * Current position in the file-tree for the
465 * upload.
466 */
467 struct GNUNET_FS_FileInformation *fi_pos;
468
469 /**
470 * Connection to the datastore service.
471 */
472 struct GNUNET_DATASTORE_Handle *dsh;
473
474 /**
475 * Space reservation ID with datastore service 475 * Space reservation ID with datastore service
476 * for this upload. 476 * for this upload.
477 */ 477 */
@@ -765,10 +765,127 @@ struct GNUNET_FS_SearchContext
765 765
766 766
767/** 767/**
768 * Information about an active download request.
769 */
770struct DownloadRequest
771{
772 /**
773 * While pending, we keep all download requests
774 * in a linked list.
775 */
776 struct DownloadRequest *next;
777
778 /**
779 * CHK for the request.
780 */
781 struct ContentHashKey chk;
782
783 /**
784 * Offset of the corresponding block.
785 */
786 uint64_t offset;
787
788 /**
789 * Depth of the corresponding block in the tree.
790 */
791 unsigned int depth;
792
793 /**
794 * Set if this request is currently in the linked list of pending
795 * requests. Needed in case we get a response for a request that we
796 * have not yet send (due to FS bug or two blocks with identical
797 * content); in this case, we would need to remove the block from
798 * the pending list (and need a fast way to check if the block is on
799 * it).
800 */
801 int is_pending;
802
803};
804
805
806/**
768 * Context for controlling a download. 807 * Context for controlling a download.
769 */ 808 */
770struct GNUNET_FS_DownloadContext 809struct GNUNET_FS_DownloadContext
771{ 810{
811
812 /**
813 * Global FS context.
814 */
815 struct GNUNET_FS_Handle *h;
816
817 /**
818 * Connection to the FS service.
819 */
820 struct GNUNET_CLIENT_Connection *client;
821
822 /**
823 * Parent download (used when downloading files
824 * in directories).
825 */
826 struct GNUNET_FS_DownloadContext *parent;
827
828 /**
829 * Context kept for the client.
830 */
831 void *client_info;
832
833 /**
834 * URI that identifies the file that
835 * we are downloading.
836 */
837 struct GNUNET_FS_Uri *uri;
838
839 /**
840 * Where are we writing the data (name of the
841 * file, can be NULL!).
842 */
843 char *filename;
844
845 /**
846 * Map of active requests (those waiting
847 * for a response). The key is the hash
848 * of the encryped block (aka query).
849 */
850 struct GNUNET_CONTAINER_MultiHashMap *active;
851
852 /**
853 * Linked list of pending requests.
854 */
855 struct DownloadRequest *pending;
856
857 /**
858 * ID of a task that is using this struct
859 * and that must be cancelled when the download
860 * is being stopped (if not GNUNET_SCHEDULER_NO_TASK).
861 * Used for the task that adds some artificial
862 * delay when trying to reconnect to the FS
863 * service.
864 */
865 GNUNET_SCHEDULER_TaskIdentifier task;
866
867 /**
868 * What is the first offset that we're interested
869 * in?
870 */
871 uint64_t offset;
872
873 /**
874 * How many bytes starting from offset are desired?
875 * This is NOT the overall length of the file!
876 */
877 uint64_t length;
878
879 /**
880 * Desired level of anonymity.
881 */
882 uint32_t anonymity;
883
884 /**
885 * Options for the download.
886 */
887 enum GNUNET_FS_DownloadOptions options;
888
772}; 889};
773 890
774struct GNUNET_FS_Namespace 891struct GNUNET_FS_Namespace
diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c
index f1954ea89..06c4672a2 100644
--- a/src/fs/fs_download.c
+++ b/src/fs/fs_download.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008 Christian Grothoff (and other contributing authors) 3 (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 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 6 it under the terms of the GNU General Public License as published
@@ -19,17 +19,288 @@
19*/ 19*/
20/** 20/**
21 * @file fs/fs_download.c 21 * @file fs/fs_download.c
22 * @brief DOWNLOAD helper methods (which do the real work). 22 * @brief download methods
23 * @author Christian Grothoff 23 * @author Christian Grothoff
24 *
25 * TODO:
26 * - process replies
27 * - callback signaling
28 * - location URI suppport (can wait)
29 * - persistence (can wait)
24 */ 30 */
25
26#include "platform.h" 31#include "platform.h"
32#include "gnunet_constants.h"
27#include "gnunet_fs_service.h" 33#include "gnunet_fs_service.h"
28#include "fs.h" 34#include "fs.h"
29 35
30#define DEBUG_DOWNLOAD GNUNET_YES 36#define DEBUG_DOWNLOAD GNUNET_YES
31 37
32 38
39/**
40 * Schedule the download of the specified
41 * block in the tree.
42 *
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
48 * the IBlock)
49 * @param depth depth of the block, 0 is the root of the tree
50 */
51static void
52schedule_block_download (struct GNUNET_FS_DownloadContext *dc,
53 const struct ContentHashKey *chk,
54 uint64_t offset,
55 unsigned int depth)
56{
57 struct DownloadRequest *sm;
58
59 sm = GNUNET_malloc (sizeof (struct DownloadRequest));
60 sm->chk = *chk;
61 sm->offset = offset;
62 sm->depth = depth;
63 sm->is_pending = GNUNET_YES;
64 sm->next = dc->pending;
65 dc->pending = sm;
66 GNUNET_CONTAINER_multihashmap_put (dc->active,
67 &chk->query,
68 sm,
69 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
70}
71
72
73/**
74 * We've lost our connection with the FS service.
75 * Re-establish it and re-transmit all of our
76 * pending requests.
77 *
78 * @param dc download context that is having trouble
79 */
80static void
81try_reconnect (struct GNUNET_FS_DownloadContext *dc);
82
83
84/**
85 * Process a search result.
86 *
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
91 */
92static void
93process_result (struct GNUNET_FS_DownloadContext *dc,
94 uint32_t type,
95 const void *data,
96 size_t size)
97{
98 GNUNET_HashCode query;
99 struct DownloadRequest *sm;
100 struct GNUNET_CRYPTO_AesSessionKey skey;
101 struct GNUNET_CRYPTO_AesInitializationVector iv;
102 char pt[size];
103
104 GNUNET_CRYPTO_hash (data, size, &query);
105 sm = GNUNET_CONTAINER_multihashmap_get (dc->active,
106 &query);
107 if (NULL == sm)
108 {
109 GNUNET_break (0);
110 return;
111 }
112 GNUNET_assert (GNUNET_YES ==
113 GNUNET_CONTAINER_multihashmap_remove (dc->active,
114 &query,
115 sm));
116 GNUNET_CRYPTO_hash_to_aes_key (&sm->chk.key, &skey, &iv);
117 GNUNET_CRYPTO_aes_decrypt (data,
118 size,
119 &skey,
120 &iv,
121 pt);
122 // FIXME: save to disk
123 // FIXME: make persistent
124 // FIXME: call progress callback
125 // FIXME: trigger next block (if applicable)
126}
127
128
129/**
130 * Type of a function to call when we receive a message
131 * from the service.
132 *
133 * @param cls closure
134 * @param msg message received, NULL on timeout or fatal error
135 */
136static void
137receive_results (void *cls,
138 const struct GNUNET_MessageHeader * msg)
139{
140 struct GNUNET_FS_DownloadContext *dc = cls;
141 const struct ContentMessage *cm;
142 uint16_t msize;
143
144 if ( (NULL == msg) ||
145 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_FS_CONTENT) ||
146 (ntohs (msg->size) <= sizeof (struct ContentMessage)) )
147 {
148 try_reconnect (dc);
149 return;
150 }
151 msize = ntohs (msg->size);
152 cm = (const struct ContentMessage*) msg;
153 process_result (dc,
154 ntohl (cm->type),
155 &cm[1],
156 msize - sizeof (struct ContentMessage));
157 /* continue receiving */
158 GNUNET_CLIENT_receive (dc->client,
159 &receive_results,
160 dc,
161 GNUNET_TIME_UNIT_FOREVER_REL);
162}
163
164
165
166/**
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.
171 *
172 * @param cls closure
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
176 */
177static size_t
178transmit_download_request (void *cls,
179 size_t size,
180 void *buf)
181{
182 struct GNUNET_FS_DownloadContext *dc = cls;
183 size_t msize;
184 struct SearchMessage *sm;
185
186 if (NULL == buf)
187 {
188 try_reconnect (dc);
189 return 0;
190 }
191 GNUNET_assert (size >= sizeof (struct SearchMessage));
192 msize = 0;
193 sm = buf;
194 while ( (dc->pending == NULL) &&
195 (size > msize + sizeof (struct SearchMessage)) )
196 {
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);
206 sm++;
207 }
208 return msize;
209}
210
211
212/**
213 * Reconnect to the FS service and transmit
214 * our queries NOW.
215 *
216 * @param cls our download context
217 * @param tc unused
218 */
219static void
220do_reconnect (void *cls,
221 const struct GNUNET_SCHEDULER_TaskContext *tc)
222{
223 struct GNUNET_FS_DownloadContext *dc = cls;
224 struct GNUNET_CLIENT_Connection *client;
225
226 dc->task = GNUNET_SCHEDULER_NO_TASK;
227 client = GNUNET_CLIENT_connect (dc->h->sched,
228 "fs",
229 dc->h->cfg);
230 if (NULL == client)
231 {
232 try_reconnect (dc);
233 return;
234 }
235 dc->client = client;
236 GNUNET_CLIENT_notify_transmit_ready (client,
237 sizeof (struct SearchMessage),
238 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
239 &transmit_download_request,
240 dc);
241 GNUNET_CLIENT_receive (client,
242 &receive_results,
243 dc,
244 GNUNET_TIME_UNIT_FOREVER_REL);
245}
246
247
248/**
249 * Add entries that are not yet pending back to
250 * the pending list.
251 *
252 * @param cls our download context
253 * @param key unused
254 * @param entry entry of type "struct DownloadRequest"
255 * @return GNUNET_OK
256 */
257static int
258retry_entry (void *cls,
259 const GNUNET_HashCode *key,
260 void *entry)
261{
262 struct GNUNET_FS_DownloadContext *dc = cls;
263 struct DownloadRequest *dr = entry;
264
265 if (! dr->is_pending)
266 {
267 dr->next = dc->pending;
268 dr->is_pending = GNUNET_YES;
269 dc->pending = entry;
270 }
271 return GNUNET_OK;
272}
273
274
275/**
276 * We've lost our connection with the FS service.
277 * Re-establish it and re-transmit all of our
278 * pending requests.
279 *
280 * @param dc download context that is having trouble
281 */
282static void
283try_reconnect (struct GNUNET_FS_DownloadContext *dc)
284{
285
286 if (NULL != dc->client)
287 {
288 GNUNET_CONTAINER_multihashmap_iterate (dc->active,
289 &retry_entry,
290 dc);
291 GNUNET_CLIENT_disconnect (dc->client);
292 dc->client = NULL;
293 }
294 dc->task
295 = GNUNET_SCHEDULER_add_delayed (dc->h->sched,
296 GNUNET_NO,
297 GNUNET_SCHEDULER_PRIORITY_IDLE,
298 GNUNET_SCHEDULER_NO_TASK,
299 GNUNET_TIME_UNIT_SECONDS,
300 &do_reconnect,
301 dc);
302}
303
33 304
34/** 305/**
35 * Download parts of a file. Note that this will store 306 * Download parts of a file. Note that this will store
@@ -50,9 +321,7 @@
50 * @param offset at what offset should we start the download (typically 0) 321 * @param offset at what offset should we start the download (typically 0)
51 * @param length how many bytes should be downloaded starting at offset 322 * @param length how many bytes should be downloaded starting at offset
52 * @param anonymity anonymity level to use for the download 323 * @param anonymity anonymity level to use for the download
53 * @param no_temporaries set to GNUNET_YES to disallow generation of temporary files 324 * @param options various options
54 * @param recursive should this be a recursive download (useful for directories
55 * to automatically trigger download of files in the directories)
56 * @param parent parent download to associate this download with (use NULL 325 * @param parent parent download to associate this download with (use NULL
57 * for top-level downloads; useful for manually-triggered recursive downloads) 326 * for top-level downloads; useful for manually-triggered recursive downloads)
58 * @return context that can be used to control this download 327 * @return context that can be used to control this download
@@ -61,31 +330,120 @@ struct GNUNET_FS_DownloadContext *
61GNUNET_FS_file_download_start (struct GNUNET_FS_Handle *h, 330GNUNET_FS_file_download_start (struct GNUNET_FS_Handle *h,
62 const struct GNUNET_FS_Uri *uri, 331 const struct GNUNET_FS_Uri *uri,
63 const char *filename, 332 const char *filename,
64 unsigned long long offset, 333 uint64_t offset,
65 unsigned long long length, 334 uint64_t length,
66 unsigned int anonymity, 335 uint32_t anonymity,
67 int no_temporaries, 336 enum GNUNET_FS_DownloadOptions options,
68 int recursive,
69 struct GNUNET_FS_DownloadContext *parent) 337 struct GNUNET_FS_DownloadContext *parent)
70{ 338{
71 return NULL; 339 struct GNUNET_FS_DownloadContext *dc;
340 struct GNUNET_CLIENT_Connection *client;
341
342 client = GNUNET_CLIENT_connect (h->sched,
343 "fs",
344 h->cfg);
345 if (NULL == client)
346 return NULL;
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) )
351 {
352 GNUNET_break (0);
353 return NULL;
354 }
355 dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext));
356 dc->h = h;
357 dc->client = client;
358 dc->parent = parent;
359 dc->uri = GNUNET_FS_uri_dup (uri);
360 dc->filename = (NULL == filename) ? NULL : GNUNET_strdup (filename);
361 dc->offset = offset;
362 dc->length = length;
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,
369 0,
370 0);
371 GNUNET_CLIENT_notify_transmit_ready (client,
372 sizeof (struct SearchMessage),
373 GNUNET_CONSTANTS_SERVICE_TIMEOUT,
374 &transmit_download_request,
375 dc);
376 GNUNET_CLIENT_receive (client,
377 &receive_results,
378 dc,
379 GNUNET_TIME_UNIT_FOREVER_REL);
380 // FIXME: signal download start
381 return dc;
382}
383
384
385/**
386 * Free entries in the map.
387 *
388 * @param cls unused (NULL)
389 * @param key unused
390 * @param entry entry of type "struct DownloadRequest" which is freed
391 * @return GNUNET_OK
392 */
393static int
394free_entry (void *cls,
395 const GNUNET_HashCode *key,
396 void *entry)
397{
398 GNUNET_free (entry);
399 return GNUNET_OK;
72} 400}
73 401
402
74/** 403/**
75 * Stop a download (aborts if download is incomplete). 404 * Stop a download (aborts if download is incomplete).
76 * 405 *
77 * @param rm handle for the download 406 * @param dc handle for the download
78 * @param do_delete delete files of incomplete downloads 407 * @param do_delete delete files of incomplete downloads
79 */ 408 */
80void 409void
81GNUNET_FS_file_download_stop (struct GNUNET_FS_DownloadContext *rm, 410GNUNET_FS_file_download_stop (struct GNUNET_FS_DownloadContext *dc,
82 int do_delete) 411 int do_delete)
83{ 412{
413 // FIXME: make unpersistent
414 // FIXME: signal download end
415
416 if (GNUNET_SCHEDULER_NO_TASK != dc->task)
417 GNUNET_SCHEDULER_cancel (dc->h->sched,
418 dc->task);
419 if (NULL != dc->client)
420 GNUNET_CLIENT_disconnect (dc->client);
421 GNUNET_CONTAINER_multihashmap_iterate (dc->active,
422 &free_entry,
423 NULL);
424 GNUNET_CONTAINER_multihashmap_destroy (dc->active);
425 GNUNET_FS_uri_destroy (dc->uri);
426 GNUNET_free_non_null (dc->filename);
427 GNUNET_free (dc);
84} 428}
85 429
86 430
87 431
88 432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
89#if 0 447#if 0
90 448
91/** 449/**