diff options
author | Christian Grothoff <christian@grothoff.org> | 2009-09-05 20:57:31 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2009-09-05 20:57:31 +0000 |
commit | 9e645511bbd3775bd5c872eb5e58439676a59199 (patch) | |
tree | f2219c6ed90fdecbd8f36ec8f7922293691cda01 /src | |
parent | 0b90310c9a26ffa0e195759b7a5d5c07fa33f642 (diff) | |
download | gnunet-9e645511bbd3775bd5c872eb5e58439676a59199.tar.gz gnunet-9e645511bbd3775bd5c872eb5e58439676a59199.zip |
towards having download
Diffstat (limited to 'src')
-rw-r--r-- | src/fs/fs.h | 28 | ||||
-rw-r--r-- | src/fs/fs_download.c | 779 |
2 files changed, 70 insertions, 737 deletions
diff --git a/src/fs/fs.h b/src/fs/fs.h index 3dc60a5dc..529a917a3 100644 --- a/src/fs/fs.h +++ b/src/fs/fs.h | |||
@@ -855,6 +855,18 @@ struct GNUNET_FS_DownloadContext | |||
855 | struct DownloadRequest *pending; | 855 | struct DownloadRequest *pending; |
856 | 856 | ||
857 | /** | 857 | /** |
858 | * The file handle, NULL if we don't create | ||
859 | * a file. | ||
860 | */ | ||
861 | struct GNUNET_DISK_FileHandle *handle; | ||
862 | |||
863 | /** | ||
864 | * Identity of the peer having the content, or all-zeros | ||
865 | * if we don't know of such a peer. | ||
866 | */ | ||
867 | struct GNUNET_PeerIdentity target; | ||
868 | |||
869 | /** | ||
858 | * ID of a task that is using this struct | 870 | * ID of a task that is using this struct |
859 | * and that must be cancelled when the download | 871 | * and that must be cancelled when the download |
860 | * is being stopped (if not GNUNET_SCHEDULER_NO_TASK). | 872 | * is being stopped (if not GNUNET_SCHEDULER_NO_TASK). |
@@ -877,11 +889,27 @@ struct GNUNET_FS_DownloadContext | |||
877 | uint64_t length; | 889 | uint64_t length; |
878 | 890 | ||
879 | /** | 891 | /** |
892 | * How many bytes have we already received within | ||
893 | * the specified range (DBlocks only). | ||
894 | */ | ||
895 | uint64_t completed; | ||
896 | |||
897 | /** | ||
898 | * Time download was started. | ||
899 | */ | ||
900 | struct GNUNET_TIME_Absolute start_time; | ||
901 | |||
902 | /** | ||
880 | * Desired level of anonymity. | 903 | * Desired level of anonymity. |
881 | */ | 904 | */ |
882 | uint32_t anonymity; | 905 | uint32_t anonymity; |
883 | 906 | ||
884 | /** | 907 | /** |
908 | * The depth of the file-tree. | ||
909 | */ | ||
910 | unsigned int treedepth; | ||
911 | |||
912 | /** | ||
885 | * Options for the download. | 913 | * Options for the download. |
886 | */ | 914 | */ |
887 | enum GNUNET_FS_DownloadOptions options; | 915 | enum GNUNET_FS_DownloadOptions options; |
diff --git a/src/fs/fs_download.c b/src/fs/fs_download.c index 06c4672a2..323be8805 100644 --- a/src/fs/fs_download.c +++ b/src/fs/fs_download.c | |||
@@ -25,6 +25,7 @@ | |||
25 | * TODO: | 25 | * TODO: |
26 | * - process replies | 26 | * - process replies |
27 | * - callback signaling | 27 | * - callback signaling |
28 | * - check if blocks exist already | ||
28 | * - location URI suppport (can wait) | 29 | * - location URI suppport (can wait) |
29 | * - persistence (can wait) | 30 | * - persistence (can wait) |
30 | */ | 31 | */ |
@@ -56,6 +57,7 @@ schedule_block_download (struct GNUNET_FS_DownloadContext *dc, | |||
56 | { | 57 | { |
57 | struct DownloadRequest *sm; | 58 | struct DownloadRequest *sm; |
58 | 59 | ||
60 | // FIXME: check if block exists on disk! | ||
59 | sm = GNUNET_malloc (sizeof (struct DownloadRequest)); | 61 | sm = GNUNET_malloc (sizeof (struct DownloadRequest)); |
60 | sm->chk = *chk; | 62 | sm->chk = *chk; |
61 | sm->offset = offset; | 63 | sm->offset = offset; |
@@ -198,7 +200,7 @@ transmit_download_request (void *cls, | |||
198 | sm->header.size = htons (sizeof (struct SearchMessage)); | 200 | sm->header.size = htons (sizeof (struct SearchMessage)); |
199 | sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH); | 201 | sm->header.type = htons (GNUNET_MESSAGE_TYPE_FS_START_SEARCH); |
200 | sm->anonymity_level = htonl (dc->anonymity); | 202 | sm->anonymity_level = htonl (dc->anonymity); |
201 | // FIXME: support 'loc' URIs (set sm->target) | 203 | sm->target = dc->target.hashPubKey; |
202 | sm->query = dc->pending->chk.query; | 204 | sm->query = dc->pending->chk.query; |
203 | dc->pending->is_pending = GNUNET_NO; | 205 | dc->pending->is_pending = GNUNET_NO; |
204 | dc->pending = dc->pending->next; | 206 | dc->pending = dc->pending->next; |
@@ -357,12 +359,36 @@ GNUNET_FS_file_download_start (struct GNUNET_FS_Handle *h, | |||
357 | dc->client = client; | 359 | dc->client = client; |
358 | dc->parent = parent; | 360 | dc->parent = parent; |
359 | dc->uri = GNUNET_FS_uri_dup (uri); | 361 | dc->uri = GNUNET_FS_uri_dup (uri); |
360 | dc->filename = (NULL == filename) ? NULL : GNUNET_strdup (filename); | 362 | if (NULL != filename) |
363 | { | ||
364 | dc->filename = GNUNET_strdup (filename); | ||
365 | dc->handle = GNUNET_DISK_file_open (filename, | ||
366 | GNUNET_DISK_OPEN_READWRITE | | ||
367 | GNUNET_DISK_OPEN_CREATE, | ||
368 | GNUNET_DISK_PERM_USER_READ | | ||
369 | GNUNET_DISK_PERM_USER_WRITE | | ||
370 | GNUNET_DISK_PERM_GROUP_READ | | ||
371 | GNUNET_DISK_PERM_OTHER_READ); | ||
372 | if (dc->handle == NULL) | ||
373 | { | ||
374 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
375 | _("Download failed: could not open file `%s': %s\n"), | ||
376 | dc->filename, | ||
377 | STRERROR (errno)); | ||
378 | GNUNET_FS_uri_destroy (dc->uri); | ||
379 | GNUNET_free (dc->filename); | ||
380 | GNUNET_CLIENT_disconnect (dc->client); | ||
381 | GNUNET_free (dc); | ||
382 | return NULL; | ||
383 | } | ||
384 | } | ||
385 | // FIXME: set "dc->target" for LOC uris! | ||
361 | dc->offset = offset; | 386 | dc->offset = offset; |
362 | dc->length = length; | 387 | dc->length = length; |
363 | dc->anonymity = anonymity; | 388 | dc->anonymity = anonymity; |
364 | dc->options = options; | 389 | dc->options = options; |
365 | dc->active = GNUNET_CONTAINER_multihashmap_create (1 + (length / DBLOCK_SIZE)); | 390 | dc->active = GNUNET_CONTAINER_multihashmap_create (1 + (length / DBLOCK_SIZE)); |
391 | // FIXME: calculate tree depth | ||
366 | // FIXME: make persistent | 392 | // FIXME: make persistent |
367 | schedule_block_download (dc, | 393 | schedule_block_download (dc, |
368 | &dc->uri->data.chk.chk, | 394 | &dc->uri->data.chk.chk, |
@@ -422,8 +448,20 @@ GNUNET_FS_file_download_stop (struct GNUNET_FS_DownloadContext *dc, | |||
422 | &free_entry, | 448 | &free_entry, |
423 | NULL); | 449 | NULL); |
424 | GNUNET_CONTAINER_multihashmap_destroy (dc->active); | 450 | GNUNET_CONTAINER_multihashmap_destroy (dc->active); |
451 | if (dc->filename != NULL) | ||
452 | { | ||
453 | GNUNET_DISK_file_close (dc->handle); | ||
454 | if ( (dc->completed != dc->length) && | ||
455 | (GNUNET_YES == do_delete) ) | ||
456 | { | ||
457 | if (0 != UNLINK (dc->filename)) | ||
458 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, | ||
459 | "unlink", | ||
460 | dc->filename); | ||
461 | } | ||
462 | GNUNET_free (dc->filename); | ||
463 | } | ||
425 | GNUNET_FS_uri_destroy (dc->uri); | 464 | GNUNET_FS_uri_destroy (dc->uri); |
426 | GNUNET_free_non_null (dc->filename); | ||
427 | GNUNET_free (dc); | 465 | GNUNET_free (dc); |
428 | } | 466 | } |
429 | 467 | ||
@@ -447,308 +485,6 @@ GNUNET_FS_file_download_stop (struct GNUNET_FS_DownloadContext *dc, | |||
447 | #if 0 | 485 | #if 0 |
448 | 486 | ||
449 | /** | 487 | /** |
450 | * Node-specific data (not shared, keep small!). 152 bytes. | ||
451 | * Nodes are kept in a doubly-linked list. | ||
452 | */ | ||
453 | struct Node | ||
454 | { | ||
455 | /** | ||
456 | * Pointer to shared data between all nodes (request manager, | ||
457 | * progress data, etc.). | ||
458 | */ | ||
459 | struct GNUNET_ECRS_DownloadContext *ctx; | ||
460 | |||
461 | /** | ||
462 | * Previous entry in DLL. | ||
463 | */ | ||
464 | struct Node *prev; | ||
465 | |||
466 | /** | ||
467 | * Next entry in DLL. | ||
468 | */ | ||
469 | struct Node *next; | ||
470 | |||
471 | /** | ||
472 | * What is the GNUNET_EC_ContentHashKey for this block? | ||
473 | */ | ||
474 | GNUNET_EC_ContentHashKey chk; | ||
475 | |||
476 | /** | ||
477 | * At what offset (on the respective level!) is this | ||
478 | * block? | ||
479 | */ | ||
480 | unsigned long long offset; | ||
481 | |||
482 | /** | ||
483 | * 0 for dblocks, >0 for iblocks. | ||
484 | */ | ||
485 | unsigned int level; | ||
486 | |||
487 | }; | ||
488 | |||
489 | /** | ||
490 | * @brief structure that keeps track of currently pending requests for | ||
491 | * a download | ||
492 | * | ||
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. | ||
496 | */ | ||
497 | struct GNUNET_ECRS_DownloadContext | ||
498 | { | ||
499 | |||
500 | /** | ||
501 | * Total number of bytes in the file. | ||
502 | */ | ||
503 | unsigned long long total; | ||
504 | |||
505 | /** | ||
506 | * Number of bytes already obtained | ||
507 | */ | ||
508 | unsigned long long completed; | ||
509 | |||
510 | /** | ||
511 | * Starting-offset in file (for partial download) | ||
512 | */ | ||
513 | unsigned long long offset; | ||
514 | |||
515 | /** | ||
516 | * Length of the download (starting at offset). | ||
517 | */ | ||
518 | unsigned long long length; | ||
519 | |||
520 | /** | ||
521 | * Time download was started. | ||
522 | */ | ||
523 | GNUNET_CronTime startTime; | ||
524 | |||
525 | /** | ||
526 | * Doubly linked list of all pending requests (head) | ||
527 | */ | ||
528 | struct Node *head; | ||
529 | |||
530 | /** | ||
531 | * Doubly linked list of all pending requests (tail) | ||
532 | */ | ||
533 | struct Node *tail; | ||
534 | |||
535 | /** | ||
536 | * FSLIB context for issuing requests. | ||
537 | */ | ||
538 | struct GNUNET_FS_SearchContext *sctx; | ||
539 | |||
540 | /** | ||
541 | * Context for error reporting. | ||
542 | */ | ||
543 | struct GNUNET_GE_Context *ectx; | ||
544 | |||
545 | /** | ||
546 | * Configuration information. | ||
547 | */ | ||
548 | struct GNUNET_GC_Configuration *cfg; | ||
549 | |||
550 | /** | ||
551 | * The file handle. | ||
552 | */ | ||
553 | int handle; | ||
554 | |||
555 | /** | ||
556 | * Do we exclusively own this sctx? | ||
557 | */ | ||
558 | int my_sctx; | ||
559 | |||
560 | /** | ||
561 | * The base-filename | ||
562 | */ | ||
563 | char *filename; | ||
564 | |||
565 | /** | ||
566 | * Main thread running the operation. | ||
567 | */ | ||
568 | struct GNUNET_ThreadHandle *main; | ||
569 | |||
570 | /** | ||
571 | * Function to call when we make progress. | ||
572 | */ | ||
573 | GNUNET_ECRS_DownloadProgressCallback dpcb; | ||
574 | |||
575 | /** | ||
576 | * Extra argument to dpcb. | ||
577 | */ | ||
578 | void *dpcbClosure; | ||
579 | |||
580 | /** | ||
581 | * Identity of the peer having the content, or all-zeros | ||
582 | * if we don't know of such a peer. | ||
583 | */ | ||
584 | GNUNET_PeerIdentity target; | ||
585 | |||
586 | /** | ||
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. | ||
591 | */ | ||
592 | int abortFlag; | ||
593 | |||
594 | /** | ||
595 | * Do we have a specific peer from which we download | ||
596 | * from? | ||
597 | */ | ||
598 | int have_target; | ||
599 | |||
600 | /** | ||
601 | * Desired anonymity level for the download. | ||
602 | */ | ||
603 | unsigned int anonymityLevel; | ||
604 | |||
605 | /** | ||
606 | * The depth of the file-tree. | ||
607 | */ | ||
608 | unsigned int treedepth; | ||
609 | |||
610 | }; | ||
611 | |||
612 | static int | ||
613 | content_receive_callback (const GNUNET_HashCode * query, | ||
614 | const GNUNET_DatastoreValue * reply, void *cls, | ||
615 | unsigned long long uid); | ||
616 | |||
617 | |||
618 | /** | ||
619 | * Close the files and free the associated resources. | ||
620 | * | ||
621 | * @param self reference to the download context | ||
622 | */ | ||
623 | static void | ||
624 | free_request_manager (struct GNUNET_ECRS_DownloadContext *rm) | ||
625 | { | ||
626 | struct Node *pos; | ||
627 | |||
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); | ||
632 | else | ||
633 | GNUNET_FS_suspend_search_context (rm->sctx); | ||
634 | while (rm->head != NULL) | ||
635 | { | ||
636 | pos = rm->head; | ||
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); | ||
640 | GNUNET_free (pos); | ||
641 | } | ||
642 | if (rm->my_sctx != GNUNET_YES) | ||
643 | GNUNET_FS_resume_search_context (rm->sctx); | ||
644 | GNUNET_GE_ASSERT (NULL, rm->tail == NULL); | ||
645 | if (rm->handle >= 0) | ||
646 | CLOSE (rm->handle); | ||
647 | if (rm->main != NULL) | ||
648 | GNUNET_thread_release_self (rm->main); | ||
649 | GNUNET_free_non_null (rm->filename); | ||
650 | rm->sctx = NULL; | ||
651 | GNUNET_free (rm); | ||
652 | } | ||
653 | |||
654 | /** | ||
655 | * Read method. | ||
656 | * | ||
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 | ||
663 | */ | ||
664 | static int | ||
665 | read_from_files (struct GNUNET_ECRS_DownloadContext *self, | ||
666 | unsigned int level, | ||
667 | unsigned long long pos, void *buf, unsigned int len) | ||
668 | { | ||
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); | ||
673 | } | ||
674 | |||
675 | /** | ||
676 | * Write method. | ||
677 | * | ||
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 | ||
684 | */ | ||
685 | static int | ||
686 | write_to_files (struct GNUNET_ECRS_DownloadContext *self, | ||
687 | unsigned int level, | ||
688 | unsigned long long pos, void *buf, unsigned int len) | ||
689 | { | ||
690 | int ret; | ||
691 | |||
692 | if (level > 0) | ||
693 | return len; /* lie -- no more temps */ | ||
694 | if (self->handle == -1) | ||
695 | return len; | ||
696 | LSEEK (self->handle, pos, SEEK_SET); | ||
697 | ret = WRITE (self->handle, buf, len); | ||
698 | if (ret != len) | ||
699 | GNUNET_GE_LOG_STRERROR_FILE (self->ectx, | ||
700 | GNUNET_GE_ERROR | GNUNET_GE_BULK | | ||
701 | GNUNET_GE_USER, "write", self->filename); | ||
702 | return ret; | ||
703 | } | ||
704 | |||
705 | /** | ||
706 | * Queue a request for execution. | ||
707 | * | ||
708 | * @param rm the request manager struct from createRequestManager | ||
709 | * @param node the node to call once a reply is received | ||
710 | */ | ||
711 | static void | ||
712 | add_request (struct Node *node) | ||
713 | { | ||
714 | struct GNUNET_ECRS_DownloadContext *rm = node->ctx; | ||
715 | |||
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, | ||
720 | &node->chk.query, | ||
721 | rm->anonymityLevel, | ||
722 | &content_receive_callback, node); | ||
723 | } | ||
724 | |||
725 | static void | ||
726 | signal_abort (struct GNUNET_ECRS_DownloadContext *rm, const char *msg) | ||
727 | { | ||
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); | ||
732 | } | ||
733 | |||
734 | /** | ||
735 | * Dequeue a request. | ||
736 | * | ||
737 | * @param self the request manager struct from createRequestManager | ||
738 | * @param node the block for which the request is canceled | ||
739 | */ | ||
740 | static void | ||
741 | delete_node (struct Node *node) | ||
742 | { | ||
743 | struct GNUNET_ECRS_DownloadContext *rm = node->ctx; | ||
744 | |||
745 | GNUNET_DLL_remove (rm->head, rm->tail, node); | ||
746 | GNUNET_free (node); | ||
747 | if (rm->head == NULL) | ||
748 | GNUNET_thread_stop_sleep (rm->main); | ||
749 | } | ||
750 | |||
751 | /** | ||
752 | * Compute how many bytes of data are stored in | 488 | * Compute how many bytes of data are stored in |
753 | * this node. | 489 | * this node. |
754 | */ | 490 | */ |
@@ -796,41 +532,6 @@ get_node_size (const struct Node *node) | |||
796 | } | 532 | } |
797 | 533 | ||
798 | /** | 534 | /** |
799 | * Notify client about progress. | ||
800 | */ | ||
801 | static void | ||
802 | notify_client_about_progress (const struct Node *node, | ||
803 | const char *data, unsigned int size) | ||
804 | { | ||
805 | struct GNUNET_ECRS_DownloadContext *rm = node->ctx; | ||
806 | GNUNET_CronTime eta; | ||
807 | |||
808 | if ((rm->abortFlag != GNUNET_NO) || (node->level != 0)) | ||
809 | return; | ||
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); | ||
820 | } | ||
821 | |||
822 | |||
823 | /** | ||
824 | * DOWNLOAD children of this GNUNET_EC_IBlock. | ||
825 | * | ||
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 | ||
829 | */ | ||
830 | static void iblock_download_children (const struct Node *node, | ||
831 | const char *data, unsigned int size); | ||
832 | |||
833 | /** | ||
834 | * Check if self block is already present on the drive. If the block | 535 | * 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 | 536 | * is a dblock and present, the ProgressModel is notified. If the |
836 | * block is present and it is an iblock, downloading the children is | 537 | * block is present and it is an iblock, downloading the children is |
@@ -934,403 +635,7 @@ iblock_download_children (const struct Node *node, | |||
934 | } | 635 | } |
935 | } | 636 | } |
936 | 637 | ||
937 | |||
938 | /** | ||
939 | * Decrypts a given data block | ||
940 | * | ||
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 | ||
946 | */ | ||
947 | static int | ||
948 | decrypt_content (const char *data, | ||
949 | unsigned int size, const GNUNET_HashCode * hashcode, | ||
950 | char *result) | ||
951 | { | ||
952 | GNUNET_AES_InitializationVector iv; | ||
953 | GNUNET_AES_SessionKey skey; | ||
954 | |||
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); | ||
958 | } | ||
959 | |||
960 | /** | ||
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). | ||
966 | * | ||
967 | * @param cls the node for which the reply is given, freed in | ||
968 | * the function! | ||
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 | ||
972 | */ | ||
973 | static int | ||
974 | content_receive_callback (const GNUNET_HashCode * query, | ||
975 | const GNUNET_DatastoreValue * reply, void *cls, | ||
976 | unsigned long long uid) | ||
977 | { | ||
978 | struct Node *node = cls; | ||
979 | struct GNUNET_ECRS_DownloadContext *rm = node->ctx; | ||
980 | struct GNUNET_GE_Context *ectx = rm->ectx; | ||
981 | GNUNET_HashCode hc; | ||
982 | unsigned int size; | ||
983 | char *data; | ||
984 | |||
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))) | ||
993 | { | ||
994 | GNUNET_GE_BREAK (ectx, 0); | ||
995 | return GNUNET_SYSERR; /* invalid size! */ | ||
996 | } | ||
997 | size -= sizeof (GNUNET_EC_DBlock); | ||
998 | data = GNUNET_malloc (size); | ||
999 | if (GNUNET_SYSERR == | ||
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))) | ||
1006 | { | ||
1007 | GNUNET_free (data); | ||
1008 | GNUNET_GE_BREAK (ectx, 0); | ||
1009 | signal_abort (rm, | ||
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; | ||
1014 | } | ||
1015 | if (size != write_to_files (rm, node->level, node->offset, data, size)) | ||
1016 | { | ||
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; | ||
1022 | } | ||
1023 | notify_client_about_progress (node, data, size); | ||
1024 | if (node->level > 0) | ||
1025 | iblock_download_children (node, data, size); | ||
1026 | GNUNET_free (data); | ||
1027 | /* request satisfied, stop requesting! */ | ||
1028 | delete_node (node); | ||
1029 | return GNUNET_OK; | ||
1030 | } | ||
1031 | |||
1032 | |||
1033 | /** | ||
1034 | * Helper function to sanitize filename | ||
1035 | * and create necessary directories. | ||
1036 | */ | ||
1037 | static char * | ||
1038 | get_real_download_filename (struct GNUNET_GE_Context *ectx, | ||
1039 | const char *filename) | ||
1040 | { | ||
1041 | struct stat buf; | ||
1042 | char *realFN; | ||
1043 | char *path; | ||
1044 | char *pos; | ||
1045 | |||
1046 | if ((filename[strlen (filename) - 1] == '/') || | ||
1047 | (filename[strlen (filename) - 1] == '\\')) | ||
1048 | { | ||
1049 | realFN = | ||
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); | ||
1054 | } | ||
1055 | else | ||
1056 | { | ||
1057 | realFN = GNUNET_strdup (filename); | ||
1058 | } | ||
1059 | path = GNUNET_malloc (strlen (realFN) * strlen (GNUNET_DIRECTORY_EXT) + 1); | ||
1060 | strcpy (path, realFN); | ||
1061 | pos = path; | ||
1062 | while (*pos != '\0') | ||
1063 | { | ||
1064 | if (*pos == DIR_SEPARATOR) | ||
1065 | { | ||
1066 | *pos = '\0'; | ||
1067 | if ((0 == STAT (path, &buf)) && (!S_ISDIR (buf.st_mode))) | ||
1068 | { | ||
1069 | *pos = DIR_SEPARATOR; | ||
1070 | memmove (pos + strlen (GNUNET_DIRECTORY_EXT), | ||
1071 | pos, strlen (pos)); | ||
1072 | memcpy (pos, | ||
1073 | GNUNET_DIRECTORY_EXT, strlen (GNUNET_DIRECTORY_EXT)); | ||
1074 | pos += strlen (GNUNET_DIRECTORY_EXT); | ||
1075 | } | ||
1076 | else | ||
1077 | { | ||
1078 | *pos = DIR_SEPARATOR; | ||
1079 | } | ||
1080 | } | ||
1081 | pos++; | ||
1082 | } | ||
1083 | GNUNET_free (realFN); | ||
1084 | return path; | ||
1085 | } | ||
1086 | |||
1087 | /* ***************** main method **************** */ | ||
1088 | |||
1089 | |||
1090 | /** | ||
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). | ||
1097 | * <p> | ||
1098 | * | ||
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. | ||
1102 | * | ||
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) | ||
1108 | */ | ||
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, | ||
1118 | int no_temporaries, | ||
1119 | GNUNET_ECRS_DownloadProgressCallback | ||
1120 | dpcb, void *dpcbClosure) | ||
1121 | { | ||
1122 | struct GNUNET_ECRS_DownloadContext *rm; | ||
1123 | struct stat buf; | ||
1124 | struct Node *top; | ||
1125 | int ret; | ||
1126 | |||
1127 | if ((!GNUNET_ECRS_uri_test_chk (uri)) && (!GNUNET_ECRS_uri_test_loc (uri))) | ||
1128 | { | ||
1129 | GNUNET_GE_BREAK (ectx, 0); | ||
1130 | return NULL; | ||
1131 | } | ||
1132 | rm = GNUNET_malloc (sizeof (struct GNUNET_ECRS_DownloadContext)); | ||
1133 | memset (rm, 0, sizeof (struct GNUNET_ECRS_DownloadContext)); | ||
1134 | if (sc == NULL) | ||
1135 | { | ||
1136 | rm->sctx = GNUNET_FS_create_search_context (ectx, cfg); | ||
1137 | if (rm->sctx == NULL) | ||
1138 | { | ||
1139 | GNUNET_free (rm); | ||
1140 | return NULL; | ||
1141 | } | ||
1142 | rm->my_sctx = GNUNET_YES; | ||
1143 | } | ||
1144 | else | ||
1145 | { | ||
1146 | rm->sctx = sc; | ||
1147 | rm->my_sctx = GNUNET_NO; | ||
1148 | } | ||
1149 | rm->ectx = ectx; | ||
1150 | rm->cfg = cfg; | ||
1151 | rm->startTime = GNUNET_get_time (); | ||
1152 | rm->anonymityLevel = anonymityLevel; | ||
1153 | rm->offset = offset; | ||
1154 | rm->length = length; | ||
1155 | rm->dpcb = dpcb; | ||
1156 | rm->dpcbClosure = dpcbClosure; | ||
1157 | rm->main = GNUNET_thread_get_self (); | ||
1158 | rm->total = GNUNET_ntohll (uri->data.fi.file_length); | ||
1159 | rm->filename = | ||
1160 | filename != NULL ? get_real_download_filename (ectx, filename) : NULL; | ||
1161 | |||
1162 | if ((rm->filename != NULL) && | ||
1163 | (GNUNET_SYSERR == | ||
1164 | GNUNET_disk_directory_create_for_file (ectx, rm->filename))) | ||
1165 | { | ||
1166 | free_request_manager (rm); | ||
1167 | return NULL; | ||
1168 | } | ||
1169 | if (0 == rm->total) | ||
1170 | { | ||
1171 | if (rm->filename != NULL) | ||
1172 | { | ||
1173 | ret = GNUNET_disk_file_open (ectx, | ||
1174 | rm->filename, | ||
1175 | O_CREAT | O_WRONLY | O_TRUNC, | ||
1176 | S_IRUSR | S_IWUSR); | ||
1177 | if (ret == -1) | ||
1178 | { | ||
1179 | free_request_manager (rm); | ||
1180 | return NULL; | ||
1181 | } | ||
1182 | CLOSE (ret); | ||
1183 | } | ||
1184 | dpcb (0, 0, rm->startTime, 0, NULL, 0, dpcbClosure); | ||
1185 | free_request_manager (rm); | ||
1186 | return NULL; | ||
1187 | } | ||
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))) | ||
1192 | { | ||
1193 | /* if exists and oversized, truncate */ | ||
1194 | if (truncate (rm->filename, rm->total) != 0) | ||
1195 | { | ||
1196 | GNUNET_GE_LOG_STRERROR_FILE (ectx, | ||
1197 | GNUNET_GE_ERROR | GNUNET_GE_ADMIN | | ||
1198 | GNUNET_GE_BULK, "truncate", | ||
1199 | rm->filename); | ||
1200 | free_request_manager (rm); | ||
1201 | return NULL; | ||
1202 | } | ||
1203 | } | ||
1204 | if (rm->filename != NULL) | ||
1205 | { | ||
1206 | rm->handle = GNUNET_disk_file_open (ectx, | ||
1207 | rm->filename, | ||
1208 | O_CREAT | O_RDWR, | ||
1209 | S_IRUSR | S_IWUSR); | ||
1210 | if (rm->handle < 0) | ||
1211 | { | ||
1212 | free_request_manager (rm); | ||
1213 | return NULL; | ||
1214 | } | ||
1215 | } | ||
1216 | else | ||
1217 | rm->handle = -1; | ||
1218 | if (GNUNET_ECRS_uri_test_loc (uri)) | ||
1219 | { | ||
1220 | GNUNET_hash (&uri->data.loc.peer, sizeof (GNUNET_RSA_PublicKey), | ||
1221 | &rm->target.hashPubKey); | ||
1222 | rm->have_target = GNUNET_YES; | ||
1223 | } | ||
1224 | top = GNUNET_malloc (sizeof (struct Node)); | ||
1225 | memset (top, 0, sizeof (struct Node)); | ||
1226 | top->ctx = rm; | ||
1227 | top->chk = uri->data.fi.chk; | ||
1228 | top->offset = 0; | ||
1229 | top->level = rm->treedepth; | ||
1230 | if (GNUNET_NO == check_node_present (top)) | ||
1231 | add_request (top); | ||
1232 | else | ||
1233 | GNUNET_free (top); | ||
1234 | return rm; | ||
1235 | } | ||
1236 | |||
1237 | int | ||
1238 | GNUNET_ECRS_file_download_partial_stop (struct GNUNET_ECRS_DownloadContext | ||
1239 | *rm) | ||
1240 | { | ||
1241 | int ret; | ||
1242 | |||
1243 | ret = rm->abortFlag; | ||
1244 | free_request_manager (rm); | ||
1245 | if (ret == GNUNET_NO) | ||
1246 | ret = GNUNET_OK; /* normal termination */ | ||
1247 | return ret; | ||
1248 | } | ||
1249 | |||
1250 | /** | ||
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). | ||
1257 | * <p> | ||
1258 | * | ||
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. | ||
1262 | * | ||
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) | ||
1268 | */ | ||
1269 | int | ||
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, | ||
1277 | int no_temporaries, | ||
1278 | GNUNET_ECRS_DownloadProgressCallback dpcb, | ||
1279 | void *dpcbClosure, | ||
1280 | GNUNET_ECRS_TestTerminate tt, | ||
1281 | void *ttClosure) | ||
1282 | { | ||
1283 | struct GNUNET_ECRS_DownloadContext *rm; | ||
1284 | int ret; | ||
1285 | |||
1286 | if (length == 0) | ||
1287 | return GNUNET_OK; | ||
1288 | rm = GNUNET_ECRS_file_download_partial_start (ectx, | ||
1289 | cfg, | ||
1290 | NULL, | ||
1291 | uri, | ||
1292 | filename, | ||
1293 | offset, | ||
1294 | length, | ||
1295 | anonymityLevel, | ||
1296 | no_temporaries, | ||
1297 | dpcb, dpcbClosure); | ||
1298 | if (rm == NULL) | ||
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); | ||
1305 | return ret; | ||
1306 | } | ||
1307 | |||
1308 | /** | ||
1309 | * Download a file (simplified API). | ||
1310 | * | ||
1311 | * @param uri the URI of the file (determines what to download) | ||
1312 | * @param filename where to store the file | ||
1313 | */ | ||
1314 | int | ||
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, | ||
1322 | void *ttClosure) | ||
1323 | { | ||
1324 | return GNUNET_ECRS_file_download_partial (ectx, | ||
1325 | cfg, | ||
1326 | uri, | ||
1327 | filename, | ||
1328 | 0, | ||
1329 | GNUNET_ECRS_uri_get_file_size | ||
1330 | (uri), anonymityLevel, GNUNET_NO, | ||
1331 | dpcb, dpcbClosure, tt, ttClosure); | ||
1332 | } | ||
1333 | |||
1334 | #endif | 638 | #endif |
1335 | 639 | ||
640 | |||
1336 | /* end of fs_download.c */ | 641 | /* end of fs_download.c */ |