diff options
Diffstat (limited to 'src/fs/fs_download.c')
-rw-r--r-- | src/fs/fs_download.c | 197 |
1 files changed, 175 insertions, 22 deletions
diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c index 3de192e12..46759f495 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, 2009 Christian Grothoff (and other contributing authors) | 3 | (C) 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009, 2010 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 |
@@ -25,6 +25,8 @@ | |||
25 | * TODO: | 25 | * TODO: |
26 | * - handle recursive downloads (need directory & | 26 | * - handle recursive downloads (need directory & |
27 | * fs-level download-parallelism management) | 27 | * fs-level download-parallelism management) |
28 | * - handle recursive downloads where directory file is | ||
29 | * NOT saved on disk (need temporary file instead then!) | ||
28 | * - location URI suppport (can wait, easy) | 30 | * - location URI suppport (can wait, easy) |
29 | * - check if blocks exist already (can wait, easy) | 31 | * - check if blocks exist already (can wait, easy) |
30 | * - check if iblocks can be computed from existing blocks (can wait, hard) | 32 | * - check if iblocks can be computed from existing blocks (can wait, hard) |
@@ -39,15 +41,12 @@ | |||
39 | #define DEBUG_DOWNLOAD GNUNET_NO | 41 | #define DEBUG_DOWNLOAD GNUNET_NO |
40 | 42 | ||
41 | /** | 43 | /** |
42 | * We're storing the IBLOCKS after the | 44 | * We're storing the IBLOCKS after the DBLOCKS on disk (so that we |
43 | * DBLOCKS on disk (so that we only have | 45 | * only have to truncate the file once we're done). |
44 | * to truncate the file once we're done). | ||
45 | * | 46 | * |
46 | * Given the offset of a block (with respect | 47 | * Given the offset of a block (with respect to the DBLOCKS) and its |
47 | * to the DBLOCKS) and its depth, return the | 48 | * depth, return the offset where we would store this block in the |
48 | * offset where we would store this block | 49 | * file. |
49 | * in the file. | ||
50 | |||
51 | * | 50 | * |
52 | * @param fsize overall file size | 51 | * @param fsize overall file size |
53 | * @param off offset of the block in the file | 52 | * @param off offset of the block in the file |
@@ -245,8 +244,7 @@ schedule_block_download (struct GNUNET_FS_DownloadContext *dc, | |||
245 | GNUNET_CONSTANTS_SERVICE_TIMEOUT, | 244 | GNUNET_CONSTANTS_SERVICE_TIMEOUT, |
246 | GNUNET_NO, | 245 | GNUNET_NO, |
247 | &transmit_download_request, | 246 | &transmit_download_request, |
248 | dc); | 247 | dc); |
249 | |||
250 | } | 248 | } |
251 | 249 | ||
252 | 250 | ||
@@ -341,6 +339,129 @@ struct ProcessResultClosure | |||
341 | 339 | ||
342 | 340 | ||
343 | /** | 341 | /** |
342 | * We found an entry in a directory. Check if the respective child | ||
343 | * already exists and if not create the respective child download. | ||
344 | * | ||
345 | * @param cls the parent download | ||
346 | * @param filename name of the file in the directory | ||
347 | * @param uri URI of the file (CHK or LOC) | ||
348 | * @param meta meta data of the file | ||
349 | * @param length number of bytes in data | ||
350 | * @param data contents of the file (or NULL if they were not inlined) | ||
351 | */ | ||
352 | static void | ||
353 | trigger_recursive_download (void *cls, | ||
354 | const char *filename, | ||
355 | const struct GNUNET_FS_Uri *uri, | ||
356 | const struct GNUNET_CONTAINER_MetaData *meta, | ||
357 | size_t length, | ||
358 | const void *data) | ||
359 | { | ||
360 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
361 | struct GNUNET_FS_DownloadContext *cpos; | ||
362 | |||
363 | cpos = dc->child_head; | ||
364 | while (cpos != NULL) | ||
365 | { | ||
366 | if (0 == strcmp (cpos->filename, | ||
367 | filename)) | ||
368 | { | ||
369 | GNUNET_break_op (GNUNET_FS_uri_test_equal (uri, | ||
370 | cpos->uri)); | ||
371 | break; | ||
372 | } | ||
373 | cpos = cpos->next; | ||
374 | } | ||
375 | if (cpos != NULL) | ||
376 | return; /* already exists */ | ||
377 | if (data != NULL) | ||
378 | { | ||
379 | /* determine on-disk filename, write data! */ | ||
380 | GNUNET_break (0); // FIXME: not implemented | ||
381 | } | ||
382 | GNUNET_FS_download_start (dc->h, | ||
383 | uri, | ||
384 | meta, | ||
385 | filename, /* FIXME: prepend directory name! */ | ||
386 | 0, | ||
387 | GNUNET_FS_uri_chk_get_file_size (uri), | ||
388 | dc->anonymity, | ||
389 | dc->options, | ||
390 | NULL, | ||
391 | dc); | ||
392 | } | ||
393 | |||
394 | |||
395 | /** | ||
396 | * We're done downloading a directory. Open the file and | ||
397 | * trigger all of the (remaining) child downloads. | ||
398 | * | ||
399 | * @param dc context of download that just completed | ||
400 | */ | ||
401 | static void | ||
402 | full_recursive_download (struct GNUNET_FS_DownloadContext *dc) | ||
403 | { | ||
404 | size_t size; | ||
405 | uint64_t size64; | ||
406 | void *data; | ||
407 | struct GNUNET_DISK_FileHandle *h; | ||
408 | struct GNUNET_DISK_MapHandle *m; | ||
409 | |||
410 | size64 = GNUNET_FS_uri_chk_get_file_size (dc->uri); | ||
411 | size = (size_t) size64; | ||
412 | if (size64 != (uint64_t) size) | ||
413 | { | ||
414 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
415 | _("Recursive downloads of directories larger than 4 GB are not supported on 32-bit systems\n")); | ||
416 | return; | ||
417 | } | ||
418 | if (dc->filename != NULL) | ||
419 | { | ||
420 | h = GNUNET_DISK_file_open (dc->filename, | ||
421 | GNUNET_DISK_OPEN_READ, | ||
422 | GNUNET_DISK_PERM_NONE); | ||
423 | } | ||
424 | else | ||
425 | { | ||
426 | /* FIXME: need to initialize (and use) temp_filename | ||
427 | in various places in order for this assertion to | ||
428 | not fail; right now, it will always fail! */ | ||
429 | GNUNET_assert (dc->temp_filename != NULL); | ||
430 | h = GNUNET_DISK_file_open (dc->temp_filename, | ||
431 | GNUNET_DISK_OPEN_READ, | ||
432 | GNUNET_DISK_PERM_NONE); | ||
433 | } | ||
434 | if (h == NULL) | ||
435 | return; /* oops */ | ||
436 | data = GNUNET_DISK_file_map (h, &m, GNUNET_DISK_MAP_TYPE_READ, size); | ||
437 | if (data == NULL) | ||
438 | { | ||
439 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
440 | _("Directory too large for system address space\n")); | ||
441 | } | ||
442 | else | ||
443 | { | ||
444 | GNUNET_FS_directory_list_contents (size, | ||
445 | data, | ||
446 | 0, | ||
447 | &trigger_recursive_download, | ||
448 | dc); | ||
449 | GNUNET_DISK_file_unmap (m); | ||
450 | } | ||
451 | GNUNET_DISK_file_close (h); | ||
452 | if (dc->filename == NULL) | ||
453 | { | ||
454 | if (0 != UNLINK (dc->temp_filename)) | ||
455 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
456 | "unlink", | ||
457 | dc->temp_filename); | ||
458 | GNUNET_free (dc->temp_filename); | ||
459 | dc->temp_filename = NULL; | ||
460 | } | ||
461 | } | ||
462 | |||
463 | |||
464 | /** | ||
344 | * Iterator over entries in the pending requests in the 'active' map for the | 465 | * Iterator over entries in the pending requests in the 'active' map for the |
345 | * reply that we just got. | 466 | * reply that we just got. |
346 | * | 467 | * |
@@ -484,6 +605,17 @@ process_result_with_request (void *cls, | |||
484 | app -= (sm->offset + prc->size) - (dc->offset + dc->length); | 605 | app -= (sm->offset + prc->size) - (dc->offset + dc->length); |
485 | } | 606 | } |
486 | dc->completed += app; | 607 | dc->completed += app; |
608 | |||
609 | if ( (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) && | ||
610 | (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ) | ||
611 | { | ||
612 | GNUNET_FS_directory_list_contents (prc->size, | ||
613 | pt, | ||
614 | off, | ||
615 | &trigger_recursive_download, | ||
616 | dc); | ||
617 | } | ||
618 | |||
487 | } | 619 | } |
488 | 620 | ||
489 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS; | 621 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_PROGRESS; |
@@ -513,11 +645,18 @@ process_result_with_request (void *cls, | |||
513 | "truncate", | 645 | "truncate", |
514 | dc->filename); | 646 | dc->filename); |
515 | } | 647 | } |
516 | /* signal completion */ | 648 | |
517 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED; | 649 | if ( (0 != (dc->options & GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE)) && |
518 | make_download_status (&pi, dc); | 650 | (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (dc->meta)) ) |
519 | dc->client_info = dc->h->upcb (dc->h->upcb_cls, | 651 | full_recursive_download (dc); |
520 | &pi); | 652 | if (dc->child_head == NULL) |
653 | { | ||
654 | /* signal completion */ | ||
655 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_COMPLETED; | ||
656 | make_download_status (&pi, dc); | ||
657 | dc->client_info = dc->h->upcb (dc->h->upcb_cls, | ||
658 | &pi); | ||
659 | } | ||
521 | GNUNET_assert (sm->depth == dc->treedepth); | 660 | GNUNET_assert (sm->depth == dc->treedepth); |
522 | } | 661 | } |
523 | // FIXME: make persistent | 662 | // FIXME: make persistent |
@@ -836,11 +975,6 @@ GNUNET_FS_download_start (struct GNUNET_FS_Handle *h, | |||
836 | GNUNET_break (0); | 975 | GNUNET_break (0); |
837 | return NULL; | 976 | return NULL; |
838 | } | 977 | } |
839 | client = GNUNET_CLIENT_connect (h->sched, | ||
840 | "fs", | ||
841 | h->cfg); | ||
842 | if (NULL == client) | ||
843 | return NULL; | ||
844 | // FIXME: add support for "loc" URIs! | 978 | // FIXME: add support for "loc" URIs! |
845 | #if DEBUG_DOWNLOAD | 979 | #if DEBUG_DOWNLOAD |
846 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 980 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
@@ -850,8 +984,13 @@ GNUNET_FS_download_start (struct GNUNET_FS_Handle *h, | |||
850 | #endif | 984 | #endif |
851 | dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext)); | 985 | dc = GNUNET_malloc (sizeof(struct GNUNET_FS_DownloadContext)); |
852 | dc->h = h; | 986 | dc->h = h; |
853 | dc->client = client; | ||
854 | dc->parent = parent; | 987 | dc->parent = parent; |
988 | if (parent != NULL) | ||
989 | { | ||
990 | GNUNET_CONTAINER_DLL_insert (parent->child_head, | ||
991 | parent->child_tail, | ||
992 | dc); | ||
993 | } | ||
855 | dc->uri = GNUNET_FS_uri_dup (uri); | 994 | dc->uri = GNUNET_FS_uri_dup (uri); |
856 | dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta); | 995 | dc->meta = GNUNET_CONTAINER_meta_data_duplicate (meta); |
857 | dc->client_info = cctx; | 996 | dc->client_info = cctx; |
@@ -897,6 +1036,12 @@ GNUNET_FS_download_start (struct GNUNET_FS_Handle *h, | |||
897 | dc->treedepth); | 1036 | dc->treedepth); |
898 | #endif | 1037 | #endif |
899 | // FIXME: make persistent | 1038 | // FIXME: make persistent |
1039 | |||
1040 | // FIXME: bound parallelism here! | ||
1041 | client = GNUNET_CLIENT_connect (h->sched, | ||
1042 | "fs", | ||
1043 | h->cfg); | ||
1044 | dc->client = client; | ||
900 | schedule_block_download (dc, | 1045 | schedule_block_download (dc, |
901 | &dc->uri->data.chk.chk, | 1046 | &dc->uri->data.chk.chk, |
902 | 0, | 1047 | 0, |
@@ -945,7 +1090,15 @@ GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc, | |||
945 | { | 1090 | { |
946 | struct GNUNET_FS_ProgressInfo pi; | 1091 | struct GNUNET_FS_ProgressInfo pi; |
947 | 1092 | ||
1093 | while (NULL != dc->child_head) | ||
1094 | GNUNET_FS_download_stop (dc->child_head, | ||
1095 | do_delete); | ||
948 | // FIXME: make unpersistent | 1096 | // FIXME: make unpersistent |
1097 | if (dc->parent != NULL) | ||
1098 | GNUNET_CONTAINER_DLL_remove (dc->parent->child_head, | ||
1099 | dc->parent->child_tail, | ||
1100 | dc); | ||
1101 | |||
949 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED; | 1102 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_STOPPED; |
950 | make_download_status (&pi, dc); | 1103 | make_download_status (&pi, dc); |
951 | dc->client_info = dc->h->upcb (dc->h->upcb_cls, | 1104 | dc->client_info = dc->h->upcb (dc->h->upcb_cls, |