diff options
Diffstat (limited to 'src/fs/fs_download.c')
-rw-r--r-- | src/fs/fs_download.c | 333 |
1 files changed, 282 insertions, 51 deletions
diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c index ebe9b5cac..6943f10b5 100644 --- a/src/fs/fs_download.c +++ b/src/fs/fs_download.c | |||
@@ -24,7 +24,6 @@ | |||
24 | * | 24 | * |
25 | * TODO: | 25 | * TODO: |
26 | * - different priority for scheduling probe downloads? | 26 | * - different priority for scheduling probe downloads? |
27 | * - check if iblocks can be computed from existing blocks (can wait, hard) | ||
28 | */ | 27 | */ |
29 | #include "platform.h" | 28 | #include "platform.h" |
30 | #include "gnunet_constants.h" | 29 | #include "gnunet_constants.h" |
@@ -32,7 +31,7 @@ | |||
32 | #include "fs.h" | 31 | #include "fs.h" |
33 | #include "fs_tree.h" | 32 | #include "fs_tree.h" |
34 | 33 | ||
35 | #define DEBUG_DOWNLOAD GNUNET_YES | 34 | #define DEBUG_DOWNLOAD GNUNET_NO |
36 | 35 | ||
37 | /** | 36 | /** |
38 | * Determine if the given download (options and meta data) should cause | 37 | * Determine if the given download (options and meta data) should cause |
@@ -415,6 +414,223 @@ match_full_data (void *cls, | |||
415 | } | 414 | } |
416 | 415 | ||
417 | 416 | ||
417 | |||
418 | /** | ||
419 | * Closure for 'reconstruct_cont' and 'reconstruct_cb'. | ||
420 | */ | ||
421 | struct ReconstructContext | ||
422 | { | ||
423 | /** | ||
424 | * File handle open for the reconstruction. | ||
425 | */ | ||
426 | struct GNUNET_DISK_FileHandle *fh; | ||
427 | |||
428 | /** | ||
429 | * the download context. | ||
430 | */ | ||
431 | struct GNUNET_FS_DownloadContext *dc; | ||
432 | |||
433 | /** | ||
434 | * Tree encoder used for the reconstruction. | ||
435 | */ | ||
436 | struct GNUNET_FS_TreeEncoder *te; | ||
437 | |||
438 | /** | ||
439 | * CHK of block we are trying to reconstruct. | ||
440 | */ | ||
441 | struct ContentHashKey chk; | ||
442 | |||
443 | /** | ||
444 | * Request that was generated. | ||
445 | */ | ||
446 | struct DownloadRequest *sm; | ||
447 | |||
448 | /** | ||
449 | * Offset of block we are trying to reconstruct. | ||
450 | */ | ||
451 | uint64_t offset; | ||
452 | |||
453 | /** | ||
454 | * Depth of block we are trying to reconstruct. | ||
455 | */ | ||
456 | unsigned int depth; | ||
457 | |||
458 | }; | ||
459 | |||
460 | |||
461 | /** | ||
462 | * Continuation after a possible attempt to reconstruct | ||
463 | * the current IBlock from the existing file. | ||
464 | * | ||
465 | * @param cls the 'struct ReconstructContext' | ||
466 | * @param tc scheduler context | ||
467 | */ | ||
468 | static void | ||
469 | reconstruct_cont (void *cls, | ||
470 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
471 | { | ||
472 | struct ReconstructContext *rcc = cls; | ||
473 | |||
474 | if (rcc->te != NULL) | ||
475 | GNUNET_FS_tree_encoder_finish (rcc->te, NULL, NULL); | ||
476 | rcc->dc->reconstruct_failed = GNUNET_YES; | ||
477 | if (rcc->fh != NULL) | ||
478 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (rcc->fh)); | ||
479 | if ( (rcc->dc->th == NULL) && | ||
480 | (rcc->dc->client != NULL) ) | ||
481 | { | ||
482 | #if DEBUG_DOWNLOAD | ||
483 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
484 | "Asking for transmission to FS service\n"); | ||
485 | #endif | ||
486 | rcc->dc->th = GNUNET_CLIENT_notify_transmit_ready (rcc->dc->client, | ||
487 | sizeof (struct SearchMessage), | ||
488 | GNUNET_CONSTANTS_SERVICE_TIMEOUT, | ||
489 | GNUNET_NO, | ||
490 | &transmit_download_request, | ||
491 | rcc->dc); | ||
492 | } | ||
493 | else | ||
494 | { | ||
495 | #if DEBUG_DOWNLOAD | ||
496 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
497 | "Transmission request not issued (%p %p)\n", | ||
498 | rcc->dc->th, | ||
499 | rcc->dc->client); | ||
500 | #endif | ||
501 | } | ||
502 | GNUNET_free (rcc); | ||
503 | } | ||
504 | |||
505 | |||
506 | static void | ||
507 | get_next_block (void *cls, | ||
508 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
509 | { | ||
510 | struct ReconstructContext *rcc = cls; | ||
511 | GNUNET_FS_tree_encoder_next (rcc->te); | ||
512 | } | ||
513 | |||
514 | |||
515 | /** | ||
516 | * Function called asking for the current (encoded) | ||
517 | * block to be processed. After processing the | ||
518 | * client should either call "GNUNET_FS_tree_encode_next" | ||
519 | * or (on error) "GNUNET_FS_tree_encode_finish". | ||
520 | * | ||
521 | * This function checks if the content on disk matches | ||
522 | * the expected content based on the URI. | ||
523 | * | ||
524 | * @param cls closure | ||
525 | * @param query the query for the block (key for lookup in the datastore) | ||
526 | * @param offset offset of the block | ||
527 | * @param type type of the block (IBLOCK or DBLOCK) | ||
528 | * @param block the (encrypted) block | ||
529 | * @param block_size size of block (in bytes) | ||
530 | */ | ||
531 | static void | ||
532 | reconstruct_cb (void *cls, | ||
533 | const GNUNET_HashCode *query, | ||
534 | uint64_t offset, | ||
535 | unsigned int depth, | ||
536 | enum GNUNET_BLOCK_Type type, | ||
537 | const void *block, | ||
538 | uint16_t block_size) | ||
539 | { | ||
540 | struct ReconstructContext *rcc = cls; | ||
541 | struct ProcessResultClosure prc; | ||
542 | struct GNUNET_FS_TreeEncoder *te; | ||
543 | uint64_t off; | ||
544 | uint64_t boff; | ||
545 | uint64_t roff; | ||
546 | unsigned int i; | ||
547 | |||
548 | roff = offset / DBLOCK_SIZE; | ||
549 | for (i=rcc->dc->treedepth;i>depth;i--) | ||
550 | roff /= CHK_PER_INODE; | ||
551 | boff = roff * DBLOCK_SIZE; | ||
552 | for (i=rcc->dc->treedepth;i>depth;i--) | ||
553 | boff *= CHK_PER_INODE; | ||
554 | /* convert reading offset into IBLOCKs on-disk offset */ | ||
555 | off = compute_disk_offset (GNUNET_FS_uri_chk_get_file_size (rcc->dc->uri), | ||
556 | boff, | ||
557 | depth, | ||
558 | rcc->dc->treedepth); | ||
559 | if ( (off == rcc->offset) && | ||
560 | (depth == rcc->depth) && | ||
561 | (0 == memcmp (query, | ||
562 | &rcc->chk.query, | ||
563 | sizeof (GNUNET_HashCode))) ) | ||
564 | { | ||
565 | /* already got it! */ | ||
566 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
567 | _("Block reconstruction at offset %llu and depth %u successful\n"), | ||
568 | (unsigned long long) offset, | ||
569 | depth); | ||
570 | prc.dc = rcc->dc; | ||
571 | prc.data = block; | ||
572 | prc.size = block_size; | ||
573 | prc.type = type; | ||
574 | prc.query = rcc->chk.query; | ||
575 | prc.do_store = GNUNET_NO; | ||
576 | process_result_with_request (&prc, | ||
577 | &rcc->chk.key, | ||
578 | rcc->sm); | ||
579 | te = rcc->te; | ||
580 | rcc->te = NULL; | ||
581 | GNUNET_FS_tree_encoder_finish (te, NULL, NULL); | ||
582 | GNUNET_free (rcc); | ||
583 | return; | ||
584 | } | ||
585 | GNUNET_SCHEDULER_add_continuation (&get_next_block, | ||
586 | rcc, | ||
587 | GNUNET_SCHEDULER_REASON_PREREQ_DONE); | ||
588 | } | ||
589 | |||
590 | |||
591 | /** | ||
592 | * Function called by the tree encoder to obtain | ||
593 | * a block of plaintext data (for the lowest level | ||
594 | * of the tree). | ||
595 | * | ||
596 | * @param cls our 'struct ReconstructContext' | ||
597 | * @param offset identifies which block to get | ||
598 | * @param max (maximum) number of bytes to get; returning | ||
599 | * fewer will also cause errors | ||
600 | * @param buf where to copy the plaintext buffer | ||
601 | * @param emsg location to store an error message (on error) | ||
602 | * @return number of bytes copied to buf, 0 on error | ||
603 | */ | ||
604 | static size_t | ||
605 | fh_reader (void *cls, | ||
606 | uint64_t offset, | ||
607 | size_t max, | ||
608 | void *buf, | ||
609 | char **emsg) | ||
610 | { | ||
611 | struct ReconstructContext *rcc = cls; | ||
612 | struct GNUNET_DISK_FileHandle *fh = rcc->fh; | ||
613 | ssize_t ret; | ||
614 | |||
615 | *emsg = NULL; | ||
616 | if (offset != | ||
617 | GNUNET_DISK_file_seek (fh, | ||
618 | offset, | ||
619 | GNUNET_DISK_SEEK_SET)) | ||
620 | { | ||
621 | *emsg = GNUNET_strdup (strerror (errno)); | ||
622 | return 0; | ||
623 | } | ||
624 | ret = GNUNET_DISK_file_read (fh, buf, max); | ||
625 | if (ret < 0) | ||
626 | { | ||
627 | *emsg = GNUNET_strdup (strerror (errno)); | ||
628 | return 0; | ||
629 | } | ||
630 | return ret; | ||
631 | } | ||
632 | |||
633 | |||
418 | /** | 634 | /** |
419 | * Schedule the download of the specified block in the tree. | 635 | * Schedule the download of the specified block in the tree. |
420 | * | 636 | * |
@@ -440,8 +656,9 @@ schedule_block_download (struct GNUNET_FS_DownloadContext *dc, | |||
440 | GNUNET_HashCode key; | 656 | GNUNET_HashCode key; |
441 | struct MatchDataContext mdc; | 657 | struct MatchDataContext mdc; |
442 | struct GNUNET_DISK_FileHandle *fh; | 658 | struct GNUNET_DISK_FileHandle *fh; |
659 | struct ReconstructContext *rcc; | ||
443 | 660 | ||
444 | total = GNUNET_ntohll (dc->uri->data.chk.file_length); | 661 | total = GNUNET_FS_uri_chk_get_file_size (dc->uri); |
445 | len = GNUNET_FS_tree_calculate_block_size (total, | 662 | len = GNUNET_FS_tree_calculate_block_size (total, |
446 | dc->treedepth, | 663 | dc->treedepth, |
447 | offset, | 664 | offset, |
@@ -485,12 +702,15 @@ schedule_block_download (struct GNUNET_FS_DownloadContext *dc, | |||
485 | GNUNET_h2s (&chk->query)); | 702 | GNUNET_h2s (&chk->query)); |
486 | #endif | 703 | #endif |
487 | fh = NULL; | 704 | fh = NULL; |
488 | if ( (dc->old_file_size > off) && | 705 | if ( ( (dc->old_file_size > off) || |
706 | ( (depth < dc->treedepth) && | ||
707 | (dc->reconstruct_failed == GNUNET_NO) ) ) && | ||
489 | (dc->filename != NULL) ) | 708 | (dc->filename != NULL) ) |
490 | fh = GNUNET_DISK_file_open (dc->filename, | 709 | fh = GNUNET_DISK_file_open (dc->filename, |
491 | GNUNET_DISK_OPEN_READ, | 710 | GNUNET_DISK_OPEN_READ, |
492 | GNUNET_DISK_PERM_NONE); | 711 | GNUNET_DISK_PERM_NONE); |
493 | if ( (fh != NULL) && | 712 | if ( (fh != NULL) && |
713 | (dc->old_file_size > off) && | ||
494 | (off == | 714 | (off == |
495 | GNUNET_DISK_file_seek (fh, | 715 | GNUNET_DISK_file_seek (fh, |
496 | off, | 716 | off, |
@@ -517,46 +737,31 @@ schedule_block_download (struct GNUNET_FS_DownloadContext *dc, | |||
517 | return; | 737 | return; |
518 | } | 738 | } |
519 | } | 739 | } |
520 | if (fh != NULL) | 740 | rcc = GNUNET_malloc (sizeof (struct ReconstructContext)); |
521 | GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fh)); | 741 | rcc->fh = fh; |
522 | if (depth < dc->treedepth) | 742 | rcc->dc = dc; |
523 | { | 743 | rcc->sm = sm; |
524 | // FIXME: try if we could | 744 | rcc->chk = *chk; |
525 | // reconstitute this IBLOCK | 745 | rcc->offset = off; |
526 | // from the existing blocks on disk (can wait) | 746 | rcc->depth = depth; |
527 | // (read block(s), encode, compare with | 747 | if ( (depth < dc->treedepth) && |
528 | // query; if matches, simply return) | 748 | (dc->reconstruct_failed == GNUNET_NO) && |
529 | } | 749 | (fh != NULL) ) |
530 | |||
531 | if ( (dc->th == NULL) && | ||
532 | (dc->client != NULL) ) | ||
533 | { | ||
534 | #if DEBUG_DOWNLOAD | ||
535 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
536 | "Asking for transmission to FS service\n"); | ||
537 | #endif | ||
538 | dc->th = GNUNET_CLIENT_notify_transmit_ready (dc->client, | ||
539 | sizeof (struct SearchMessage), | ||
540 | GNUNET_CONSTANTS_SERVICE_TIMEOUT, | ||
541 | GNUNET_NO, | ||
542 | &transmit_download_request, | ||
543 | dc); | ||
544 | } | ||
545 | else | ||
546 | { | 750 | { |
547 | #if DEBUG_DOWNLOAD | 751 | rcc->te = GNUNET_FS_tree_encoder_create (dc->h, |
548 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 752 | dc->old_file_size, |
549 | "Transmission request not issued (%p %p)\n", | 753 | rcc, |
550 | dc->th, | 754 | fh_reader, |
551 | dc->client); | 755 | &reconstruct_cb, |
552 | #endif | 756 | NULL, |
553 | 757 | &reconstruct_cont); | |
758 | GNUNET_FS_tree_encoder_next (rcc->te); | ||
759 | return; | ||
554 | } | 760 | } |
555 | 761 | reconstruct_cont (rcc, NULL); | |
556 | } | 762 | } |
557 | 763 | ||
558 | 764 | ||
559 | |||
560 | /** | 765 | /** |
561 | * Suggest a filename based on given metadata. | 766 | * Suggest a filename based on given metadata. |
562 | * | 767 | * |
@@ -1624,6 +1829,29 @@ deactivate_fs_download (void *cls) | |||
1624 | 1829 | ||
1625 | 1830 | ||
1626 | /** | 1831 | /** |
1832 | * Task that creates the initial (top-level) download | ||
1833 | * request for the file. | ||
1834 | * | ||
1835 | * @param cls the 'struct GNUNET_FS_DownloadContext' | ||
1836 | * @param tc scheduler context | ||
1837 | */ | ||
1838 | void | ||
1839 | GNUNET_FS_download_start_task_ (void *cls, | ||
1840 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1841 | { | ||
1842 | struct GNUNET_FS_DownloadContext *dc = cls; | ||
1843 | |||
1844 | dc->start_task = GNUNET_SCHEDULER_NO_TASK; | ||
1845 | schedule_block_download (dc, | ||
1846 | (dc->uri->type == chk) | ||
1847 | ? &dc->uri->data.chk.chk | ||
1848 | : &dc->uri->data.loc.fi.chk, | ||
1849 | 0, | ||
1850 | 1 /* 0 == CHK, 1 == top */); | ||
1851 | } | ||
1852 | |||
1853 | |||
1854 | /** | ||
1627 | * Create SUSPEND event for the given download operation | 1855 | * Create SUSPEND event for the given download operation |
1628 | * and then clean up our state (without stop signal). | 1856 | * and then clean up our state (without stop signal). |
1629 | * | 1857 | * |
@@ -1634,7 +1862,12 @@ GNUNET_FS_download_signal_suspend_ (void *cls) | |||
1634 | { | 1862 | { |
1635 | struct GNUNET_FS_DownloadContext *dc = cls; | 1863 | struct GNUNET_FS_DownloadContext *dc = cls; |
1636 | struct GNUNET_FS_ProgressInfo pi; | 1864 | struct GNUNET_FS_ProgressInfo pi; |
1637 | 1865 | ||
1866 | if (dc->start_task != GNUNET_SCHEDULER_NO_TASK) | ||
1867 | { | ||
1868 | GNUNET_SCHEDULER_cancel (dc->start_task); | ||
1869 | dc->start_task = GNUNET_SCHEDULER_NO_TASK; | ||
1870 | } | ||
1638 | if (dc->top != NULL) | 1871 | if (dc->top != NULL) |
1639 | GNUNET_FS_end_top (dc->h, dc->top); | 1872 | GNUNET_FS_end_top (dc->h, dc->top); |
1640 | while (NULL != dc->child_head) | 1873 | while (NULL != dc->child_head) |
@@ -1785,12 +2018,7 @@ GNUNET_FS_download_start (struct GNUNET_FS_Handle *h, | |||
1785 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_START; | 2018 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_START; |
1786 | pi.value.download.specifics.start.meta = meta; | 2019 | pi.value.download.specifics.start.meta = meta; |
1787 | GNUNET_FS_download_make_status_ (&pi, dc); | 2020 | GNUNET_FS_download_make_status_ (&pi, dc); |
1788 | schedule_block_download (dc, | 2021 | dc->start_task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc); |
1789 | (dc->uri->type == chk) | ||
1790 | ? &dc->uri->data.chk.chk | ||
1791 | : &dc->uri->data.loc.fi.chk, | ||
1792 | 0, | ||
1793 | 1 /* 0 == CHK, 1 == top */); | ||
1794 | GNUNET_FS_download_sync_ (dc); | 2022 | GNUNET_FS_download_sync_ (dc); |
1795 | GNUNET_FS_download_start_downloading_ (dc); | 2023 | GNUNET_FS_download_start_downloading_ (dc); |
1796 | return dc; | 2024 | return dc; |
@@ -1913,16 +2141,12 @@ GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h, | |||
1913 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_START; | 2141 | pi.status = GNUNET_FS_STATUS_DOWNLOAD_START; |
1914 | pi.value.download.specifics.start.meta = dc->meta; | 2142 | pi.value.download.specifics.start.meta = dc->meta; |
1915 | GNUNET_FS_download_make_status_ (&pi, dc); | 2143 | GNUNET_FS_download_make_status_ (&pi, dc); |
1916 | schedule_block_download (dc, | 2144 | dc->start_task = GNUNET_SCHEDULER_add_now (&GNUNET_FS_download_start_task_, dc); |
1917 | &dc->uri->data.chk.chk, | ||
1918 | 0, | ||
1919 | 1 /* 0 == CHK, 1 == top */); | ||
1920 | GNUNET_FS_download_sync_ (dc); | 2145 | GNUNET_FS_download_sync_ (dc); |
1921 | GNUNET_FS_download_start_downloading_ (dc); | 2146 | GNUNET_FS_download_start_downloading_ (dc); |
1922 | return dc; | 2147 | return dc; |
1923 | } | 2148 | } |
1924 | 2149 | ||
1925 | |||
1926 | /** | 2150 | /** |
1927 | * Start the downloading process (by entering the queue). | 2151 | * Start the downloading process (by entering the queue). |
1928 | * | 2152 | * |
@@ -1931,6 +2155,8 @@ GNUNET_FS_download_start_from_search (struct GNUNET_FS_Handle *h, | |||
1931 | void | 2155 | void |
1932 | GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc) | 2156 | GNUNET_FS_download_start_downloading_ (struct GNUNET_FS_DownloadContext *dc) |
1933 | { | 2157 | { |
2158 | if (dc->completed == dc->length) | ||
2159 | return; | ||
1934 | GNUNET_assert (dc->job_queue == NULL); | 2160 | GNUNET_assert (dc->job_queue == NULL); |
1935 | dc->job_queue = GNUNET_FS_queue_ (dc->h, | 2161 | dc->job_queue = GNUNET_FS_queue_ (dc->h, |
1936 | &activate_fs_download, | 2162 | &activate_fs_download, |
@@ -1955,6 +2181,11 @@ GNUNET_FS_download_stop (struct GNUNET_FS_DownloadContext *dc, | |||
1955 | 2181 | ||
1956 | if (dc->top != NULL) | 2182 | if (dc->top != NULL) |
1957 | GNUNET_FS_end_top (dc->h, dc->top); | 2183 | GNUNET_FS_end_top (dc->h, dc->top); |
2184 | if (dc->start_task != GNUNET_SCHEDULER_NO_TASK) | ||
2185 | { | ||
2186 | GNUNET_SCHEDULER_cancel (dc->start_task); | ||
2187 | dc->start_task = GNUNET_SCHEDULER_NO_TASK; | ||
2188 | } | ||
1958 | if (dc->search != NULL) | 2189 | if (dc->search != NULL) |
1959 | { | 2190 | { |
1960 | dc->search->download = NULL; | 2191 | dc->search->download = NULL; |