aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2009-09-05 20:57:31 +0000
committerChristian Grothoff <christian@grothoff.org>2009-09-05 20:57:31 +0000
commit9e645511bbd3775bd5c872eb5e58439676a59199 (patch)
treef2219c6ed90fdecbd8f36ec8f7922293691cda01 /src
parent0b90310c9a26ffa0e195759b7a5d5c07fa33f642 (diff)
downloadgnunet-9e645511bbd3775bd5c872eb5e58439676a59199.tar.gz
gnunet-9e645511bbd3775bd5c872eb5e58439676a59199.zip
towards having download
Diffstat (limited to 'src')
-rw-r--r--src/fs/fs.h28
-rw-r--r--src/fs/fs_download.c779
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 */
453struct 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 */
497struct 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
612static int
613content_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 */
623static void
624free_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 */
664static int
665read_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 */
685static int
686write_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 */
711static void
712add_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
725static void
726signal_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 */
740static void
741delete_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 */
801static void
802notify_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 */
830static 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 */
947static int
948decrypt_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 */
973static int
974content_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 */
1037static char *
1038get_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 */
1109struct GNUNET_ECRS_DownloadContext *
1110GNUNET_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
1237int
1238GNUNET_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 */
1269int
1270GNUNET_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 */
1314int
1315GNUNET_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 */